Skip to content

Commit 9b36161

Browse files
fix: use createPortal for all internal popovers (#636)
1 parent ed0944e commit 9b36161

File tree

13 files changed

+1248
-2892
lines changed

13 files changed

+1248
-2892
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { Meta, Title, Description, Story, Preview, Props } from '@storybook/addon-docs/blocks';
2+
import { createSelectArgTypes } from '@shared/stories/createSelectArgTypes';
3+
import { ActionSheet } from '@ui5/webcomponents-react/lib/ActionSheet';
4+
import { Button } from '@ui5/webcomponents-react/lib/Button';
5+
import { PlacementType } from '@ui5/webcomponents-react/lib/PlacementType';
6+
import { PopoverHorizontalAlign } from '@ui5/webcomponents-react/lib/PopoverHorizontalAlign';
7+
import { PopoverVerticalAlign } from '@ui5/webcomponents-react/lib/PopoverVerticalAlign';
8+
import { useCallback, useRef } from 'react';
9+
10+
<Meta
11+
title="Components / ActionSheet"
12+
component={ActionSheet}
13+
argTypes={{
14+
...createSelectArgTypes({
15+
placementType: PlacementType,
16+
horizontalAlign: PopoverHorizontalAlign,
17+
verticalAlign: PopoverVerticalAlign
18+
}),
19+
children: {
20+
type: null
21+
},
22+
footer: {
23+
type: null
24+
},
25+
header: {
26+
type: null
27+
},
28+
ref: {
29+
type: null
30+
}
31+
}}
32+
args={{
33+
horizontalAlign: PopoverHorizontalAlign.Center,
34+
placementType: PlacementType.Right,
35+
verticalAlign: PopoverVerticalAlign.Center
36+
}}
37+
/>
38+
39+
<Title />
40+
<Subtitle />
41+
<Description />
42+
43+
<Preview>
44+
<Story name="Default">
45+
{(args) => {
46+
const actionSheetRef = useRef();
47+
const onButtonClick = useCallback(
48+
(e) => {
49+
actionSheetRef.current.open(e.target);
50+
},
51+
[actionSheetRef]
52+
);
53+
return (
54+
<>
55+
<Button onClick={onButtonClick}>Open ActionSheet</Button>
56+
<ActionSheet ref={actionSheetRef} {...args}>
57+
<Button icon="add">Accept</Button>
58+
<Button>Reject</Button>
59+
<Button>This is my super long text!</Button>
60+
</ActionSheet>
61+
</>
62+
);
63+
}}
64+
</Story>
65+
</Preview>
66+
67+
<Props story="Default" components={{ ActionSheet }} />

packages/main/src/components/ActionSheet/ActionSheet.test.tsx

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,40 @@
1+
import { cleanStaticAreaAfterEachTest, fireEvent, render, screen } from '@shared/tests';
12
import { createPassThroughPropsTest } from '@shared/tests/utils';
2-
import { mount } from 'enzyme';
33
import { ActionSheet } from '@ui5/webcomponents-react/lib/ActionSheet';
44
import { Button } from '@ui5/webcomponents-react/lib/Button';
55
import { Label } from '@ui5/webcomponents-react/lib/Label';
66
import React, { createRef, RefObject } from 'react';
7-
import sinon from 'sinon';
87
import { Ui5PopoverDomRef } from '../../interfaces/Ui5PopoverDomRef';
98

109
describe('ActionSheet', () => {
10+
cleanStaticAreaAfterEachTest();
11+
1112
test('Render without Crashing', () => {
12-
const button = <Button />;
13-
const wrapper = mount(
14-
<ActionSheet openBy={button} className="myCustomClass">
15-
<Button icon={'add'}>Accept</Button>
13+
const ref = createRef();
14+
const wrapper = render(
15+
<ActionSheet className="myCustomClass" ref={ref}>
16+
<Button>Accept</Button>
1617
<Button>Reject</Button>
1718
<Button>This is my super long text!</Button>
1819
</ActionSheet>
1920
);
20-
expect(wrapper.render()).toMatchSnapshot();
21+
22+
expect(wrapper.container.parentElement).toMatchSnapshot();
2123
});
2224

2325
test('Button Click handler', () => {
24-
const callback = sinon.spy();
25-
const button = <Button />;
26-
const wrapper = mount(
27-
<ActionSheet openBy={button} className="myCustomClass">
28-
<Button icon={'add'} onClick={callback}>
29-
Accept
30-
</Button>
26+
const callback = jest.fn();
27+
render(
28+
<ActionSheet className="myCustomClass">
29+
<Button onClick={callback}>Accept</Button>
3130
<Button>Reject</Button>
3231
<Button>This is my super long text!</Button>
3332
</ActionSheet>
3433
);
35-
wrapper
36-
.find('ui5-button')
37-
.first()
38-
.instance()
39-
// @ts-ignore
40-
.fireEvent('click');
41-
wrapper.update();
4234

43-
wrapper
44-
.find('ui5-responsive-popover ui5-button')
45-
.first()
46-
.instance()
47-
// @ts-ignore
48-
.fireEvent('click');
49-
expect(callback.called).toBe(true);
35+
fireEvent.click(screen.getByText('Accept'));
36+
37+
expect(callback).toBeCalled();
5038
});
5139

5240
test('Test Legacy Ref', () => {
@@ -55,10 +43,9 @@ describe('ActionSheet', () => {
5543
const ref = (el) => {
5644
legacyRef = el;
5745
};
58-
const button = <Button />;
59-
mount(
60-
<ActionSheet ref={ref} openBy={button}>
61-
<Button icon={'add'}>Accept</Button>
46+
render(
47+
<ActionSheet ref={ref}>
48+
<Button>Accept</Button>
6249
<Button>Reject</Button>
6350
<Button>This is my super long text!</Button>
6451
</ActionSheet>
@@ -68,19 +55,17 @@ describe('ActionSheet', () => {
6855

6956
test('Ref object', () => {
7057
const ref: RefObject<Ui5PopoverDomRef> = createRef();
71-
const button = <Button />;
72-
mount(<ActionSheet ref={ref} openBy={button} />);
58+
render(<ActionSheet ref={ref} />);
7359
expect((ref.current as any).tagName).toEqual('UI5-RESPONSIVE-POPOVER');
7460
});
7561

7662
test('does not crash with other component', () => {
77-
const button = <Button />;
78-
const wrapper = mount(
79-
<ActionSheet openBy={button}>
63+
const { container } = render(
64+
<ActionSheet>
8065
<Label>I should not crash</Label>
8166
</ActionSheet>
8267
);
83-
expect(wrapper.render()).toMatchSnapshot();
68+
expect(container.parentElement).toMatchSnapshot();
8469
});
8570

8671
createPassThroughPropsTest(ActionSheet);
Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,51 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

33
exports[`ActionSheet Render without Crashing 1`] = `
4-
<ui5-responsive-popover
5-
class="ActionSheet-actionSheet-0 myCustomClass"
6-
horizontal-align="Center"
7-
placement-type="Right"
8-
vertical-align="Center"
9-
>
10-
<ui5-button
11-
data-is-action-sheet-button=""
12-
design="Transparent"
13-
icon="add"
4+
<body>
5+
<div />
6+
<ui5-responsive-popover
7+
class="ActionSheet-actionSheet myCustomClass"
8+
horizontal-align="Center"
9+
placement-type="Right"
10+
vertical-align="Center"
1411
>
15-
Accept
16-
</ui5-button>
17-
<ui5-button
18-
data-is-action-sheet-button=""
19-
design="Transparent"
20-
>
21-
Reject
22-
</ui5-button>
23-
<ui5-button
24-
data-is-action-sheet-button=""
25-
design="Transparent"
26-
>
27-
This is my super long text!
28-
</ui5-button>
29-
</ui5-responsive-popover>
12+
<ui5-button
13+
data-is-action-sheet-button=""
14+
design="Transparent"
15+
>
16+
Accept
17+
</ui5-button>
18+
<ui5-button
19+
data-is-action-sheet-button=""
20+
design="Transparent"
21+
>
22+
Reject
23+
</ui5-button>
24+
<ui5-button
25+
data-is-action-sheet-button=""
26+
design="Transparent"
27+
>
28+
This is my super long text!
29+
</ui5-button>
30+
</ui5-responsive-popover>
31+
</body>
3032
`;
3133

3234
exports[`ActionSheet does not crash with other component 1`] = `
33-
<ui5-responsive-popover
34-
class="ActionSheet-actionSheet-0"
35-
horizontal-align="Center"
36-
placement-type="Right"
37-
vertical-align="Center"
38-
>
39-
<ui5-label
40-
data-is-action-sheet-button=""
41-
design="Transparent"
35+
<body>
36+
<div />
37+
<ui5-responsive-popover
38+
class="ActionSheet-actionSheet"
39+
horizontal-align="Center"
40+
placement-type="Right"
41+
vertical-align="Center"
4242
>
43-
I should not crash
44-
</ui5-label>
45-
</ui5-responsive-popover>
43+
<ui5-label
44+
data-is-action-sheet-button=""
45+
design="Transparent"
46+
>
47+
I should not crash
48+
</ui5-label>
49+
</ui5-responsive-popover>
50+
</body>
4651
`;

packages/main/src/components/ActionSheet/demo.stories.tsx

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

packages/main/src/components/ActionSheet/index.tsx

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,15 @@ import { StyleClassHelper } from '@ui5/webcomponents-react-base/lib/StyleClassHe
44
import { useConsolidatedRef } from '@ui5/webcomponents-react-base/lib/useConsolidatedRef';
55
import { usePassThroughHtmlProps } from '@ui5/webcomponents-react-base/lib/usePassThroughHtmlProps';
66
import { ButtonDesign } from '@ui5/webcomponents-react/lib/ButtonDesign';
7-
import { PlacementType } from '@ui5/webcomponents-react/lib/PlacementType';
87
import { ResponsivePopover } from '@ui5/webcomponents-react/lib/ResponsivePopover';
98
import React, { Children, cloneElement, FC, forwardRef, ReactElement, RefObject } from 'react';
9+
import { createPortal } from 'react-dom';
1010
import { Ui5ResponsivePopoverDomRef } from '../../interfaces/Ui5ResponsivePopoverDomRef';
1111
import { ButtonPropTypes } from '../../webComponents/Button';
1212
import { ResponsivePopoverPropTypes } from '../../webComponents/ResponsivePopover';
1313
import styles from './ActionSheet.jss';
1414

15-
export interface ActionSheetPropTypes extends ResponsivePopoverPropTypes {
16-
placement?: PlacementType;
15+
export interface ActionSheetPropTypes extends Omit<ResponsivePopoverPropTypes, 'children'> {
1716
children?: ReactElement<ButtonPropTypes> | ReactElement<ButtonPropTypes>[];
1817
}
1918

@@ -56,10 +55,7 @@ const ActionSheet: FC<ActionSheetPropTypes> = forwardRef(
5655

5756
const classes = useStyles();
5857

59-
const actionSheetClasses = StyleClassHelper.of(classes.actionSheet);
60-
if (className) {
61-
actionSheetClasses.put(className);
62-
}
58+
const actionSheetClasses = StyleClassHelper.of(classes.actionSheet).putIfPresent(className);
6359

6460
const popoverRef: RefObject<Ui5ResponsivePopoverDomRef> = useConsolidatedRef(ref);
6561

@@ -86,12 +82,12 @@ const ActionSheet: FC<ActionSheetPropTypes> = forwardRef(
8682
'onBeforeOpen'
8783
]);
8884

89-
return (
85+
return createPortal(
9086
<ResponsivePopover
9187
ref={popoverRef}
9288
style={style}
9389
slot={slot}
94-
className={actionSheetClasses.valueOf()}
90+
className={actionSheetClasses.className}
9591
allowTargetOverlap={allowTargetOverlap}
9692
headerText={headerText}
9793
horizontalAlign={horizontalAlign}
@@ -109,14 +105,12 @@ const ActionSheet: FC<ActionSheetPropTypes> = forwardRef(
109105
{...passThroughProps}
110106
>
111107
{Children.map(children, renderActionSheetButton)}
112-
</ResponsivePopover>
108+
</ResponsivePopover>,
109+
document.body
113110
);
114111
}
115112
);
116113

117-
ActionSheet.defaultProps = {
118-
placement: PlacementType.Bottom
119-
};
120114
ActionSheet.displayName = 'ActionSheet';
121115

122116
export { ActionSheet };

0 commit comments

Comments
 (0)