@channel.io/bezier-react 4.0.0-next.3 → 4.0.0-next.4
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.
- package/dist/cjs/styles.css +1 -1
- package/dist/cjs/types/props-helpers.js +8 -0
- package/dist/cjs/types/props-helpers.js.map +1 -1
- package/dist/cjs/v3/BaseStack/BaseStack.js +46 -0
- package/dist/cjs/v3/BaseStack/BaseStack.js.map +1 -0
- package/dist/cjs/v3/BaseStack/BaseStack.module.scss.js +8 -0
- package/dist/cjs/v3/BaseStack/BaseStack.module.scss.js.map +1 -0
- package/dist/cjs/v3/Box/Box.js +56 -0
- package/dist/cjs/v3/Box/Box.js.map +1 -0
- package/dist/cjs/v3/Box/Box.module.scss.js +8 -0
- package/dist/cjs/v3/Box/Box.module.scss.js.map +1 -0
- package/dist/cjs/v3/Divider/Divider.js +32 -0
- package/dist/cjs/v3/Divider/Divider.js.map +1 -0
- package/dist/cjs/v3/Divider/Divider.module.scss.js +8 -0
- package/dist/cjs/v3/Divider/Divider.module.scss.js.map +1 -0
- package/dist/cjs/v3/HStack/HStack.js +19 -0
- package/dist/cjs/v3/HStack/HStack.js.map +1 -0
- package/dist/cjs/v3/Icon/Icon.js +51 -0
- package/dist/cjs/v3/Icon/Icon.js.map +1 -0
- package/dist/cjs/v3/Icon/Icon.module.scss.js +8 -0
- package/dist/cjs/v3/Icon/Icon.module.scss.js.map +1 -0
- package/dist/cjs/v3/SmoothCornersBox/SmoothCornersBox.js +53 -0
- package/dist/cjs/v3/SmoothCornersBox/SmoothCornersBox.js.map +1 -0
- package/dist/cjs/v3/SmoothCornersBox/SmoothCornersBox.module.scss.js +8 -0
- package/dist/cjs/v3/SmoothCornersBox/SmoothCornersBox.module.scss.js.map +1 -0
- package/dist/cjs/v3/Spinner/Spinner.js +45 -0
- package/dist/cjs/v3/Spinner/Spinner.js.map +1 -0
- package/dist/cjs/v3/Spinner/Spinner.module.scss.js +8 -0
- package/dist/cjs/v3/Spinner/Spinner.module.scss.js.map +1 -0
- package/dist/cjs/v3/Text/Text.js +60 -0
- package/dist/cjs/v3/Text/Text.js.map +1 -0
- package/dist/cjs/v3/Text/Text.module.scss.js +8 -0
- package/dist/cjs/v3/Text/Text.module.scss.js.map +1 -0
- package/dist/cjs/v3/VStack/VStack.js +19 -0
- package/dist/cjs/v3/VStack/VStack.js.map +1 -0
- package/dist/cjs/v3/index.js +22 -0
- package/dist/cjs/v3/index.js.map +1 -0
- package/dist/esm/styles.css +1 -1
- package/dist/esm/types/props-helpers.mjs +5 -1
- package/dist/esm/types/props-helpers.mjs.map +1 -1
- package/dist/esm/v3/BaseStack/BaseStack.mjs +44 -0
- package/dist/esm/v3/BaseStack/BaseStack.mjs.map +1 -0
- package/dist/esm/v3/BaseStack/BaseStack.module.scss.mjs +4 -0
- package/dist/esm/v3/BaseStack/BaseStack.module.scss.mjs.map +1 -0
- package/dist/esm/v3/Box/Box.mjs +54 -0
- package/dist/esm/v3/Box/Box.mjs.map +1 -0
- package/dist/esm/v3/Box/Box.module.scss.mjs +4 -0
- package/dist/esm/v3/Box/Box.module.scss.mjs.map +1 -0
- package/dist/esm/v3/Divider/Divider.mjs +30 -0
- package/dist/esm/v3/Divider/Divider.mjs.map +1 -0
- package/dist/esm/v3/Divider/Divider.module.scss.mjs +4 -0
- package/dist/esm/v3/Divider/Divider.module.scss.mjs.map +1 -0
- package/dist/esm/v3/HStack/HStack.mjs +17 -0
- package/dist/esm/v3/HStack/HStack.mjs.map +1 -0
- package/dist/esm/v3/Icon/Icon.mjs +49 -0
- package/dist/esm/v3/Icon/Icon.mjs.map +1 -0
- package/dist/esm/v3/Icon/Icon.module.scss.mjs +4 -0
- package/dist/esm/v3/Icon/Icon.module.scss.mjs.map +1 -0
- package/dist/esm/v3/SmoothCornersBox/SmoothCornersBox.mjs +51 -0
- package/dist/esm/v3/SmoothCornersBox/SmoothCornersBox.mjs.map +1 -0
- package/dist/esm/v3/SmoothCornersBox/SmoothCornersBox.module.scss.mjs +4 -0
- package/dist/esm/v3/SmoothCornersBox/SmoothCornersBox.module.scss.mjs.map +1 -0
- package/dist/esm/v3/Spinner/Spinner.mjs +43 -0
- package/dist/esm/v3/Spinner/Spinner.mjs.map +1 -0
- package/dist/esm/v3/Spinner/Spinner.module.scss.mjs +4 -0
- package/dist/esm/v3/Spinner/Spinner.module.scss.mjs.map +1 -0
- package/dist/esm/v3/Text/Text.mjs +58 -0
- package/dist/esm/v3/Text/Text.mjs.map +1 -0
- package/dist/esm/v3/Text/Text.module.scss.mjs +4 -0
- package/dist/esm/v3/Text/Text.module.scss.mjs.map +1 -0
- package/dist/esm/v3/VStack/VStack.mjs +17 -0
- package/dist/esm/v3/VStack/VStack.mjs.map +1 -0
- package/dist/esm/v3/index.mjs +9 -0
- package/dist/esm/v3/index.mjs.map +1 -0
- package/dist/types/types/beta-tokens.d.ts +4 -0
- package/dist/types/types/beta-tokens.d.ts.map +1 -1
- package/dist/types/types/props-helpers.d.ts +48 -1
- package/dist/types/types/props-helpers.d.ts.map +1 -1
- package/dist/types/types/props.d.ts +44 -0
- package/dist/types/types/props.d.ts.map +1 -1
- package/dist/types/v3/BaseStack/BaseStack.d.ts +6 -0
- package/dist/types/v3/BaseStack/BaseStack.d.ts.map +1 -0
- package/dist/types/v3/BaseStack/BaseStack.types.d.ts +45 -0
- package/dist/types/v3/BaseStack/BaseStack.types.d.ts.map +1 -0
- package/dist/types/v3/Box/Box.d.ts +19 -0
- package/dist/types/v3/Box/Box.d.ts.map +1 -0
- package/dist/types/v3/Box/Box.types.d.ts +12 -0
- package/dist/types/v3/Box/Box.types.d.ts.map +1 -0
- package/dist/types/v3/Box/index.d.ts +3 -0
- package/dist/types/v3/Box/index.d.ts.map +1 -0
- package/dist/types/v3/Divider/Divider.d.ts +13 -0
- package/dist/types/v3/Divider/Divider.d.ts.map +1 -0
- package/dist/types/v3/Divider/Divider.types.d.ts +27 -0
- package/dist/types/v3/Divider/Divider.types.d.ts.map +1 -0
- package/dist/types/v3/Divider/index.d.ts +3 -0
- package/dist/types/v3/Divider/index.d.ts.map +1 -0
- package/dist/types/v3/HStack/HStack.d.ts +7 -0
- package/dist/types/v3/HStack/HStack.d.ts.map +1 -0
- package/dist/types/v3/HStack/HStack.types.d.ts +2 -0
- package/dist/types/v3/HStack/HStack.types.d.ts.map +1 -0
- package/dist/types/v3/HStack/index.d.ts +3 -0
- package/dist/types/v3/HStack/index.d.ts.map +1 -0
- package/dist/types/v3/Icon/Icon.d.ts +19 -0
- package/dist/types/v3/Icon/Icon.d.ts.map +1 -0
- package/dist/types/v3/Icon/Icon.types.d.ts +21 -0
- package/dist/types/v3/Icon/Icon.types.d.ts.map +1 -0
- package/dist/types/v3/Icon/index.d.ts +3 -0
- package/dist/types/v3/Icon/index.d.ts.map +1 -0
- package/dist/types/v3/SmoothCornersBox/SmoothCornersBox.d.ts +15 -0
- package/dist/types/v3/SmoothCornersBox/SmoothCornersBox.d.ts.map +1 -0
- package/dist/types/v3/SmoothCornersBox/SmoothCornersBox.types.d.ts +61 -0
- package/dist/types/v3/SmoothCornersBox/SmoothCornersBox.types.d.ts.map +1 -0
- package/dist/types/v3/SmoothCornersBox/index.d.ts +3 -0
- package/dist/types/v3/SmoothCornersBox/index.d.ts.map +1 -0
- package/dist/types/v3/Spinner/Spinner.d.ts +15 -0
- package/dist/types/v3/Spinner/Spinner.d.ts.map +1 -0
- package/dist/types/v3/Spinner/Spinner.types.d.ts +5 -0
- package/dist/types/v3/Spinner/Spinner.types.d.ts.map +1 -0
- package/dist/types/v3/Spinner/index.d.ts +3 -0
- package/dist/types/v3/Spinner/index.d.ts.map +1 -0
- package/dist/types/v3/Text/Text.d.ts +16 -0
- package/dist/types/v3/Text/Text.d.ts.map +1 -0
- package/dist/types/v3/Text/Text.types.d.ts +44 -0
- package/dist/types/v3/Text/Text.types.d.ts.map +1 -0
- package/dist/types/v3/Text/index.d.ts +3 -0
- package/dist/types/v3/Text/index.d.ts.map +1 -0
- package/dist/types/v3/VStack/VStack.d.ts +7 -0
- package/dist/types/v3/VStack/VStack.d.ts.map +1 -0
- package/dist/types/v3/VStack/VStack.types.d.ts +2 -0
- package/dist/types/v3/VStack/VStack.types.d.ts.map +1 -0
- package/dist/types/v3/VStack/index.d.ts +3 -0
- package/dist/types/v3/VStack/index.d.ts.map +1 -0
- package/dist/types/v3/index.d.ts +9 -0
- package/dist/types/v3/index.d.ts.map +1 -0
- package/package.json +6 -1
- package/src/types/beta-tokens.ts +8 -0
- package/src/types/props-helpers.ts +22 -1
- package/src/types/props.ts +52 -0
- package/src/v3/BaseStack/BaseStack.module.scss +73 -0
- package/src/v3/BaseStack/BaseStack.test.tsx +83 -0
- package/src/v3/BaseStack/BaseStack.tsx +72 -0
- package/src/v3/BaseStack/BaseStack.types.ts +59 -0
- package/src/v3/Box/Box.module.scss +13 -0
- package/src/v3/Box/Box.stories.tsx +27 -0
- package/src/v3/Box/Box.test.tsx +57 -0
- package/src/v3/Box/Box.tsx +77 -0
- package/src/v3/Box/Box.types.ts +24 -0
- package/src/v3/Box/index.ts +2 -0
- package/src/v3/Divider/Divider.module.scss +52 -0
- package/src/v3/Divider/Divider.stories.tsx +95 -0
- package/src/v3/Divider/Divider.test.tsx +47 -0
- package/src/v3/Divider/Divider.tsx +57 -0
- package/src/v3/Divider/Divider.types.ts +32 -0
- package/src/v3/Divider/index.ts +2 -0
- package/src/v3/HStack/HStack.stories.tsx +58 -0
- package/src/v3/HStack/HStack.test.tsx +14 -0
- package/src/v3/HStack/HStack.tsx +21 -0
- package/src/v3/HStack/HStack.types.ts +1 -0
- package/src/v3/HStack/index.ts +2 -0
- package/src/v3/Icon/Icon.module.scss +20 -0
- package/src/v3/Icon/Icon.stories.tsx +173 -0
- package/src/v3/Icon/Icon.test.tsx +64 -0
- package/src/v3/Icon/Icon.tsx +67 -0
- package/src/v3/Icon/Icon.types.ts +32 -0
- package/src/v3/Icon/index.ts +2 -0
- package/src/v3/SmoothCornersBox/SmoothCornersBox.module.scss +48 -0
- package/src/v3/SmoothCornersBox/SmoothCornersBox.stories.tsx +41 -0
- package/src/v3/SmoothCornersBox/SmoothCornersBox.test.tsx +83 -0
- package/src/v3/SmoothCornersBox/SmoothCornersBox.tsx +84 -0
- package/src/v3/SmoothCornersBox/SmoothCornersBox.types.ts +69 -0
- package/src/v3/SmoothCornersBox/index.ts +2 -0
- package/src/v3/Spinner/Spinner.module.scss +58 -0
- package/src/v3/Spinner/Spinner.stories.tsx +28 -0
- package/src/v3/Spinner/Spinner.test.tsx +65 -0
- package/src/v3/Spinner/Spinner.tsx +61 -0
- package/src/v3/Spinner/Spinner.types.ts +12 -0
- package/src/v3/Spinner/index.ts +2 -0
- package/src/v3/Text/Text.module.scss +69 -0
- package/src/v3/Text/Text.stories.tsx +52 -0
- package/src/v3/Text/Text.test.tsx +84 -0
- package/src/v3/Text/Text.tsx +81 -0
- package/src/v3/Text/Text.types.ts +70 -0
- package/src/v3/Text/index.ts +2 -0
- package/src/v3/VStack/VStack.stories.tsx +58 -0
- package/src/v3/VStack/VStack.test.tsx +14 -0
- package/src/v3/VStack/VStack.tsx +21 -0
- package/src/v3/VStack/VStack.types.ts +1 -0
- package/src/v3/VStack/index.ts +2 -0
- package/src/v3/index.ts +10 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
@use '../../styles/mixins/dimension';
|
|
2
|
+
|
|
3
|
+
@keyframes spin {
|
|
4
|
+
0% {
|
|
5
|
+
transform: rotate(0deg);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
100% {
|
|
9
|
+
transform: rotate(360deg);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.Spinner {
|
|
14
|
+
--b-v3-spinner-color: initial;
|
|
15
|
+
|
|
16
|
+
display: inline-block;
|
|
17
|
+
flex-shrink: 0;
|
|
18
|
+
|
|
19
|
+
border-style: solid;
|
|
20
|
+
border-top-color: transparent;
|
|
21
|
+
border-right-color: var(--b-v3-spinner-color);
|
|
22
|
+
border-bottom-color: var(--b-v3-spinner-color);
|
|
23
|
+
/* stylelint-disable-next-line declaration-block-no-redundant-longhand-properties */
|
|
24
|
+
border-left-color: var(--b-v3-spinner-color);
|
|
25
|
+
border-radius: 50%;
|
|
26
|
+
|
|
27
|
+
animation: spin 1s linear infinite;
|
|
28
|
+
|
|
29
|
+
&:where(.size-xl) {
|
|
30
|
+
@include dimension.square(50px);
|
|
31
|
+
|
|
32
|
+
border-width: 4px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&:where(.size-l, .size-m) {
|
|
36
|
+
border-width: 3px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&:where(.size-l) {
|
|
40
|
+
@include dimension.square(24px);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
&:where(.size-m) {
|
|
44
|
+
@include dimension.square(20px);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
&:where(.size-s, .size-xs) {
|
|
48
|
+
border-width: 2px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
&:where(.size-s) {
|
|
52
|
+
@include dimension.square(16px);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&:where(.size-xs) {
|
|
56
|
+
@include dimension.square(12px);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Meta, type StoryFn } from '@storybook/react'
|
|
2
|
+
|
|
3
|
+
import { Spinner } from './Spinner'
|
|
4
|
+
import { type SpinnerProps } from './Spinner.types'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Spinner> = {
|
|
7
|
+
title: 'V3 components/Spinner',
|
|
8
|
+
component: Spinner,
|
|
9
|
+
argTypes: {
|
|
10
|
+
size: {
|
|
11
|
+
control: {
|
|
12
|
+
type: 'radio',
|
|
13
|
+
},
|
|
14
|
+
options: ['xs', 's', 'm', 'l', 'xl'],
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
export default meta
|
|
19
|
+
|
|
20
|
+
const Template: StoryFn<SpinnerProps> = ({ ...args }) => <Spinner {...args} />
|
|
21
|
+
|
|
22
|
+
export const Primary = {
|
|
23
|
+
render: Template,
|
|
24
|
+
|
|
25
|
+
args: {
|
|
26
|
+
size: 'm',
|
|
27
|
+
},
|
|
28
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
import { colorTokenCssVar } from '~/src/utils/style'
|
|
4
|
+
import { render } from '~/src/utils/test'
|
|
5
|
+
|
|
6
|
+
import { Spinner } from './Spinner'
|
|
7
|
+
import { type SpinnerProps } from './Spinner.types'
|
|
8
|
+
|
|
9
|
+
import styles from './Spinner.module.scss'
|
|
10
|
+
|
|
11
|
+
describe('Spinner', () => {
|
|
12
|
+
const renderSpinner = (props?: SpinnerProps) => render(<Spinner {...props} />)
|
|
13
|
+
|
|
14
|
+
it('should render loading status by default', () => {
|
|
15
|
+
const { getByRole } = renderSpinner()
|
|
16
|
+
const spinner = getByRole('status', { name: 'Loading' })
|
|
17
|
+
|
|
18
|
+
expect(spinner).toHaveClass(styles.Spinner)
|
|
19
|
+
expect(spinner).toHaveClass(styles['size-m'])
|
|
20
|
+
expect(spinner).toHaveStyle(
|
|
21
|
+
`--b-v3-spinner-color: ${colorTokenCssVar('icon-neutral')}`
|
|
22
|
+
)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('should allow overriding accessibility status', () => {
|
|
26
|
+
const { getByRole } = renderSpinner({
|
|
27
|
+
role: 'progressbar',
|
|
28
|
+
'aria-label': 'Submitting',
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
expect(getByRole('progressbar', { name: 'Submitting' })).toBeInTheDocument()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should forward ref', () => {
|
|
35
|
+
const ref = React.createRef<HTMLDivElement>()
|
|
36
|
+
|
|
37
|
+
render(<Spinner ref={ref} />)
|
|
38
|
+
|
|
39
|
+
expect(ref.current).toBeInTheDocument()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('should receive style and class name', () => {
|
|
43
|
+
const { getByRole } = renderSpinner({
|
|
44
|
+
style: { color: 'red' },
|
|
45
|
+
className: 'test-class',
|
|
46
|
+
})
|
|
47
|
+
const spinner = getByRole('status')
|
|
48
|
+
|
|
49
|
+
expect(spinner).toHaveStyle('color: red')
|
|
50
|
+
expect(spinner).toHaveClass('test-class')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('should receive color and size', () => {
|
|
54
|
+
const { getByRole } = renderSpinner({
|
|
55
|
+
color: 'icon-neutral-heavier',
|
|
56
|
+
size: 'xl',
|
|
57
|
+
})
|
|
58
|
+
const spinner = getByRole('status')
|
|
59
|
+
|
|
60
|
+
expect(spinner).toHaveClass(styles['size-xl'])
|
|
61
|
+
expect(spinner).toHaveStyle(
|
|
62
|
+
`--b-v3-spinner-color: ${colorTokenCssVar('icon-neutral-heavier')}`
|
|
63
|
+
)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { forwardRef } from 'react'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
|
|
6
|
+
import classNames from 'classnames'
|
|
7
|
+
|
|
8
|
+
import type { BetaSemanticColor } from '~/src/types/beta-tokens'
|
|
9
|
+
import { colorTokenCssVar } from '~/src/utils/style'
|
|
10
|
+
|
|
11
|
+
import type { SpinnerProps } from './Spinner.types'
|
|
12
|
+
|
|
13
|
+
import styles from './Spinner.module.scss'
|
|
14
|
+
|
|
15
|
+
const DEFAULT_SPINNER_COLOR = 'icon-neutral' satisfies BetaSemanticColor
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `Spinner` is a component for indicating loading state.
|
|
19
|
+
* @example
|
|
20
|
+
*
|
|
21
|
+
* ```tsx
|
|
22
|
+
* <Spinner
|
|
23
|
+
* size="m"
|
|
24
|
+
* color="icon-neutral"
|
|
25
|
+
* />
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export const Spinner = forwardRef<HTMLDivElement, SpinnerProps>(
|
|
29
|
+
function Spinner(
|
|
30
|
+
{
|
|
31
|
+
style,
|
|
32
|
+
className,
|
|
33
|
+
size = 'm',
|
|
34
|
+
color = DEFAULT_SPINNER_COLOR,
|
|
35
|
+
role = 'status',
|
|
36
|
+
'aria-label': ariaLabel,
|
|
37
|
+
...rest
|
|
38
|
+
},
|
|
39
|
+
forwardedRef
|
|
40
|
+
) {
|
|
41
|
+
return (
|
|
42
|
+
<div
|
|
43
|
+
{...rest}
|
|
44
|
+
ref={forwardedRef}
|
|
45
|
+
role={role}
|
|
46
|
+
aria-label={ariaLabel ?? (role === 'status' ? 'Loading' : undefined)}
|
|
47
|
+
style={
|
|
48
|
+
{
|
|
49
|
+
'--b-v3-spinner-color': colorTokenCssVar(color),
|
|
50
|
+
...style,
|
|
51
|
+
} as React.CSSProperties
|
|
52
|
+
}
|
|
53
|
+
className={classNames(
|
|
54
|
+
styles.Spinner,
|
|
55
|
+
styles[`size-${size}`],
|
|
56
|
+
className
|
|
57
|
+
)}
|
|
58
|
+
/>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BezierComponentProps,
|
|
3
|
+
SizeProps,
|
|
4
|
+
V3ColorProps,
|
|
5
|
+
} from '~/src/types/props'
|
|
6
|
+
|
|
7
|
+
export type SpinnerSize = 'xl' | 'l' | 'm' | 's' | 'xs'
|
|
8
|
+
|
|
9
|
+
export interface SpinnerProps
|
|
10
|
+
extends Omit<BezierComponentProps<'div'>, keyof V3ColorProps>,
|
|
11
|
+
SizeProps<SpinnerSize>,
|
|
12
|
+
V3ColorProps {}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
@use '../../styles/mixins/typography';
|
|
2
|
+
|
|
3
|
+
$typo-sizes: 11, 12, 13, 14, 15, 16, 17, 18, 22, 24, 30, 36;
|
|
4
|
+
|
|
5
|
+
.Text {
|
|
6
|
+
--b-text-color: initial;
|
|
7
|
+
--b-text-font-weight: var(--typography-font-weight-400);
|
|
8
|
+
--b-text-line-clamp: 1;
|
|
9
|
+
--b-text-line-height: initial;
|
|
10
|
+
--b-text-font-size: initial;
|
|
11
|
+
--b-text-letter-spacing: initial;
|
|
12
|
+
|
|
13
|
+
font-size: var(--b-text-font-size);
|
|
14
|
+
font-weight: var(--b-text-font-weight);
|
|
15
|
+
font-style: normal;
|
|
16
|
+
line-height: var(--b-text-line-height);
|
|
17
|
+
color: var(--b-text-color);
|
|
18
|
+
letter-spacing: var(--b-text-letter-spacing);
|
|
19
|
+
|
|
20
|
+
&:where(.bold) {
|
|
21
|
+
--b-text-font-weight: var(--typography-font-weight-600);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&:where(.italic) {
|
|
25
|
+
font-style: italic;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&:where(.truncated) {
|
|
29
|
+
overflow: hidden;
|
|
30
|
+
display: block;
|
|
31
|
+
text-overflow: ellipsis;
|
|
32
|
+
white-space: nowrap;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&:where(.multi-line-truncated) {
|
|
36
|
+
overflow: hidden;
|
|
37
|
+
display: -webkit-box;
|
|
38
|
+
-webkit-box-orient: vertical;
|
|
39
|
+
|
|
40
|
+
max-height: calc(var(--b-text-line-clamp) * var(--b-text-line-height));
|
|
41
|
+
|
|
42
|
+
text-overflow: ellipsis;
|
|
43
|
+
|
|
44
|
+
-webkit-line-clamp: var(--b-text-line-clamp);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
&:where(.align-left) {
|
|
48
|
+
text-align: left;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
&:where(.align-center) {
|
|
52
|
+
text-align: center;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&:where(.align-right) {
|
|
56
|
+
text-align: right;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@each $size in $typo-sizes {
|
|
60
|
+
&:where(.typo-#{$size}) {
|
|
61
|
+
@include typography.size(
|
|
62
|
+
$size,
|
|
63
|
+
'b-text-font-size',
|
|
64
|
+
'b-text-line-height',
|
|
65
|
+
'b-text-letter-spacing'
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type Meta, type StoryFn, type StoryObj } from '@storybook/react'
|
|
2
|
+
|
|
3
|
+
import { Box } from '~/src/v3/Box'
|
|
4
|
+
|
|
5
|
+
import { Text } from './Text'
|
|
6
|
+
import { type TextProps } from './Text.types'
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Text> = {
|
|
9
|
+
title: 'V3 components/Text',
|
|
10
|
+
component: Text,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const Template: StoryFn<TextProps> = ({ children, ...rest }) => (
|
|
14
|
+
<Box width={200}>
|
|
15
|
+
<Text {...rest}>{children}</Text>
|
|
16
|
+
</Box>
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
export const Primary: StoryObj<typeof Text> = {
|
|
20
|
+
render: Template,
|
|
21
|
+
args: {
|
|
22
|
+
bold: false,
|
|
23
|
+
italic: false,
|
|
24
|
+
truncated: false,
|
|
25
|
+
color: 'text-neutral',
|
|
26
|
+
children: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
|
27
|
+
typo: '15',
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const MultiLineTruncated: StoryFn<TextProps> = ({ children, ...rest }) => (
|
|
32
|
+
<Box width={200}>
|
|
33
|
+
<Text
|
|
34
|
+
{...rest}
|
|
35
|
+
color="text-neutral"
|
|
36
|
+
>
|
|
37
|
+
{children}
|
|
38
|
+
</Text>
|
|
39
|
+
</Box>
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
export const Secondary: StoryObj<typeof Text> = {
|
|
43
|
+
render: MultiLineTruncated,
|
|
44
|
+
args: {
|
|
45
|
+
truncated: 4,
|
|
46
|
+
children:
|
|
47
|
+
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed dolor nunc, bibendum vel neque eget, facilisis ornare justo. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In laoreet ipsum in commodo eleifend. Duis vestibulum, nulla ut tincidunt molestie, eros massa commodo risus, non sollicitudin turpis diam vitae elit.',
|
|
48
|
+
},
|
|
49
|
+
name: 'Multi-line truncated',
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default meta
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
import { colorTokenCssVar } from '~/src/utils/style'
|
|
4
|
+
import { render } from '~/src/utils/test'
|
|
5
|
+
|
|
6
|
+
import { Text } from './Text'
|
|
7
|
+
import { type TextProps } from './Text.types'
|
|
8
|
+
|
|
9
|
+
import styles from './Text.module.scss'
|
|
10
|
+
|
|
11
|
+
const TEXT = 'Hello, Channel!'
|
|
12
|
+
|
|
13
|
+
describe('Text', () => {
|
|
14
|
+
const renderText = (props?: Partial<TextProps>) =>
|
|
15
|
+
render(<Text {...props}>{TEXT}</Text>)
|
|
16
|
+
|
|
17
|
+
it('should have default style', () => {
|
|
18
|
+
const { getByText } = renderText()
|
|
19
|
+
const rendered = getByText(TEXT)
|
|
20
|
+
|
|
21
|
+
expect(rendered).toHaveClass(styles.Text)
|
|
22
|
+
expect(rendered).toHaveClass(styles['typo-15'])
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('should render as the given element', () => {
|
|
26
|
+
const { getByText } = renderText({ as: 'p' })
|
|
27
|
+
const rendered = getByText(TEXT)
|
|
28
|
+
|
|
29
|
+
expect(rendered.tagName).toBe('P')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should forward ref', () => {
|
|
33
|
+
const ref = React.createRef<HTMLElement>()
|
|
34
|
+
|
|
35
|
+
render(<Text ref={ref}>{TEXT}</Text>)
|
|
36
|
+
|
|
37
|
+
expect(ref.current).toBeInTheDocument()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('should receive color and font weight', () => {
|
|
41
|
+
const { getByText } = renderText({
|
|
42
|
+
color: 'text-neutral',
|
|
43
|
+
fontWeight: '600',
|
|
44
|
+
})
|
|
45
|
+
const rendered = getByText(TEXT)
|
|
46
|
+
|
|
47
|
+
expect(rendered).toHaveStyle(
|
|
48
|
+
`--b-text-color: ${colorTokenCssVar('text-neutral')}`
|
|
49
|
+
)
|
|
50
|
+
expect(rendered).toHaveStyle(
|
|
51
|
+
'--b-text-font-weight: var(--typography-font-weight-600)'
|
|
52
|
+
)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('should receive text styles', () => {
|
|
56
|
+
const { getByText } = renderText({
|
|
57
|
+
bold: true,
|
|
58
|
+
italic: true,
|
|
59
|
+
align: 'center',
|
|
60
|
+
typo: '24',
|
|
61
|
+
})
|
|
62
|
+
const rendered = getByText(TEXT)
|
|
63
|
+
|
|
64
|
+
expect(rendered).toHaveClass(styles.bold)
|
|
65
|
+
expect(rendered).toHaveClass(styles.italic)
|
|
66
|
+
expect(rendered).toHaveClass(styles['align-center'])
|
|
67
|
+
expect(rendered).toHaveClass(styles['typo-24'])
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('should receive truncated style', () => {
|
|
71
|
+
const { getByText } = renderText({ truncated: true })
|
|
72
|
+
const rendered = getByText(TEXT)
|
|
73
|
+
|
|
74
|
+
expect(rendered).toHaveClass(styles.truncated)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should receive multi-line truncated style', () => {
|
|
78
|
+
const { getByText } = renderText({ truncated: 2 })
|
|
79
|
+
const rendered = getByText(TEXT)
|
|
80
|
+
|
|
81
|
+
expect(rendered).toHaveClass(styles['multi-line-truncated'])
|
|
82
|
+
expect(rendered).toHaveStyle('--b-text-line-clamp: 2')
|
|
83
|
+
})
|
|
84
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { createElement, forwardRef } from 'react'
|
|
4
|
+
|
|
5
|
+
import classNames from 'classnames'
|
|
6
|
+
|
|
7
|
+
import { getMarginStyles, splitByMarginProps } from '~/src/types/props-helpers'
|
|
8
|
+
import { colorTokenCssVar } from '~/src/utils/style'
|
|
9
|
+
import { isNumber } from '~/src/utils/type'
|
|
10
|
+
|
|
11
|
+
import { type TextProps } from './Text.types'
|
|
12
|
+
|
|
13
|
+
import styles from './Text.module.scss'
|
|
14
|
+
|
|
15
|
+
function fontWeightTokenCssVar(fontWeight?: TextProps['fontWeight']) {
|
|
16
|
+
return fontWeight ? `var(--typography-font-weight-${fontWeight})` : undefined
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* `Text` is a component for representing the typography of a design system.
|
|
21
|
+
* @example
|
|
22
|
+
*
|
|
23
|
+
* ```tsx
|
|
24
|
+
* <Text
|
|
25
|
+
* typo="15"
|
|
26
|
+
* color="text-neutral"
|
|
27
|
+
* >
|
|
28
|
+
* Hello, Channel!
|
|
29
|
+
* </Text>
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export const Text = forwardRef<HTMLElement, TextProps>(
|
|
33
|
+
function Text(props, forwardedRef) {
|
|
34
|
+
const [marginProps, marginRest] = splitByMarginProps(props)
|
|
35
|
+
const marginStyles = getMarginStyles(marginProps)
|
|
36
|
+
|
|
37
|
+
const {
|
|
38
|
+
children,
|
|
39
|
+
style,
|
|
40
|
+
className,
|
|
41
|
+
as = 'span',
|
|
42
|
+
typo = '15',
|
|
43
|
+
color,
|
|
44
|
+
bold,
|
|
45
|
+
fontWeight,
|
|
46
|
+
italic,
|
|
47
|
+
truncated,
|
|
48
|
+
align,
|
|
49
|
+
...rest
|
|
50
|
+
} = marginRest
|
|
51
|
+
const isMultiLineTruncated = isNumber(truncated) && truncated >= 1
|
|
52
|
+
|
|
53
|
+
return createElement(
|
|
54
|
+
as,
|
|
55
|
+
{
|
|
56
|
+
ref: forwardedRef,
|
|
57
|
+
style: {
|
|
58
|
+
'--b-text-color': colorTokenCssVar(color),
|
|
59
|
+
'--b-text-font-weight': fontWeightTokenCssVar(fontWeight),
|
|
60
|
+
'--b-text-line-clamp': isMultiLineTruncated ? truncated : undefined,
|
|
61
|
+
...marginStyles.style,
|
|
62
|
+
...style,
|
|
63
|
+
},
|
|
64
|
+
className: classNames(
|
|
65
|
+
styles.Text,
|
|
66
|
+
styles[`typo-${typo}`],
|
|
67
|
+
bold && styles.bold,
|
|
68
|
+
italic && styles.italic,
|
|
69
|
+
truncated === true
|
|
70
|
+
? styles.truncated
|
|
71
|
+
: isMultiLineTruncated && styles['multi-line-truncated'],
|
|
72
|
+
align && styles[`align-${align}`],
|
|
73
|
+
marginStyles.className,
|
|
74
|
+
className
|
|
75
|
+
),
|
|
76
|
+
...rest,
|
|
77
|
+
},
|
|
78
|
+
children
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type BetaTextSemanticColor,
|
|
3
|
+
type BetaTypographyFontWeight,
|
|
4
|
+
} from '~/src/types/beta-tokens'
|
|
5
|
+
import {
|
|
6
|
+
type BezierComponentProps,
|
|
7
|
+
type ChildrenProps,
|
|
8
|
+
type PolymorphicProps,
|
|
9
|
+
type V3MarginProps,
|
|
10
|
+
} from '~/src/types/props'
|
|
11
|
+
|
|
12
|
+
type Typography =
|
|
13
|
+
| '11'
|
|
14
|
+
| '12'
|
|
15
|
+
| '13'
|
|
16
|
+
| '14'
|
|
17
|
+
| '15'
|
|
18
|
+
| '16'
|
|
19
|
+
| '17'
|
|
20
|
+
| '18'
|
|
21
|
+
| '22'
|
|
22
|
+
| '24'
|
|
23
|
+
| '30'
|
|
24
|
+
| '36'
|
|
25
|
+
|
|
26
|
+
type TextAlign = 'left' | 'center' | 'right'
|
|
27
|
+
|
|
28
|
+
interface TextOwnProps {
|
|
29
|
+
/**
|
|
30
|
+
* Typography style of the text.
|
|
31
|
+
* @default '15'
|
|
32
|
+
*/
|
|
33
|
+
typo?: Typography
|
|
34
|
+
/**
|
|
35
|
+
* Color of the text. If no value is specified, it inherits the color of the parent element.
|
|
36
|
+
*/
|
|
37
|
+
color?: BetaTextSemanticColor
|
|
38
|
+
/**
|
|
39
|
+
* Whether the text is bold.
|
|
40
|
+
* @default false
|
|
41
|
+
*/
|
|
42
|
+
bold?: boolean
|
|
43
|
+
/**
|
|
44
|
+
* Font weight of the text.
|
|
45
|
+
* If `bold` and `fontWeight` are used together, `fontWeight` takes precedence.
|
|
46
|
+
*/
|
|
47
|
+
fontWeight?: BetaTypographyFontWeight
|
|
48
|
+
/**
|
|
49
|
+
* Whether the text is italic.
|
|
50
|
+
* @default false
|
|
51
|
+
*/
|
|
52
|
+
italic?: boolean
|
|
53
|
+
/**
|
|
54
|
+
* Whether the text is truncated.
|
|
55
|
+
* If it is a positive integer, it means the maximum number of lines.
|
|
56
|
+
* @default false
|
|
57
|
+
*/
|
|
58
|
+
truncated?: boolean | number
|
|
59
|
+
/**
|
|
60
|
+
* Horizontal alignment of the text.
|
|
61
|
+
*/
|
|
62
|
+
align?: TextAlign
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface TextProps
|
|
66
|
+
extends Omit<BezierComponentProps, keyof TextOwnProps>,
|
|
67
|
+
PolymorphicProps,
|
|
68
|
+
ChildrenProps,
|
|
69
|
+
V3MarginProps,
|
|
70
|
+
TextOwnProps {}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { type Meta, type StoryFn, type StoryObj } from '@storybook/react'
|
|
4
|
+
|
|
5
|
+
import { range } from '~/src/utils/number'
|
|
6
|
+
import { Box } from '~/src/v3/Box'
|
|
7
|
+
import { Text } from '~/src/v3/Text'
|
|
8
|
+
|
|
9
|
+
import { VStack } from './VStack'
|
|
10
|
+
|
|
11
|
+
const meta = {
|
|
12
|
+
title: 'V3 components/VStack',
|
|
13
|
+
component: VStack,
|
|
14
|
+
} satisfies Meta<typeof VStack>
|
|
15
|
+
|
|
16
|
+
type Story = StoryObj<typeof meta>
|
|
17
|
+
|
|
18
|
+
function DecorativeBox({ children }: React.PropsWithChildren<{}>) {
|
|
19
|
+
return (
|
|
20
|
+
<Box
|
|
21
|
+
width={50}
|
|
22
|
+
height={50}
|
|
23
|
+
backgroundColor="fill-neutral"
|
|
24
|
+
borderRadius="8"
|
|
25
|
+
borderWidth={1}
|
|
26
|
+
borderColor="border-neutral"
|
|
27
|
+
>
|
|
28
|
+
<Text>{children}</Text>
|
|
29
|
+
</Box>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const Template: StoryFn<typeof VStack> = (args) => (
|
|
34
|
+
<VStack
|
|
35
|
+
{...args}
|
|
36
|
+
borderColor="border-neutral"
|
|
37
|
+
borderWidth={1}
|
|
38
|
+
>
|
|
39
|
+
{range(4).map((i) => (
|
|
40
|
+
<DecorativeBox key={`item-${i}`}>{i + 1}</DecorativeBox>
|
|
41
|
+
))}
|
|
42
|
+
</VStack>
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
export const Primary: Story = {
|
|
46
|
+
render: Template,
|
|
47
|
+
args: {
|
|
48
|
+
justify: 'start',
|
|
49
|
+
align: 'start',
|
|
50
|
+
reverse: false,
|
|
51
|
+
wrap: true,
|
|
52
|
+
width: 300,
|
|
53
|
+
height: 300,
|
|
54
|
+
spacing: 6,
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default meta
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { render } from '~/src/utils/test'
|
|
2
|
+
import styles from '~/src/v3/BaseStack/BaseStack.module.scss'
|
|
3
|
+
|
|
4
|
+
import { VStack } from './VStack'
|
|
5
|
+
|
|
6
|
+
describe('VStack', () => {
|
|
7
|
+
it('should render vertical stack', () => {
|
|
8
|
+
const { getByText } = render(<VStack>Hello, Channel!</VStack>)
|
|
9
|
+
const rendered = getByText('Hello, Channel!')
|
|
10
|
+
|
|
11
|
+
expect(rendered).toHaveClass(styles.BaseStack)
|
|
12
|
+
expect(rendered).toHaveClass(styles['direction-vertical'])
|
|
13
|
+
})
|
|
14
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { createElement, forwardRef } from 'react'
|
|
4
|
+
|
|
5
|
+
import { BaseStack } from '~/src/v3/BaseStack/BaseStack'
|
|
6
|
+
|
|
7
|
+
import type { VStackProps } from './VStack.types'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* `VStack` is a shorthand component equivalent to `Stack` with a vertical direction property.
|
|
11
|
+
* @see BaseStack
|
|
12
|
+
*/
|
|
13
|
+
export const VStack = forwardRef<HTMLElement, VStackProps>(
|
|
14
|
+
function VStack(props, forwardedRef) {
|
|
15
|
+
return createElement(BaseStack, {
|
|
16
|
+
...props,
|
|
17
|
+
direction: 'vertical',
|
|
18
|
+
ref: forwardedRef,
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { VStackProps } from '~/src/v3/BaseStack/BaseStack.types'
|