forked from openedx/paragon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.jsx
176 lines (161 loc) · 5.22 KB
/
index.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import React, { useCallback, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import BaseAlert from 'react-bootstrap/Alert';
import divWithClassName from 'react-bootstrap/divWithClassName';
import { FormattedMessage } from 'react-intl';
import { useMediaQuery } from 'react-responsive';
import Icon from '../Icon';
import breakpoints from '../utils/breakpoints';
import Button from '../Button';
import ActionRow from '../ActionRow';
export const ALERT_CLOSE_LABEL_TEXT = 'Dismiss';
const Alert = React.forwardRef(({
children,
icon,
actions,
dismissible,
onClose,
closeLabel,
stacked,
...props
}, ref) => {
const [isStacked, setIsStacked] = useState(stacked);
const isExtraSmall = useMediaQuery({ maxWidth: breakpoints.extraSmall.maxWidth });
const actionButtonSize = 'sm';
useEffect(() => {
if (isExtraSmall) {
setIsStacked(true);
} else {
setIsStacked(stacked);
}
}, [isExtraSmall, stacked]);
const cloneActionElement = useCallback(
(Action) => {
const addtlActionProps = { size: actionButtonSize, key: Action.props.children };
return React.cloneElement(Action, addtlActionProps);
},
[],
);
return (
<BaseAlert
{...props}
className={classNames('alert-content', props.className)}
ref={ref}
>
{icon && <Icon src={icon} className="alert-icon" />}
<div
className={classNames({
'pgn__alert-message-wrapper': !isStacked,
'pgn__alert-message-wrapper-stacked': isStacked,
})}
>
<div className="alert-message-content">
{children}
</div>
{(dismissible || actions?.length > 0) && (
<ActionRow className="pgn__alert-actions">
<ActionRow.Spacer />
{dismissible && (
<Button
size={actionButtonSize}
variant="tertiary"
onClick={onClose}
>
{closeLabel || (
<FormattedMessage
id="pgn.Alert.closeLabel"
defaultMessage="Dismiss"
description="Label of a close button on Alert component"
/>
)}
</Button>
)}
{actions && actions.map(cloneActionElement)}
</ActionRow>
)}
</div>
</BaseAlert>
);
});
// This is needed to display a default prop for Alert.Heading element
// Copied from react-bootstrap since BaseAlert.Heading component doesn't have defaultProps,
// so there seems to be no other way of providing correct default prop for base element
const DivStyledAsH4 = divWithClassName('h4');
DivStyledAsH4.displayName = 'DivStyledAsH4';
function AlertHeading(props) {
return <BaseAlert.Heading {...props} />;
}
function AlertLink(props) {
return <BaseAlert.Link {...props} />;
}
const commonPropTypes = {
/** Specifies the base element */
as: PropTypes.elementType,
/** Overrides underlying component base CSS class name */
bsPrefix: PropTypes.string,
};
AlertLink.propTypes = commonPropTypes;
AlertHeading.propTypes = commonPropTypes;
AlertLink.defaultProps = {
as: 'a',
bsPrefix: 'alert-link',
};
AlertHeading.defaultProps = {
as: DivStyledAsH4,
bsPrefix: 'alert-heading',
};
Alert.propTypes = {
...BaseAlert.propTypes,
/** Specifies class name to append to the base element */
className: PropTypes.string,
/** Overrides underlying component base CSS class name */
bsPrefix: PropTypes.string,
/** Specifies variant to use. */
variant: PropTypes.oneOf(['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'dark', 'light']),
/**
* Animate the entering and exiting of the Alert. `true` will use the `<Fade>` transition,
* more detailed customization is also provided.
*/
transition: PropTypes.oneOfType([PropTypes.bool, PropTypes.shape({
in: PropTypes.bool,
appear: PropTypes.bool,
children: PropTypes.node,
onEnter: PropTypes.func,
onEntered: PropTypes.func,
onEntering: PropTypes.func,
onExit: PropTypes.func,
onExited: PropTypes.func,
onExiting: PropTypes.func,
})]),
/** Docstring for the children prop */
children: PropTypes.node,
/** Docstring for the icon prop... Icon that will be shown in the alert */
icon: PropTypes.func,
/** Whether the alert is shown. */
show: PropTypes.bool,
/** Whether the alert is dismissible. Defaults to true. */
dismissible: PropTypes.bool,
/** Optional callback function for when the alert it dismissed. */
onClose: PropTypes.func,
/** Optional list of action elements. May include, at most, 2 actions, or 1 if dismissible is true. */
actions: PropTypes.arrayOf(PropTypes.element),
/** Position of the dismiss and call-to-action buttons. Defaults to ``false``. */
stacked: PropTypes.bool,
/** Sets the text for alert close button, defaults to 'Dismiss'. */
closeLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
};
Alert.defaultProps = {
...BaseAlert.defaultProps,
children: undefined,
icon: undefined,
actions: undefined,
dismissible: false,
onClose: () => {},
closeLabel: undefined,
show: true,
stacked: false,
};
Alert.Heading = AlertHeading;
Alert.Link = AlertLink;
export default Alert;