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

SAP / ui5-webcomponents-react / 3658276346

pending completion
3658276346

Pull #3856

github

GitHub
Merge 5d0b46815 into beb032e09
Pull Request #3856: fix(Modals - TypeScript): allow typing the container element

3040 of 6031 branches covered (50.41%)

63 of 63 new or added lines in 3 files covered. (100.0%)

4225 of 5253 relevant lines covered (80.43%)

1079.1 hits per line

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

61.24
/packages/main/src/components/Modals/index.tsx
1
import React, { createRef, MutableRefObject, RefObject, useCallback } from 'react';
2
import { getRandomId } from '../../internal/getRandomId';
3
import { UpdateModalStateAction, useModalsContext } from '../../internal/ModalsContext';
4
import {
5
  Dialog,
6
  DialogDomRef,
7
  DialogPropTypes,
8
  Popover,
9
  PopoverDomRef,
10
  PopoverPropTypes,
11
  ResponsivePopover,
12
  ResponsivePopoverDomRef,
13
  ResponsivePopoverPropTypes,
14
  Toast,
15
  ToastDomRef,
16
  ToastPropTypes
17
} from '../../webComponents';
18
import { MessageBox, MessageBoxPropTypes } from '../MessageBox';
19

20
type ModalReturnType<DomRef> = {
21
  ref: RefObject<DomRef>;
22
};
23

24
type ClosableModalReturnType<DomRef> = ModalReturnType<DomRef> & {
25
  close: () => void;
26
};
27

28
type ModalHookReturnType<Props, DomRef, ContainerElement = HTMLElement> = (
29
  props: Props,
30
  container?: ContainerElement
31
) => ModalReturnType<DomRef>;
32
type CloseableModalHookReturnType<Props, DomRef, ContainerElement = HTMLElement> = (
33
  props: Props,
34
  container?: ContainerElement
35
) => ClosableModalReturnType<DomRef>;
36

37
const checkContext = (context: any): void => {
21✔
38
  if (!context) {
10!
39
    // eslint-disable-next-line no-console
40
    console.error(`Please make sure that your application is wrapped in the '<ThemeProvider />' component.`);
×
41
  }
42
};
43

44
function showDialog<ContainerElement>(
45
  props: DialogPropTypes,
46
  setModal: React.Dispatch<UpdateModalStateAction<DialogPropTypes, DialogDomRef, ContainerElement>>,
47
  container?: ContainerElement
48
) {
49
  checkContext(setModal);
2✔
50
  const id = getRandomId();
2✔
51
  const ref = createRef<DialogDomRef>();
2✔
52
  setModal?.({
2✔
53
    type: 'set',
54
    payload: {
55
      Component: Dialog,
56
      props: {
57
        ...props,
58
        open: true,
59
        onAfterClose: (event) => {
60
          if (typeof props.onAfterClose === 'function') {
×
61
            props.onAfterClose(event);
×
62
          }
63
          setModal({
×
64
            type: 'reset',
65
            payload: { id }
66
          });
67
        }
68
      },
69
      ref,
70
      container,
71
      id
72
    }
73
  });
74
  return { ref };
2✔
75
}
76

77
function showPopover<ContainerElement>(
78
  props: PopoverPropTypes,
79
  setModal: React.Dispatch<UpdateModalStateAction<PopoverPropTypes, PopoverDomRef, ContainerElement>>,
80
  container?: ContainerElement
81
) {
82
  checkContext(setModal);
2✔
83
  const id = getRandomId();
2✔
84
  const ref = createRef<PopoverDomRef>();
2✔
85
  setModal?.({
2✔
86
    type: 'set',
87
    payload: {
88
      Component: Popover,
89
      props: {
90
        ...props,
91

92
        open: true,
93
        onAfterClose: (event) => {
94
          if (typeof props.onAfterClose === 'function') {
×
95
            props.onAfterClose(event);
×
96
          }
97
          setModal({
×
98
            type: 'reset',
99
            payload: { id }
100
          });
101
        }
102
      },
103
      ref,
104
      container,
105
      id
106
    }
107
  });
108
  return { ref };
2✔
109
}
110

