Skip to content

Commit 19e9187

Browse files
docs: replace root with subpath imports (#7704)
Co-authored-by: Marcus Notheis <[email protected]>
1 parent 4275835 commit 19e9187

21 files changed

+133
-195
lines changed

.storybook/components/DocsHeader.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export const InfoTable = ({
7272
const supportsClipboardApi = typeof ClipboardItem !== 'undefined';
7373

7474
const handleCopy = async (e) => {
75-
const importStatementCell = e.currentTarget.parentElement.querySelector('[data-import]');
75+
const importStatementCell = e.currentTarget.parentElement.parentElement.querySelector('[data-import-cell]');
7676
if (supportsClipboardApi && importStatementCell) {
7777
const html = new Blob([importStatementCell.outerHTML], { type: 'text/html' });
7878
const text = new Blob([importStatementCell.outerText], { type: 'text/plain' });
@@ -89,7 +89,7 @@ export const InfoTable = ({
8989
<Label>Usage</Label>
9090
</th>
9191
<td data-import-cell={supportsClipboardApi}>
92-
<Import moduleNames={[moduleName]} componentId={preparedMeta.componentId} />
92+
<Import moduleName={moduleName} componentId={preparedMeta.componentId} />
9393
{supportsClipboardApi && (
9494
<Button
9595
design={ButtonDesign.Transparent}
@@ -142,7 +142,12 @@ export const InfoTable = ({
142142
<Label>Subcomponents</Label>
143143
</th>
144144
<td data-import-cell={supportsClipboardApi}>
145-
<Import moduleNames={subComps} componentId={preparedMeta.componentId} />
145+
{subComps.map((subComp) => (
146+
<>
147+
<Import key={subComp} moduleName={subComp} componentId={preparedMeta.componentId} />
148+
<br />
149+
</>
150+
))}
146151
{supportsClipboardApi && (
147152
<Button
148153
design={ButtonDesign.Transparent}

.storybook/components/Import.tsx

Lines changed: 20 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import { Fragment } from 'react';
2-
31
interface ImportStatementPropTypes {
42
/**
5-
* Names of module/component (e.g. "Button")
3+
* Name of module/component (e.g. "Button")
64
*/
7-
moduleNames?: string[];
5+
moduleName?: string;
86
/**
97
* Package name (e.g. "@ui5/webcomponents-react")
108
*/
@@ -24,34 +22,23 @@ interface FromPathPropTypes extends Pick<ImportStatementPropTypes, 'packageName'
2422
deepPath?: null | undefined | DeepPath;
2523
}
2624

27-
function FromPath({ packageName, deepPath }: FromPathPropTypes) {
25+
function FromPath({ packageName }: FromPathPropTypes) {
2826
return (
2927
<>
3028
<span style={{ color: 'rgb(0, 0, 136)', fontSize: '14px' }}>from</span>
3129
<span> </span>
32-
<span style={{ color: 'rgb(0, 136, 0)', fontSize: '14px' }}>
33-
{deepPath ? packageName.slice(0, -1) : packageName}
34-
{deepPath && deepPath.path}
35-
{deepPath && "'"}
36-
</span>
30+
<span style={{ color: 'rgb(0, 136, 0)', fontSize: '14px' }}>{packageName}</span>
3731
<span style={{ fontSize: '14px' }}>;</span>
38-
{deepPath && <br />}
3932
</>
4033
);
4134
}
4235

4336
FromPath.displayName = 'FromPath';
4437

45-
export const ImportStatement = ({ moduleNames, packageName, defaultImport }: ImportStatementPropTypes) => {
46-
if (!moduleNames) {
38+
export const ImportStatement = ({ moduleName, packageName, defaultImport }: ImportStatementPropTypes) => {
39+
if (!moduleName) {
4740
return null;
4841
}
49-
const isCompat = packageName.includes('compat');
50-
const paths = isCompat
51-
? moduleNames.map((item) => {
52-
return { path: `/dist/components/${item}/index.js`, moduleName: item };
53-
})
54-
: [null];
5542

5643
return (
5744
<pre
@@ -68,45 +55,16 @@ export const ImportStatement = ({ moduleNames, packageName, defaultImport }: Imp
6855
}}
6956
>
7057
<code style={{ whiteSpace: 'pre' }}>
71-
{!paths[0] && <span style={{ color: 'rgb(0, 0, 136)', fontSize: '14px' }}>import</span>}
72-
{paths.map((deepPath) => {
73-
if (!deepPath) {
74-
return (
75-
<span style={{ fontSize: '14px' }} key="0">
76-
{!defaultImport && ' {'}
77-
{moduleNames.length > 2 ? (
78-
<>
79-
{moduleNames.map((item) => {
80-
return (
81-
<Fragment key={item}>
82-
<br />
83-
&nbsp;&nbsp;
84-
{item},
85-
</Fragment>
86-
);
87-
})}
88-
<br />
89-
</>
90-
) : (
91-
<>&nbsp;{moduleNames.join(', ')}&nbsp;</>
92-
)}
93-
{!defaultImport && '} '}
94-
</span>
95-
);
96-
} else {
97-
return (
98-
<Fragment key={deepPath.path}>
99-
<span style={{ color: 'rgb(0, 0, 136)', fontSize: '14px' }}>import</span>
100-
<span style={{ fontSize: '14px' }}>
101-
{' '}
102-
{'{'}&nbsp;{deepPath.moduleName}&nbsp;{'}'}{' '}
103-
</span>
104-
<FromPath packageName={packageName} deepPath={deepPath} />
105-
</Fragment>
106-
);
107-
}
108-
})}
109-
{!paths[0] && <FromPath packageName={packageName} />}
58+
<span style={{ color: 'rgb(0, 0, 136)', fontSize: '14px' }}>import</span>
59+
<span style={{ fontSize: '14px' }}>
60+
{!defaultImport && ' {'}
61+
<>
62+
&nbsp;&nbsp;
63+
{moduleName}{' '}
64+
</>
65+
{!defaultImport && '} '}
66+
</span>
67+
<FromPath packageName={packageName} />
11068
</code>
11169
</pre>
11270
);
@@ -118,19 +76,19 @@ interface ImportProps {
11876
/**
11977
* Names of module/component (e.g. "Button")
12078
*/
121-
moduleNames: ImportStatementPropTypes['moduleNames'];
79+
moduleName: ImportStatementPropTypes['moduleName'];
12280
componentId: string;
12381
}
12482

12583
export const Import = (props: ImportProps) => {
126-
const { componentId, moduleNames } = props;
84+
const { componentId, moduleName } = props;
12785
const isChart = componentId.startsWith('charts-');
12886
const isCompat = componentId.startsWith('legacy-');
12987

13088
return (
13189
<ImportStatement
132-
moduleNames={moduleNames}
133-
packageName={`'@ui5/webcomponents-react${isChart ? '-charts' : isCompat ? '-compat' : ''}'`}
90+
moduleName={moduleName}
91+
packageName={`'@ui5/webcomponents-react${isChart ? '-charts' : isCompat ? '-compat' : ''}/${moduleName}'`}
13492
/>
13593
);
13694
};

.storybook/utils.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type * as CEM from '@ui5/webcomponents-tools/lib/cem/types-internal';
12
import { useMemo } from 'react';
23
// @ts-expect-error: storybook can handle this
34
import cemAi from './custom-element-manifests/ai.json';
@@ -50,16 +51,16 @@ const replaceSubComps = {
5051
TableHeaderCellActionBase: ['TableHeaderCellActionAI'],
5152
};
5253

53-
function findSubComponentsRecursively(moduleName: string, cem: any): string[] {
54+
function findSubComponentsRecursively(moduleName: string, cem: CEM.Package): string[] {
5455
const subComponentsSet = new Set<string>();
5556

5657
const recursiveFind = (moduleName: string) => {
57-
const module = cem?.modules.find((module: any) => module.path === `dist/${moduleName}.js`);
58+
const module = cem?.modules.find((module: CEM.Module) => module.path === `dist/${moduleName}.js`);
5859
if (!module) return;
5960

60-
module.declarations.forEach((decl: any) => {
61-
(decl.slots || []).forEach((slot: any) => {
62-
(slot._ui5type?.references || []).forEach((ref: any) => {
61+
module.declarations.forEach((decl: CEM.CustomElement) => {
62+
(decl.slots || []).forEach((slot: CEM.Slot) => {
63+
(slot._ui5type?.references || []).forEach((ref: CEM.TypeReference) => {
6364
const name = ref.name.replace(/^I([A-Z])/g, '$1');
6465
const subComps = replaceSubComps[name] || [name];
6566
subComps.forEach((subComp: string) => {

README.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,30 @@ Since version `v2.14.0` of `@ui5/webcomponents-react`, `@ui5/webcomponents-fiori
9494
- You want to use the [VariantManagement](https://sap.github.io/ui5-webcomponents-react/v2/?path=/docs/inputs-variantmanagement--docs) component.
9595
- You import anything from the `@ui5/webcomponents-fiori` package.
9696

97-
**Note:** Most popular bundlers enable tree-shaking for production builds, so there’s no difference in the final bundle size between the recommended and minimal installations.
98-
9997
```sh
10098
npm install @ui5/webcomponents-react @ui5/webcomponents
10199
```
102100

101+
**Note:** Most popular bundlers enable tree-shaking for production builds, so there’s no difference in the final bundle size between the recommended and minimal installations.
102+
103+
> ⚠️ **Warning**
104+
>
105+
> If your bundler does **not** support tree-shaking, you must use **subpath imports**.
106+
>
107+
> Otherwise, since `@ui5/webcomponents-react` re-exports all components, **every component** (including those that depend on the `@ui5/webcomponents-fiori` package) will be included in your bundle, which will lead to errors due to the missing module.
108+
>
109+
> **✅ Do:**
110+
>
111+
> ```tsx
112+
> import { Button } from '@ui5/webcomponents-react/Button';
113+
> ```
114+
>
115+
> **❌ Don’t:**
116+
>
117+
> ```tsx
118+
> import { Button } from '@ui5/webcomponents-react';
119+
> ```
120+
103121
## End of Support for Version 1.x
104122
105123
The support for version 1.x of `ui5-webcomponents-react` has ended on **July 1, 2025**. We recommend migrating to version 2.x as soon as possible. For more information, please refer to our [Migration Guide](https://sap.github.io/ui5-webcomponents-react/v2/?path=/docs/migration-guide--docs).
@@ -135,7 +153,7 @@ You can find a curated list of project templates and examples on our [Project Te
135153
2. Import the `ThemeProvider` component and wrap your root component with it:
136154
137155
```tsx
138-
import { ThemeProvider } from '@ui5/webcomponents-react';
156+
import { ThemeProvider } from '@ui5/webcomponents-react/ThemeProvider';
139157
...
140158
createRoot(document.getElementById('root') as HTMLElement).render(
141159
<ThemeProvider>
@@ -148,7 +166,7 @@ You can find a curated list of project templates and examples on our [Project Te
148166
For example, to use the `Button` component you need to import it:
149167

150168
```jsx
151-
import { Button } from '@ui5/webcomponents-react'; // loads ui5-button wrapped in a ui5-webcomponents-react component
169+
import { Button } from '@ui5/webcomponents-react/Button'; // loads ui5-button wrapped in a ui5-webcomponents-react component
152170
```
153171

154172
4. Add the imported component to your JSX:

docs/Welcome.mdx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ npm install @ui5/webcomponents-react @ui5/webcomponents
8383
In order to use `@ui5/webcomponents-react` you have to wrap your application's root component into the `ThemeProvider` component.
8484

8585
```tsx
86-
import { ThemeProvider } from '@ui5/webcomponents-react';
86+
import { ThemeProvider } from '@ui5/webcomponents-react/ThemeProvider';
8787
...
8888
createRoot(document.getElementById('root') as HTMLElement).render(
8989
<ThemeProvider>
@@ -96,7 +96,7 @@ Then you are ready to use `@ui5/webcomponents-react` and you can import the desi
9696
For example, to use the `Button` component you need to import it:
9797

9898
```jsx
99-
import { Button } from '@ui5/webcomponents-react'; // loads ui5-button wrapped in a ui5-webcomponents-react component
99+
import { Button } from '@ui5/webcomponents-react/Button'; // loads ui5-button wrapped in a ui5-webcomponents-react component
100100
```
101101

102102
Then, you can use the Button in your app:
@@ -161,8 +161,11 @@ Small app with a popover opened by clicking a button including type declarations
161161

162162
```tsx
163163
import { useState, useRef } from 'react';
164-
import type { ButtonPropTypes, PopoverDomRef, PopoverPropTypes } from '@ui5/webcomponents-react';
165-
import { ThemeProvider, Button, Popover } from '@ui5/webcomponents-react';
164+
import type { ButtonPropTypes } from '@ui5/webcomponents-react/Button';
165+
import type { PopoverDomRef, PopoverPropTypes } from '@ui5/webcomponents-react/Popover';
166+
import { ThemeProvider } from '@ui5/webcomponents-react/ThemeProvider';
167+
import { Button } from '@ui5/webcomponents-react/Button';
168+
import { Popover } from '@ui5/webcomponents-react/Popover';
166169

167170
export default function App() {
168171
const [open, setOpen] = useState<PopoverPropTypes['open']>(false);

docs/knowledge-base/FAQ.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ setCustomElementsScopingSuffix('demo');
113113
// app main file, e.g index.js, main.tsx, etc.
114114
import './scoping-config.js';
115115
// now, all other component imports - the scoping config import must be the first import of the app
116-
import { Button } from '@ui5/webcomponents-react';
116+
import { Button } from '@ui5/webcomponents-react/Button';
117117
// ...
118118
```
119119

docs/knowledge-base/Internationalization.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ registerI18nLoader('myApp', 'de', async () => {
103103

104104
Add the following import statement to the component where you want to use translated texts:
105105

106-
```js
107-
import { useI18nBundle } from '@ui5/webcomponents-react-base';
106+
```ts
107+
import { useI18nBundle } from '@ui5/webcomponents-react-base/hooks';
108108
```
109109

110110
Now, you can use the `useI18nBundle` hook in your functional components in order to access the i18nBundle and get your

docs/knowledge-base/Public-Utils.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ The `@ui5/webcomponents-react-base` package is providing a couple of utils, whic
2424

2525
## Device
2626

27-
<Source code={`import { Device } from '@ui5/webcomponents-react-base';`} />
27+
<Source code={`import * as Device from '@ui5/webcomponents-react-base/Device';`} />
2828

2929
The `Device` allows you to detect information about the environment where your app is running:
3030

@@ -73,7 +73,7 @@ The `Device` allows you to detect information about the environment where your a
7373

7474
## Theming Parameters
7575

76-
<Source code={`import { ThemingParameters } from '@ui5/webcomponents-react-base';`} />
76+
<Source code={`import { ThemingParameters } from '@ui5/webcomponents-react-base/ThemingParameters';`} />
7777

7878
By using the global `--sap...` CSS variables, you can define the look and feel of your application without the need to hard-code any
7979
colors. The `ThemingParameters` is an JS object containing all available CSS variables mapped to their name.
@@ -98,14 +98,14 @@ Is equivalent to:
9898

9999
### `useI18nBundle`
100100

101-
<Source code={`import { useI18nBundle } from '@ui5/webcomponents-react-base';`} />
101+
<Source code={`import { useI18nBundle } from '@ui5/webcomponents-react-base/hooks';`} />
102102

103103
The `useI18nBundle` hook can be used for adding internationalization to your application. Learn more about it in our
104104
[Internationalization Guide](?path=/docs/knowledge-base-internationalization--docs).
105105

106106
### `useViewportRange`
107107

108-
<Source code={`import { useViewportRange } from '@ui5/webcomponents-react-base';`} />
108+
<Source code={`import { useViewportRange } from '@ui5/webcomponents-react-base/hooks';`} />
109109

110110
The `useViewportRange` hook is a utility hook based on the `Device.getCurrentRange()` and `Device.attachMediaHandler` API.
111111
It will always return a string with the name of the currently active range.

docs/knowledge-base/Styling.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ In order to reuse our central styling approach, you can import the `ThemingParam
7272
You can then create a custom component by following this recipe:
7373

7474
```tsx
75-
import { ThemingParameters } from '@ui5/webcomponents-react-base';
75+
import { ThemingParameters } from '@ui5/webcomponents-react-base/ThemingParameters';
7676
import './MyCustomElement.css';
7777

7878
export const MyCustomElement = () => {

packages/base/src/hooks/useSyncRef.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,8 @@ import { useCallback, useRef } from 'react';
1010
* ```tsx
1111
* const MyComponent = forwardRef<HTMLDivElement, PropTypes>((props, ref) => {
1212
* const [componentRef, localRef] = useSyncRef<HTMLDivElement>(ref);
13-
*
14-
* useEffect(() => {
15-
* // `localRef.current` is always the latest DOM node (or `null`)
16-
* console.log('current node:', localRef.current);
17-
* }, []);
13+
* // `localRef.current` is always the latest DOM node (or `null`)
14+
* console.log('current node:', localRef.current);
1815
*
1916
* return <div ref={componentRef}>Hello World!</div>;
2017
* });

0 commit comments

Comments
 (0)