Skip to content

Commit

Permalink
[EuiText] Support component prop (#7993)
Browse files Browse the repository at this point in the history
Co-authored-by: Cee Chen <[email protected]>
  • Loading branch information
mgadewoll and cee-chen authored Sep 6, 2024
1 parent c60a91c commit ba42930
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 65 deletions.
1 change: 1 addition & 0 deletions packages/eui/changelogs/upcoming/7993.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Updated `EuiText`, `EuiTextColor`, and `EuiTextAlign` with a new `component` prop that allows changing the default rendered `<div>` wrapper to a `<span>` or `<p>` tag.
1 change: 1 addition & 0 deletions packages/eui/src/components/text/text.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const meta: Meta<EuiTextProps> = {
grow: true,
color: 'default',
textAlign: 'left',
component: 'div',
},
};
moveStorybookControlsToCategory(meta, ['color'], 'EuiTextColor props');
Expand Down
10 changes: 10 additions & 0 deletions packages/eui/src/components/text/text.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,15 @@ describe('EuiText', () => {

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

test('component', () => {
const { container } = render(
<EuiText {...requiredProps} component="span">
Content
</EuiText>
);

expect(container.firstChild?.nodeName).toBe('SPAN');
});
});
});
42 changes: 24 additions & 18 deletions packages/eui/src/components/text/text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,31 @@
* Side Public License, v 1.
*/

import React, { FunctionComponent, HTMLAttributes, CSSProperties } from 'react';
import React, { FunctionComponent } from 'react';
import classNames from 'classnames';
import { CommonProps } from '../common';

import { useEuiMemoizedStyles } from '../../services';
import { euiTextStyles } from './text.styles';

import { TextColor, EuiTextColor } from './text_color';

import { EuiTextAlign, TextAlignment } from './text_align';
import type { SharedTextProps, EuiTextColors, EuiTextAlignment } from './types';
import { EuiTextColor } from './text_color';
import { EuiTextAlign } from './text_align';
import { euiTextStyles } from './text.styles';

export const TEXT_SIZES = ['xs', 's', 'm', 'relative'] as const;
export type TextSize = (typeof TEXT_SIZES)[number];

export type EuiTextProps = CommonProps &
Omit<HTMLAttributes<HTMLDivElement>, 'color'> & {
textAlign?: TextAlignment;
export type EuiTextProps = SharedTextProps &
EuiTextColors &
EuiTextAlignment & {
/**
* Determines the text size. Choose `relative` to control the `font-size` based on the value of a parent container.
*/
size?: TextSize;
/**
* Any of our named colors or a `hex`, `rgb` or `rgba` value.
* @default inherit
*/
color?: TextColor | CSSProperties['color'];
grow?: boolean;
};

export const EuiText: FunctionComponent<EuiTextProps> = ({
component = 'div',
size = 'm',
color,
grow = true,
Expand All @@ -52,24 +47,35 @@ export const EuiText: FunctionComponent<EuiTextProps> = ({
];

const classes = classNames('euiText', className);
const Component = component;

let text = (
<div css={cssStyles} className={classes} {...rest}>
<Component css={cssStyles} className={classes} {...rest}>
{children}
</div>
</Component>
);

if (color) {
text = (
<EuiTextColor color={color} className={classes} cloneElement>
<EuiTextColor
component={component}
color={color}
className={classes}
cloneElement
>
{text}
</EuiTextColor>
);
}

if (textAlign) {
text = (
<EuiTextAlign textAlign={textAlign} className={classes} cloneElement>
<EuiTextAlign
component={component}
textAlign={textAlign}
className={classes}
cloneElement
>
{text}
</EuiTextAlign>
);
Expand Down
1 change: 1 addition & 0 deletions packages/eui/src/components/text/text_align.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const meta: Meta<EuiTextAlignProps> = {
args: {
textAlign: 'left',
cloneElement: false,
component: 'div',
},
};
hideStorybookControls(meta, ['aria-label']);
Expand Down
10 changes: 10 additions & 0 deletions packages/eui/src/components/text/text_align.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,15 @@ describe('EuiTextAlign', () => {

shouldRenderCustomStyles(<EuiTextAlign cloneElement textAlign="right" />);
});

test('component', () => {
const { container } = render(
<EuiTextAlign {...requiredProps} component="span">
Content
</EuiTextAlign>
);

expect(container.firstChild?.nodeName).toBe('SPAN');
});
});
});
24 changes: 7 additions & 17 deletions packages/eui/src/components/text/text_align.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,21 @@
* Side Public License, v 1.
*/

import React, {
FunctionComponent,
HTMLAttributes,
isValidElement,
} from 'react';
import { CommonProps } from '../common';
import React, { FunctionComponent, isValidElement } from 'react';
import { cloneElementWithCss } from '../../services';

import type { SharedTextProps, CloneElement, EuiTextAlignment } from './types';
import { euiTextAlignStyles as styles } from './text_align.styles';

export const ALIGNMENTS = ['left', 'right', 'center'] as const;
export type TextAlignment = (typeof ALIGNMENTS)[number];

export type EuiTextAlignProps = CommonProps &
HTMLAttributes<HTMLDivElement> & {
textAlign?: TextAlignment;
/**
* Applies text styling to the child element instead of rendering a parent wrapper `div`.
* Can only be used when wrapping a *single* child element/tag, and not raw text.
*/
cloneElement?: boolean;
};
export type EuiTextAlignProps = SharedTextProps &
CloneElement &
EuiTextAlignment;

export const EuiTextAlign: FunctionComponent<EuiTextAlignProps> = ({
children,
component: Component = 'div',
textAlign = 'left',
cloneElement = false,
...rest
Expand All @@ -42,6 +32,6 @@ export const EuiTextAlign: FunctionComponent<EuiTextAlignProps> = ({
if (isValidElement(children) && cloneElement) {
return cloneElementWithCss(children, props);
} else {
return <div {...props}>{children}</div>;
return <Component {...props}>{children}</Component>;
}
};
10 changes: 10 additions & 0 deletions packages/eui/src/components/text/text_color.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,15 @@ describe('EuiTextColor', () => {
</EuiTextColor>
);
});

test('component', () => {
const { container } = render(
<EuiTextColor {...requiredProps} component="span">
Content
</EuiTextColor>
);

expect(container.firstChild?.nodeName).toBe('SPAN');
});
});
});
34 changes: 4 additions & 30 deletions packages/eui/src/components/text/text_color.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,9 @@
* Side Public License, v 1.
*/