111
function showResponsivePopover<ContainerElement>(
112
  props: ResponsivePopoverPropTypes,
113
  setModal: React.Dispatch<
114
    UpdateModalStateAction<ResponsivePopoverPropTypes, ResponsivePopoverDomRef, ContainerElement>
115
  >,
116
  container?: ContainerElement
117
) {
118
  checkContext(setModal);
2✔
119
  const id = getRandomId();
2✔
120
  const ref = createRef<ResponsivePopoverDomRef>();
2✔
121
  setModal?.({
2✔
122
    type: 'set',
123
    payload: {
124
      Component: ResponsivePopover,
125
      props: {
126
        ...props,
127
        open: true,
128
        onAfterClose: (event) => {
129
          if (typeof props.onAfterClose === 'function') {
×
130
            props.onAfterClose(event);
×
131
          }
132
          setModal({
×
133
            type: 'reset',
134
            payload: { id }
135
          });
136
        }
137
      },
138
      ref,
139
      container,
140
      id
141
    }
142
  });
143
  return { ref };
2✔
144
}
145

146
function showMessageBox<ContainerElement>(
147
  props: MessageBoxPropTypes,
148
  setModal: React.Dispatch<UpdateModalStateAction<MessageBoxPropTypes, DialogDomRef, ContainerElement>>,
149
  container?: ContainerElement
150
) {
151
  checkContext(setModal);
2✔
152
  const id = getRandomId();
2✔
153
  const ref = createRef<DialogDomRef>();
2✔
154
  setModal?.({
2✔
155
    type: 'set',
156
    payload: {
157
      Component: MessageBox,
158
      props: {
159
        ...props,
160
        open: true,
161
        onClose: (event) => {
162
          if (typeof props.onClose === 'function') {
×
163
            props.onClose(event);
×
164
          }
165
          setModal({
×
166
            type: 'reset',
167
            payload: { id }
168
          });
169
        }
170
      },
171
      ref,
172
      container,
173
      id
174
    }
175
  });
176
  return { ref };
2✔
177
}
178

179
function showToast<ContainerElement>(
180
  props: ToastPropTypes,
181
  setModal: React.Dispatch<UpdateModalStateAction<ToastPropTypes, ToastDomRef, ContainerElement>>,
182
  container?: ContainerElement
183
) {
184
  const ref = createRef<ToastDomRef>() as MutableRefObject<ToastDomRef>;
2✔
185
  checkContext(setModal);
2✔
186
  const id = getRandomId();
2✔
187
  setModal?.({
2✔
188
    type: 'set',
189
    payload: {
190
      Component: Toast,
191
      props: {
192
        ...props
193
      },
194
      ref: (el: ToastDomRef & { open: boolean }) => {
195
        ref.current = el;
4✔
196
        if (el && !el.open) {
4!
197
          el.show();
2✔
198
          setTimeout(() => {
2✔
199
            setModal({
×
200
              type: 'reset',
201
              payload: { id }
202
            });
203
          }, props.duration ?? Toast.defaultProps.duration);
4!
204
        }
205
      },
206
      container,
207
      id
208
    }
209
  });
210
  return { ref };
2✔
211
}
212

213
function showDialogFn<ContainerElement = HTMLElement>(
214
  props: DialogPropTypes,
215
  container?: ContainerElement
216
): ClosableModalReturnType<DialogDomRef> {
217
  const setModal = window['@ui5/webcomponents-react']?.setModal;
1✔
218

219
  const { ref } = showDialog<ContainerElement>(props, setModal, container);
1✔
220

221
  return {
1✔
222
    ref,
223
    close: () => {
224
      ref.current?.close();
×
225
    }
226
  };
227
}
228

229
function useShowDialogHook<ContainerElement>(): CloseableModalHookReturnType<
×
230
  DialogPropTypes,
231
  DialogDomRef,
232
  ContainerElement
233
> {
×
234
  const { setModal } = useModalsContext();
2✔
235

236
  return useCallback(
2✔
237
    (props, container) => {
238
      const { ref } = showDialog<ContainerElement>(props, setModal, container);
1✔
239

240
      return {
1✔
241
        ref,
242
        close: () => {
243
          ref.current?.close();
×
244
        }
245
      };
246
    },
247
    [setModal]
248
  );
249
}
19✔
250

251
function showPopoverFn<ContainerElement>(
252
  props: PopoverPropTypes,
253
  container?: ContainerElement
254
): ClosableModalReturnType<PopoverDomRef> {
255
  const setModal = window['@ui5/webcomponents-react']?.setModal;
1✔
256
  const { ref } = showPopover(props, setModal, container);
1✔
257

258
  return {
1✔
259
    ref,
260
    close: () => {
261
      ref.current?.close();
×
262
    }
263
  };
264
}
265

266
function useShowPopoverHook<ContainerElement>(): CloseableModalHookReturnType<
×
267
  PopoverPropTypes,
268
  PopoverDomRef,
