• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SAP / ui5-webcomponents-react / 13678059219

05 Mar 2025 02:17PM CUT coverage: 87.425%. Remained the same
13678059219

Pull #7035

github

web-flow
Merge f4e673a4b into 44bcdd7ce
Pull Request #7035: feat: update to UI5 Web Components 2.8.0

2927 of 3884 branches covered (75.36%)

5110 of 5845 relevant lines covered (87.43%)

44162.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

53.42
/packages/main/src/components/Modals/index.tsx
1
'use client';
2

3
import { useIsomorphicLayoutEffect, useSyncRef } from '@ui5/webcomponents-react-base';
4
import type { MutableRefObject } from 'react';
5
import { createRef, useSyncExternalStore } from 'react';
6
import { createPortal } from 'react-dom';
7
import { getRandomId } from '../../internal/getRandomId.js';
8
import type { IModal } from '../../internal/ModalStore.js';
9
import { ModalStore } from '../../internal/ModalStore.js';
10
import type {
11
  DialogDomRef,
12
  DialogPropTypes,
13
  MenuDomRef,
14
  MenuPropTypes,
15
  PopoverDomRef,
16
  PopoverPropTypes,
17
  ResponsivePopoverDomRef,
18
  ResponsivePopoverPropTypes,
19
  ToastDomRef,
20
  ToastPropTypes
21
} from '../../webComponents/index.js';
22
import { Dialog, Menu, Popover, ResponsivePopover, Toast } from '../../webComponents/index.js';
23
import type { MessageBoxPropTypes } from '../MessageBox/index.js';
24
import { MessageBox } from '../MessageBox/index.js';
25

26
type ModalReturnType<DomRef> = {
27
  ref: MutableRefObject<DomRef>;
28
};
29

30
type ClosableModalReturnType<DomRef> = ModalReturnType<DomRef> & {
31
  close: () => void;
32
};
33

34
function showDialogFn(
35
  props: DialogPropTypes,
36
  container?: Element | DocumentFragment
37
): ClosableModalReturnType<DialogDomRef> {
38
  const id = getRandomId();
7✔
39
  const ref = createRef<DialogDomRef>();
7✔
40
  ModalStore.addModal({
7✔
41
    Component: Dialog,
42
    props: {
43
      ...props,
44
      open: true,
45
      onClose: (event) => {
46
        if (typeof props.onClose === 'function') {
7!
47
          props.onClose(event);
×
48
        }
49
        ModalStore.removeModal(id);
7✔
50
      }
51
    },
52
    ref,
53
    container,
54
    id
55
  });
56

57
  return {
7✔
58
    ref,
59
    close: () => {
60
      if (ref.current) {
7✔
61
        ref.current.open = false;
7✔
62
      }
63
    }
64
  };
65
}
66

67
function showPopoverFn(
68
  props: PopoverPropTypes,
69
  container?: Element | DocumentFragment
70
): ClosableModalReturnType<PopoverDomRef> {
71
  const id = getRandomId();
×
72
  const ref = createRef<PopoverDomRef>();
×
73
  ModalStore.addModal({
×
74
    Component: Popover,
75
    props: {
76
      ...props,
77
      open: true,
78
      onClose: (event) => {
79
        if (typeof props.onClose === 'function') {
×
80
          props.onClose(event);
×
81
        }
82
        ModalStore.removeModal(id);
×
83
      }
84
    },
85
    ref,
86
    container,
87
    id
88
  });
89

90
  return {
×
91
    ref,
92
    close: () => {
93
      if (ref.current) {
×
94
        ref.current.open = false;
×
95
      }
96
    }
97
  };
98
}
99

100
function showResponsivePopoverFn(
101
  props: ResponsivePopoverPropTypes,
102
  container?: Element | DocumentFragment
103
): ClosableModalReturnType<ResponsivePopoverDomRef> {
104
  const id = getRandomId();
×
105
  const ref = createRef<ResponsivePopoverDomRef>();
×
106
  ModalStore.addModal({
×
107
    Component: ResponsivePopover,
108
    props: {
109
      ...props,
110
      open: true,
111
      onClose: (event) => {
112
        if (typeof props.onClose === 'function') {
×
113
          props.onClose(event);
×
114
        }
115
        ModalStore.removeModal(id);
×
116
      }
117
    },
118
    ref,
119
    container,
120
    id
121
  });
122

123
  return {
×
124
    ref,
125
    close: () => {
126
      if (ref.current) {
×
127
        ref.current.open = false;
×
128
      }
129
    }
130
  };
131
}
132