import React, {
FunctionComponent,
HTMLAttributes,
CSSProperties,
isValidElement,
} from 'react';

import { CommonProps } from '../common';
import React, { FunctionComponent, isValidElement } from 'react';
import { useEuiMemoizedStyles, cloneElementWithCss } from '../../services';

import type { SharedTextProps, CloneElement, EuiTextColors } from './types';
import { euiTextColorStyles } from './text_color.styles';

export const COLORS = [
Expand All @@ -32,30 +25,12 @@ export type TextColor = (typeof COLORS)[number];
export const _isNamedColor = (color: any): color is TextColor =>
COLORS.includes(color);

export type EuiTextColorProps = CommonProps &
Omit<
HTMLAttributes<HTMLDivElement> & HTMLAttributes<HTMLSpanElement>,
'color'
> & {
/**
* Any of our named colors or a `hex`, `rgb` or `rgba` value.
*/
color?: TextColor | CSSProperties['color'];
/**
* Determines the root element
*/
component?: 'div' | 'span';
/**
* Applies text styling to the child element instead of rendering a parent wrapper `span`/`div`.
* Can only be used when wrapping a *single* child element/tag, and not raw text.
*/
cloneElement?: boolean;
};
export type EuiTextColorProps = SharedTextProps & CloneElement & EuiTextColors;

export const EuiTextColor: FunctionComponent<EuiTextColorProps> = ({
children,
color = 'default',
component = 'span',
component: Component = 'span',
cloneElement = false,
style,
...rest
Expand Down Expand Up @@ -84,7 +59,6 @@ export const EuiTextColor: FunctionComponent<EuiTextColorProps> = ({
const childrenStyle = { ...children.props.style, ...euiTextStyle };
return cloneElementWithCss(children, { ...props, style: childrenStyle });
} else {
const Component = component;
return <Component {...props}>{children}</Component>;
}
};
48 changes: 48 additions & 0 deletions packages/eui/src/components/text/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { HTMLAttributes, CSSProperties } from 'react';
import type { CommonProps } from '../common';

import type { TextColor } from './text_color';
import type { TextAlignment } from './text_align';

export type SharedTextProps = CommonProps &
Omit<HTMLAttributes<HTMLElement>, 'color'> & {
/**
* The HTML element/tag to render.
* Use with care when nesting multiple components to ensure valid XHTML:
* - `<div>` and other block tags are not valid to use inside `<p>`. If using the `<p>` tag, we recommend passing strings/text only.
* - `<span>` is valid to be nested in any tag, and can have any tag nested within it.
*/
component?: 'div' | 'span' | 'p';
};

export type CloneElement = {
/**
* Applies text styling to the child element instead of rendering a parent wrapper.
* Can only be used when wrapping a *single* child element/tag, and not raw text.
*/
cloneElement?: boolean;
};

export type EuiTextColors = {
/**
* Any of our named colors or a `hex`, `rgb` or `rgba` value.
* @default inherit
*/
color?: TextColor | CSSProperties['color'];
};

export type EuiTextAlignment = {
/**
* Applies horizontal text alignment
* @default left
*/
textAlign?: TextAlignment;
};

0 comments on commit ba42930

Please sign in to comment.