Skip to content

Commit 80d8c0b

Browse files
authored
feat: move Loader to compat package & replace with BusyIndicator (#6020)
BREAKING CHANGE: The deprecated `Loader` component has been moved to the `@ui5/webcomponents-react-compat` package. Please use the `BusyIndicator` instead, as it's now the only loading indicator that is supported by our UX guidelines. Please visit our [Migration Guide](https://sap.github.io/ui5-webcomponents-react/main/?path=/docs/migration-guide--docs) for more details.
1 parent 160aa88 commit 80d8c0b

File tree

20 files changed

+284
-272
lines changed

20 files changed

+284
-272
lines changed

docs/MigrationGuide.mdx

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,54 @@ function MyComponent() {
347347
}
348348
```
349349

350+
### Loader
351+
352+
There is no longer a concept of a Loader component defined by the UX guidelines. To indicate a loading state, it is now recommended using the `BusyIndicator` instead.
353+
For backwards compatibility, the Loader is still available in the `@ui5/webcomponents-react-compat` package, but it may lack accessibility features and no longer receives feature updates.
354+
355+
#### Replacing `Loader` with `BusyIndicator`
356+
357+
Unfortunately, there is no general rule of thumb for how to replace the `Loader` component with the `BusyIndicator` component. In most cases it should be sufficient wrapping your component inside the `BusyIndicator`, as shown below:
358+
359+
```jsx
360+
// v1
361+
362+
<Card header={<CardHeader titleText="Card Header" />}>
363+
<Loader />
364+
<div style={{ height: '400px', padding: '1rem' }}>
365+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer sed felis tristique, molestie tellus id, rutrum
366+
urna. Quisque mattis risus imperdiet gravida accumsan. Proin elementum efficitur diam eu interdum.
367+
</div>
368+
</Card>
369+
370+
// v2
371+
372+
<Card header={<CardHeader titleText="Card Header" />}>
373+
<BusyIndicator active delay={0}>
374+
<div style={{ height: '400px', padding: '1rem' }}>
375+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer sed felis tristique, molestie tellus id,
376+
rutrum urna. Quisque mattis risus imperdiet gravida accumsan. Proin elementum efficitur diam eu interdum.
377+
</div>
378+
</BusyIndicator>
379+
</Card>
380+
```
381+
382+
However, for components that apply complex styles such as `absolute/fixed` positioning, this might not be the case, as the `BusyIndicator` brings its own set of styles.
383+
In such instances, we recommend positioning the `BusyIndicator` above the element that should receive a loading indicator e.g. via `position: absolute`.
384+
If you encounter any issues migrating to the `BusyIndicator`, please feel free to reach out via [GitHub Discussions](https://github.com/SAP/ui5-webcomponents-react/discussions) or create an [Issue](https://github.com/SAP/ui5-webcomponents-react/issues/new/choose) if the behavior seems like a bug.
385+
386+
#### Keep the `Loader`
387+
388+
If you'd like to keep the `Loader` component instead of the `BusyIndicator` component, you now need to include the `@ui5/webcomponents-react-compat` package in your dependencies and adjust all import paths accordingly:
389+
390+
```ts
391+
// v1
392+
import { Loader } from '@ui5/webcomponents-react';
393+
394+
// v2
395+
import { Loader } from '@ui5/webcomponents-react-compat';
396+
```
397+
350398
### ObjectPage
351399

352400
The newly introduced `DynamicPage` web component comes with its own `DynamicPageHeader` and `DynamicPageTitle` components, which are unfortunately incompatible with our `ObjectPage` implementation.

packages/charts/src/internal/ChartContainer.module.css

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,19 @@
33
color: var(--sapTextColor);
44
font-family: var(--sapFontFamily);
55
width: 100%;
6-
/*todo remove in v2*/
76
height: 400px;
7+
min-height: fit-content;
88
position: relative;
99
}
1010

11+
.busyIndicator {
12+
background-color: color-mix(in srgb, var(--sapBackgroundColor) 45%, transparent);
13+
position: absolute;
14+
inset: 0;
15+
height: 100%;
16+
z-index: 1;
17+
}
18+
1119
:global(.has-click-handler) {
1220
:global(.recharts-pie-sector),
1321
:global(.recharts-bar-rectangles),

packages/charts/src/internal/ChartContainer.tsx

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
1-
import { type CommonProps, Label, Loader } from '@ui5/webcomponents-react';
1+
import { BusyIndicator, Label } from '@ui5/webcomponents-react';
2+
import type { CommonProps } from '@ui5/webcomponents-react';
3+
import { addCustomCSSWithScoping } from '@ui5/webcomponents-react/dist/internal/addCustomCSSWithScoping.js';
24
import { useStylesheet } from '@ui5/webcomponents-react-base';
35
import { clsx } from 'clsx';
4-
import type { ComponentType, CSSProperties, ReactElement, ReactNode } from 'react';
6+
import type { ComponentType, ReactElement, ReactNode } from 'react';
57
import { Component, forwardRef } from 'react';
68
import { ResponsiveContainer } from 'recharts';
79
import { classNames, styleData } from './ChartContainer.module.css.js';
810

11+
//todo: add feature request for parts or even a fix if this turns out to be a bug
12+
addCustomCSSWithScoping(
13+
'ui5-busy-indicator',
14+
`
15+
:host([data-component-name="ChartContainerBusyIndicator"]) .ui5-busy-indicator-busy-area{
16+
background:unset;
17+
},
18+
:host([data-component-name="ChartContainerBusyIndicator"]) .ui5-busy-indicator-busy-area:focus {
19+
border-radius: 0;
20+
}
21+
`
22+
);
23+
924
export interface ContainerProps extends CommonProps {
1025
children: ReactElement;
1126
Placeholder?: ComponentType;
@@ -14,13 +29,6 @@ export interface ContainerProps extends CommonProps {
1429
resizeDebounce: number;
1530
}
1631

17-
const loaderStyles: CSSProperties = {
18-
position: 'absolute',
19-
top: 0,
20-
left: 0,
21-
right: 0
22-
};
23-
2432
class ErrorBoundary extends Component<{ children: ReactNode }, { errorCount: number }> {
2533
state = {
2634
errorCount: 0
@@ -49,7 +57,13 @@ const ChartContainer = forwardRef<HTMLDivElement, ContainerProps>((props, ref) =
4957
<div ref={ref} className={clsx(classNames.container, className)} slot={slot} {...rest}>
5058
{dataset?.length > 0 ? (
5159
<>
52-
{loading && <Loader style={loaderStyles} />}
60+
{loading && (
61+
<BusyIndicator
62+
active
63+
className={classNames.busyIndicator}
64+
data-component-name="ChartContainerBusyIndicator"
65+
/>
66+
)}
5367
<ErrorBoundary>
5468
<ResponsiveContainer debounce={resizeDebounce}>{children}</ResponsiveContainer>
5569
</ErrorBoundary>

packages/compat/src/components/Loader/Loader.cy.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1+
import { LoaderType } from '../../enums/index.js';
2+
import { Loader } from './index.js';
13
import { cypressPassThroughTestsFactory } from '@/cypress/support/utils';
2-
import type { LoaderType } from '@/packages/main';
3-
import { Loader } from '@/packages/main';
44

5-
// skip until component is moved to this package
6-
describe.skip('Loader', () => {
5+
describe('Loader', () => {
76
it('indeterminate', () => {
87
cy.mount(<Loader data-testid="loader" />);
98
cy.findByTestId('loader').should('have.css', 'animation-duration', '1.2s');
Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,41 @@
1-
.dummy {
2-
background-color: red;
1+
.loader {
2+
position: relative;
3+
height: 0.25rem;
4+
width: 100%;
5+
6+
&:before {
7+
content: '';
8+
position: absolute;
9+
left: 0;
10+
width: 100%;
11+
height: 100%;
12+
background-color: var(--sapContent_BusyColor);
13+
opacity: 0.15;
14+
}
15+
16+
&.loaderDeterminate {
17+
background: linear-gradient(to right, var(--sapContent_BusyColor), var(--sapContent_BusyColor)) repeat-y;
18+
}
19+
20+
&.loaderIndeterminate {
21+
background-size: 40%;
22+
background: linear-gradient(
23+
to right,
24+
transparent 0px,
25+
var(--sapContent_BusyColor) calc(50% - 2rem),
26+
var(--sapContent_BusyColor) calc(50% + 2rem),
27+
transparent 100%
28+
)
29+
repeat-y;
30+
animation: scroll 1.2s linear infinite;
31+
}
32+
}
33+
34+
@keyframes scroll {
35+
0% {
36+
background-position: -100% 0;
37+
}
38+
100% {
39+
background-position: 200% 0;
40+
}
341
}

packages/main/src/components/Loader/Loader.stories.tsx renamed to packages/compat/src/components/Loader/Loader.stories.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import type { Meta, StoryObj } from '@storybook/react';
22
import activateIcon from '@ui5/webcomponents-icons/dist/activate.js';
3+
import { Card, CardHeader, FlexBox, FlexBoxDirection, Icon, Text } from '@ui5/webcomponents-react';
34
import { useEffect, useRef, useState } from 'react';
4-
import { FlexBoxDirection, LoaderType } from '../../enums/index.js';
5-
import { Card } from '../../webComponents/Card/index.js';
6-
import { CardHeader } from '../../webComponents/CardHeader/index.js';
7-
import { Icon } from '../../webComponents/Icon/index.js';
8-
import { Text } from '../../webComponents/Text/index.js';
9-
import { FlexBox } from '../FlexBox/index.js';
5+
import { LoaderType } from '../../enums/LoaderType.js';
106
import { Loader } from './index.js';
117

128
const meta = {
13-
title: 'User Feedback / Loader',
9+
title: 'Loader',
1410
component: Loader,
1511
argTypes: {},
1612
args: {

packages/main/src/components/Loader/index.tsx renamed to packages/compat/src/components/Loader/index.tsx

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
'use client';
22

3-
import { deprecationNotice, useI18nBundle, useStylesheet } from '@ui5/webcomponents-react-base';
3+
import type { CommonProps } from '@ui5/webcomponents-react';
4+
import { PLEASE_WAIT } from '@ui5/webcomponents-react/dist/i18n/i18n-defaults.js';
5+
import { useI18nBundle, useStylesheet } from '@ui5/webcomponents-react-base';
46
import { clsx } from 'clsx';
57
import type { CSSProperties } from 'react';
68
import { forwardRef, useEffect, useState } from 'react';
7-
import { LoaderType } from '../../enums/index.js';
8-
import { PLEASE_WAIT } from '../../i18n/i18n-defaults.js';
9-
import type { CommonProps } from '../../types/index.js';
9+
import { LoaderType } from '../../enums/LoaderType.js';
1010
import { classNames, styleData } from './Loader.module.css.js';
1111

1212
export interface LoaderPropTypes extends CommonProps {
@@ -35,12 +35,10 @@ export interface LoaderPropTypes extends CommonProps {
3535
}
3636

3737
/**
38-
* The `Loader` signals that an operation is currently being executed. It uses as little space as possible to allow the user to interact with the UI.<br />
39-
* It can be used to signal a data update on an already existing dataset, or where an expansion will happen.
40-
*
41-
* __Note:__ This component is __deprecated__ and will be removed with our next major release (v2.0.0)! Please use the [BusyIndicator](https://sap.github.io/ui5-webcomponents-react/?path=/docs/user-feedback-busyindicator--docs) instead.
38+
* __Note__: There is no longer a concept of a Loader component defined by the UX guidelines! To indicate a loading state, please use the `BusyIndicator` instead. For backwards compatibility, the Loader is still available in the `@ui5/webcomponents-react-compat` package, but it may lack accessibility features and no longer receives feature updates.
4239
*
43-
* @deprecated This component is deprecated and will be removed with our next major release (v2.0.0)! Please use the [BusyIndicator](https://sap.github.io/ui5-webcomponents-react/?path=/docs/user-feedback-busyindicator--docs) instead.
40+
* The `Loader` signals that an operation is currently being executed. It uses as little space as possible to allow the user to interact with the UI.
41+
* It can be used to signal a data update on an already existing dataset, or where an expansion will happen.
4442
*/
4543
const Loader = forwardRef<HTMLDivElement, LoaderPropTypes>((props, ref) => {
4644
const { className, type = LoaderType.Indeterminate, progress = '0px', slot, style, delay = 0, ...rest } = props;
@@ -51,10 +49,6 @@ const Loader = forwardRef<HTMLDivElement, LoaderPropTypes>((props, ref) => {
5149
const loaderClasses = clsx(classNames.loader, className, classNames[`loader${type}`]);
5250
const backgroundSize = type !== LoaderType.Determinate ? '40%' : progress;
5351

54-
useEffect(() => {
55-
deprecationNotice('Loader', 'The `Loader` component is deprecated. Please use the `BusyIndicator` instead.');
56-
}, []);
57-
5852
useEffect(() => {
5953
let timeout;
6054
if (delay > 0) {

packages/compat/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ export * from './components/TableCell/index.js';
33
export * from './components/TableColumn/index.js';
44
export * from './components/TableGroupRow/index.js';
55
export * from './components/TableRow/index.js';
6+
export * from './components/Loader/index.js';
7+
8+
export { LoaderType } from './enums/LoaderType.js';

packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1438,7 +1438,7 @@ describe('AnalyticalTable', () => {
14381438
cy.mount(<AnalyticalTable data={[]} columns={columns} loading />);
14391439
cy.get('[data-component-name="AnalyticalTableLoadingPlaceholder"]').should('be.visible');
14401440
cy.mount(<AnalyticalTable data={data} columns={columns} loading />);
1441-
cy.get('[data-component-name="Loader"]').should('be.visible');
1441+
cy.get('[data-component-name="AnalyticalTableBusyIndicator"]').should('be.visible');
14421442
cy.mount(<AnalyticalTable data={[]} columns={columns} />);
14431443
cy.findByText('No data').should('be.visible');
14441444
cy.mount(<AnalyticalTable data={data} columns={columns} filterable globalFilterValue="test123" />);

packages/main/src/components/AnalyticalTable/AnalyticalTable.stories.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ const meta = {
136136
reactTableOptions: { control: { disable: true } },
137137
tableHooks: { control: { disable: true } },
138138
NoDataComponent: { control: { disable: true } },
139-
LoadingComponent: { control: { disable: true } },
140139
extension: { control: { disable: true } },
141140
tableInstance: { control: { disable: true } },
142141
portalContainer: { control: { disable: true } }

packages/main/src/components/AnalyticalTable/defaults/LoadingComponent/DefaultLoadingComponent.module.css

Lines changed: 0 additions & 7 deletions
This file was deleted.

packages/main/src/components/AnalyticalTable/defaults/LoadingComponent/index.tsx

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)