diff --git a/.changeset/empty-otters-happen.md b/.changeset/empty-otters-happen.md new file mode 100644 index 0000000000..7ed3256230 --- /dev/null +++ b/.changeset/empty-otters-happen.md @@ -0,0 +1,5 @@ +--- +'@alfalab/core-components-skeleton': minor +--- + +Перенос хука useSkeleton из `Typography` в `Skeleton` diff --git a/.changeset/old-ants-hear.md b/.changeset/old-ants-hear.md new file mode 100644 index 0000000000..3addf64a47 --- /dev/null +++ b/.changeset/old-ants-hear.md @@ -0,0 +1,5 @@ +--- +'@alfalab/core-components-typography': minor +--- + +Перенос хука useSkeleton из `Typography` в `Skeleton` diff --git a/packages/skeleton/src/__image_snapshots__/skeleton-text-align-0-snap.png b/packages/skeleton/src/__image_snapshots__/skeleton-text-align-0-snap.png new file mode 100644 index 0000000000..0e1b18c002 --- /dev/null +++ b/packages/skeleton/src/__image_snapshots__/skeleton-text-align-0-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe0b2be4039eac36b10c26b11f3bc374e805aacac82b6ac77e74299fd5ce40df +size 38247 diff --git a/packages/skeleton/src/__image_snapshots__/skeleton-text-align-1-snap.png b/packages/skeleton/src/__image_snapshots__/skeleton-text-align-1-snap.png new file mode 100644 index 0000000000..cb0f957982 --- /dev/null +++ b/packages/skeleton/src/__image_snapshots__/skeleton-text-align-1-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:65494344a7727c88aae92682d3a2bfc408f98dbcd61524822a67c8db4b275388 +size 38256 diff --git a/packages/skeleton/src/__image_snapshots__/skeleton-text-align-2-snap.png b/packages/skeleton/src/__image_snapshots__/skeleton-text-align-2-snap.png new file mode 100644 index 0000000000..ffd1f81277 --- /dev/null +++ b/packages/skeleton/src/__image_snapshots__/skeleton-text-align-2-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36a0bb5d969fd4538cb4a3fc25d01af40c342ecabc14070eebbf1b4fe70a5fa8 +size 38243 diff --git a/packages/skeleton/src/__image_snapshots__/skeleton-text-rows-0-snap.png b/packages/skeleton/src/__image_snapshots__/skeleton-text-rows-0-snap.png new file mode 100644 index 0000000000..34876c60ad --- /dev/null +++ b/packages/skeleton/src/__image_snapshots__/skeleton-text-rows-0-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:269d7a98808bfef6a72e58d52e7281f40eb27beb4d300ed441cb56fb055f738d +size 38226 diff --git a/packages/skeleton/src/__image_snapshots__/skeleton-text-rows-1-snap.png b/packages/skeleton/src/__image_snapshots__/skeleton-text-rows-1-snap.png new file mode 100644 index 0000000000..fc4cc3141d --- /dev/null +++ b/packages/skeleton/src/__image_snapshots__/skeleton-text-rows-1-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:337f04ccbe04f9e8090d62d14fd3acfaced9765bb5c330430a3144c4ee27609d +size 38591 diff --git a/packages/skeleton/src/__image_snapshots__/skeleton-text-rows-2-snap.png b/packages/skeleton/src/__image_snapshots__/skeleton-text-rows-2-snap.png new file mode 100644 index 0000000000..b67791c073 --- /dev/null +++ b/packages/skeleton/src/__image_snapshots__/skeleton-text-rows-2-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:870abf26ab671c0fd4b592cd721df671b7801cdcf550094ad60bf673559fa860 +size 38928 diff --git a/packages/skeleton/src/__image_snapshots__/skeleton-text-width-0-snap.png b/packages/skeleton/src/__image_snapshots__/skeleton-text-width-0-snap.png new file mode 100644 index 0000000000..34876c60ad --- /dev/null +++ b/packages/skeleton/src/__image_snapshots__/skeleton-text-width-0-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:269d7a98808bfef6a72e58d52e7281f40eb27beb4d300ed441cb56fb055f738d +size 38226 diff --git a/packages/skeleton/src/__image_snapshots__/skeleton-text-width-1-snap.png b/packages/skeleton/src/__image_snapshots__/skeleton-text-width-1-snap.png new file mode 100644 index 0000000000..64440d3782 --- /dev/null +++ b/packages/skeleton/src/__image_snapshots__/skeleton-text-width-1-snap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5681911fabc51b147ba35c6ab8d7cb6256a74c5c7a15dffccabe83e8bdff65a1 +size 38249 diff --git a/packages/skeleton/src/component.screenshots.test.tsx b/packages/skeleton/src/component.screenshots.test.tsx index 741ee57f40..9af85ee726 100644 --- a/packages/skeleton/src/component.screenshots.test.tsx +++ b/packages/skeleton/src/component.screenshots.test.tsx @@ -2,6 +2,7 @@ import { setupScreenshotTesting, createSpriteStorybookUrl, createPreview, + generateTestCases, } from '../../screenshot-utils'; const screenshotTesting = setupScreenshotTesting({ @@ -52,3 +53,44 @@ describe( }, }), ); + +describe( + 'SkeletonText', + screenshotTesting({ + cases: [ + ...generateTestCases({ + componentName: 'Skeleton', + subComponentName: 'SkeletonText', + testStory: false, + knobs: { + rows: [undefined, 6, 8], + }, + }), + ...generateTestCases({ + componentName: 'Skeleton', + subComponentName: 'SkeletonText', + testStory: false, + knobs: { + width: [undefined, '[100, 200, 300, 400]'], + }, + }), + ...generateTestCases({ + componentName: 'Skeleton', + subComponentName: 'SkeletonText', + testStory: false, + knobs: { + width: '200px', + align: ['left', 'center', 'right'], + }, + }), + ], + screenshotOpts: { + clip: { + x: 0, + y: 0, + width: 1024, + height: 250, + }, + }, + }), +); diff --git a/packages/skeleton/src/docs/Component.docs.mdx b/packages/skeleton/src/docs/Component.docs.mdx new file mode 100644 index 0000000000..a0fee2fd4d --- /dev/null +++ b/packages/skeleton/src/docs/Component.docs.mdx @@ -0,0 +1,20 @@ +import { Meta, Markdown } from '@storybook/addon-docs'; +import { ComponentHeader, Tabs } from 'storybook/blocks'; +import * as Stories from './Component.stories'; + +import Description from './description.mdx'; +import Development from './development.mdx'; +import Changelog from '../../CHANGELOG.md?raw'; + + + + + +} + development={} + changelog={{Changelog}} +/> diff --git a/packages/skeleton/src/docs/Component.stories.mdx b/packages/skeleton/src/docs/Component.stories.mdx deleted file mode 100644 index 5924df9392..0000000000 --- a/packages/skeleton/src/docs/Component.stories.mdx +++ /dev/null @@ -1,64 +0,0 @@ -import { Meta, Story, Markdown } from '@storybook/addon-docs'; -import { text, boolean, select } from '@storybook/addon-knobs'; -import { ComponentHeader, Tabs } from 'storybook/blocks'; -import { Skeleton } from '@alfalab/core-components-skeleton'; - -import Description from './description.mdx'; -import Development from './development.mdx'; -import Changelog from '../../CHANGELOG.md?raw'; - - - -{/* Canvas */} - - - {React.createElement(() => { - const colors = select('colors', ['default', 'inverted'], 'default'); - const borderRadius = select('borderRadius', [0, 2, 4, 6, 8, 10, 12, 16, 20, 24, 32, 36, 64, 'pill'], 8) - - return ( -
- - Фижер - -
- ); - })} -
- -{/* Docs */} - - - -} - development={} - changelog={{Changelog}} -/> diff --git a/packages/skeleton/src/docs/Component.stories.tsx b/packages/skeleton/src/docs/Component.stories.tsx new file mode 100644 index 0000000000..e14aadc2dd --- /dev/null +++ b/packages/skeleton/src/docs/Component.stories.tsx @@ -0,0 +1,115 @@ +import React, { Fragment, RefObject } from 'react'; +import { Story } from '@storybook/addon-docs'; +import { text, boolean, select } from '@storybook/addon-knobs'; +import { Skeleton, useSkeleton } from '@alfalab/core-components-skeleton'; +import type { Meta, StoryObj } from '@storybook/react'; + +const meta: Meta = { + title: 'Components/Skeleton', + component: Skeleton, + id: 'Skeleton', +}; + +type Story = StoryObj; + +export const skeleton: Story = { + name: 'Skeleton', + render: () => { + const colors = select('colors', ['default', 'inverted'], 'default'); + const borderRadius = select( + 'borderRadius', + [0, 2, 4, 6, 8, 10, 12, 16, 20, 24, 32, 36, 64, 'pill'], + 8, + ); + + return ( +
+ + Фижер + +
+ ); + }, +}; + +export const skeleton_text: Story = { + name: 'SkeletonText', + render: () => { + const rows = select('rows', [undefined, 2, 4, 6, 8, 10], undefined); + const width = select( + 'width', + [undefined, [100, 200, 300, 400], 100, 200, 300, 400], + undefined, + ); + const align = select('align', [undefined, 'left', 'center', 'right'], undefined); + + // дополнительные преобразования нужны для скриншот-тестирования, так как все кнобсы в этом случае приходят в виде строки + const getWidth = () => { + if (typeof width === 'string') { + if ((width as string).startsWith('[') && (width as string).endsWith(']')) { + return JSON.parse(width); + } + } + + return width; + }; + + // дополнительные преобразования нужны для скриншот-тестирования, так как все кнобсы в этом случае приходят в виде строки + const getRows = () => { + if (typeof rows === 'string') { + return Number(rows); + } + + return rows; + }; + + const { renderSkeleton, textRef } = useSkeleton(true, { + rows: getRows(), + width: getWidth(), + align, + }); + + return ( + +
} style={{ lineHeight: '20px' }}> + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis + nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu + fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in + culpa qui officia deserunt mollit anim id est laborum. +
+ {renderSkeleton({})} +
+ ); + }, +}; + +export default meta; diff --git a/packages/skeleton/src/docs/description.mdx b/packages/skeleton/src/docs/description.mdx index 5c53e45d2f..02ed0f204b 100644 --- a/packages/skeleton/src/docs/description.mdx +++ b/packages/skeleton/src/docs/description.mdx @@ -23,7 +23,7 @@ render(() => { onChange={() => setVisible((p) => !p)} /> - +
@@ -40,3 +40,107 @@ render(() => { ); }); ``` + +## Скелетонизация текста + +Пакет `Skeleton` предоставляет хук `useSkeleton`, с помощью которого можно "скелетонизировать" текст, а также кастомизировать его отображение.
+*Обратите особое внимание на то, что у текста должны быть заданы `fontSize` и `lineHeight`*. + +```jsx live +const rowsOptions = [ + { key: 'computed', content: 'Computed' }, + { key: '2', content: '2' }, + { key: '4', content: '4' }, + { key: '6', content: '6' }, + { key: '8', content: '8' }, + { key: '10', content: '10' }, +]; + +const widthOptions = [ + { key: 'computed', content: 'Computed' }, + { key: '100', content: '100px' }, + { key: '200', content: '200px' }, + { key: '300', content: '300px' }, + { key: '400', content: '400px' }, + { key: 'array', content: '100-200-300-400' }, +]; + +const alignOptions = [ + { key: 'left', content: 'left' }, + { key: 'center', content: 'center' }, + { key: 'right', content: 'right' }, +]; + +render(() => { + const [visible, setVisible] = React.useState(false); + const [rows, setRows] = React.useState('computed'); + const [width, setWidth] = React.useState('computed'); + const [align, setAlign] = React.useState('left'); + + const { renderSkeleton, textRef } = useSkeleton(visible, { + align, + ...(rows !== 'computed' && { rows: Number(rows) }), + ...(width !== 'computed' && width !== 'array' && { width: Number(width) }), + ...(width === 'array' && { width: [100, 200, 300, 400] }), + }); + + const skeleton = renderSkeleton({ + wrapperClassName: 'wrapperClassName', + dataTestId: 'dataTestId', + }); + + return ( + <> + {skeleton ? ( + skeleton + ) : ( +
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis + nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu + fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in + culpa qui officia deserunt mollit anim id est laborum. +
+ )} + + setRows(selected.key)} + /> + + setWidth(selected.key)} + /> + + setAlign(selected.key)} + /> + + + setVisible((prev) => !prev)} + /> + + ); +}); +``` diff --git a/packages/skeleton/src/hooks/use-skeleton/index.ts b/packages/skeleton/src/hooks/use-skeleton/index.ts new file mode 100644 index 0000000000..bb1d014d11 --- /dev/null +++ b/packages/skeleton/src/hooks/use-skeleton/index.ts @@ -0,0 +1 @@ +export { useSkeleton } from './use-skeleton'; diff --git a/packages/typography/src/hooks/use-skeleton.module.css b/packages/skeleton/src/hooks/use-skeleton/use-skeleton.module.css similarity index 83% rename from packages/typography/src/hooks/use-skeleton.module.css rename to packages/skeleton/src/hooks/use-skeleton/use-skeleton.module.css index b81272b296..a6bb4ac6d9 100644 --- a/packages/typography/src/hooks/use-skeleton.module.css +++ b/packages/skeleton/src/hooks/use-skeleton/use-skeleton.module.css @@ -1,4 +1,4 @@ -@import '../../../vars/src/index.css'; +@import '../../../../vars/src/index.css'; .skeletonText.skeletonText { border-radius: var(--border-radius-pill); diff --git a/packages/typography/src/hooks/use-skeleton.test.tsx b/packages/skeleton/src/hooks/use-skeleton/use-skeleton.test.tsx similarity index 74% rename from packages/typography/src/hooks/use-skeleton.test.tsx rename to packages/skeleton/src/hooks/use-skeleton/use-skeleton.test.tsx index 4f4f0ed7f2..02f33a8a53 100644 --- a/packages/typography/src/hooks/use-skeleton.test.tsx +++ b/packages/skeleton/src/hooks/use-skeleton/use-skeleton.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useSkeleton } from './use-skeleton'; -import { TextSkeletonProps } from '../types'; +import { TextSkeletonProps } from '../../types/text-skeleton-props'; import { render } from '@testing-library/react'; const Skeleton = (props: TextSkeletonProps) => { @@ -39,9 +39,14 @@ describe('useSkeleton tests', () => { expect((rows[2] as HTMLDivElement).style.width).toBe(''); }); - for (const align of ['left', 'center', 'right'] as const) { - it(`should set \`${align}\` className`, () => { - const { container } = render(); - }); - } + it.each` + align | expectValue + ${'left'} | ${`left`} + ${'center'} | ${`center`} + ${'right'} | ${`right`} + `('should set align className $align', ({ align, expectValue }) => { + const { container } = render(); + + expect(container.firstElementChild).toHaveClass(expectValue); + }); }); diff --git a/packages/typography/src/hooks/use-skeleton.tsx b/packages/skeleton/src/hooks/use-skeleton/use-skeleton.tsx similarity index 96% rename from packages/typography/src/hooks/use-skeleton.tsx rename to packages/skeleton/src/hooks/use-skeleton/use-skeleton.tsx index 0c1cb096af..6c05ae1ada 100644 --- a/packages/typography/src/hooks/use-skeleton.tsx +++ b/packages/skeleton/src/hooks/use-skeleton/use-skeleton.tsx @@ -1,10 +1,10 @@ import React, { useRef, useState } from 'react'; import cn from 'classnames'; -import { Skeleton } from '@alfalab/core-components-skeleton'; import { useLayoutEffect_SAFE_FOR_SSR } from '@alfalab/hooks'; -import { TextSkeletonProps } from '../types'; +import { Skeleton } from '../../Component'; +import { TextSkeletonProps } from '../../types/text-skeleton-props'; import styles from './use-skeleton.module.css'; diff --git a/packages/skeleton/src/index.ts b/packages/skeleton/src/index.ts index e51a5d2440..3d20139197 100644 --- a/packages/skeleton/src/index.ts +++ b/packages/skeleton/src/index.ts @@ -1 +1,4 @@ export * from './Component'; + +export { useSkeleton } from './hooks/use-skeleton'; +export type { TextSkeletonProps } from './types/text-skeleton-props'; diff --git a/packages/skeleton/src/types/text-skeleton-props.ts b/packages/skeleton/src/types/text-skeleton-props.ts new file mode 100644 index 0000000000..1413734594 --- /dev/null +++ b/packages/skeleton/src/types/text-skeleton-props.ts @@ -0,0 +1,23 @@ +type WidthUnit = number | string; + +export type TextSkeletonProps = { + /** + * Кол-во строк текста + */ + rows?: number; + + /** + * Ширина строки + */ + width?: WidthUnit | WidthUnit[]; + + /** + * Выравнивание элементов по горизонтали + */ + align?: 'left' | 'center' | 'right'; + + /** + * Класс для обертки скелетона + */ + wrapperClassName?: string; +}; diff --git a/packages/typography/src/hooks/index.ts b/packages/typography/src/hooks/index.ts deleted file mode 100644 index 84b30c9fca..0000000000 --- a/packages/typography/src/hooks/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './use-skeleton'; diff --git a/packages/typography/src/text/component.tsx b/packages/typography/src/text/component.tsx index e2a68e1e41..73d6d80db4 100644 --- a/packages/typography/src/text/component.tsx +++ b/packages/typography/src/text/component.tsx @@ -2,9 +2,10 @@ import React, { forwardRef, HTMLAttributes } from 'react'; import mergeRefs from 'react-merge-refs'; import cn from 'classnames'; +import { type TextSkeletonProps, useSkeleton } from '@alfalab/core-components-skeleton'; + import { Color } from '../colors'; -import { useSkeleton } from '../hooks'; -import { TextElementType, TextSkeletonProps } from '../types'; +import { TextElementType } from '../types'; import colors from '../colors.module.css'; import styles from './index.module.css'; diff --git a/packages/typography/src/title/component.tsx b/packages/typography/src/title/component.tsx index b304368260..b325d60c84 100644 --- a/packages/typography/src/title/component.tsx +++ b/packages/typography/src/title/component.tsx @@ -2,9 +2,9 @@ import React, { forwardRef, HTMLAttributes } from 'react'; import mergeRefs from 'react-merge-refs'; import cn from 'classnames'; +import { type TextSkeletonProps, useSkeleton } from '@alfalab/core-components-skeleton'; + import { Color } from '../colors'; -import { useSkeleton } from '../hooks'; -import { TextSkeletonProps } from '../types'; import { getDefaultWeight } from './utils'; diff --git a/packages/typography/src/types.ts b/packages/typography/src/types.ts index 3010a07bc2..67db0d2b1c 100644 --- a/packages/typography/src/types.ts +++ b/packages/typography/src/types.ts @@ -1,25 +1 @@ export type TextElementType = HTMLParagraphElement | HTMLSpanElement | HTMLDivElement; - -type WidthUnit = number | string; - -export type TextSkeletonProps = { - /** - * Кол-во строк текста - */ - rows?: number; - - /** - * Ширина строки - */ - width?: WidthUnit | WidthUnit[]; - - /** - * Выравнивание элементов по горизонтали - */ - align?: 'left' | 'center' | 'right'; - - /** - * Класс для обертки скелетона - */ - wrapperClassName?: string; -};