133
function showMenuFn(props: MenuPropTypes, container?: Element | DocumentFragment): ClosableModalReturnType<MenuDomRef> {
134
  const id = getRandomId();
×
135
  const ref = createRef<MenuDomRef>();
×
136
  ModalStore.addModal({
×
137
    Component: Menu,
138
    props: {
139
      ...props,
140
      open: true,
141
      onClose: (event) => {
142
        if (typeof props.onClose === 'function') {
×
143
          props.onClose(event);
×
144
        }
145
        ModalStore.removeModal(id);
×
146
      }
147
    },
148
    ref,
149
    container,
150
    id
151
  });
152

153
  return {
×
154
    ref,
155
    close: () => {
156
      if (ref.current) {
×
157
        ref.current.open = false;
×
158
      }
159
    }
160
  };
161
}
162

163
function showMessageBoxFn(
164
  props: MessageBoxPropTypes,
165
  container?: Element | DocumentFragment
166
): ClosableModalReturnType<DialogDomRef> {
167
  const id = getRandomId();
3✔
168
  const ref = createRef<DialogDomRef>();
3✔
169
  ModalStore.addModal({
3✔
170
    // @ts-expect-error: props type safety is covered by the `props` property
171
    Component: MessageBox,
172
    props: {
173
      ...props,
174
      open: true,
175
      onClose: (event) => {
176
        if (typeof props.onClose === 'function') {
3!
177
          props.onClose(event);
×
178
        }
179
        ModalStore.removeModal(id);
3✔
180
      }
181
    },
182
    ref,
183
    container,
184
    id
185
  });
186

187
  return {
3✔
188
    ref,
189
    close: () => {
190
      if (ref.current) {
×
191
        ref.current.open = false;
×
192
      }
193
    }
194
  };
195
}
196

197
function showToastFn(props: ToastPropTypes, container?: Element | DocumentFragment): ModalReturnType<ToastDomRef> {
198
  const ref = createRef<ToastDomRef>();
2✔
199
  const id = getRandomId();
2✔
200
  ModalStore.addModal({
2✔
201
    Component: Toast,
202
    props: {
203
      ...props,
204
      open: true,
205
      onClose: (event) => {
206
        if (typeof props.onClose === 'function') {
×
207
          props.onClose(event);
×
208
        }
209
        ModalStore.removeModal(id);
×
210
      }
211
    },
212
    ref,
213
    container,
214
    id
215
  });
216

217
  return {
2✔
218
    ref
219
  };
220
}
221

222
// todo: remove this once it's possible initializing popovers with `open=true` again
223
function ModalComponent({ modal }: { modal: IModal }) {
224
  const [componentRef, modalsRef] = useSyncRef(modal.ref);
12✔
225
  useIsomorphicLayoutEffect(() => {
12✔
226
    const modalElement = modalsRef.current as PopoverDomRef;
12✔
227
    if (modalElement) {
12✔
228
      requestAnimationFrame(() => {
12✔
229
        modalElement.open = true;
12✔
230
      });
231
    }
232
  }, []);
233

234
  const { open: _0, ...props } = modal.props;
12✔
235

236
  // @ts-expect-error: ref is supported by all supported modals
237
  return <modal.Component {...props} ref={componentRef} data-id={modal.id} />;
12✔
238
}
239

240
/**
241
 * Utility class for opening modals in an imperative way.
242
 *
243
 * These static helper methods might be useful for showing e.g. Toasts or MessageBoxes after successful or failed
244
 * network calls.
245
 *
246
 * **In order to use these helpers, please make sure to render the `Modals` component somewhere in your application tree.**
247
 *
248
 * @since 0.22.2
249
 */
250
export function Modals() {
251
  const modals = useSyncExternalStore(ModalStore.subscribe, ModalStore.getSnapshot, ModalStore.getServerSnapshot);
34✔
252

253
  return (
34✔
254
    <>
255
      {modals.map((modal) => {
256
        if (modal?.Component) {
12✔
257
          if (modal.container) {
12✔
258
            return createPortal(<ModalComponent modal={modal} key={modal.id} />, modal.container);
2✔
259
          }
260
          return <ModalComponent modal={modal} key={modal.id} />;
10✔
261
        }
262
      })}
263
    </>
264
  );
265
}
266

267
Modals.displayName = 'Modals';
426✔
268

269
Modals.showDialog = showDialogFn;
426✔
270
Modals.showPopover = showPopoverFn;
426✔
271
Modals.showResponsivePopover = showResponsivePopoverFn;
426✔
272
Modals.showMenu = showMenuFn;
426✔
273
Modals.showMessageBox = showMessageBoxFn;
426✔
274
Modals.showToast = showToastFn;
426✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc