Skip to content

Commit

Permalink
feat(Text): allow pass thml attributes durind Box inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
IsaevAlexandr committed May 15, 2024
1 parent fcd2ff3 commit 3abfe9e
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 54 deletions.
2 changes: 1 addition & 1 deletion src/components/Alert/__snapshots__/Alert.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ exports[`Alert has predicted styles if inline layout rendered 1`] = `
style="flex-direction: column; flex-grow: 1; column-gap: 4px; row-gap: 4px; justify-content: center;"
>
<span
class="g-text g-text_variant_subheader-2 g-alert__title"
class="g-box g-text g-text_variant_subheader-2 g-alert__title"
>
Where will you go, hero?
</span>
Expand Down
69 changes: 26 additions & 43 deletions src/components/Text/Text.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';

import type {QAProps} from '../types';
import {Box} from '../layout';
import type {BoxProps} from '../layout';

import {colorText} from './colorText/colorText';
import type {ColorTextBaseProps} from './colorText/colorText';
Expand All @@ -9,24 +10,14 @@ import type {TextBaseProps} from './text/text';

export interface TextProps<C extends React.ElementType = 'span'>
extends Omit<TextBaseProps, 'ellipsisLines'>,
ColorTextBaseProps,
QAProps {
/**
* Ability to override default html tag
*/
as?: C;
style?: React.CSSProperties;
className?: string;
id?: string;
children?: React.ReactNode;
title?: string;
Omit<BoxProps<C>, 'color'>,
ColorTextBaseProps {
ellipsisLines?: number;
}

type TextRef<C extends React.ElementType> = React.ComponentPropsWithRef<C>['ref'];

type TextPropsWithoutRef<C extends React.ElementType> = TextProps<C> &
Omit<React.ComponentPropsWithoutRef<C>, keyof TextProps<C>>;
type TextPropsWithTypedAttrs<T extends React.ElementType> = TextProps<T> &
Omit<React.ComponentPropsWithoutRef<T>, keyof TextProps<T>>;

/**
* A component for working with typography.
Expand Down Expand Up @@ -54,24 +45,20 @@ type TextPropsWithoutRef<C extends React.ElementType> = TextProps<C> &
* <span className={textStyles}>some text</span>
* ```
*/
export const Text = React.forwardRef(function Text<C extends React.ElementType = 'span'>(
{
as,
children,
variant,
className,
ellipsis,
color,
whiteSpace,
wordBreak,
ellipsisLines,
style: outerStyle,
qa,
...rest
}: TextPropsWithoutRef<C>,
ref?: TextRef<C>,
) {
const Tag: React.ElementType = as || 'span';
export const Text = function Text<C extends React.ElementType = 'span'>({
as: propsAs,
children,
variant,
className,
ellipsis,
color,
whiteSpace,
wordBreak,
ellipsisLines,
style: outerStyle,
...rest
}: TextProps<C>) {
const as: React.ElementType = propsAs || 'span';

const style: React.CSSProperties = {
...outerStyle,
Expand All @@ -82,8 +69,8 @@ export const Text = React.forwardRef(function Text<C extends React.ElementType =
}

return (
<Tag
ref={ref}
<Box
as={as}
className={text(
{
variant,
Expand All @@ -95,15 +82,11 @@ export const Text = React.forwardRef(function Text<C extends React.ElementType =
color ? colorText({color}, className) : className,
)}
style={style}
data-qa={qa}
{...rest}
>
{children}
</Tag>
</Box>
);
}) as (<C extends React.ElementType = 'span'>({
ref,
...props
}: TextPropsWithoutRef<C> & {ref?: TextRef<C>}) => React.ReactElement) & {displayName: string};

Text.displayName = 'Text';
} as (<C extends React.ElementType = 'div'>(
props: TextPropsWithTypedAttrs<C> & {ref?: TextRef<C>},
) => React.ReactElement) & {displayName: string};
65 changes: 65 additions & 0 deletions src/components/Text/__tests__/Text.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react';

import {render} from '../../../../test-utils/utils';
import {Button} from '../../Button';
import {Text} from '../Text';

describe('Text', () => {
test('should return expected jsx if no props passed', () => {
const {container} = render(<Text>Hello World!</Text>);

expect(container).toMatchSnapshot();
});

test('should return expected jsx if qa attr passed and variant changed', () => {
const {container} = render(
<Text qa="test" variant={'caption-1'}>
Hello World!
</Text>,
);

expect(container).toMatchSnapshot();
});

test('should return expected jsx if html attributes passed and changed default html tag and with typed ref', () => {
const ComponentWithRef = () => {
const ref = React.useRef<HTMLLabelElement>(null);

return (
<Text as="label" htmlFor="some-id" ref={ref}>
Hello World!
</Text>
);
};

const {container} = render(<ComponentWithRef />);

expect(container).toMatchSnapshot();
});

test('should return expected jsx if passed props what converted to classNames and styles', () => {
const {container} = render(
<Text
width={200}
ellipsisLines={2}
ellipsis={true}
whiteSpace="break-spaces"
wordBreak="break-word"
>
Hello World!
</Text>,
);

expect(container).toMatchSnapshot();
});

test('should return expected jsx with another component substitution', () => {
const {container} = render(
<Text as={Button} size={'m'} width={100} spacing={{mr: 2}}>
Hello World!
</Text>,
);

expect(container).toMatchSnapshot();
});
});
60 changes: 60 additions & 0 deletions src/components/Text/__tests__/__snapshots__/Text.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Text should return expected jsx if html attributes passed and changed default html tag and with typed ref 1`] = `
<div>
<label
class="g-box g-text g-text_variant_body-1"
for="some-id"
>
Hello World!
</label>
</div>
`;

exports[`Text should return expected jsx if no props passed 1`] = `
<div>
<span
class="g-box g-text g-text_variant_body-1"
>
Hello World!
</span>
</div>
`;

exports[`Text should return expected jsx if passed props what converted to classNames and styles 1`] = `
<div>
<span
class="g-box g-text g-text_variant_body-1 g-text_ellipsis g-text_ws_break-spaces g-text_wb_break-word g-text_ellipsis-lines"
style="width: 200px;"
>
Hello World!
</span>
</div>
`;

exports[`Text should return expected jsx if qa attr passed and variant changed 1`] = `
<div>
<span
class="g-box g-text g-text_variant_caption-1"
data-qa="test"
>
Hello World!
</span>
</div>
`;

exports[`Text should return expected jsx with another component substitution 1`] = `
<div>
<button
class="g-button g-button_view_normal g-button_size_m g-button_pin_round-round g-box g-s__mr_2 g-text g-text_variant_body-1"
style="width: 100px;"
type="button"
>
<span
class="g-button__text"
>
Hello World!
</span>
</button>
</div>
`;
11 changes: 9 additions & 2 deletions src/components/layout/Box/Box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export interface BoxProps<T extends React.ElementType = 'div'>
spacing?: SpacingProps;
}

type BoxRef<C extends React.ElementType> = React.ComponentPropsWithRef<C>['ref'];

type BoxPropsWithTypedAttrs<T extends React.ElementType> = BoxProps<T> &
Omit<React.ComponentPropsWithoutRef<T>, keyof BoxProps<T>>;

/**
* Basic block to build other components and for standalone usage as a smart block with build in support of most usable css properties and shortcut `spacing` properties.
* ```tsx
Expand Down Expand Up @@ -63,7 +68,7 @@ export const Box = React.forwardRef(function Box<T extends React.ElementType = '
overflow,
...props
}: BoxProps<T>,
ref?: React.ComponentPropsWithRef<T>['ref'],
ref?: BoxRef<T>,
) {
const Tag: React.ElementType = as || 'div';

Expand All @@ -88,4 +93,6 @@ export const Box = React.forwardRef(function Box<T extends React.ElementType = '
{children}
</Tag>
);
});
}) as (<C extends React.ElementType = 'div'>(
props: BoxPropsWithTypedAttrs<C> & {ref?: BoxRef<C>},
) => React.ReactElement) & {displayName: string};
22 changes: 14 additions & 8 deletions src/components/layout/Flex/Flex.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';