269
  ContainerElement
270
> {
×
271
  const { setModal } = useModalsContext();
2✔
272
  return useCallback(
2✔
273
    (props, container) => {
274
      const { ref } = showPopover<ContainerElement>(props, setModal, container);
1✔
275

276
      return {
1✔
277
        ref,
278
        close: () => {
279
          ref.current?.close();
×
280
        }
281
      };
282
    },
283
    [setModal]
284
  );
285
}
19✔
286

287
function showResponsivePopoverFn<ContainerElement>(
288
  props: ResponsivePopoverPropTypes,
289
  container?: ContainerElement
290
): ClosableModalReturnType<ResponsivePopoverDomRef> {
291
  const setModal = window['@ui5/webcomponents-react']?.setModal;
1✔
292
  const { ref } = showResponsivePopover<ContainerElement>(props, setModal, container);
1✔
293

294
  return {
1✔
295
    ref,
296
    close: () => {
297
      ref.current?.close();
×
298
    }
299
  };
300
}
301

302
function useResponsivePopoverHook<ContainerElement>(): CloseableModalHookReturnType<
×
303
  ResponsivePopoverPropTypes,
304
  ResponsivePopoverDomRef,
305
  ContainerElement
306
> {
×
307
  const { setModal } = useModalsContext();
2✔
308
  return useCallback(
2✔
309
    (props, container) => {
310
      const { ref } = showResponsivePopover<ContainerElement>(props, setModal, container);
1✔
311

312
      return {
1✔
313
        ref,
314
        close: () => {
315
          ref.current?.close();
×
316
        }
317
      };
318
    },
319
    [setModal]
320
  );
321
}
19✔
322

323
function showMessageBoxFn<ContainerElement>(
324
  props: MessageBoxPropTypes,
325
  container?: ContainerElement
326
): ClosableModalReturnType<DialogDomRef> {
327
  const setModal = window['@ui5/webcomponents-react']?.setModal;
1✔
328
  const { ref } = showMessageBox<ContainerElement>(props, setModal, container);
1✔
329

330
  return {
1✔
331
    ref,
332
    close: () => {
333
      ref.current?.close();
×
334
    }
335
  };
336
}
337
function useShowMessageBox<ContainerElement>(): CloseableModalHookReturnType<
×
338
  MessageBoxPropTypes,
339
  DialogDomRef,
340
  ContainerElement
341
> {
×
342
  const { setModal } = useModalsContext();
2✔
343
  return useCallback(
2✔
344
    (props, container) => {
345
      const { ref } = showMessageBox<ContainerElement>(props, setModal, container);
1✔
346

347
      return {
1✔
348
        ref,
349
        close: () => {
350
          ref.current?.close();
×
351
        }
352
      };
353
    },
354
    [setModal]
355
  );
356
}
19✔
357

358
function showToastFn<ContainerElement>(
359
  props: ToastPropTypes,
360
  container?: ContainerElement
361
): ModalReturnType<ToastDomRef> {
362
  const setModal = window['@ui5/webcomponents-react']?.setModal;
1✔
363
  const { ref } = showToast<ContainerElement>(props, setModal, container);
1✔
364

365
  return {
1✔
366
    ref
367
  };
368
}
369

370
function useShowToastHook<ContainerElement>(): ModalHookReturnType<ToastPropTypes, ToastDomRef, ContainerElement> {
×
371
  const { setModal } = useModalsContext();
2✔
372

373
  return useCallback(
2✔
374
    (props: ToastPropTypes, container?) => {
375
      const { ref } = showToast<ContainerElement>(props, setModal, container);
1✔
376
      return {
1✔
377
        ref
378
      };
379
    },
380
    [setModal]
381
  );
382
}
383

384
/**
19✔
385
 * Utility class for opening modals in an imperative way.
386
 *
387
 * These static helper methods might be useful for showing e.g. Toasts or MessageBoxes after successful or failed
388
 * network calls.
389
 *
390
 * @since 0.22.2
391
 */
392
export const Modals = {
21✔
393
  showDialog: showDialogFn,
394
  useShowDialog: useShowDialogHook,
395
  showPopover: showPopoverFn,
396
  useShowPopover: useShowPopoverHook,
397
  showResponsivePopover: showResponsivePopoverFn,
398
  useShowResponsivePopover: useResponsivePopoverHook,
399
  showMessageBox: showMessageBoxFn,
400
  useShowMessageBox: useShowMessageBox,
401
  showToast: showToastFn,
402
  useShowToast: useShowToastHook
403
};
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