@telus-uds/components-base 1.73.0 → 1.75.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +27 -3
  2. package/lib/ExpandCollapse/Panel.js +1 -1
  3. package/lib/Footnote/Footnote.js +328 -0
  4. package/lib/Footnote/FootnoteLink.js +108 -0
  5. package/lib/Footnote/dictionary.js +19 -0
  6. package/lib/Footnote/index.js +12 -0
  7. package/lib/Notification/Notification.js +213 -35
  8. package/lib/Responsive/Responsive.js +8 -0
  9. package/lib/Responsive/ResponsiveWithMediaQueryStyleSheet.js +6 -3
  10. package/lib/Typography/Typography.js +3 -1
  11. package/lib/index.js +8 -0
  12. package/lib/utils/ssr-media-query/create-stylesheet/index.js +1 -2
  13. package/lib-module/ExpandCollapse/Panel.js +1 -1
  14. package/lib-module/Footnote/Footnote.js +319 -0
  15. package/lib-module/Footnote/FootnoteLink.js +101 -0
  16. package/lib-module/Footnote/dictionary.js +12 -0
  17. package/lib-module/Footnote/index.js +4 -0
  18. package/lib-module/Notification/Notification.js +216 -38
  19. package/lib-module/Responsive/Responsive.js +8 -0
  20. package/lib-module/Responsive/ResponsiveWithMediaQueryStyleSheet.js +6 -3
  21. package/lib-module/Typography/Typography.js +3 -1
  22. package/lib-module/index.js +1 -0
  23. package/lib-module/utils/ssr-media-query/create-stylesheet/index.js +1 -2
  24. package/package.json +2 -2
  25. package/src/ExpandCollapse/Panel.jsx +1 -1
  26. package/src/Footnote/Footnote.jsx +316 -0
  27. package/src/Footnote/FootnoteLink.jsx +95 -0
  28. package/src/Footnote/dictionary.js +12 -0
  29. package/src/Footnote/index.js +6 -0
  30. package/src/Notification/Notification.jsx +213 -34
  31. package/src/Responsive/Responsive.jsx +8 -2
  32. package/src/Responsive/ResponsiveWithMediaQueryStyleSheet.jsx +6 -4
  33. package/src/Typography/Typography.jsx +6 -1
  34. package/src/index.js +1 -0
  35. package/src/utils/ssr-media-query/create-stylesheet/index.js +3 -2
