diff --git a/packages/web-react/src/components/UNSTABLE_Truncate/README.md b/packages/web-react/src/components/UNSTABLE_Truncate/README.md new file mode 100644 index 0000000000..e09ef09a6d --- /dev/null +++ b/packages/web-react/src/components/UNSTABLE_Truncate/README.md @@ -0,0 +1,36 @@ +# UNSTABLE Truncate + +> ⚠️ This component is UNSTABLE. It may significantly change at any point in the future. +> Please use it with caution. + +Truncate is a component that truncates text based on defined number of rows. + +```jsx +import { UNSTABLE_Truncate } from '@lmc-eu/spirit-web-react'; + +{/* Text go here */}; +``` + +## Lines + +You can add the number of lines to which you want to truncate the text using `lines` props + +```jsx +{/* Text go here */} +``` + +## API + +| Name | Type | Default | Required | Description | +| ------------- | ----------------------- | ------- | -------- | -------------------------------------------------- | +| `children` | `string` \| `ReactNode` | `null` | ✓ | Content of the Truncate | +| `elementType` | `ElementType` | `span` | ✕ | Type of element used | +| `lines` | `number` | 3 | ✕ | The number of lines on which the text is truncated | + +The components accept [additional attributes][readme-additional-attributes]. +If you need more control over the styling of a component, you can use [style props][readme-style-props] +and [escape hatches][readme-escape-hatches]. + +[readme-additional-attributes]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#additional-attributes +[readme-escape-hatches]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#escape-hatches +[readme-style-props]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#style-props diff --git a/packages/web-react/src/components/UNSTABLE_Truncate/UNSTABLE_Truncate.tsx b/packages/web-react/src/components/UNSTABLE_Truncate/UNSTABLE_Truncate.tsx new file mode 100644 index 0000000000..447e33d11f --- /dev/null +++ b/packages/web-react/src/components/UNSTABLE_Truncate/UNSTABLE_Truncate.tsx @@ -0,0 +1,36 @@ +import classNames from 'classnames'; +import React, { ElementType } from 'react'; +import { useClassNamePrefix, useStyleProps } from '../../hooks'; +import { SpiritTruncateProps } from '../../types/truncate'; +import { useTruncateStyleProps } from './useTruncateStyleProps'; + +const defaultProps = { + elementType: 'span', +}; + +const UNSTABLE_Truncate = (props: SpiritTruncateProps): JSX.Element => { + const propsWithDefaults = { ...defaultProps, ...props }; + const { children, elementType: ElementTag = 'span', ...restProps } = propsWithDefaults; + + const { classProps, props: modifiedProps, styleProps: truncateStyle } = useTruncateStyleProps(restProps); + const { styleProps, props: otherProps } = useStyleProps(modifiedProps); + + const truncateStyleProps = { + style: { + ...styleProps.style, + ...truncateStyle, + }, + }; + + return ( + + {children} + + ); +}; + +export default UNSTABLE_Truncate; diff --git a/packages/web-react/src/components/UNSTABLE_Truncate/__tests__/UNSTABLE_Truncate.test.tsx b/packages/web-react/src/components/UNSTABLE_Truncate/__tests__/UNSTABLE_Truncate.test.tsx new file mode 100644 index 0000000000..e05eb208e5 --- /dev/null +++ b/packages/web-react/src/components/UNSTABLE_Truncate/__tests__/UNSTABLE_Truncate.test.tsx @@ -0,0 +1,41 @@ +import '@testing-library/jest-dom'; +import { render, screen } from '@testing-library/react'; +import React from 'react'; +import { classNamePrefixProviderTest } from '../../../../tests/providerTests/classNamePrefixProviderTest'; +import { restPropsTest } from '../../../../tests/providerTests/restPropsTest'; +import { stylePropsTest } from '../../../../tests/providerTests/stylePropsTest'; +import UNSTABLE_Truncate from '../UNSTABLE_Truncate'; + +describe('UNSTABLE_Truncate', () => { + classNamePrefixProviderTest(UNSTABLE_Truncate, 'text-truncate-multiline'); + + stylePropsTest(UNSTABLE_Truncate); + + restPropsTest(UNSTABLE_Truncate, 'span'); + + it('should have default classname', () => { + render(Text content); + + expect(screen.getByText('Text content')).toHaveClass('text-truncate-multiline'); + }); + + it('should have correct style based on lines', () => { + render(Text content); + const text = screen.getByText('Text content'); + + expect(text).toHaveClass('text-truncate-multiline'); + expect(text).toHaveStyle('--text-truncate-lines:2;'); + }); + + it('should render children', () => { + render(Text content); + + expect(screen.getByText('Text content')).toBeInTheDocument(); + }); + + it('should render with custom elementType', () => { + render(Text content); + + expect(screen.getByText('Text content')).toContainHTML('h2'); + }); +}); diff --git a/packages/web-react/src/components/UNSTABLE_Truncate/__tests__/useTruncateStyleProps.test.ts b/packages/web-react/src/components/UNSTABLE_Truncate/__tests__/useTruncateStyleProps.test.ts new file mode 100644 index 0000000000..6616f9d70e --- /dev/null +++ b/packages/web-react/src/components/UNSTABLE_Truncate/__tests__/useTruncateStyleProps.test.ts @@ -0,0 +1,19 @@ +import { renderHook } from '@testing-library/react'; +import { useTruncateStyleProps } from '../useTruncateStyleProps'; + +describe('useTruncateStyleProps', () => { + it('should return defaults', () => { + const props = {}; + const { result } = renderHook(() => useTruncateStyleProps(props)); + + expect(result.current.classProps).toBe('text-truncate-multiline'); + }); + + it('should return correct style based on lines', () => { + const props = { lines: 2 }; + const { result } = renderHook(() => useTruncateStyleProps(props)); + + expect(result.current.classProps).toBe('text-truncate-multiline'); + expect(result.current.styleProps).toEqual({ '--text-truncate-lines': 2 }); + }); +}); diff --git a/packages/web-react/src/components/UNSTABLE_Truncate/demo/TruncateDefault.tsx b/packages/web-react/src/components/UNSTABLE_Truncate/demo/TruncateDefault.tsx new file mode 100644 index 0000000000..3532a7fe26 --- /dev/null +++ b/packages/web-react/src/components/UNSTABLE_Truncate/demo/TruncateDefault.tsx @@ -0,0 +1,48 @@ +import React, { useState } from 'react'; +import { TextField } from '../../TextField'; +import UNSTABLE_Truncate from '../UNSTABLE_Truncate'; + +const TruncateDefault = () => { + const [lines, setLines] = useState(3); + + return ( + <> +
+
+ setLines(Number(e.currentTarget.value))} + label="Number of truncated lines:" + name="linesNumber" + id="truncate-lines" + helperText="Maximum number of lines for demo purposes is 10." + /> +
+
+ + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nam quis nulla. Vivamus ac leo pretium faucibus. + Pellentesque pretium lectus id turpis. Maecenas lorem. Maecenas sollicitudin. Nullam justo enim, consectetuer + nec, ullamcorper ac, vestibulum in, elit. Sed ut perspiciatis unde omnis iste natus error sit voluptatem + accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi + architecto beatae vitae dicta sunt explicabo. Suspendisse sagittis ultrices augue. Aenean fermentum risus id + tortor. Etiam bibendum elit eget erat. Nulla quis diam. Donec iaculis gravida nulla. Nulla pulvinar eleifend + sem. Fusce aliquam vestibulum ipsum. Sed ac dolor sit amet purus malesuada congue. In dapibus augue non sapien. + Morbi imperdiet, mauris ac auctor dictum, nisl ligula egestas nulla, et sollicitudin sem purus in lacus. Nam sed + tellus id magna elementum tincidunt. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nam quis nulla. + Vivamus ac leo pretium faucibus. Pellentesque pretium lectus id turpis. Maecenas lorem. Maecenas sollicitudin. + Nullam justo enim, consectetuer nec, ullamcorper ac, vestibulum in, elit. Sed ut perspiciatis unde omnis iste + natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo + inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Suspendisse sagittis ultrices augue. + Aenean fermentum risus id tortor. Etiam bibendum elit eget erat. Nulla quis diam. Donec iaculis gravida nulla. + Nulla pulvinar eleifend sem. Fusce aliquam vestibulum ipsum. Sed ac dolor sit amet purus malesuada congue. In + dapibus augue non sapien. Morbi imperdiet, mauris ac auctor dictum, nisl ligula egestas nulla, et sollicitudin + sem purus in lacus. Nam sed tellus id magna elementum tincidunt. + + + ); +}; + +export default TruncateDefault; diff --git a/packages/web-react/src/components/UNSTABLE_Truncate/demo/index.tsx b/packages/web-react/src/components/UNSTABLE_Truncate/demo/index.tsx new file mode 100644 index 0000000000..2eb69a61df --- /dev/null +++ b/packages/web-react/src/components/UNSTABLE_Truncate/demo/index.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import DocsSection from '../../../../docs/DocsSections'; +import TruncateDefault from './TruncateDefault'; + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + + + , +); diff --git a/packages/web-react/src/components/UNSTABLE_Truncate/index.html b/packages/web-react/src/components/UNSTABLE_Truncate/index.html new file mode 100644 index 0000000000..23972ef557 --- /dev/null +++ b/packages/web-react/src/components/UNSTABLE_Truncate/index.html @@ -0,0 +1 @@ +{{> web-react/demo}} diff --git a/packages/web-react/src/components/UNSTABLE_Truncate/index.ts b/packages/web-react/src/components/UNSTABLE_Truncate/index.ts new file mode 100644 index 0000000000..9a7354262a --- /dev/null +++ b/packages/web-react/src/components/UNSTABLE_Truncate/index.ts @@ -0,0 +1,3 @@ +export * from './UNSTABLE_Truncate'; +export * from './useTruncateStyleProps'; +export { default as UNSTABLE_Truncate } from './UNSTABLE_Truncate'; diff --git a/packages/web-react/src/components/UNSTABLE_Truncate/stories/UNSTABLE_Truncate.stories.tsx b/packages/web-react/src/components/UNSTABLE_Truncate/stories/UNSTABLE_Truncate.stories.tsx new file mode 100644 index 0000000000..15bdd0aba1 --- /dev/null +++ b/packages/web-react/src/components/UNSTABLE_Truncate/stories/UNSTABLE_Truncate.stories.tsx @@ -0,0 +1,45 @@ +import { Markdown } from '@storybook/blocks'; +import type { Meta, StoryObj } from '@storybook/react'; +import React from 'react'; +import ReadMe from '../README.md'; +import UNSTABLE_Truncate from '../UNSTABLE_Truncate'; + +const meta: Meta = { + title: 'Experimental/UNSTABLE_Truncate', + component: UNSTABLE_Truncate, + parameters: { + docs: { + page: () => {ReadMe}, + }, + }, + argTypes: { + children: { + control: 'text', + }, + lines: { + control: 'number', + table: { + defaultValue: { summary: '3' }, + }, + }, + }, + args: { + children: + 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nam quis nulla. Vivamus ac leo pretium faucibus.\n' + + 'Pellentesque pretium lectus id turpis. Maecenas lorem. Maecenas sollicitudin. Nullam justo enim, consectetuer nec,\n' + + 'ullamcorper ac, vestibulum in, elit. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium\n' + + 'doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae\n' + + 'vitae dicta sunt explicabo. Suspendisse sagittis ultrices augue. Aenean fermentum risus id tortor. Etiam bibendum\n' + + 'elit eget erat. Nulla quis diam. Donec iaculis gravida nulla. Nulla pulvinar eleifend sem. Fusce aliquam vestibulum\n' + + 'ipsum. Sed ac dolor sit amet purus malesuada congue. In dapibus augue non sapien. Morbi imperdiet, mauris ac auctor\n' + + 'dictum, nisl ligula egestas nulla, et sollicitudin sem purus in lacus. Nam sed tellus id magna elementum tincidunt.', + lines: 3, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Playground: Story = { + name: 'UNSTABLE_Truncate', +}; diff --git a/packages/web-react/src/components/UNSTABLE_Truncate/useTruncateStyleProps.ts b/packages/web-react/src/components/UNSTABLE_Truncate/useTruncateStyleProps.ts new file mode 100644 index 0000000000..cef6991a86 --- /dev/null +++ b/packages/web-react/src/components/UNSTABLE_Truncate/useTruncateStyleProps.ts @@ -0,0 +1,28 @@ +import { CSSProperties, ElementType } from 'react'; +import { SpiritTruncateProps } from '../../types/truncate'; + +interface TruncateCSSProperties extends CSSProperties { + '--text-truncate-lines'?: number; +} + +export interface TruncateStyles { + classProps: string; + props: SpiritTruncateProps; + styleProps: TruncateCSSProperties; +} + +export function useTruncateStyleProps(props: SpiritTruncateProps): TruncateStyles { + const { lines, ...restProps } = props; + + const TruncateMultilinesClass = 'text-truncate-multiline'; + const classProps = TruncateMultilinesClass; + + const truncateStyle: TruncateCSSProperties = {}; + truncateStyle['--text-truncate-lines'] = lines; + + return { + classProps, + props: restProps, + styleProps: truncateStyle, + }; +} diff --git a/packages/web-react/src/types/truncate.ts b/packages/web-react/src/types/truncate.ts new file mode 100644 index 0000000000..8aada1338f --- /dev/null +++ b/packages/web-react/src/types/truncate.ts @@ -0,0 +1,8 @@ +import { ElementType } from 'react'; +import { ChildrenProps, StyleProps } from './shared'; + +export type SpiritTruncateProps = { + elementType?: E; + lines?: number; +} & StyleProps & + ChildrenProps; diff --git a/tests/e2e/demo-components-compare.spec.ts-snapshots/unstable-truncate-chromium-linux.png b/tests/e2e/demo-components-compare.spec.ts-snapshots/unstable-truncate-chromium-linux.png new file mode 100644 index 0000000000..c6032ed0eb Binary files /dev/null and b/tests/e2e/demo-components-compare.spec.ts-snapshots/unstable-truncate-chromium-linux.png differ diff --git a/tests/e2e/demo-homepages.spec.ts-snapshots/web-react-chromium-linux.png b/tests/e2e/demo-homepages.spec.ts-snapshots/web-react-chromium-linux.png index d2935d1d0e..8b405c1c33 100644 Binary files a/tests/e2e/demo-homepages.spec.ts-snapshots/web-react-chromium-linux.png and b/tests/e2e/demo-homepages.spec.ts-snapshots/web-react-chromium-linux.png differ