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

react-ui-org / react-ui / 16140343605

08 Jul 2025 10:10AM UTC coverage: 91.76%. Remained the same
16140343605

Pull #656

github

web-flow
Merge aa9b22fc9 into 3c7e5b4c2
Pull Request #656: Add custom properties for the border of disabled cards

762 of 836 branches covered (91.15%)

Branch coverage included in aggregate %.

719 of 778 relevant lines covered (92.42%)

77.33 hits per line

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

90.32
/src/components/Modal/Modal.jsx
1
import PropTypes from 'prop-types';
2
import React, {
3
  useCallback,
4
  useEffect,
5
  useImperativeHandle,
6
  useRef,
7
} from 'react';
8
import { createPortal } from 'react-dom';
9
import { classNames } from '../../helpers/classNames';
10
import { transferProps } from '../../helpers/transferProps';
11
import { withGlobalProps } from '../../providers/globalProps';
12
import { getRootColorClassName } from '../_helpers/getRootColorClassName';
13
import { dialogOnCancelHandler } from './_helpers/dialogOnCancelHandler';
14
import { dialogOnClickHandler } from './_helpers/dialogOnClickHandler';
15
import { dialogOnCloseHandler } from './_helpers/dialogOnCloseHandler';
16
import { dialogOnKeyDownHandler } from './_helpers/dialogOnKeyDownHandler';
17
import { getPositionClassName } from './_helpers/getPositionClassName';
18
import { getSizeClassName } from './_helpers/getSizeClassName';
19
import { useModalFocus } from './_hooks/useModalFocus';
20
import { useModalScrollPrevention } from './_hooks/useModalScrollPrevention';
21
import styles from './Modal.module.scss';
22

23
const preRender = (
4✔
24
  children,
25
  color,
26
  dialogRef,
27
  position,
28
  size,
29
  events,
30
  restProps,
31
) => (
32
  <dialog
62✔
33
    {...transferProps(restProps)}
34
    {...transferProps(events)}
35
    className={classNames(
36
      styles.root,
37
      color && getRootColorClassName(color, styles),
37✔
38
      getSizeClassName(size, styles),
39
      getPositionClassName(position, styles),
40
    )}
41
    ref={dialogRef}
42
  >
43
    {children}
44
  </dialog>
45
);
46

47
export const Modal = ({
4✔
48
  allowCloseOnBackdropClick,
49
  allowCloseOnEscapeKey,
50
  allowPrimaryActionOnEnterKey,
51
  autoFocus,
52
  children,
53
  closeButtonRef,
54
  color,
55
  dialogRef,
56
  portalId,
57
  position,
58
  preventScrollUnderneath,
59
  primaryButtonRef,
60
  size,
61
  ...restProps
62
}) => {
63
  const internalDialogRef = useRef();
62✔
64
  const mouseDownTarget = useRef(null);
62✔
65

66
  useEffect(() => {
62✔
67
    internalDialogRef.current.showModal();
62✔
68
  }, []);
69

70
  // We need to have a reference to the dialog element to be able to call its methods,
71
  // but at the same time we want to expose this reference to the parent component for
72
  // case someone wants to call dialog methods from outside the component.
73
  useImperativeHandle(dialogRef, () => internalDialogRef.current);
62✔
74

75
  useModalFocus(autoFocus, internalDialogRef, primaryButtonRef);
62✔
76
  useModalScrollPrevention(preventScrollUnderneath);
62✔
77

78
  const onCancel = useCallback(
62✔
79
    (e) => dialogOnCancelHandler(e, closeButtonRef, restProps.onCancel),
×
80
    [closeButtonRef, restProps.onCancel],
81
  );
82
  const onClick = useCallback(
62✔
83
    (e) => dialogOnClickHandler(
6✔
84
      e,
85
      closeButtonRef,
86
      internalDialogRef,
87
      allowCloseOnBackdropClick,
88
      mouseDownTarget.current,
89
    ),
90
    [allowCloseOnBackdropClick, closeButtonRef, internalDialogRef],
91
  );
92
  const onClose = useCallback(
62✔
93
    (e) => dialogOnCloseHandler(e, closeButtonRef, restProps.onClose),
×
94
    [closeButtonRef, restProps.onClose],
95
  );
96
  const onKeyDown = useCallback(
62✔
97
    (e) => dialogOnKeyDownHandler(
6✔
98
      e,
99
      closeButtonRef,
100
      primaryButtonRef,
101
      allowCloseOnEscapeKey,
102
      allowPrimaryActionOnEnterKey,
103
    ),
104
    [
105
      allowCloseOnEscapeKey,
106
      allowPrimaryActionOnEnterKey,
107
      closeButtonRef,
108
      primaryButtonRef,
109
    ],
110
  );
111

112
  const onMouseDown = useCallback((e) => {
62✔
113
    mouseDownTarget.current = e.target;
×
114
  }, []);
115

116
  const events = {
62✔
117
    onCancel,
118
    onClick,
119
    onClose,
120
    onKeyDown,
121
    onMouseDown,
122
  };
123

124
  if (portalId === null) {
62✔
125
    return preRender(
60✔
126
      children,
127
      color,
128
      internalDialogRef,
129
      position,
130
      size,
131
      events,
132
      restProps,
133
    );
134
  }
135

136
  return createPortal(
2✔
137
    preRender(
138
      children,
139
      color,
140
      internalDialogRef,
141
      position,
142
      size,
143
      events,
144
      restProps,
145
    ),
146
    document.getElementById(portalId),
147
  );
148
};
149

