ff-ds-ui 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/.husky/pre-commit +2 -0
  2. package/.prettierrc +7 -0
  3. package/.storybook/main.ts +14 -0
  4. package/.storybook/preview.ts +21 -0
  5. package/.storybook/vitest.setup.ts +7 -0
  6. package/LICENSE +21 -21
  7. package/README.md +1 -73
  8. package/eslint.config.js +29 -23
  9. package/package.json +57 -36
  10. package/src/components/Avatar/Avatar.scss +90 -0
  11. package/src/components/Avatar/Avatar.tsx +40 -0
  12. package/src/components/Avatar/__stories__/Avatar.stories.tsx +21 -0
  13. package/src/components/Avatar/__test__/utils.test.ts +76 -0
  14. package/src/components/Avatar/index.ts +1 -0
  15. package/src/components/Avatar/utils.tsx +26 -0
  16. package/src/components/Breadcrumbs/Breadcrumbs.tsx +40 -0
  17. package/src/components/Breadcrumbs/__stories__/Breadcrumbs.stories.tsx +20 -0
  18. package/src/components/Breadcrumbs/index.ts +1 -0
  19. package/src/components/Button/Button.scss +245 -0
  20. package/src/components/Button/Button.tsx +34 -0
  21. package/src/components/Button/__stories__/Button.stories.tsx +168 -0
  22. package/src/components/Button/const.ts +26 -0
  23. package/src/components/Button/index.ts +1 -0
  24. package/src/components/Card/Card.scss +13 -0
  25. package/src/components/Card/Card.tsx +12 -0
  26. package/src/components/Card/__stories__/Card.stories.tsx +18 -0
  27. package/src/components/Card/index.ts +1 -0
  28. package/src/components/Checkbox/Checkbox.scss +28 -0
  29. package/src/components/Checkbox/Checkbox.tsx +23 -0
  30. package/src/components/Checkbox/__stories__/Checkbox.stories.tsx +21 -0
  31. package/src/components/Checkbox/index.ts +1 -0
  32. package/src/components/Divider/Divider.scss +62 -0
  33. package/src/components/Divider/Divider.tsx +21 -0
  34. package/src/components/Divider/__stories__/Divider.stories.tsx +68 -0
  35. package/src/components/Divider/index.ts +1 -0
  36. package/src/components/Flex/Flex.scss +94 -0
  37. package/src/components/Flex/Flex.tsx +41 -0
  38. package/src/components/Flex/__stories__/Flex.stories.tsx +27 -0
  39. package/src/components/Flex/index.ts +1 -0
  40. package/src/components/Label/Label.scss +53 -0
  41. package/src/components/Label/Label.tsx +27 -0
  42. package/src/components/Label/__stories__/Label.stories.tsx +26 -0
  43. package/src/components/Label/index.ts +1 -0
  44. package/src/components/Link/Link.scss +13 -0
  45. package/src/components/Link/Link.tsx +18 -0
  46. package/src/components/Link/__stories__/Link.stories.tsx +20 -0
  47. package/src/components/Link/index.ts +1 -0
  48. package/src/components/Loader/Loader.scss +56 -0
  49. package/src/components/Loader/Loader.tsx +33 -0
  50. package/src/components/Loader/__stories__/Loader.stories.tsx +18 -0
  51. package/src/components/Loader/const.ts +7 -0
  52. package/src/components/Loader/index.ts +1 -0
  53. package/src/components/Radio/Radio.scss +28 -0
  54. package/src/components/Radio/Radio.tsx +24 -0
  55. package/src/components/Radio/__stories__/Radio.stories.tsx +21 -0
  56. package/src/components/Radio/index.ts +1 -0
  57. package/src/components/Skeleton/Skeleton.scss +73 -0
  58. package/src/components/Skeleton/Skeleton.tsx +16 -0
  59. package/src/components/Skeleton/__stories__/Skeleton.stories.tsx +16 -0
  60. package/src/components/Skeleton/index.ts +1 -0
  61. package/src/components/Tabs/Tabs.scss +25 -0
  62. package/src/components/Tabs/Tabs.tsx +39 -0
  63. package/src/components/Tabs/__stories__/Tabs.stories.tsx +19 -0
  64. package/src/components/Tabs/index.ts +1 -0
  65. package/src/components/Text/Text.scss +30 -0
  66. package/src/components/Text/Text.tsx +68 -0
  67. package/src/components/Text/_stories_/Text.stories.tsx +95 -0
  68. package/src/components/Text/index.ts +1 -0
  69. package/src/components/TextArea/TextArea.scss +45 -0
  70. package/src/components/TextArea/TextArea.tsx +39 -0
  71. package/src/components/TextArea/__stories__/TextArea.stories.tsx +20 -0
  72. package/src/components/TextArea/index.ts +1 -0
  73. package/src/components/TextInput/TextInput.scss +92 -0
  74. package/src/components/TextInput/TextInput.tsx +43 -0
  75. package/src/components/TextInput/__stories__/TextInput.stories.tsx +40 -0
  76. package/src/components/TextInput/index.ts +1 -0
  77. package/src/components/User/User.scss +7 -0
  78. package/src/components/User/User.tsx +23 -0
  79. package/src/components/User/__stories__/User.stories.tsx +23 -0
  80. package/src/components/User/index.ts +1 -0
  81. package/src/components/index.ts +17 -0
  82. package/src/index.ts +1 -1
  83. package/src/stories/modules.mdx +14 -0
  84. package/src/styles/fonts.scss +1 -0
  85. package/src/styles/mixins/_index.scss +18 -0
  86. package/src/styles/mixins/button.scss +29 -0
  87. package/src/styles/mixins/typography.scss +107 -0
  88. package/src/styles/styles.scss +3 -0
  89. package/src/styles/variables/_index.scss +25 -0
  90. package/src/styles/variables/colors.scss +45 -0
  91. package/src/styles/variables/padding.scss +7 -0
  92. package/src/styles/variables/radius.scss +8 -0
  93. package/src/styles/variables/spacing.scss +15 -0
  94. package/src/styles/variables/typography.scss +66 -0
  95. package/src/utils/bem/__tests__/bem.test.ts +25 -0
  96. package/src/utils/bem/bem.ts +24 -0
  97. package/src/utils/index.ts +1 -0
  98. package/tsconfig.app.json +31 -28
  99. package/tsconfig.build.json +20 -0
  100. package/tsconfig.json +4 -7
  101. package/tsconfig.node.json +26 -26
  102. package/vite.config.ts +83 -32
  103. package/vitest.shims.d.ts +1 -0
  104. package/index.html +0 -13
  105. package/public/vite.svg +0 -1
  106. package/src/components/Button.tsx +0 -9
