Skip to content

feat(Text): replace with UI5 Web Component #5988

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 2, 2024
35 changes: 35 additions & 0 deletions docs/MigrationGuide.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,35 @@ Please use the following components instead:

Also, the namings of internal `data-component-name` attributes have been adjusted accordingly. E.g. `data-component-name="DynamicPageTitleSubHeader"` has been renamed to `data-component-name="ObjectPageTitleSubHeader"`

### Text

The `Text` component has been replaced with the `ui5-text` Web Component.

The following props have been removed:

- `wrapping`
Can be achieved via the `maxLines` property. If `maxLines` is set to `1`, the text will not wrap, otherwise it will wrap.
- `renderWhitespace`
Can be achieved by adding `white-space: pre-wrap;` via inline styles or `className` to the `Text` component.
- `hyphenated` and `emptyIndicator`
These props are currently not supported by the new `ui5-text` component. You can follow this [feature request](https://github.com/SAP/ui5-webcomponents/issues/9244) for updates.

```tsx
// v1
import { Text } from '@ui5/webcomponents-react';

function MyComponent() {
return <Text wrapping={false}>Lorem Impsum</Text>;
}

// v2
import { Text } from '@ui5/webcomponents-react';

function MyComponent() {
return <Text maxLines={1}>Lorem Impsum</Text>;
}
```

## Components with API Changes

### ActionSheet
Expand Down Expand Up @@ -338,6 +367,12 @@ function MyComponent() {
}
```

### Expandable Text

The prop `portalContainer` has been removed as it is no longer needed due to the [popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API) which is now used in the UI5 Web Components.
As the underlying `Text` component has been replaced with the UI5 Web Component, some inherited props `hyphenated` and `emptyIndicator` from the `Text` component have been removed.
You can follow this [feature request](https://github.com/SAP/ui5-webcomponents/issues/9244) for updates.

## Enum Changes

For a better alignment with the UI5 Web Components, the following enums have been renamed:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { ThemingParameters } from '@ui5/webcomponents-react-base';
import { DeviationIndicator, ValueColor } from '../../enums';
import type { CardPropTypes } from '../../webComponents/Card/index.js';
import { Card } from '../../webComponents/Card/index.js';
import { Text } from '../../webComponents/Text/index.js';
import { NumericSideIndicator } from '../NumericSideIndicator';
import { Text } from '../Text';
import type { AnalyticalCardHeaderPropTypes } from './index';
import { AnalyticalCardHeader } from './index';
import { cypressPassThroughTestsFactory } from '@/cypress/support/utils';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ import {
FlexBoxJustifyContent,
TextAlign
} from '../../enums/index.js';
import { Button, MultiComboBox, MultiComboBoxItem, Option, Select, Tag } from '../../webComponents/index.js';
import { Button, MultiComboBox, MultiComboBoxItem, Option, Select, Tag, Text } from '../../webComponents/index.js';
import { FlexBox } from '../FlexBox';
import { Text } from '../Text';
import { AnalyticalTable } from './index.js';

const meta = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import type { Meta, StoryObj } from '@storybook/react';
import InputType from '@ui5/webcomponents/dist/types/InputType.js';
import React, { useReducer, useState } from 'react';
import { AnalyticalTableSelectionMode, FlexBoxAlignItems, FlexBoxDirection } from '../../enums';
import { Button, CheckBox, Input, Label, ToggleButton } from '../../webComponents';
import { Button, CheckBox, Input, Label, ToggleButton, Text } from '../../webComponents';
import { FlexBox } from '../FlexBox';
import { Text } from '../Text';
import meta from './AnalyticalTable.stories';
import * as AnalyticalTableHooks from './pluginHooks/AnalyticalTableHooks';
import { AnalyticalTable } from './index';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type {
} from 'react';
import { useRef, useState } from 'react';
import { Icon } from '../../../webComponents/Icon/index.js';
import { Text } from '../../Text/index.js';
import { Text } from '../../../webComponents/Text/index.js';
import type { ColumnType } from '../types/ColumnType.js';
import type { DivWithCustomScrollProp } from '../types/index.js';
import { classNames, styleData } from './ColumnHeader.module.css.js';
Expand Down Expand Up @@ -200,7 +200,7 @@ export const ColumnHeader = (props: ColumnHeaderProps) => {
<div className={classNames.header} data-h-align={column.hAlign}>
<Text
title={tooltip}
wrapping={false}
maxLines={1}
style={textStyle}
className={clsx(
classNames.text,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useStylesheet } from '@ui5/webcomponents-react-base';
import { makeRenderer } from 'react-table';
import { FlexBoxAlignItems, FlexBoxDirection, FlexBoxWrap } from '../../../../enums/index.js';
import { Text } from '../../../../webComponents/Text/index.js';
import { FlexBox } from '../../../FlexBox/index.js';
import { Text } from '../../../Text/index.js';
import { classNames, styleData } from './PopIn.module.css.js';

export const PopIn = (instance) => {
Expand Down Expand Up @@ -43,15 +43,15 @@ export const PopIn = (instance) => {
const cell = item.column.Cell;
if (typeof cell === 'string') {
return (
<Text wrapping={false} title={cell}>
<Text maxLines={1} title={cell}>
{cell}
</Text>
);
}
return makeRenderer({ ...instance, ...popInInstanceProps, isPopIn: true }, item.column)(item.column.Cell);
}
return popInInstanceProps?.value ? (
<Text wrapping={false} title={popInInstanceProps.value}>
<Text maxLines={1} title={popInInstanceProps.value}>
{popInInstanceProps.value}
</Text>
) : null;
Expand Down
2 changes: 1 addition & 1 deletion packages/main/src/components/AnalyticalTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ import {
SELECT_PRESS_SPACE,
UNSELECT_PRESS_SPACE
} from '../../i18n/i18n-defaults.js';
import { Text } from '../../webComponents/Text/index.js';
import { FlexBox } from '../FlexBox/index.js';
import { Text } from '../Text/index.js';
import { classNames, styleData } from './AnalyticalTable.module.css.js';
import { ColumnHeaderContainer } from './ColumnHeader/ColumnHeaderContainer.js';
import { DefaultColumn } from './defaults/Column/index.js';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
.expandableText {
composes: text from '../Text/Text.module.css';
font-family: var(--sapFontFamily);
font-size: var(--sapFontSize);
font-weight: normal;
color: var(--sapTextColor);
display: inline-block;
box-sizing: border-box;
white-space: pre-line;
word-wrap: break-word;
max-width: 100%;
}

.text {
display: inline;
}

.renderWhitespace {
white-space: pre-wrap;
}

.ellipsis {
word-spacing: 0.125rem;
}
Expand All @@ -17,4 +29,3 @@
.popover::part(content) {
padding: 1rem;
}

122 changes: 38 additions & 84 deletions packages/main/src/components/ExpandableText/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,16 @@
import LinkAccessibleRole from '@ui5/webcomponents/dist/types/LinkAccessibleRole.js';
import { useI18nBundle, useIsomorphicId, useStylesheet } from '@ui5/webcomponents-react-base';
import { clsx } from 'clsx';
import { forwardRef, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { forwardRef, useState } from 'react';
import { CLOSE_POPOVER, SHOW_FULL_TEXT, SHOW_LESS, SHOW_MORE } from '../../i18n/i18n-defaults.js';
import { useCanRenderPortal } from '../../internal/ssr.js';
import { getUi5TagWithSuffix } from '../../internal/utils.js';
import type { CommonProps } from '../../types/index.js';
import type { LinkDomRef } from '../../webComponents/index.js';
import { Link } from '../../webComponents/index.js';
import { ResponsivePopover } from '../../webComponents/ResponsivePopover/index.js';
import type { TextPropTypes } from '../Text/index.js';
import { Text } from '../Text/index.js';
import type { TextPropTypes } from '../../webComponents/Text/index.js';
import { Text } from '../../webComponents/Text/index.js';
import { classNames, styleData } from './ExpandableText.module.css.js';

export interface ExpandableTextPropTypes
extends Omit<TextPropTypes, 'maxLines' | 'wrapping' | 'children'>,
CommonProps {
export interface ExpandableTextPropTypes extends Omit<TextPropTypes, 'maxLines' | 'children'>, CommonProps {
/**
* Determines the text to be displayed.
*/
Expand All @@ -34,13 +28,9 @@ export interface ExpandableTextPropTypes
*/
showOverflowInPopover?: boolean;
/**
* Defines where modals are rendered into via `React.createPortal`.
*
* You can find out more about this [here](https://sap.github.io/ui5-webcomponents-react/?path=/docs/knowledge-base-working-with-portals--page).
*
* @default document.body
* Defines how white-space inside <code>Text</code> is handled. If set to true, sequences of white space are preserved.
*/
portalContainer?: Element;
renderWhitespace?: boolean;
}

/**
Expand All @@ -51,23 +41,12 @@ export interface ExpandableTextPropTypes
* @since 1.23.0
*/
const ExpandableText = forwardRef<HTMLSpanElement, ExpandableTextPropTypes>((props, ref) => {
const {
children,
emptyIndicator,
renderWhitespace,
hyphenated,
showOverflowInPopover,
maxCharacters = 100,
portalContainer,
className,
...rest
} = props;
const { children, showOverflowInPopover, maxCharacters = 100, renderWhitespace, className, ...rest } = props;

useStylesheet(styleData, ExpandableText.displayName);

const [collapsed, setCollapsed] = useState(true);
const [popoverOpen, setPopoverOpen] = useState(false);
const linkRef = useRef<LinkDomRef>(null);
const uniqueId = useIsomorphicId();
const i18nBundle = useI18nBundle('@ui5/webcomponents-react');
const trimmedChildren = renderWhitespace ? children : children?.replace(/\s+/g, ' ').trim();
Expand All @@ -87,64 +66,39 @@ const ExpandableText = forwardRef<HTMLSpanElement, ExpandableTextPropTypes>((pro
setPopoverOpen(false);
};

useEffect(() => {
const tagName = getUi5TagWithSuffix('ui5-link');
void customElements.whenDefined(tagName).then(() => {
if (linkRef.current) {
if (showOverflowInPopover) {
linkRef.current.accessibilityAttributes = { hasPopup: 'dialog' };
} else {
linkRef.current.accessibilityAttributes = { expanded: !collapsed };
}
}
});
}, [collapsed, showOverflowInPopover]);

const canRenderPortal = useCanRenderPortal();
if (showOverflowInPopover && !canRenderPortal) {
return null;
}
return (
<span className={clsx(classNames.expandableText, className)} {...rest} ref={ref}>
<Text
emptyIndicator={emptyIndicator}
renderWhitespace={renderWhitespace}
hyphenated={hyphenated}
className={classNames.text}
>
{strippedChildren}
</Text>
{isOverflow && (
<>
<span className={classNames.ellipsis}>{showOverflowInPopover || collapsed ? '… ' : ' '}</span>
<Link
accessibleName={
showOverflowInPopover
? collapsed
? i18nBundle.getText(SHOW_FULL_TEXT)
: i18nBundle.getText(CLOSE_POPOVER)
: undefined
}
accessibleRole={LinkAccessibleRole.Button}
onClick={handleClick}
ref={linkRef}
id={`${uniqueId}-link`}
>
{collapsed ? i18nBundle.getText(SHOW_MORE) : i18nBundle.getText(SHOW_LESS)}
</Link>
</>
)}
{showOverflowInPopover &&
popoverOpen &&
createPortal(
<ResponsivePopover opener={`${uniqueId}-link`} open onClose={closePopover} className={classNames.popover}>
<Text renderWhitespace={renderWhitespace} hyphenated={hyphenated} className={classNames.text}>
{children}
</Text>
</ResponsivePopover>,
portalContainer ?? document.body
<>
<span className={clsx(classNames.expandableText, className)} {...rest} ref={ref}>
<Text className={clsx(classNames.text, renderWhitespace && classNames.renderWhitespace)}>
{strippedChildren}
</Text>
{isOverflow && (
<>
<span className={classNames.ellipsis}>{showOverflowInPopover || collapsed ? '… ' : ' '}</span>
<Link
accessibleName={
showOverflowInPopover
? collapsed
? i18nBundle.getText(SHOW_FULL_TEXT)
: i18nBundle.getText(CLOSE_POPOVER)
: undefined
}
accessibleRole={LinkAccessibleRole.Button}
accessibilityAttributes={showOverflowInPopover ? { hasPopup: 'dialog' } : { expanded: !collapsed }}
onClick={handleClick}
id={`${uniqueId}-link`}
>
{collapsed ? i18nBundle.getText(SHOW_MORE) : i18nBundle.getText(SHOW_LESS)}
</Link>
</>
)}
</span>
</span>
{showOverflowInPopover && popoverOpen && (
<ResponsivePopover opener={`${uniqueId}-link`} open onClose={closePopover} className={classNames.popover}>
<Text className={clsx(classNames.text, renderWhitespace && classNames.renderWhitespace)}>{children}</Text>
</ResponsivePopover>
)}
</>
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ import {
Title,
Token
} from '../../webComponents/index.js';
import { Text } from '../../webComponents/Text/index.js';
import { FilterGroupItem } from '../FilterGroupItem/index.js';
import { FlexBox } from '../FlexBox/index.js';
import { Text } from '../Text/index.js';
import { VariantManagement } from '../VariantManagement/index.js';
import { VariantItem } from '../VariantManagement/VariantItem.js';
import { FilterBar } from './index.js';
Expand Down
2 changes: 1 addition & 1 deletion packages/main/src/components/Loader/Loader.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { FlexBoxDirection, LoaderType } from '../../enums/index.js';
import { Card } from '../../webComponents/Card/index.js';
import { CardHeader } from '../../webComponents/CardHeader/index.js';
import { Icon } from '../../webComponents/Icon/index.js';
import { Text } from '../../webComponents/Text/index.js';
import { FlexBox } from '../FlexBox/index.js';
import { Text } from '../Text/index.js';
import { Loader } from './index.js';

const meta = {
Expand Down
3 changes: 1 addition & 2 deletions packages/main/src/components/MessageBox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ import {
YES
} from '../../i18n/i18n-defaults.js';
import type { ButtonPropTypes, DialogDomRef, DialogPropTypes } from '../../webComponents/index.js';
import { Button, Dialog, Icon, Title } from '../../webComponents/index.js';
import { Text } from '../Text/index.js';
import { Button, Dialog, Icon, Text, Title } from '../../webComponents/index.js';
import { classNames, styleData } from './MessageBox.module.css.js';

// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Meta, StoryObj } from '@storybook/react';
import ValueState from '@ui5/webcomponents-base/dist/types/ValueState.js';
import { Text } from '../../webComponents/Text/index.js';
import { FlexBox } from '../FlexBox/index.js';
import { Text } from '../Text/index.js';
import { MessageViewButton } from './index.js';

const meta = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { clsx } from 'clsx';
import { forwardRef } from 'react';
import { ValueColor } from '../../enums/index.js';
import type { CommonProps } from '../../types/index.js';
import { Text } from '../Text/index.js';
import { Text } from '../../webComponents/Text/index.js';
import { classNames, styleData } from './NumericSideIndicator.module.css.js';

export interface NumericSideIndicatorPropTypes extends CommonProps {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { StoryObj, Meta } from '@storybook/react';
import { ThemingParameters } from '@ui5/webcomponents-react-base';
import { Text } from '../Text/index.js';
import { Text } from '../../webComponents/Text/index.js';
import { ResponsiveGridLayout } from './index.js';

const meta = {
Expand Down
2 changes: 1 addition & 1 deletion packages/main/src/components/SelectDialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import type {
ListItemStandardDomRef
} from '../../webComponents/index.js';
import { Button, Dialog, Icon, Input, List, Title } from '../../webComponents/index.js';
import { Text } from '../Text/index.js';
import { Text } from '../../webComponents/Text/index.js';
import { Toolbar } from '../Toolbar/index.js';
import { classNames, styleData } from './SelectDialog.module.css.js';

Expand Down
Loading
Loading