Skip to content

Commit e0818c4

Browse files
refactor(Modals): replace context with use-sync-external-store (#6042)
BREAKING CHANGE: **ThemeProvider**: the prop `withoutModalsProvider` has been removed. For more information, please refer to our [Migration Guide](https://sap.github.io/ui5-webcomponents-react/main/?path=/docs/migration-guide--docs). BREAKING CHANGE: the hooks `useShowDialog`, `useShowPopover`, `useShowResponsivePopover`, `useShowMenu`, `useShowMessageBox` and `useShowToast` have been removed. For more information, please refer to our [Migration Guide](https://sap.github.io/ui5-webcomponents-react/main/?path=/docs/migration-guide--docs#modals). --------- Co-authored-by: Lukas Harbarth <[email protected]>
1 parent d4c3bcd commit e0818c4

File tree

13 files changed

+391
-820
lines changed

13 files changed

+391
-820
lines changed

.storybook/preview.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Preview } from '@storybook/react';
44
import { setLanguage } from '@ui5/webcomponents-base/dist/config/Language.js';
55
import { setTheme } from '@ui5/webcomponents-base/dist/config/Theme.js';
66
import applyDirection from '@ui5/webcomponents-base/dist/locale/applyDirection.js';
7-
import { ContentDensity, ThemeProvider } from '@ui5/webcomponents-react';
7+
import { ContentDensity, Modals, ThemeProvider } from '@ui5/webcomponents-react';
88
import { useEffect } from 'react';
99
import 'tocbot/dist/tocbot.css';
1010
import '../packages/main/dist/Assets.js';
@@ -27,7 +27,7 @@ const preview: Preview = {
2727
}
2828
},
2929
decorators: [
30-
(Story, { globals }) => {
30+
(Story, { globals, viewMode }) => {
3131
const { theme, contentDensity, direction, language } = globals;
3232

3333
useEffect(() => {
@@ -57,6 +57,7 @@ const preview: Preview = {
5757

5858
return (
5959
<ThemeProvider>
60+
{viewMode !== 'docs' && <Modals />}
6061
<Story />
6162
</ThemeProvider>
6263
);

docs/MigrationGuide.mdx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,19 @@ function MyComponent() {
610610

611611
```
612612

613+
### Modals
614+
615+
All Modal helper hooks have been removed. They can be replaced with the regular methods:
616+
617+
- `useShowDialog` --> `showDialog`
618+
- `useShowPopover` --> `showPopover`
619+
- `useShowResponsivePopover` --> `showResponsivePopover`
620+
- `useShowMenu` --> `showMenu`
621+
- `useShowMessageBox` --> `showMessageBox`
622+
- `useShowToast` --> `showToast`
623+
624+
The regular methods are now general purpose, so they can be used both inside the React content (components) as well as outside of the React context (redux, redux-saga, etc.).
625+
613626
### ObjectPageSection
614627

615628
The prop `titleText` is now required and the default value `true` has been removed for the `titleTextUppercase` prop to comply with the updated Fiori design guidelines.
@@ -622,6 +635,11 @@ The prop `titleText` is now required.
622635

623636
For better alignment with the UI5 Web Components the `active` prop has been renamed to `interactive`.
624637

638+
### ThemeProvider
639+
640+
The prop `withoutModalsProvider` has been removed.
641+
In order to provide a place for the `Modals` helper to mount the popovers, you have to render the new `Modals` component in your application tree.
642+
625643
## Enum Changes
626644

627645
For better alignment with the UI5 Web Components, the following enums have been renamed:

packages/cli/src/scripts/codemod/transforms/v2/codemodConfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,9 @@
486486
"valueState": "ValueState"
487487
}
488488
},
489+
"ThemeProvider": {
490+
"removedProps": ["withoutModalsProvider"]
491+
},
489492
"TimePicker": {
490493
"renamedEnums": {
491494
"valueState": "ValueState"

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

Lines changed: 63 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ describe('Modals - static helpers', () => {
66
const TestComp = () => {
77
return (
88
<>
9+
<Modals />
910
<Button
1011
onClick={() => {
1112
const { close } = Modals.showDialog({
@@ -30,18 +31,21 @@ describe('Modals - static helpers', () => {
3031
it('showPopover', () => {
3132
const TestComp = () => {
3233
return (
33-
<Button
34-
id="modals-show-popover"
35-
onClick={() => {
36-
const { close } = Modals.showPopover({
37-
opener: 'modals-show-popover',
38-
children: 'Popover Content',
39-
footer: <Bar endContent={<Button onClick={() => close()}>Close</Button>} />
40-
});
41-
}}
42-
>
43-
Show Popover
44-
</Button>
34+
<>
35+
<Modals />
36+
<Button
37+
id="modals-show-popover"
38+
onClick={() => {
39+
const { close } = Modals.showPopover({
40+
opener: 'modals-show-popover',
41+
children: 'Popover Content',
42+
footer: <Bar endContent={<Button onClick={() => close()}>Close</Button>} />
43+
});
44+
}}
45+
>
46+
Show Popover
47+
</Button>
48+
</>
4549
);
4650

4751
cy.mount(<TestComp />);
@@ -56,18 +60,21 @@ describe('Modals - static helpers', () => {
5660
it('showResponsivePopover', () => {
5761
const TestComp = () => {
5862
return (
59-
<Button
60-
id="modals-show-popover"
61-
onClick={() => {
62-
const { close } = Modals.showResponsivePopover({
63-
opener: 'modals-show-popover',
64-
children: 'Popover Content',
65-
footer: <Bar endContent={<Button onClick={() => close()}>Close</Button>} />
66-
});
67-
}}
68-
>
69-
Show Popover
70-
</Button>
63+
<>
64+
<Modals />
65+
<Button
66+
id="modals-show-popover"
67+
onClick={() => {
68+
const { close } = Modals.showResponsivePopover({
69+
opener: 'modals-show-popover',
70+
children: 'Popover Content',
71+
footer: <Bar endContent={<Button onClick={() => close()}>Close</Button>} />
72+
});
73+
}}
74+
>
75+
Show Popover
76+
</Button>
77+
</>
7178
);
7279

7380
cy.mount(<TestComp />);
@@ -82,17 +89,20 @@ describe('Modals - static helpers', () => {
8289
it('showMenu', () => {
8390
const TestComp = () => {
8491
return (
85-
<Button
86-
id="modals-show-popover"
87-
onClick={() => {
88-
Modals.showMenu({
89-
opener: 'modals-show-popover',
90-
children: <MenuItem text="MenuItem" />
91-
});
92-
}}
93-
>
94-
Show Menu
95-
</Button>
92+
<>
93+
<Modals />
94+
<Button
95+
id="modals-show-popover"
96+
onClick={() => {
97+
Modals.showMenu({
98+
opener: 'modals-show-popover',
99+
children: <MenuItem text="MenuItem" />
100+
});
101+
}}
102+
>
103+
Show Menu
104+
</Button>
105+
</>
96106
);
97107

98108
cy.mount(<TestComp />);
@@ -108,6 +118,7 @@ describe('Modals - static helpers', () => {
108118
const TestComp = () => {
109119
return (
110120
<>
121+
<Modals />
111122
<Button
112123
onClick={() => {
113124
Modals.showMessageBox({
@@ -131,20 +142,23 @@ describe('Modals - static helpers', () => {
131142
it('showToast', () => {
132143
const TestComp = () => {
133144
return (
134-
<div id="container">
135-
<Button
136-
onClick={() => {
137-
Modals.showToast(
138-
{
139-
children: 'Toast Content'
140-
},
141-
document.getElementById('container')
142-
);
143-
}}
144-
>
145-
Show Toast
146-
</Button>
147-
</div>
145+
<>
146+
<Modals />
147+
<div id="container">
148+
<Button
149+
onClick={() => {
150+
Modals.showToast(
151+
{
152+
children: 'Toast Content'
153+
},
154+
document.getElementById('container')
155+
);
156+
}}
157+
>
158+
Show Toast
159+
</Button>
160+
</div>
161+
</>
148162
);
149163
};
150164
cy.mount(<TestComp />);
@@ -153,136 +167,3 @@ describe('Modals - static helpers', () => {
153167
cy.findByText('Toast Content').should('exist');
154168
});
155169
});
156-
157-
describe('Modals - hooks', () => {
158-
interface PropTypes {
159-
hookFn: any;
160-
modalProps: any;
161-
}
162-
const TestComponent = ({ hookFn, modalProps }: PropTypes) => {
163-
const hook = hookFn();
164-
165-
return (
166-
<button
167-
onClick={() => {
168-
hook(modalProps);
169-
}}
170-
>
171-
Open Modal
172-
</button>
173-
);
174-
};
175-
176-
const TestComponentClosable = ({ hookFn, modalProps }: PropTypes) => {
177-
const hook = hookFn();
178-
179-
return (
180-
<Button
181-
onClick={() => {
182-
const { close } = hook({
183-
...modalProps,
184-
children: [
185-
...modalProps?.children,
186-
<Button key="btn" onClick={() => close()}>
187-
Close
188-
</Button>
189-
]
190-
});
191-
}}
192-
>
193-
Open Modal
194-
</Button>
195-
);
196-
};
197-
198-
it('useShowDialog', () => {
199-
cy.mount(
200-
<TestComponentClosable
201-
hookFn={Modals.useShowDialog}
202-
modalProps={{
203-
children: 'Dialog Content'
204-
}}
205-
/>
206-
);
207-
cy.findByText('Open Modal').click();
208-
cy.findByText('Dialog Content').should('be.visible');
209-
cy.findByText('Close').click();
210-
cy.findByText('Dialog Content').should('not.exist');
211-
});
212-
213-
it('useShowPopover', () => {
214-
cy.mount(
215-
<>
216-
<span id="opener" />
217-
<TestComponentClosable
218-
hookFn={Modals.useShowPopover}
219-
modalProps={{ children: 'Popover Content', opener: 'opener' }}
220-
/>
221-
</>
222-
);
223-
cy.findByText('Open Modal').click();
224-
cy.findByText('Popover Content').should('be.visible');
225-
cy.findByText('Close').click();
226-
cy.findByText('Popover Content').should('not.exist');
227-
});
228-
229-
it('useShowResponsivePopover', () => {
230-
cy.mount(
231-
<>
232-
<span id="opener" />
233-
<TestComponentClosable
234-
hookFn={Modals.useShowResponsivePopover}
235-
modalProps={{ children: 'Popover Content', opener: 'opener' }}
236-
/>
237-
</>
238-
);
239-
cy.findByText('Open Modal').click();
240-
cy.findByText('Popover Content').should('be.visible');
241-
cy.findByText('Close').click();
242-
cy.findByText('Popover Content').should('not.exist');
243-
});
244-
245-
it('useShowMenu', () => {
246-
const TestComp = () => {
247-
const showMenu = Modals.useShowMenu();
248-
return (
249-
<div id="container">
250-
<Button
251-
id="modals-show-popover"
252-
onClick={() => {
253-
showMenu(
254-
{
255-
opener: 'modals-show-popover',
256-
children: <MenuItem text="MenuItem" />
257-
},
258-
document.getElementById('container')
259-
);
260-
}}
261-
>
262-
Show Menu
263-
</Button>
264-
</div>
265-
);
266-
};
267-
268-
cy.mount(<TestComp />);
269-
270-
cy.findByText('Show Menu').click();
271-
cy.get('[ui5-menu-item]').click();
272-
cy.get('[ui5-menu]').should('not.exist');
273-
});
274-
275-
it('useShowMessageBox', () => {
276-
cy.mount(<TestComponent hookFn={Modals.useShowMessageBox} modalProps={{ children: 'MessageBox Content' }} />);
277-
cy.findByText('Open Modal').click();
278-
cy.findByText('MessageBox Content').should('be.visible');
279-
cy.findByText('OK').click();
280-
cy.findByText('MessageBox Content').should('not.exist');
281-
});
282-
283-
it('useShowToast', () => {
284-
cy.mount(<TestComponent hookFn={Modals.useShowToast} modalProps={{ children: 'Toast Content' }} />);
285-
cy.findByText('Open Modal').click();
286-
cy.findByText('Toast Content').should('exist');
287-
});
288-
});

0 commit comments

Comments
 (0)