@@ -0,0 +1,16 @@
1
+ import React from 'react'
2
+ import './Skeleton.scss'
3
+ import { bemBlock } from '@/utils'
4
+
5
+ interface SkeletonProps {
6
+ view?: 'circle' | 'square'
7
+ fullWidth?: boolean
8
+ width?: number
9
+ height?: number
10
+ }
11
+
12
+ export const Skeleton: React.FC<SkeletonProps> = ({ width, view, height, fullWidth }) => {
13
+ const b = bemBlock('skeleton')
14
+
15
+ return <div style={{ height, width }} className={b({ fullWidth, view })} />
16
+ }
@@ -0,0 +1,16 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { Skeleton } from '../Skeleton'
3
+
4
+ const meta = {
5
+ title: 'Components/Skeleton',
6
+ component: Skeleton,
7
+ tags: ['autodocs'],
8
+ } satisfies Meta<typeof Skeleton>
9
+
10
+ export default meta
11
+
12
+ type Story = StoryObj<typeof meta>
13
+
14
+ export const Skeleton_default: Story = {
15
+ args: {},
16
+ }
@@ -0,0 +1 @@
1
+ export * from './Skeleton'
@@ -0,0 +1,25 @@
1
+ @use '../../styles/styles.scss' as MainStyles;
2
+
3
+ $tabBlock: '.#{MainStyles.$ns}tabs';
4
+ $tabElemBlock: '.#{MainStyles.$ns}tabsElem';
5
+
6
+ #{$tabBlock} {
7
+ display: flex;
8
+ gap: 20px;
9
+ border-bottom: 2px solid lightgrey;
10
+ padding-bottom: 8px;
11
+ }
12
+
13
+ #{$tabElemBlock} {
14
+ position: relative;
15
+ cursor: pointer;
16
+ &_isActive::after {
17
+ content: '';
18
+ position: absolute;
19
+ left: 0;
20
+ right: 0;
21
+ bottom: -10px;
22
+ height: 2px;
23
+ background-color: rgb(45, 45, 227);
24
+ }
25
+ }
@@ -0,0 +1,39 @@
1
+ import React, { useState } from 'react'
2
+ import './Tabs.scss'
3
+ import { bemBlock } from '@/utils'
4
+ import { Text } from '../Text'
5
+
6
+ interface TabsProps {
7
+ tabs: string[]
8
+ initialActiveTab?: number
9
+ onTabClick?: (value: string) => void
10
+ }
11
+
12
+ export const Tabs: React.FC<TabsProps> = ({ tabs, onTabClick, initialActiveTab }) => {
13
+ const [activeTab, setActiveTab] = useState(initialActiveTab ? tabs[initialActiveTab] : tabs[0])
14
+
15
+ const tabsB = bemBlock('tabs')
16
+ const tabElemB = bemBlock('tabsElem')
17
+
18
+ const tabClickHandler = (el: string) => {
19
+ if (onTabClick) onTabClick(el)
20
+ setActiveTab(el)
21
+ }
22
+
23
+ return (
24
+ <div className={tabsB()}>
25
+ {tabs.map((el) => {
26
+ return (
27
+ <Text
28
+ variant="caption2"
29
+ color="secondary"
30
+ className={tabElemB({ isActive: el === activeTab })}
31
+ onClick={() => tabClickHandler(el)}
32
+ >
33
+ {el}
34
+ </Text>
35
+ )
36
+ })}
37
+ </div>
38
+ )
39
+ }
@@ -0,0 +1,19 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { Tabs } from '../Tabs'
3
+
4
+ const meta = {
5
+ title: 'Components/Tabs',
6
+ component: Tabs,
7
+ tags: ['autodocs'],
8
+ } satisfies Meta<typeof Tabs>
9
+
10
+ export default meta
11
+
12
+ type Story = StoryObj<typeof meta>
13
+
14
+ export const Tabs_default: Story = {
15
+ args: {
16
+ tabs: ['left', 'center', 'right'],
17
+ onTabClick: (tabEl: string) => console.log(tabEl),
18
+ },
19
+ }
@@ -0,0 +1 @@
1
+ export * from './Tabs'
@@ -0,0 +1,30 @@
1
+ @use '../../styles/variables/index' as Variables;
2
+ @use '../../styles/styles.scss' as MainStyles;
3
+ @use '../../styles/mixins/index' as Mixins;
4
+
5
+ $block: '.#{MainStyles.$ns}text';
6
+
7
+ #{$block} {
8
+ @include Variables.variables-typography;
9
+
10
+ display: block;
11
+ color: black;
12
+ font-family: var(--FF-DS-font-family-sans);
13
+
14
+ &_ellipsis {
15
+ overflow: hidden;
16
+ white-space: nowrap;
17
+ text-overflow: ellipsis;
18
+ -webkit-box-orient: vertical;
19
+ }
20
+
21
+ /*
22
+ VARIANTS
23
+ */
24
+ @include Mixins.mixins-typography-variants;
25
+
26
+ /*
27
+ ColorS
28
+ */
29
+ @include Mixins.mixins-typography-colors;
30
+ }
@@ -0,0 +1,68 @@
1
+ import React from 'react'
2
+ import './Text.scss'
3
+ import { bemBlock } from '@/utils'
4
+
5
+ export type TextVariantType =
6
+ | 'body1'
7
+ | 'body2'
8
+ | 'body3'
9
+ | 'caption1'
10
+ | 'caption2'
11
+ | 'header1'
12
+ | 'header2'
13
+ | 'subheader1'
14
+ | 'subheader2'
15
+ | 'subheader3'
16
+ | 'display1'
17
+ | 'display2'
18
+ | 'display3'
19
+ | 'display4'
20
+
21
+ export type TextColorType = 'primary' | 'warning' | 'secondary' | 'danger' | 'default' | 'success'
22
+
23
+ export interface TextProps extends React.HTMLAttributes<HTMLSpanElement> {
24
+ variant?: TextVariantType
25
+ color?: TextColorType
26
+ ellipsis?: boolean
27
+ ellipsisLines?: number
28
+ className?: string
29
+ }
30
+
31
+ export const Text = React.forwardRef<HTMLSpanElement, TextProps>(
32
+ (
33
+ {
34
+ children,
35
+ variant = 'body3',
36
+ color,
37
+ ellipsis,
38
+ ellipsisLines,
39
+ className,
40
+ style: commonStyle,
41
+ ...props
42
+ },
43
+ ref
44
+ ) => {
45
+ const b = bemBlock('text')
46
+
47
+ const style = {
48
+ ...commonStyle,
49
+ }
50
+
51
+ if (typeof ellipsisLines === 'number') {
52
+ style.WebkitLineClamp = ellipsisLines
53
+ style.whiteSpace = 'normal'
54
+ style.display = '-webkit-box'
55
+ }
56
+
57
+ return (
58
+ <span
59
+ style={style}
60
+ className={b({ variant, color, ellipsis, ellipsisLines }, className)}
61
+ ref={ref}
62
+ {...props}
63
+ >
64
+ {children}
65
+ </span>
66
+ )
67
+ }
68
+ )
@@ -0,0 +1,95 @@
1
+ import { Flex } from '@/components/Flex'
2
+ import { Text } from '../Text'
3
+ import type { Meta, StoryObj } from '@storybook/react'
4
+
5
+ const meta = {
6
+ title: 'Components/Text',
7
+ component: Text,
8
+ tags: ['autodocs'],
9
+ } satisfies Meta<typeof Text>
10
+
11
+ export default meta
12
+
13
+ type Story = StoryObj<typeof meta>
14
+
15
+ export const BodySection: Story = {
16
+ args: {},
17
+ render: (props) => (
18
+ <Flex gap="m">
19
+ <Text variant="body1" {...props}>
20
+ body1
21
+ </Text>
22
+ <Text variant="body2" {...props}>
23
+ body2
24
+ </Text>
25
+ <Text variant="body3" {...props}>
26
+ body3
27
+ </Text>
28
+ </Flex>
29
+ ),
30
+ }
31
+
32
+ export const CaptionSection: Story = {
33
+ args: {},
34
+ render: (props) => (
35
+ <Flex gap="m">
36
+ <Text variant="caption1" {...props}>
37
+ Caption1
38
+ </Text>
39
+ <Text variant="caption2" {...props}>
40
+ Caption2
41
+ </Text>
42
+ </Flex>
43
+ ),
44
+ }
45
+
46
+ export const HeaderSection: Story = {
47
+ args: {},
48
+ render: (props) => (
49
+ <Flex gap="m">
50
+ <Text variant="header1" {...props}>
51
+ header1
52
+ </Text>
53
+ <Text variant="header2" {...props}>
54
+ header2
55
+ </Text>
56
+ </Flex>
57
+ ),
58
+ }
59
+
60
+ export const SubHeaderSection: Story = {
61
+ args: {},
62
+ render: (props) => (
63
+ <Flex gap="m">
64
+ <Text variant="subheader1" {...props}>
65
+ subheader1
66
+ </Text>
67
+ <Text variant="subheader2" {...props}>
68
+ subheader2
69
+ </Text>
70
+ <Text variant="subheader3" {...props}>
71
+ subheader3
72
+ </Text>
73
+ </Flex>
74
+ ),
75
+ }
76
+
77
+ export const DisplaySection: Story = {
78
+ args: {},
79
+ render: (props) => (
80
+ <Flex gap="m">
81
+ <Text variant="display1" {...props}>
82
+ display1
83
+ </Text>
84
+ <Text variant="display2" {...props}>
85
+ display2
86
+ </Text>
87
+ <Text variant="display3" {...props}>
88
+ display3
89
+ </Text>
90
+ <Text variant="display4" {...props}>
91
+ display4
92
+ </Text>
93
+ </Flex>
94
+ ),
95
+ }
@@ -0,0 +1 @@
1
+ export * from './Text'
@@ -0,0 +1,45 @@
1
+ @use '../../styles/variables/index' as Variables;
2
+ @use '../../styles/styles.scss' as MainStyles;
3
+ @use '../../styles/mixins/index' as Mixins;
4
+
5
+ $block: '.#{MainStyles.$ns}textarea';
6
+
7
+ #{$block} {
8
+ @include Variables.variables-padding;
9
+ @include Variables.variables-radius;
10
+ @include Variables.variables-typography;
11
+ @include Variables.variables-colors;
12
+
13
+ font-family: var(--FF-DS-text-body-font-family);
14
+ font-weight: var(--FF-DS-text-body-font-weight);
15
+ font-size: var(--FF-DS-text-body-1-font-size);
16
+ line-height: var(--FF-DS-text-body-1-line-height);
17
+ border: 2px solid var(--FF-DS-color-secondary-border);
18
+ box-sizing: border-box;
19
+ padding: 10px;
20
+ background: transparent;
21
+ border-radius: 8px;
22
+ -webkit-border-radius: 8px;
23
+ -moz-border-radius: 8px;
24
+ -ms-border-radius: 8px;
25
+ -o-border-radius: 8px;
26
+ outline: none;
27
+
28
+ &:disabled {
29
+ opacity: background 0.5;
30
+ }
31
+
32
+ &_hasError {
33
+ border: 2px solid var(--FF-DS-color-error-border);
34
+
35
+ &:hover {
36
+ border: 2px solid var(--FF-DS-color-error-hover);
37
+ }
38
+ &:focus {
39
+ border: 2px solid var(--FF-DS-color-error-base);
40
+ }
41
+ &::placeholder {
42
+ color: var(--FF-DS-color-error-base);
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,39 @@
1
+ import React from 'react'
2
+ import './TextArea.scss'
3
+ import { bemBlock } from '@/utils'
4
+ import { Text } from '../Text/Text'
5
+ import { Flex } from '../Flex'
6
+
7
+ interface TextAreaProps extends React.HTMLAttributes<HTMLTextAreaElement> {
8
+ placeholder?: string
9
+ label?: string
10
+ disabled?: boolean
11
+ hasError?: boolean
12
+ errorMessage?: string
13
+ }
14
+
15
+ export const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
16
+ ({ placeholder, label, disabled, hasError, errorMessage, ...props }, ref) => {
17
+ const b = bemBlock('textarea')
18
+
19
+ return (
20
+ <Flex direction="col">
21
+ {/* //TODO: change to htmlFor="textarea" */}
22
+ <Text variant="caption2">{label}</Text>
23
+ <textarea
24
+ id={'textarea'}
25
+ className={b({ hasError })}
26
+ disabled={disabled}
27
+ placeholder={placeholder}
28
+ ref={ref}
29
+ {...props}
30
+ />
31
+ {hasError && (
32
+ <Text variant="body1" color="danger">
33
+ {errorMessage}
34
+ </Text>
35
+ )}
36
+ </Flex>
37
+ )
38
+ }
39
+ )
@@ -0,0 +1,20 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { TextArea } from '../TextArea'
3
+
4
+ const meta = {
5
+ title: 'Components/TextArea',
6
+ component: TextArea,
7
+ tags: ['autodocs'],
8
+ } satisfies Meta<typeof TextArea>
9
+
10
+ export default meta
11
+
12
+ type Story = StoryObj<typeof meta>
13
+
14
+ export const TextArea_default: Story = {
15
+ args: {
16
+ label: 'label',
17
+ hasError: true,
18
+ errorMessage: 'error msg',
19
+ },
20
+ }
@@ -0,0 +1 @@
1
+ export * from './TextArea'
@@ -0,0 +1,92 @@
1
+ @use '../../styles/styles.scss' as MainStyles;
2
+ @use '../../styles/variables' as Variables;
3
+ @use '../../styles/mixins' as Mixins;
4
+
5
+ $inputBlock: '.#{MainStyles.$ns}textInput';
6
+ $labelBlock: '.#{MainStyles.$ns}label';
7
+ $labelErrorBlock: '.#{MainStyles.$ns}errorLabel';
8
+
9
+ #{$inputBlock} {
10
+ @include Variables.variables-padding;
11
+ @include Variables.variables-radius;
12
+ @include Variables.variables-typography;
13
+ @include Variables.variables-colors;
14
+
15
+ font-family: var(--FF-DS-text-body-font-family);
16
+ font-weight: var(--FF-DS-text-body-font-weight);
17
+ font-size: var(--FF-DS-text-body-1-font-size);
18
+ line-height: var(--FF-DS-text-body-1-line-height);
19
+ border: 2px solid var(--FF-DS-color-secondary-border);
20
+ box-sizing: border-box;
21
+ background: transparent;
22
+ border-radius: 8px;
23
+ -webkit-border-radius: 8px;
24
+ -moz-border-radius: 8px;
25
+ -ms-border-radius: 8px;
26
+ -o-border-radius: 8px;
27
+ outline: none;
28
+
29
+ &:disabled {
30
+ opacity: background 0.5;
31
+ }
32
+
33
+ &_hasError {
34
+ border: 2px solid var(--FF-DS-color-error-border);
35
+ color: var(--FF-DS-color-error-base);
36
+
37
+ &:hover {
38
+ border: 2px solid var(--FF-DS-color-error-hover);
39
+ }
40
+ &:focus {
41
+ border: 2px solid var(--FF-DS-color-error-base);
42
+ }
43
+ &::placeholder {
44
+ color: var(--FF-DS-color-error-base);
45
+ }
46
+ }
47
+
48
+ &_size {
49
+ &_xs {
50
+ height: 20px;
51
+ border-radius: var(--FF-DS-border-radius-xs);
52
+ padding: var(--FF-DS-padding-xs);
53
+ }
54
+ &_s {
55
+ height: 24px;
56
+ border-radius: var(--FF-DS-border-radius-s);
57
+ padding: var(--FF-DS-padding-s);
58
+ }
59
+ &_m {
60
+ height: 28px;
61
+ border-radius: var(--FF-DS-border-radius-m);
62
+ padding: var(--FF-DS-padding-m);
63
+ }
64
+ &_l {
65
+ height: 36px;
66
+ border-radius: var(--FF-DS-border-radius-l);
67
+ padding: var(--FF-DS-padding-l);
68
+ }
69
+ &_xl {
70
+ height: 44px;
71
+ border-radius: var(--FF-DS-border-radius-xl);
72
+ padding: var(--FF-DS-padding-xl);
73
+ }
74
+ }
75
+ }
76
+
77
+ #{$labelBlock} {
78
+ border: none;
79
+ cursor: pointer;
80
+ padding: 0;
81
+ font-family: var(--FF-DS-font-family-sans);
82
+ font-size: var(--FF-DS-text-caption-2-font-size);
83
+ font-weight: var(--FF-DS-text-caption-2-line-height);
84
+ }
85
+
86
+ #{$labelErrorBlock} {
87
+ @include Variables.variables-colors;
88
+
89
+ &_hasError {
90
+ color: var(--FF-DS-color-error-base);
91
+ }
92
+ }
@@ -0,0 +1,43 @@
1
+ import React from 'react'
2
+ import './TextInput.scss'
3
+ import { bemBlock } from '@/utils'
4
+ import { Flex } from '@/components/Flex'
5
+ import { Text } from '../Text/Text'
6
+
7
+ interface TextInputProps extends React.HTMLAttributes<HTMLInputElement> {
8
+ size?: 's' | 'm' | 'l' | 'xl'
9
+ label?: string
10
+ placeholder?: string
11
+ disabled?: boolean
12
+ hasClearBtn?: boolean
13
+ // Error state
14
+ hasError?: boolean
15
+ errorMessage?: string
16
+ }
17
+
18
+ export const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>(
19
+ ({ size, label, placeholder, errorMessage, hasError, hasClearBtn, ...props }, ref) => {
20
+ const inputBlock = bemBlock('textInput')
21
+
22
+ return (
23
+ <Flex direction="col" alignItems="start">
24
+ {/* TODO: add htmlFor="input" as="label" */}
25
+ <Text variant="body1">{label}</Text>
26
+ <input
27
+ id="input"
28
+ className={inputBlock({ size, hasError })}
29
+ ref={ref}
30
+ placeholder={placeholder}
31
+ {...props}
32
+ />
33
+ {/* TODO: add Button icon, add clear functionality */}
34
+ {hasClearBtn && <></>}
35
+ {hasError && (
36
+ <Text variant="body1" color="danger">
37
+ {errorMessage}
38
+ </Text>
39
+ )}
40
+ </Flex>
41
+ )
42
+ }
43
+ )
@@ -0,0 +1,40 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { TextInput } from '../TextInput'
3
+
4
+ const meta = {
5
+ title: 'Components/TextInput',
6
+ component: TextInput,
7
+ tags: ['autodocs'],
8
+ argTypes: {},
9
+ } satisfies Meta<typeof TextInput>
10
+
11
+ export default meta
12
+
13
+ type Story = StoryObj<typeof meta>
14
+
15
+ export const BaseInput: Story = {
16
+ args: {
17
+ label: 'Input label',
18
+ size: 'xl',
19
+ placeholder: 'Placeholder...',
20
+ },
21
+ }
22
+
23
+ export const Error: Story = {
24
+ args: {
25
+ label: 'Input with error',
26
+ size: 'xl',
27
+ placeholder: 'Placeholder...',
28
+ hasError: true,
29
+ errorMessage: 'Invalid data',
30
+ },
31
+ }
32
+
33
+ export const Disabled: Story = {
34
+ args: {
35
+ label: 'Disabled input',
36
+ placeholder: 'Type here...',
37
+ size: 'xl',
38
+ disabled: true,
39
+ },
40
+ }
@@ -0,0 +1 @@
1
+ export * from './TextInput'
@@ -0,0 +1,7 @@
1
+ @use '../../styles/styles.scss' as MainStyles;
2
+
3
+ $block: '.#{MainStyles.$ns}user';
4
+
5
+ #{$block} {
6
+ display: flex;
7
+ }
@@ -0,0 +1,23 @@
1
+ import React from 'react'
2
+ import './User.scss'
3
+ import { Avatar, type AvatarProps } from '../Avatar'
4
+ import { Flex, Text } from '@/components'
5
+
6
+ interface UserProps {
7
+ avatar?: AvatarProps
8
+ size?: 's' | 'm' | 'l' | 'xl'
9
+ name?: string
10
+ description?: string
11
+ }
12
+
13
+ export const User: React.FC<UserProps> = ({ avatar, size, name, description }) => {
14
+ return (
15
+ <Flex direction="row" gap={'s'}>
16
+ <Avatar text={name} size={size} {...avatar} />
17
+ <Flex direction="col" justifyContent="center">
18
+ <Text variant="body1">{name}</Text>
19
+ {size !== 's' && size !== 'm' && <Text variant="caption1">{description}</Text>}
20
+ </Flex>
21
+ </Flex>
22
+ )
23
+ }
@@ -0,0 +1,23 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { User } from '../User'
3
+
4
+ const meta = {
5
+ title: 'Components/User',
6
+ component: User,
7
+ tags: ['autodocs'],
8
+ } satisfies Meta<typeof User>
9
+
10
+ export default meta
11
+
12
+ type Story = StoryObj<typeof meta>
13
+
14
+ export const User_default: Story = {
15
+ args: {
16
+ avatar: {
17
+ text: 'Author Comp',
18
+ },
19
+ name: 'David Jonson',
20
+ description: 'Project manager',
21
+ size: 'xl',
22
+ },
23
+ }
@@ -0,0 +1 @@
1
+ export * from './User'