Skip to content

Commit

Permalink
Feat(web-react): Introduce UNSTABLE_Truncate component #DS-1098
Browse files Browse the repository at this point in the history
  • Loading branch information
curdaj committed Jul 17, 2024
1 parent 100ad5e commit 10802b8
Show file tree
Hide file tree
Showing 13 changed files with 277 additions and 0 deletions.
36 changes: 36 additions & 0 deletions packages/web-react/src/components/UNSTABLE_Truncate/README.md
Original file line number Diff line number Diff line change
@@ -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';

<UNSTABLE_Truncate>{/* Text go here */}</UNSTABLE_Truncate>;
```

## Lines

You can add the number of lines to which you want to truncate the text using `lines` props

```jsx
<UNSTABLE_Truncate lines={2}>{/* Text go here */}</UNSTABLE_Truncate>
```

## 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
Original file line number Diff line number Diff line change
@@ -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 = <T extends ElementType = 'span'>(props: SpiritTruncateProps<T>): 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 (
<ElementTag
{...otherProps}
{...truncateStyleProps}
className={classNames(useClassNamePrefix(classProps), styleProps.className)}
>
{children}
</ElementTag>
);
};

export default UNSTABLE_Truncate;
Original file line number Diff line number Diff line change
@@ -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(<UNSTABLE_Truncate>Text content</UNSTABLE_Truncate>);

expect(screen.getByText('Text content')).toHaveClass('text-truncate-multiline');
});

it('should have correct style based on lines', () => {
render(<UNSTABLE_Truncate lines={2}>Text content</UNSTABLE_Truncate>);
const text = screen.getByText('Text content');

expect(text).toHaveClass('text-truncate-multiline');
expect(text).toHaveStyle('--text-truncate-lines:2;');
});

it('should render children', () => {
render(<UNSTABLE_Truncate>Text content</UNSTABLE_Truncate>);

expect(screen.getByText('Text content')).toBeInTheDocument();
});

it('should render with custom elementType', () => {
render(<UNSTABLE_Truncate elementType="h2">Text content</UNSTABLE_Truncate>);

expect(screen.getByText('Text content')).toContainHTML('h2');
});
});
Original file line number Diff line number Diff line change
@@ -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 });
});
});
Original file line number Diff line number Diff line change
@@ -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 (
<>
<form>
<fieldset style={{ border: 0 }}>
<TextField
type="number"
min="1"
max="10"
value={lines}
onChange={(e) => 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."
/>
</fieldset>
</form>
<UNSTABLE_Truncate lines={lines}>
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.
</UNSTABLE_Truncate>
</>
);
};

export default TruncateDefault;
12 changes: 12 additions & 0 deletions packages/web-react/src/components/UNSTABLE_Truncate/demo/index.tsx
Original file line number Diff line number Diff line change
@@ -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(
<React.StrictMode>
<DocsSection title="Basic Usage" stackAlignment="stretch">
<TruncateDefault />
</DocsSection>
</React.StrictMode>,
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{> web-react/demo}}
3 changes: 3 additions & 0 deletions packages/web-react/src/components/UNSTABLE_Truncate/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './UNSTABLE_Truncate';
export * from './useTruncateStyleProps';
export { default as UNSTABLE_Truncate } from './UNSTABLE_Truncate';
Original file line number Diff line number Diff line change
@@ -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<typeof UNSTABLE_Truncate> = {
title: 'Experimental/UNSTABLE_Truncate',
component: UNSTABLE_Truncate,
parameters: {
docs: {
page: () => <Markdown>{ReadMe}</Markdown>,
},
},
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<typeof UNSTABLE_Truncate>;

export const Playground: Story = {
name: 'UNSTABLE_Truncate',
};
Original file line number Diff line number Diff line change
@@ -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<T extends ElementType> {
classProps: string;
props: SpiritTruncateProps<T>;
styleProps: TruncateCSSProperties;
}

export function useTruncateStyleProps<T extends ElementType>(props: SpiritTruncateProps<T>): TruncateStyles<T> {
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,
};
}
8 changes: 8 additions & 0 deletions packages/web-react/src/types/truncate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ElementType } from 'react';
import { ChildrenProps, StyleProps } from './shared';

export type SpiritTruncateProps<E extends ElementType> = {
elementType?: E;
lines?: number;
} & StyleProps &
ChildrenProps;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 10802b8

Please sign in to comment.