import type {QAProps} from '../../types';
import {block} from '../../utils/cn';
import {Box} from '../Box/Box';
import type {BoxProps} from '../Box/Box';
Expand All @@ -12,7 +11,7 @@ import './Flex.scss';

const b = block('flex');

export interface FlexProps<T extends React.ElementType = 'div'> extends QAProps, BoxProps<T> {
export interface FlexProps<T extends React.ElementType = 'div'> extends BoxProps<T> {
/**
* `flex-direction` property
*/
Expand Down Expand Up @@ -90,6 +89,11 @@ export interface FlexProps<T extends React.ElementType = 'div'> extends QAProps,
space?: Space | MediaPartial<Space>;
}

type FlexRef<C extends React.ElementType> = React.ComponentPropsWithRef<C>['ref'];

type FlexPropsWithTypedAttrs<T extends React.ElementType> = FlexProps<T> &
Omit<React.ComponentPropsWithoutRef<T>, keyof FlexProps<T>>;

/**
* Flexbox model utility component.
*
Expand Down Expand Up @@ -124,11 +128,9 @@ export interface FlexProps<T extends React.ElementType = 'div'> extends QAProps,
* ---
* Storybook - https://preview.gravity-ui.com/uikit/?path=/docs/layout--playground#flex
*/
export const Flex = React.forwardRef(function Flex<T extends React.ElementType = 'div'>(
props: FlexProps<T>,
ref: React.ComponentPropsWithRef<T>['ref'],
) {
export const Flex = function Flex<T extends React.ElementType = 'div'>(props: FlexProps<T>) {
const {
as: propsAs,
direction,
grow,
basis,
Expand All @@ -151,6 +153,8 @@ export const Flex = React.forwardRef(function Flex<T extends React.ElementType =
...restProps
} = props;

const as: React.ElementType = propsAs || 'div';

const {
getClosestMediaProps,
theme: {spaceBaseSize},
Expand All @@ -174,6 +178,7 @@ export const Flex = React.forwardRef(function Flex<T extends React.ElementType =

return (
<Box
as={as}
className={b(
{
'center-content': centerContent,
Expand All @@ -198,7 +203,6 @@ export const Flex = React.forwardRef(function Flex<T extends React.ElementType =
justifySelf: applyMediaProps(justifySelf),
...style,
}}
ref={ref}
{...restProps}
>
{space
Expand All @@ -209,4 +213,6 @@ export const Flex = React.forwardRef(function Flex<T extends React.ElementType =
: children}
</Box>
);
});
} as (<C extends React.ElementType = 'div'>(
props: FlexPropsWithTypedAttrs<C> & {ref?: FlexRef<C>},
) => React.ReactElement) & {displayName: string};

0 comments on commit 3abfe9e

Please sign in to comment.