150
Modal.defaultProps = {
4✔
151
  allowCloseOnBackdropClick: true,
152
  allowCloseOnEscapeKey: true,
153
  allowPrimaryActionOnEnterKey: true,
154
  autoFocus: true,
155
  children: null,
156
  closeButtonRef: null,
157
  color: undefined,
158
  dialogRef: null,
159
  portalId: null,
160
  position: 'center',
161
  preventScrollUnderneath: window.document.body,
162
  primaryButtonRef: null,
163
  size: 'medium',
164
};
165

166
Modal.propTypes = {
4✔
167
  /**
168
   * If `true`, the `Modal` can be closed by clicking on the backdrop.
169
   */
170
  allowCloseOnBackdropClick: PropTypes.bool,
171
  /**
172
   * If `true`, the `Modal` can be closed by pressing the Escape key.
173
   */
174
  allowCloseOnEscapeKey: PropTypes.bool,
175
  /**
176
   * If `true`, the `Modal` can be submitted by pressing the Enter key.
177
   */
178
  allowPrimaryActionOnEnterKey: PropTypes.bool,
179
  /**
180
   * If `true`, focus the first input element in the `Modal`, or primary button (referenced by the `primaryButtonRef`
181
   * prop), or other focusable element when the `Modal` is opened. If there are none or `autoFocus` is set to `false`,
182
   * focus the Modal itself.
183
   */
184
  autoFocus: PropTypes.bool,
185
  /**
186
   * Nested elements. Supported types are:
187
   *
188
   * * `ModalHeader`
189
   * * `ModalBody`
190
   * * `ModalFooter`
191
   *
192
   * At least `ModalBody` is required.
193
   */
194
  children: PropTypes.node,
195
  /**
196
   * Reference to close button element. It is used to close modal when Escape key is pressed
197
   * or the backdrop is clicked.
198
   */
199
  closeButtonRef: PropTypes.shape({
200
    // eslint-disable-next-line react/forbid-prop-types
201
    current: PropTypes.any,
202
  }),
203
  /**
204
   * Color to clarify importance and meaning of the modal. Implements
205
   * [Feedback color collection](/docs/foundation/collections#colors).
206
   */
207
  color: PropTypes.oneOf(['success', 'warning', 'danger', 'help', 'info', 'note']),
208
  /**
209
   * Reference to dialog element
210
   */
211
  dialogRef: PropTypes.shape({
212
    // eslint-disable-next-line react/forbid-prop-types
213
    current: PropTypes.any,
214
  }),
215
  /**
216
   * If set, modal is rendered in the React Portal with that ID.
217
   */
218
  portalId: PropTypes.string,
219
  /**
220
   * Vertical position of the modal inside browser window.
221
   */
222
  position: PropTypes.oneOf(['top', 'center']),
223
  /**
224
   * Mode in which Modal prevents scroll of elements bellow:
225
   * * `off` - Modal does not prevent any scroll
226
   * * [HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) - Modal prevents scroll on this HTML element
227
   * * object
228
   *   * `reset` - method called on Modal's unmount to reset scroll prevention
229
   *   * `start` - method called on Modal's mount to custom scroll prevention
230
   */
231
  preventScrollUnderneath: PropTypes.oneOfType([
232
    PropTypes.oneOf([
233
      HTMLElement,
234
      'off',
235
    ]),
236
    PropTypes.shape({
237
      reset: PropTypes.func,
238
      start: PropTypes.func,
239
    }),
240
  ]),
241
  /**
242
   * Reference to primary button element. It is used to submit modal when Enter key is pressed and as fallback
243
   * when `autoFocus` functionality does not find any input element to be focused.
244
   */
245
  primaryButtonRef: PropTypes.shape({
246
    // eslint-disable-next-line react/forbid-prop-types
247
    current: PropTypes.any,
248
  }),
249
  /**
250
   * Size of the modal.
251
   */
252
  size: PropTypes.oneOf(['small', 'medium', 'large', 'fullscreen', 'auto']),
253
};
254

255
export const ModalWithGlobalProps = withGlobalProps(Modal, 'Modal');
4✔
256

257
export default ModalWithGlobalProps;
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

© 2026 Coveralls, Inc