@@ -0,0 +1,319 @@
1
+ import React, { useEffect, useState, useCallback } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
4
+ import Text from "react-native-web/dist/exports/Text";
5
+ import Dimensions from "react-native-web/dist/exports/Dimensions";
6
+ import TouchableWithoutFeedback from "react-native-web/dist/exports/TouchableWithoutFeedback";
7
+ import Modal from "react-native-web/dist/exports/Modal";
8
+ import View from "react-native-web/dist/exports/View";
9
+ import SafeAreaView from "react-native-web/dist/exports/SafeAreaView";
10
+ import ScrollView from "react-native-web/dist/exports/ScrollView";
11
+ import defaultDictionary from './dictionary';
12
+ import { getTokensPropType, htmlAttrs, selectSystemProps, useCopy, viewProps } from '../utils';
13
+ import { useViewport } from '../ViewportProvider';
14
+ import { useTheme, useThemeTokens } from '../ThemeProvider';
15
+ import Typography from '../Typography';
16
+ import Icon from '../Icon';
17
+ import OrderedList from '../OrderedList';
18
+ import { jsx as _jsx } from "react/jsx-runtime";
19
+ import { jsxs as _jsxs } from "react/jsx-runtime";
20
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs, viewProps]);
21
+ const selectFootnoteStyle = _ref => {
22
+ let {
23
+ screenHeight,
24
+ footnoteBackground,
25
+ isVisible,
26
+ footnoteBorderTopSizeMd,
27
+ footnoteBorderColorMd
28
+ } = _ref;
29
+ return {
30
+ left: 0,
31
+ backgroundColor: footnoteBackground,
32
+ display: isVisible ? 'flex' : 'none',
33
+ height: screenHeight,
34
+ borderTopColor: footnoteBorderColorMd,
35
+ borderTopWidth: footnoteBorderTopSizeMd
36
+ };
37
+ };
38
+ const selectFootnoteHeaderContentStyle = _ref2 => {
39
+ let {
40
+ footnoteHeaderPaddingTop,
41
+ footnoteHeaderPaddingBottom,
42
+ footnoteHeaderPaddingRight,
43
+ footnoteHeaderPaddingLeft,
44
+ headerMargin
45
+ } = _ref2;
46
+ return {
47
+ paddingTop: footnoteHeaderPaddingTop,
48
+ paddingBottom: footnoteHeaderPaddingBottom,
49
+ paddingRight: footnoteHeaderPaddingRight,
50
+ paddingLeft: footnoteHeaderPaddingLeft,
51
+ marginRight: headerMargin
52
+ };
53
+ };
54
+ const selectFootnoteCloseButtonStyle = _ref3 => {
55
+ let {
56
+ closeButtonBorderSize,
57
+ closeButtonBorderColor,
58
+ closeButtonHeight,
59
+ closeButtonBackgroundColor,
60
+ closeButtonMarginTop,
61
+ closeButtonMarginRight,
62
+ closeButtonMarginBottom,
63
+ closeButtonMarginLeft,
64
+ closeButtonWidth
65
+ } = _ref3;
66
+ return {
67
+ backgroundColor: closeButtonBackgroundColor,
68
+ borderWidth: closeButtonBorderSize,
69
+ borderColor: closeButtonBorderColor,
70
+ height: closeButtonHeight,
71
+ marginTop: closeButtonMarginTop,
72
+ marginRight: closeButtonMarginRight,
73
+ marginBottom: closeButtonMarginBottom,
74
+ marginLeft: closeButtonMarginLeft,
75
+ width: closeButtonWidth
76
+ };
77
+ };
78
+ const selectFootnoteBodyStyle = _ref4 => {
79
+ let {
80
+ maxWidth,
81
+ footnoteBodyBackground,
82
+ footnoteBodyPaddingTop,
83
+ footnoteBodyPaddingRight,
84
+ footnoteBodyPaddingBottom,
85
+ footnoteBodyPaddingLeft,
86
+ screenWidth
87
+ } = _ref4;
88
+ return {
89
+ maxWidth,
90
+ backgroundColor: footnoteBodyBackground,
91
+ paddingTop: footnoteBodyPaddingTop,
92
+ paddingRight: footnoteBodyPaddingRight * 2,
93
+ paddingBottom: footnoteBodyPaddingBottom,
94
+ paddingLeft: footnoteBodyPaddingLeft,
95
+ width: screenWidth
96
+ };
97
+ };
98
+ const selectCustomContentFontStyle = _ref5 => {
99
+ let {
100
+ listItemColor,
101
+ listItemFontSize,
102
+ listItemLineHeight,
103
+ listItemPaddingLeft,
104
+ listItemFontName,
105
+ listItemFontWeight
106
+ } = _ref5;
107
+ return {
108
+ fontSize: listItemFontSize,
109
+ lineHeight: listItemLineHeight * listItemFontSize,
110
+ paddingLeft: listItemPaddingLeft,
111
+ color: listItemColor,
112
+ fontName: listItemFontName,
113
+ fontWeight: listItemFontWeight
114
+ };
115
+ };
116
+
117
+ /**
118
+ * Use `Footnote` to display a single legal content.
119
+ *
120
+ * ## Usage Criteria
121
+ *
122
+ * - Use `Footnote` to display a single legal statement
123
+ * - Display on top of all UI, including other sticky elements such as Cart Summary
124
+ * - Dismiss by clicking on the close button, clicking anywhere outside of the `Footnote`
125
+ * - Use copy to set language, ‘en’ for English or ‘fr’ for French
126
+ *
127
+ * ## Accessibility requirements
128
+ *
129
+ * - Only one instance of `Footnote` should display at a time
130
+ * - Place `Footnote` as the last element in the body or main
131
+ * - When `Footnote` is open, the inert prop must be set on all children of body excluding the Footnote
132
+ * - When `Footnote` is closed, focus must return to the initiating element
133
+ */
134
+ const Footnote = _ref6 => {
135
+ let {
136
+ copy = 'en',
137
+ number = undefined,
138
+ content = undefined,
139
+ onClose,
140
+ isOpen = false,
141
+ tokens,
142
+ variant = {},
143
+ dictionary = defaultDictionary,
144
+ ...rest
145
+ } = _ref6;
146
+ const viewport = useViewport();
147
+ const themeTokens = useThemeTokens('Footnote', tokens, variant, {
148
+ viewport
149
+ });
150
+ const themeOptions = useTheme();
151
+ const getCopy = useCopy({
152
+ dictionary,
153
+ copy
154
+ });
155
+ const [isVisible, setIsVisible] = useState(false);
156
+ const screenHeight = Dimensions.get('screen').height;
157
+ const screenWidth = Dimensions.get('screen').width;
158
+ const getFootnoteBodyContent = useCallback(() => {
159
+ if (!number || !content) {
160
+ return null;
161
+ }
162
+ if ( /*#__PURE__*/React.isValidElement(content)) {
163
+ return /*#__PURE__*/_jsx(View, {
164
+ style: selectCustomContentFontStyle(themeTokens),
165
+ children: content
166
+ });
167
+ }
168
+ return (
169
+ /*#__PURE__*/
170
+ // TODO: Extract the OrderedList.Item from the array when the issue #4361 is fixed
171
+ _jsx(OrderedList, {
172
+ start: number,
173
+ children: [/*#__PURE__*/_jsx(OrderedList.Item, {
174
+ children: /*#__PURE__*/_jsx(Text, {
175
+ style: selectCustomContentFontStyle(themeTokens),
176
+ children: content
177
+ })
178
+ }, number)]
179
+ })
180
+ );
181
+ }, [content, number, themeTokens]);
182
+ useEffect(() => {
183
+ if (isOpen) {
184
+ setIsVisible(true);
185
+ }
186
+ }, [isOpen]);
187
+ const closeFootnote = useCallback((event, options) => {
188
+ onClose(event, options);
189
+ setIsVisible(false);
190
+ }, [onClose]);
191
+ const handleClose = event => closeFootnote(event, {
192
+ returnFocus: true
193
+ });
194
+ return /*#__PURE__*/_jsx(View, {
195
+ ...selectProps(rest),
196
+ children: /*#__PURE__*/_jsx(Modal, {
197
+ visible: isVisible,
198
+ animationType: "slide",
199
+ children: /*#__PURE__*/_jsx(SafeAreaView, {
200
+ style: staticStyles.container,
201
+ children: /*#__PURE__*/_jsx(ScrollView, {
202
+ style: selectFootnoteStyle({
203
+ screenHeight,
204
+ isVisible,
205
+ ...themeTokens
206
+ }),
207
+ children: /*#__PURE__*/_jsxs(View, {
208
+ style: staticStyles.content,
209
+ children: [/*#__PURE__*/_jsxs(View, {
210
+ style: [selectFootnoteHeaderContentStyle(themeTokens), staticStyles.headerContent],
211
+ children: [/*#__PURE__*/_jsx(Typography, {
212
+ tokens: {
213
+ fontSize: themeTokens === null || themeTokens === void 0 ? void 0 : themeTokens.headerFontSize,
214
+ lineHeight: themeTokens === null || themeTokens === void 0 ? void 0 : themeTokens.headerLineHeight
215
+ },
216
+ variant: {
217
+ size: 'h4'
218
+ },
219
+ children: getCopy('heading')
220
+ }), /*#__PURE__*/_jsx(TouchableWithoutFeedback, {
221
+ onPress: handleClose,
222
+ accessibilityLabel: getCopy('close'),
223
+ children: /*#__PURE__*/_jsx(View, {
224
+ style: [selectFootnoteCloseButtonStyle(themeTokens), staticStyles.closeButton],
225
+ children: /*#__PURE__*/_jsx(Icon, {
226
+ icon: themeTokens === null || themeTokens === void 0 ? void 0 : themeTokens.closeIcon,
227
+ tokens: {
228
+ size: themeTokens === null || themeTokens === void 0 ? void 0 : themeTokens.closeButtonIconSize
229
+ }
230
+ })
231
+ })
232
+ })]
233
+ }), /*#__PURE__*/_jsx(View, {
234
+ style: selectFootnoteBodyStyle({
235
+ maxWidth: themeOptions.contentMaxWidth,
236
+ screenWidth,
237
+ ...themeTokens
238
+ }),
239
+ children: getFootnoteBodyContent()
240
+ })]
241
+ })
242
+ })
243
+ })
244
+ })
245
+ });
246
+ };
247
+ const copyShape = PropTypes.shape({
248
+ close: PropTypes.string.isRequired,
249
+ heading: PropTypes.string.isRequired
250
+ });
251
+
252
+ // If a language dictionary entry is provided, it must contain every key
253
+ const dictionaryContentShape = PropTypes.shape({
254
+ a11yLabel: PropTypes.string.isRequired,
255
+ close: PropTypes.string.isRequired,
256
+ heading: PropTypes.string.isRequired
257
+ });
258
+ Footnote.propTypes = {
259
+ ...selectedSystemPropTypes,
260
+ tokens: getTokensPropType('Footnote'),
261
+ /**
262
+ * The content.
263
+ */
264
+ content: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
265
+ /**
266
+ * Use the `copy` prop to either select provided English or French copy by passing 'en' or 'fr' respectively.
267
+ * To provide your own, pass a JSON object with the keys `heading` and `close`.
268
+ */
269
+ copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), copyShape]),
270
+ /**
271
+ * A boolean flag used hide or show the `Footnote`. Set to `true` to open the `Footnote`.
272
+ */
273
+ isOpen: PropTypes.bool,
274
+ /**
275
+ * The number, must match the number of the `FootnoteLink` that initiated the `Footnote`.
276
+ */
277
+ number: PropTypes.number,
278
+ /**
279
+ * A callback function to handle the closing of the footnote.
280
+ *
281
+ * @param {SyntheticEvent} event The React `SyntheticEvent`
282
+ * @param {Object} options Custom options
283
+ * @param {boolean} options.returnFocus Should the `Footnote` return focus on close
284
+ */
285
+ onClose: PropTypes.func.isRequired,
286
+ /**
287
+ * Override the default dictionary, by passing the complete dictionary object for `en` and `fr`
288
+ */
289
+ dictionary: PropTypes.shape({
290
+ en: dictionaryContentShape,
291
+ fr: dictionaryContentShape
292
+ })
293
+ };
294
+ export default Footnote;
295
+ const staticStyles = StyleSheet.create({
296
+ container: {
297
+ flex: 1
298
+ },
299
+ content: {
300
+ marginLeft: 'auto',
301
+ marginRight: 'auto',
302
+ left: 0,
303
+ right: 0,
304
+ maxWidth: 1200,
305
+ paddingBottom: 100
306
+ },
307
+ headerContent: {
308
+ alignItems: 'center',
309
+ display: 'flex',
310
+ flexDirection: 'row',
311
+ justifyContent: 'space-between'
312
+ },
313
+ closeButton: {
314
+ alignItems: 'center',
315
+ borderRadius: 50,
316
+ display: 'flex',
317
+ justifyContent: 'center'
318
+ }
319
+ });
@@ -0,0 +1,101 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import View from "react-native-web/dist/exports/View";
4
+ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
5
+ import Platform from "react-native-web/dist/exports/Platform";
6
+ import TouchableWithoutFeedback from "react-native-web/dist/exports/TouchableWithoutFeedback";
7
+ import dictionary from './dictionary';
8
+ import { htmlAttrs, selectSystemProps, useCopy, viewProps, wrapStringsInText } from '../utils';
9
+ import { applyTextStyles, useThemeTokens } from '../ThemeProvider';
10
+ import { jsx as _jsx } from "react/jsx-runtime";
11
+ import { Fragment as _Fragment } from "react/jsx-runtime";
12
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs, viewProps]);
13
+
14
+ // The top property varies between devices due to how devices render the viewport.
15
+ const selectTextStyle = _ref => {
16
+ let {
17
+ fontSize,
18
+ lineHeight
19
+ } = _ref;
20
+ return {
21
+ fontSize,
22
+ lineHeight: lineHeight !== 1 ? lineHeight : fontSize * 2,
23
+ ...Platform.select({
24
+ ios: {
25
+ top: -fontSize / 2
26
+ },
27
+ android: {
28
+ top: fontSize / 4
29
+ },
30
+ web: {
31
+ top: -fontSize / 2.8
32
+ }
33
+ })
34
+ };
35
+ };
36
+ const FootnoteLink = _ref2 => {
37
+ let {
38
+ copy = 'en',
39
+ content = [],
40
+ onClick,
41
+ tokens,
42
+ variant = {},
43
+ ...rest
44
+ } = _ref2;
45
+ const themeTokens = useThemeTokens('FootnoteLink', tokens, variant);
46
+ const textStyles = applyTextStyles(themeTokens);
47
+ const numbers = Array.isArray(content) ? content : [content];
48
+ const refs = numbers.map(() => /*#__PURE__*/React.createRef());
49
+ const handleOnClick = index => {
50
+ onClick(numbers[index], refs[index]);
51
+ };
52
+ const getCopy = useCopy({
53
+ dictionary,
54
+ copy
55
+ });
56
+ return /*#__PURE__*/_jsx(_Fragment, {
57
+ children: numbers.map((num, index) => /*#__PURE__*/_jsx(View, {
58
+ accessibilityLabel: getCopy('a11yLabel'),
59
+ ref: refs[index],
60
+ ...selectProps(rest),
61
+ children: /*#__PURE__*/_jsx(TouchableWithoutFeedback, {
62
+ onPress: handleOnClick,
63
+ accessibilityRole: "button",
64
+ children: wrapStringsInText(`${num}${index !== numbers.length - 1 ? ',' : ''}`, {
65
+ style: {
66
+ ...textStyles,
67
+ ...staticStyles.text,
68
+ ...selectTextStyle(themeTokens)
69
+ }
70
+ })
71
+ })
72
+ }, num))
73
+ });
74
+ };
75
+ const copyShape = PropTypes.shape({
76
+ a11yLabel: PropTypes.string.isRequired
77
+ });
78
+ FootnoteLink.propTypes = {
79
+ ...selectedSystemPropTypes,
80
+ /**
81
+ * Use the `copy` prop to either select provided English or French copy by passing 'en' or 'fr' respectively.
82
+ * To provide your own, pass a JSON object with the key `a11yLabel`.
83
+ */
84
+ copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), copyShape]),
85
+ /**
86
+ * The footnote number, or multiple numbers if passed as an array.
87
+ * If using an array, a comma-separated group of numbers will be rendered as superscript.
88
+ */
89
+ content: PropTypes.oneOfType([PropTypes.number, PropTypes.arrayOf(PropTypes.number), PropTypes.string, PropTypes.arrayOf(PropTypes.string)]).isRequired,
90
+ /**
91
+ * A callback function to handle the click of a FootnoteLink.
92
+ */
93
+ onClick: PropTypes.func.isRequired
94
+ };
95
+ export default FootnoteLink;
96
+ const staticStyles = StyleSheet.create({
97
+ text: {
98
+ position: 'relative',
99
+ textDecorationLine: 'underline'
100
+ }
101
+ });
@@ -0,0 +1,12 @@
1
+ export default {
2
+ en: {
3
+ a11yLabel: 'Read legal footnote',
4
+ close: 'close',
5
+ heading: 'Terms and conditions'
6
+ },
7
+ fr: {
8
+ a11yLabel: 'Lire la note de bas de page légale',
9
+ close: 'fermer',
10
+ heading: 'Modalités et conditions'
11
+ }
12
+ };
@@ -0,0 +1,4 @@
1
+ import Footnote from './Footnote';
2
+ import FootnoteLink from './FootnoteLink';
3
+ Footnote.Link = FootnoteLink;
4
+ export default Footnote;