@telus-uds/components-web 2.42.0 → 2.44.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.
package/CHANGELOG.md CHANGED
@@ -1,12 +1,37 @@
1
1
  # Change Log - @telus-uds/components-web
2
2
 
3
- This log was last generated on Sat, 12 Oct 2024 00:31:05 GMT and should not be manually modified.
3
+ This log was last generated on Wed, 13 Nov 2024 17:18:39 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 2.44.0
8
+
9
+ Wed, 13 Nov 2024 17:18:39 GMT
10
+
11
+ ### Minor changes
12
+
13
+ - `DatePicker`: add initialVisibleMonth prop (guillermo.peitzner@telus.com)
14
+
15
+ ## 2.43.0
16
+
17
+ Thu, 31 Oct 2024 05:09:12 GMT
18
+
19
+ ### Minor changes
20
+
21
+ - `Breadcrumbs`: add expander item on xs viewport (guillermo.peitzner@telus.com)
22
+ - `Modal`: Enable `footerTopWidth` token for `Modal` component (jaime.tuyuc@telus.com)
23
+ - `Types`: Export BaseProviderProps and add FileUpload types (6854874+kyletsang@users.noreply.github.com)
24
+ - Bump @telus-uds/components-base to v1.97.0
25
+ - Bump @telus-uds/system-theme-tokens to v2.66.0
26
+
27
+ ### Patches
28
+
29
+ - `DatePicker`: calendar misalignment and static overlay behavior fixed when the component is inside a modal (35577399+JoshHC@users.noreply.github.com)
30
+ - `Table`: added null check for container during resizing (kristina.kirpichnikova@telus.com)
31
+
7
32
  ## 2.42.0
8
33
 
9
- Sat, 12 Oct 2024 00:31:05 GMT
34
+ Sat, 12 Oct 2024 00:40:49 GMT
10
35
 
11
36
  ### Minor changes
12
37
 
@@ -8,6 +8,7 @@ var _react = _interopRequireWildcard(require("react"));
8
8
  var _propTypes = _interopRequireDefault(require("prop-types"));
9
9
  var _styledComponents = _interopRequireDefault(require("styled-components"));
10
10
  var _reactHelmetAsync = require("react-helmet-async");
11
+ var _lodash = require("lodash");
11
12
  var _componentsBase = require("@telus-uds/components-base");
12
13
  var _utils = require("../utils");
13
14
  var _Item = _interopRequireDefault(require("./Item/Item"));
@@ -79,10 +80,11 @@ const getItems = (items, params, concatenatePaths) => {
79
80
  } = item;
80
81
  return {
81
82
  breadcrumbName,
82
- href,
83
+ href: item.isExpander ? '#' : href,
83
84
  current: isLast,
84
85
  LinkRouter,
85
86
  linkRouterProps,
87
+ onPress: item.onPress,
86
88
  ...omitProps(selectProps(item))
87
89
  };
88
90
  });
@@ -97,6 +99,7 @@ const getStructuredData = (items, baseUrl) => {
97
99
  }
98
100
  }));
99
101
  };
102
+ const MAX_ITEMS_ON_XS_VIEWPORT = 4;
100
103
 
101
104
  /**
102
105
  * Display a hierarchy of links, commonly used for navigation.
@@ -138,7 +141,33 @@ const Breadcrumbs = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
138
141
  ...itemRest
139
142
  };
140
143
  }) : routes.filter(route => route.path && route.breadcrumbName);
141
- const items = getItems(activeRoutes, params, !children);
144
+ const [optionsHidden, setOptionsHidden] = _react.default.useState(false);
145
+ const [itemsToBeRendered, setItemsToBeRendered] = _react.default.useState([]);
146
+ const viewport = (0, _componentsBase.useViewport)();
147
+ _react.default.useEffect(() => {
148
+ if (optionsHidden) {
149
+ if (viewport !== 'xs' && !(0, _lodash.isEqual)(itemsToBeRendered, activeRoutes)) {
150
+ setItemsToBeRendered(activeRoutes);
151
+ }
152
+ return;
153
+ }
154
+ if (viewport === 'xs' && activeRoutes.length >= MAX_ITEMS_ON_XS_VIEWPORT) {
155
+ const newItems = [...activeRoutes.slice(0, 2), {
156
+ path: '#',
157
+ breadcrumbName: '...',
158
+ onPress: event => {
159
+ event.preventDefault();
160
+ setItemsToBeRendered(activeRoutes);
161
+ },
162
+ isExpander: true
163
+ }, activeRoutes[activeRoutes.length - 1]];
164
+ setItemsToBeRendered(newItems);
165
+ setOptionsHidden(true);
166
+ } else if (!(0, _lodash.isEqual)(itemsToBeRendered, activeRoutes)) {
167
+ setItemsToBeRendered(activeRoutes);
168
+ }
169
+ }, [viewport, activeRoutes, optionsHidden, itemsToBeRendered]);
170
+ const items = getItems(itemsToBeRendered, params, !children);
142
171
  const themeTokens = (0, _componentsBase.useThemeTokens)('Breadcrumbs', tokens, variant);
143
172
  const metadata = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactHelmetAsync.HelmetProvider, {
144
173
  context: helmetContext,
@@ -168,6 +197,7 @@ const Breadcrumbs = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
168
197
  breadcrumbName,
169
198
  LinkRouter: ItemLinkRouter = LinkRouter,
170
199
  linkRouterProps: itemLinkRouterProps,
200
+ onPress,
171
201
  ...itemRest
172
202
  } = _ref4;
173
203
  return /*#__PURE__*/(0, _react.createElement)(_Item.default, {
@@ -184,7 +214,8 @@ const Breadcrumbs = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
184
214
  ...variant,
185
215
  size: 'micro'
186
216
  },
187
- LinkRouter: ItemLinkRouter
217
+ LinkRouter: ItemLinkRouter,
218
+ onPress: onPress
188
219
  }, breadcrumbName);
189
220
  })
190
221
  }), metadata]
@@ -86,6 +86,7 @@ const Item = /*#__PURE__*/_react.default.forwardRef((_ref8, ref) => {
86
86
  // `light` variant (shared with the `Link` component) is default by design
87
87
  LinkRouter,
88
88
  linkRouterProps,
89
+ onPress,
89
90
  ...rest
90
91
  } = _ref8;
91
92
  const {
@@ -132,6 +133,7 @@ const Item = /*#__PURE__*/_react.default.forwardRef((_ref8, ref) => {
132
133
  LinkRouter: LinkRouter,
133
134
  linkRouterProps: linkRouterProps,
134
135
  variant: variant,
136
+ onPress: onPress,
135
137
  children: children
136
138
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(IconContainer, {
137
139
  iconPadding: iconPadding,
@@ -169,7 +171,11 @@ Item.propTypes = {
169
171
  */
170
172
  variant: _propTypes.default.shape({
171
173
  inverse: _propTypes.default.bool
172
- })
174
+ }),
175
+ /**
176
+ * Function to be called when the Item is clicked.
177
+ */
178
+ onPress: _propTypes.default.func
173
179
  };
174
180
  var _default = Item;
175
181
  exports.default = _default;
@@ -71,6 +71,12 @@ const PortalPositionedContainer = /*#__PURE__*/_styledComponents.default.div.wit
71
71
  }
72
72
  });
73
73
  const validDatePattern = /^([0-2][0-9]|3[0-1])(\/)(0[1-9]|1[0-2])\2(\d{4})$/;
74
+ const getInitialVisibleMonth = (initialVisibleMonth, inputDate) => {
75
+ if (initialVisibleMonth === '' || inputDate instanceof _moment.default) {
76
+ return null;
77
+ }
78
+ return () => (0, _moment.default)(initialVisibleMonth);
79
+ };
74
80
 
75
81
  /**
76
82
  * Use DatePicker to select a date on a calendar.
@@ -114,6 +120,7 @@ const DatePicker = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
114
120
  prevTestID = '',
115
121
  nextTestID = '',
116
122
  placeholder = 'DD / MM / YYYY',
123
+ initialVisibleMonth = '',
117
124
  ...rest
118
125
  } = _ref3;
119
126
  const [inputDate, setInputDate] = _react.default.useState(date instanceof _moment.default ? date : undefined);
@@ -124,25 +131,39 @@ const DatePicker = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
124
131
  left: 0,
125
132
  top: 0
126
133
  });
134
+ const datePickerRef = _react.default.useRef(null);
127
135
  (0, _componentsBase.useSafeLayoutEffect)(() => {
128
- const updateDimensions = () => {
129
- if (inline || !textInputRef.current) return;
136
+ const updateDatePickerPosition = () => {
137
+ if (inline || !(textInputRef !== null && textInputRef !== void 0 && textInputRef.current)) return;
130
138
  const {
131
139
  left,
132
140
  top
133
141
  } = textInputRef.current.getBoundingClientRect();
134
142
  const inputTop = top + window.scrollY;
143
+ const inputLeft = left + window.scrollX;
135
144
  setDatePickerPosition({
136
- left,
137
- top: inputTop + textInputRef.current.offsetHeight
145
+ top: inputTop + textInputRef.current.offsetHeight,
146
+ left: inputLeft
138
147
  });
139
148
  };
140
- const throttledUpdateDimensions = (0, _lodash.throttle)(updateDimensions, 100, {
149
+ const throttledUpdate = (0, _lodash.throttle)(updateDatePickerPosition, 100, {
141
150
  leading: false
142
151
  });
143
- throttledUpdateDimensions();
144
- window.addEventListener('resize', throttledUpdateDimensions);
145
- return () => window.removeEventListener('resize', throttledUpdateDimensions);
152
+
153
+ // Initial call to set the position
154
+ updateDatePickerPosition();
155
+
156
+ // Register event listeners
157
+ window.addEventListener('resize', throttledUpdate);
158
+ window.addEventListener('scroll', updateDatePickerPosition, {
159
+ capture: true
160
+ });
161
+ return () => {
162
+ window.removeEventListener('resize', throttledUpdate);
163
+ window.removeEventListener('scroll', updateDatePickerPosition, {
164
+ capture: true
165
+ });
166
+ };
146
167
  }, []);
147
168
  const [isFocused, setIsFocused] = _react.default.useState(false);
148
169
  const [isClickedInside, setIsClickedInside] = _react.default.useState(false);
@@ -323,7 +344,7 @@ const DatePicker = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
323
344
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_DayPickerSingleDateController.default, {
324
345
  date: inputDate,
325
346
  onDateChange: onChange,
326
- focused: inputDate ?? isFocused,
347
+ focused: (inputDate ?? isFocused) || initialVisibleMonth !== '',
327
348
  onFocusChange: onFocusChange,
328
349
  numberOfMonths: 1,
329
350
  hideKeyboardShortcutsPanel: true,
@@ -333,6 +354,7 @@ const DatePicker = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
333
354
  renderNavNextButton: renderNextButton,
334
355
  isOutsideRange: isDayDisabled,
335
356
  phrases: getCopy(),
357
+ initialVisibleMonth: getInitialVisibleMonth(initialVisibleMonth, inputDate),
336
358
  renderMonthElement: _ref7 => {
337
359
  let {
338
360
  month
@@ -369,6 +391,7 @@ const DatePicker = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
369
391
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(PortalPositionedContainer, {
370
392
  top: datePickerPosition.top,
371
393
  left: datePickerPosition.left,
394
+ ref: datePickerRef,
372
395
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_CalendarContainer.default, {
373
396
  ...selectProps(rest),
374
397
  daySize: daySize,
@@ -395,6 +418,7 @@ const DatePicker = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
395
418
  phrases: getCopy(),
396
419
  id: id,
397
420
  renderNavNextButton: renderNextButton,
421
+ initialVisibleMonth: getInitialVisibleMonth(initialVisibleMonth, inputDate),
398
422
  renderMonthElement: _ref8 => {
399
423
  let {
400
424
  month
@@ -491,7 +515,11 @@ DatePicker.propTypes = {
491
515
  /**
492
516
  * Optional placeholder for the date input. If not set the default value will be `DD / MM / YYYY`
493
517
  */
494
- placeholder: _propTypes.default.string
518
+ placeholder: _propTypes.default.string,
519
+ /**
520
+ * The initial month to display when the calendar is opened. Expects a string in the format `YYYY-MM`
521
+ */
522
+ initialVisibleMonth: _propTypes.default.string
495
523
  };
496
524
  var _default = DatePicker;
497
525
  exports.default = _default;
@@ -66,8 +66,9 @@ const Table = _ref2 => {
66
66
  const [tableWidth, setTableWidth] = _react.default.useState(0);
67
67
  (0, _componentsBase.useSafeLayoutEffect)(() => {
68
68
  const updateDimensions = () => {
69
- const containerClientWidth = containerRef.current.clientWidth;
70
- const responsiveTableWidth = fullWidth ? containerClientWidth : tableRef.current.clientWidth;
69
+ var _containerRef$current, _tableRef$current;
70
+ const containerClientWidth = (_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.clientWidth;
71
+ const responsiveTableWidth = fullWidth ? containerClientWidth : (_tableRef$current = tableRef.current) === null || _tableRef$current === void 0 ? void 0 : _tableRef$current.clientWidth;
71
72
  setContainerWidth(containerClientWidth);
72
73
  setTableWidth(responsiveTableWidth);
73
74
  };
@@ -2,7 +2,8 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import styled from 'styled-components';
4
4
  import { Helmet, HelmetProvider } from 'react-helmet-async';
5
- import { componentPropType, selectSystemProps, unpackFragment, withLinkRouter, getTokensPropType, useThemeTokens } from '@telus-uds/components-base';
5
+ import { isEqual } from 'lodash';
6
+ import { componentPropType, selectSystemProps, unpackFragment, withLinkRouter, getTokensPropType, useThemeTokens, useViewport } from '@telus-uds/components-base';
6
7
  import { htmlAttrs } from '../utils';
7
8
  import Item from './Item/Item';
8
9
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -72,10 +73,11 @@ const getItems = (items, params, concatenatePaths) => {
72
73
  } = item;
73
74
  return {
74
75
  breadcrumbName,
75
- href,
76
+ href: item.isExpander ? '#' : href,
76
77
  current: isLast,
77
78
  LinkRouter,
78
79
  linkRouterProps,
80
+ onPress: item.onPress,
79
81
  ...omitProps(selectProps(item))
80
82
  };
81
83
  });
@@ -90,6 +92,7 @@ const getStructuredData = (items, baseUrl) => {
90
92
  }
91
93
  }));
92
94
  };
95
+ const MAX_ITEMS_ON_XS_VIEWPORT = 4;
93
96
 
94
97
  /**
95
98
  * Display a hierarchy of links, commonly used for navigation.
@@ -131,7 +134,33 @@ const Breadcrumbs = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
131
134
  ...itemRest
132
135
  };
133
136
  }) : routes.filter(route => route.path && route.breadcrumbName);
134
- const items = getItems(activeRoutes, params, !children);
137
+ const [optionsHidden, setOptionsHidden] = React.useState(false);
138
+ const [itemsToBeRendered, setItemsToBeRendered] = React.useState([]);
139
+ const viewport = useViewport();
140
+ React.useEffect(() => {
141
+ if (optionsHidden) {
142
+ if (viewport !== 'xs' && !isEqual(itemsToBeRendered, activeRoutes)) {
143
+ setItemsToBeRendered(activeRoutes);
144
+ }
145
+ return;
146
+ }
147
+ if (viewport === 'xs' && activeRoutes.length >= MAX_ITEMS_ON_XS_VIEWPORT) {
148
+ const newItems = [...activeRoutes.slice(0, 2), {
149
+ path: '#',
150
+ breadcrumbName: '...',
151
+ onPress: event => {
152
+ event.preventDefault();
153
+ setItemsToBeRendered(activeRoutes);
154
+ },
155
+ isExpander: true
156
+ }, activeRoutes[activeRoutes.length - 1]];
157
+ setItemsToBeRendered(newItems);
158
+ setOptionsHidden(true);
159
+ } else if (!isEqual(itemsToBeRendered, activeRoutes)) {
160
+ setItemsToBeRendered(activeRoutes);
161
+ }
162
+ }, [viewport, activeRoutes, optionsHidden, itemsToBeRendered]);
163
+ const items = getItems(itemsToBeRendered, params, !children);
135
164
  const themeTokens = useThemeTokens('Breadcrumbs', tokens, variant);
136
165
  const metadata = /*#__PURE__*/_jsx(HelmetProvider, {
137
166
  context: helmetContext,
@@ -161,6 +190,7 @@ const Breadcrumbs = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
161
190
  breadcrumbName,
162
191
  LinkRouter: ItemLinkRouter = LinkRouter,
163
192
  linkRouterProps: itemLinkRouterProps,
193
+ onPress,
164
194
  ...itemRest
165
195
  } = _ref4;
166
196
  return /*#__PURE__*/_createElement(Item, {
@@ -177,7 +207,8 @@ const Breadcrumbs = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
177
207
  ...variant,
178
208
  size: 'micro'
179
209
  },
180
- LinkRouter: ItemLinkRouter
210
+ LinkRouter: ItemLinkRouter,
211
+ onPress: onPress
181
212
  }, breadcrumbName);
182
213
  })
183
214
  }), metadata]
@@ -81,6 +81,7 @@ const Item = /*#__PURE__*/React.forwardRef((_ref8, ref) => {
81
81
  // `light` variant (shared with the `Link` component) is default by design
82
82
  LinkRouter,
83
83
  linkRouterProps,
84
+ onPress,
84
85
  ...rest
85
86
  } = _ref8;
86
87
  const {
@@ -127,6 +128,7 @@ const Item = /*#__PURE__*/React.forwardRef((_ref8, ref) => {
127
128
  LinkRouter: LinkRouter,
128
129
  linkRouterProps: linkRouterProps,
129
130
  variant: variant,
131
+ onPress: onPress,
130
132
  children: children
131
133
  }), /*#__PURE__*/_jsx(IconContainer, {
132
134
  iconPadding: iconPadding,
@@ -164,6 +166,10 @@ Item.propTypes = {
164
166
  */
165
167
  variant: PropTypes.shape({
166
168
  inverse: PropTypes.bool
167
- })
169
+ }),
170
+ /**
171
+ * Function to be called when the Item is clicked.
172
+ */
173
+ onPress: PropTypes.func
168
174
  };
169
175
  export default Item;
@@ -66,6 +66,12 @@ const PortalPositionedContainer = /*#__PURE__*/styled.div.withConfig({
66
66
  }
67
67
  });
68
68
  const validDatePattern = /^([0-2][0-9]|3[0-1])(\/)(0[1-9]|1[0-2])\2(\d{4})$/;
69
+ const getInitialVisibleMonth = (initialVisibleMonth, inputDate) => {
70
+ if (initialVisibleMonth === '' || inputDate instanceof moment) {
71
+ return null;
72
+ }
73
+ return () => moment(initialVisibleMonth);
74
+ };
69
75
 
70
76
  /**
71
77
  * Use DatePicker to select a date on a calendar.
@@ -109,6 +115,7 @@ const DatePicker = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
109
115
  prevTestID = '',
110
116
  nextTestID = '',
111
117
  placeholder = 'DD / MM / YYYY',
118
+ initialVisibleMonth = '',
112
119
  ...rest
113
120
  } = _ref3;
114
121
  const [inputDate, setInputDate] = React.useState(date instanceof moment ? date : undefined);
@@ -119,25 +126,39 @@ const DatePicker = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
119
126
  left: 0,
120
127
  top: 0
121
128
  });
129
+ const datePickerRef = React.useRef(null);
122
130
  useSafeLayoutEffect(() => {
123
- const updateDimensions = () => {
124
- if (inline || !textInputRef.current) return;
131
+ const updateDatePickerPosition = () => {
132
+ if (inline || !(textInputRef !== null && textInputRef !== void 0 && textInputRef.current)) return;
125
133
  const {
126
134
  left,
127
135
  top
128
136
  } = textInputRef.current.getBoundingClientRect();
129
137
  const inputTop = top + window.scrollY;
138
+ const inputLeft = left + window.scrollX;
130
139
  setDatePickerPosition({
131
- left,
132
- top: inputTop + textInputRef.current.offsetHeight
140
+ top: inputTop + textInputRef.current.offsetHeight,
141
+ left: inputLeft
133
142
  });
134
143
  };
135
- const throttledUpdateDimensions = throttle(updateDimensions, 100, {
144
+ const throttledUpdate = throttle(updateDatePickerPosition, 100, {
136
145
  leading: false
137
146
  });
138
- throttledUpdateDimensions();
139
- window.addEventListener('resize', throttledUpdateDimensions);
140
- return () => window.removeEventListener('resize', throttledUpdateDimensions);
147
+
148
+ // Initial call to set the position
149
+ updateDatePickerPosition();
150
+
151
+ // Register event listeners
152
+ window.addEventListener('resize', throttledUpdate);
153
+ window.addEventListener('scroll', updateDatePickerPosition, {
154
+ capture: true
155
+ });
156
+ return () => {
157
+ window.removeEventListener('resize', throttledUpdate);
158
+ window.removeEventListener('scroll', updateDatePickerPosition, {
159
+ capture: true
160
+ });
161
+ };
141
162
  }, []);
142
163
  const [isFocused, setIsFocused] = React.useState(false);
143
164
  const [isClickedInside, setIsClickedInside] = React.useState(false);
@@ -318,7 +339,7 @@ const DatePicker = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
318
339
  children: /*#__PURE__*/_jsx(DayPickerSingleDateController, {
319
340
  date: inputDate,
320
341
  onDateChange: onChange,
321
- focused: inputDate ?? isFocused,
342
+ focused: (inputDate ?? isFocused) || initialVisibleMonth !== '',
322
343
  onFocusChange: onFocusChange,
323
344
  numberOfMonths: 1,
324
345
  hideKeyboardShortcutsPanel: true,
@@ -328,6 +349,7 @@ const DatePicker = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
328
349
  renderNavNextButton: renderNextButton,
329
350
  isOutsideRange: isDayDisabled,
330
351
  phrases: getCopy(),
352
+ initialVisibleMonth: getInitialVisibleMonth(initialVisibleMonth, inputDate),
331
353
  renderMonthElement: _ref7 => {
332
354
  let {
333
355
  month
@@ -364,6 +386,7 @@ const DatePicker = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
364
386
  children: /*#__PURE__*/_jsx(PortalPositionedContainer, {
365
387
  top: datePickerPosition.top,
366
388
  left: datePickerPosition.left,
389
+ ref: datePickerRef,
367
390
  children: /*#__PURE__*/_jsx(CalendarContainer, {
368
391
  ...selectProps(rest),
369
392
  daySize: daySize,
@@ -390,6 +413,7 @@ const DatePicker = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
390
413
  phrases: getCopy(),
391
414
  id: id,
392
415
  renderNavNextButton: renderNextButton,
416
+ initialVisibleMonth: getInitialVisibleMonth(initialVisibleMonth, inputDate),
393
417
  renderMonthElement: _ref8 => {
394
418
  let {
395
419
  month
@@ -486,6 +510,10 @@ DatePicker.propTypes = {
486
510
  /**
487
511
  * Optional placeholder for the date input. If not set the default value will be `DD / MM / YYYY`
488
512
  */
489
- placeholder: PropTypes.string
513
+ placeholder: PropTypes.string,
514
+ /**
515
+ * The initial month to display when the calendar is opened. Expects a string in the format `YYYY-MM`
516
+ */
517
+ initialVisibleMonth: PropTypes.string
490
518
  };
491
519
  export default DatePicker;
@@ -58,8 +58,9 @@ const Table = _ref2 => {
58
58
  const [tableWidth, setTableWidth] = React.useState(0);
59
59
  useSafeLayoutEffect(() => {
60
60
  const updateDimensions = () => {
61
- const containerClientWidth = containerRef.current.clientWidth;
62
- const responsiveTableWidth = fullWidth ? containerClientWidth : tableRef.current.clientWidth;
61
+ var _containerRef$current, _tableRef$current;
62
+ const containerClientWidth = (_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.clientWidth;
63
+ const responsiveTableWidth = fullWidth ? containerClientWidth : (_tableRef$current = tableRef.current) === null || _tableRef$current === void 0 ? void 0 : _tableRef$current.clientWidth;
63
64
  setContainerWidth(containerClientWidth);
64
65
  setTableWidth(responsiveTableWidth);
65
66
  };
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  ],
6
6
  "dependencies": {
7
7
  "@gorhom/portal": "^1.0.14",
8
- "@telus-uds/components-base": "1.96.0",
8
+ "@telus-uds/components-base": "1.97.0",
9
9
  "@telus-uds/system-constants": "^1.3.0",
10
10
  "fscreen": "^1.2.0",
11
11
  "lodash.omit": "^4.5.0",
@@ -13,7 +13,7 @@
13
13
  "react-dates": "^21.8.0",
14
14
  "react-helmet-async": "^1.3.0",
15
15
  "react-moment-proptypes": "^1.8.1",
16
- "@telus-uds/system-theme-tokens": "^2.65.0",
16
+ "@telus-uds/system-theme-tokens": "^2.66.0",
17
17
  "prop-types": "^15.7.2",
18
18
  "lodash.throttle": "^4.1.1",
19
19
  "react-youtube": "^10.1.0",
@@ -83,5 +83,5 @@
83
83
  "skip": true
84
84
  },
85
85
  "types": "types/index.d.ts",
86
- "version": "2.42.0"
86
+ "version": "2.44.0"
87
87
  }
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
3
3
  import styled from 'styled-components'
4
4
 
5
5
  import { Helmet, HelmetProvider } from 'react-helmet-async'
6
+ import { isEqual } from 'lodash'
6
7
 
7
8
  import {
8
9
  componentPropType,
@@ -10,7 +11,8 @@ import {
10
11
  unpackFragment,
11
12
  withLinkRouter,
12
13
  getTokensPropType,
13
- useThemeTokens
14
+ useThemeTokens,
15
+ useViewport
14
16
  } from '@telus-uds/components-base'
15
17
  import { htmlAttrs } from '../utils'
16
18
  import Item from './Item/Item'
@@ -79,10 +81,11 @@ const getItems = (items, params, concatenatePaths) => {
79
81
  const { LinkRouter, linkRouterProps } = item
80
82
  return {
81
83
  breadcrumbName,
82
- href,
84
+ href: item.isExpander ? '#' : href,
83
85
  current: isLast,
84
86
  LinkRouter,
85
87
  linkRouterProps,
88
+ onPress: item.onPress,
86
89
  ...omitProps(selectProps(item))
87
90
  }
88
91
  })
@@ -99,6 +102,8 @@ const getStructuredData = (items, baseUrl) => {
99
102
  }))
100
103
  }
101
104
 
105
+ const MAX_ITEMS_ON_XS_VIEWPORT = 4
106
+
102
107
  /**
103
108
  * Display a hierarchy of links, commonly used for navigation.
104
109
  */
@@ -139,7 +144,40 @@ const Breadcrumbs = React.forwardRef(
139
144
  )
140
145
  : routes.filter((route) => route.path && route.breadcrumbName)
141
146
 
142
- const items = getItems(activeRoutes, params, !children)
147
+ const [optionsHidden, setOptionsHidden] = React.useState(false)
148
+ const [itemsToBeRendered, setItemsToBeRendered] = React.useState([])
149
+
150
+ const viewport = useViewport()
151
+
152
+ React.useEffect(() => {
153
+ if (optionsHidden) {
154
+ if (viewport !== 'xs' && !isEqual(itemsToBeRendered, activeRoutes)) {
155
+ setItemsToBeRendered(activeRoutes)
156
+ }
157
+ return
158
+ }
159
+ if (viewport === 'xs' && activeRoutes.length >= MAX_ITEMS_ON_XS_VIEWPORT) {
160
+ const newItems = [
161
+ ...activeRoutes.slice(0, 2),
162
+ {
163
+ path: '#',
164
+ breadcrumbName: '...',
165
+ onPress: (event) => {
166
+ event.preventDefault()
167
+ setItemsToBeRendered(activeRoutes)
168
+ },
169
+ isExpander: true
170
+ },
171
+ activeRoutes[activeRoutes.length - 1]
172
+ ]
173
+ setItemsToBeRendered(newItems)
174
+ setOptionsHidden(true)
175
+ } else if (!isEqual(itemsToBeRendered, activeRoutes)) {
176
+ setItemsToBeRendered(activeRoutes)
177
+ }
178
+ }, [viewport, activeRoutes, optionsHidden, itemsToBeRendered])
179
+
180
+ const items = getItems(itemsToBeRendered, params, !children)
143
181
  const themeTokens = useThemeTokens('Breadcrumbs', tokens, variant)
144
182
 
145
183
  const metadata = (
@@ -168,6 +206,7 @@ const Breadcrumbs = React.forwardRef(
168
206
  breadcrumbName,
169
207
  LinkRouter: ItemLinkRouter = LinkRouter,
170
208
  linkRouterProps: itemLinkRouterProps,
209
+ onPress,
171
210
  ...itemRest
172
211
  }) => {
173
212
  return (
@@ -180,6 +219,7 @@ const Breadcrumbs = React.forwardRef(
180
219
  linkRouterProps={{ ...linkRouterProps, ...itemLinkRouterProps }}
181
220
  variant={{ ...variant, size: 'micro' }}
182
221
  LinkRouter={ItemLinkRouter}
222
+ onPress={onPress}
183
223
  >
184
224
  {breadcrumbName}
185
225
  </Item>
@@ -40,6 +40,7 @@ const Item = React.forwardRef(
40
40
  variant = { light: true }, // `light` variant (shared with the `Link` component) is default by design
41
41
  LinkRouter,
42
42
  linkRouterProps,
43
+ onPress,
43
44
  ...rest
44
45
  },
45
46
  ref
@@ -89,6 +90,7 @@ const Item = React.forwardRef(
89
90
  LinkRouter={LinkRouter}
90
91
  linkRouterProps={linkRouterProps}
91
92
  variant={variant}
93
+ onPress={onPress}
92
94
  >
93
95
  {children}
94
96
  </Link>
@@ -129,7 +131,11 @@ Item.propTypes = {
129
131
  /**
130
132
  * Variant to render.
131
133
  */
132
- variant: PropTypes.shape({ inverse: PropTypes.bool })
134
+ variant: PropTypes.shape({ inverse: PropTypes.bool }),
135
+ /**
136
+ * Function to be called when the Item is clicked.
137
+ */
138
+ onPress: PropTypes.func
133
139
  }
134
140
 
135
141
  export default Item
@@ -58,6 +58,13 @@ const PortalPositionedContainer = styled.div({
58
58
 
59
59
  const validDatePattern = /^([0-2][0-9]|3[0-1])(\/)(0[1-9]|1[0-2])\2(\d{4})$/
60
60
 
61
+ const getInitialVisibleMonth = (initialVisibleMonth, inputDate) => {
62
+ if (initialVisibleMonth === '' || inputDate instanceof moment) {
63
+ return null
64
+ }
65
+ return () => moment(initialVisibleMonth)
66
+ }
67
+
61
68
  /**
62
69
  * Use DatePicker to select a date on a calendar.
63
70
  *
@@ -100,6 +107,7 @@ const DatePicker = React.forwardRef(
100
107
  prevTestID = '',
101
108
  nextTestID = '',
102
109
  placeholder = 'DD / MM / YYYY',
110
+ initialVisibleMonth = '',
103
111
  ...rest
104
112
  },
105
113
  ref
@@ -111,21 +119,33 @@ const DatePicker = React.forwardRef(
111
119
  const textInputRef = React.useRef()
112
120
  const prevButtonRef = React.useRef()
113
121
  const [datePickerPosition, setDatePickerPosition] = React.useState({ left: 0, top: 0 })
122
+ const datePickerRef = React.useRef(null)
114
123
 
115
124
  useSafeLayoutEffect(() => {
116
- const updateDimensions = () => {
117
- if (inline || !textInputRef.current) return
125
+ const updateDatePickerPosition = () => {
126
+ if (inline || !textInputRef?.current) return
118
127
  const { left, top } = textInputRef.current.getBoundingClientRect()
119
128
  const inputTop = top + window.scrollY
129
+ const inputLeft = left + window.scrollX
120
130
  setDatePickerPosition({
121
- left,
122
- top: inputTop + textInputRef.current.offsetHeight
131
+ top: inputTop + textInputRef.current.offsetHeight,
132
+ left: inputLeft
123
133
  })
124
134
  }
125
- const throttledUpdateDimensions = throttle(updateDimensions, 100, { leading: false })
126
- throttledUpdateDimensions()
127
- window.addEventListener('resize', throttledUpdateDimensions)
128
- return () => window.removeEventListener('resize', throttledUpdateDimensions)
135
+
136
+ const throttledUpdate = throttle(updateDatePickerPosition, 100, { leading: false })
137
+
138
+ // Initial call to set the position
139
+ updateDatePickerPosition()
140
+
141
+ // Register event listeners
142
+ window.addEventListener('resize', throttledUpdate)
143
+ window.addEventListener('scroll', updateDatePickerPosition, { capture: true })
144
+
145
+ return () => {
146
+ window.removeEventListener('resize', throttledUpdate)
147
+ window.removeEventListener('scroll', updateDatePickerPosition, { capture: true })
148
+ }
129
149
  }, [])
130
150
 
131
151
  const [isFocused, setIsFocused] = React.useState(false)
@@ -309,7 +329,7 @@ const DatePicker = React.forwardRef(
309
329
  <DayPickerSingleDateController
310
330
  date={inputDate}
311
331
  onDateChange={onChange}
312
- focused={inputDate ?? isFocused}
332
+ focused={(inputDate ?? isFocused) || initialVisibleMonth !== ''}
313
333
  onFocusChange={onFocusChange}
314
334
  numberOfMonths={1}
315
335
  hideKeyboardShortcutsPanel={true}
@@ -319,6 +339,7 @@ const DatePicker = React.forwardRef(
319
339
  renderNavNextButton={renderNextButton}
320
340
  isOutsideRange={isDayDisabled}
321
341
  phrases={getCopy()}
342
+ initialVisibleMonth={getInitialVisibleMonth(initialVisibleMonth, inputDate)}
322
343
  renderMonthElement={({ month }) => (
323
344
  <MonthCenterContainer>
324
345
  <div>
@@ -373,6 +394,7 @@ const DatePicker = React.forwardRef(
373
394
  <PortalPositionedContainer
374
395
  top={datePickerPosition.top}
375
396
  left={datePickerPosition.left}
397
+ ref={datePickerRef}
376
398
  >
377
399
  <CalendarContainer
378
400
  {...selectProps(rest)}
@@ -401,6 +423,7 @@ const DatePicker = React.forwardRef(
401
423
  phrases={getCopy()}
402
424
  id={id}
403
425
  renderNavNextButton={renderNextButton}
426
+ initialVisibleMonth={getInitialVisibleMonth(initialVisibleMonth, inputDate)}
404
427
  renderMonthElement={({ month }) => (
405
428
  <MonthCenterContainer>
406
429
  <div>
@@ -503,7 +526,11 @@ DatePicker.propTypes = {
503
526
  /**
504
527
  * Optional placeholder for the date input. If not set the default value will be `DD / MM / YYYY`
505
528
  */
506
- placeholder: PropTypes.string
529
+ placeholder: PropTypes.string,
530
+ /**
531
+ * The initial month to display when the calendar is opened. Expects a string in the format `YYYY-MM`
532
+ */
533
+ initialVisibleMonth: PropTypes.string
507
534
  }
508
535
 
509
536
  export default DatePicker
@@ -60,8 +60,8 @@ const Table = ({
60
60
 
61
61
  useSafeLayoutEffect(() => {
62
62
  const updateDimensions = () => {
63
- const containerClientWidth = containerRef.current.clientWidth
64
- const responsiveTableWidth = fullWidth ? containerClientWidth : tableRef.current.clientWidth
63
+ const containerClientWidth = containerRef.current?.clientWidth
64
+ const responsiveTableWidth = fullWidth ? containerClientWidth : tableRef.current?.clientWidth
65
65
  setContainerWidth(containerClientWidth)
66
66
  setTableWidth(responsiveTableWidth)
67
67
  }
@@ -1,22 +1,22 @@
1
1
  import type { ComponentType } from 'react'
2
2
 
3
- declare interface ThemeMetadata {
3
+ export interface ThemeMetadata {
4
4
  themeTokensVersion: string
5
5
  name: string
6
6
  }
7
7
 
8
- declare interface DefaultTheme {
8
+ export interface Theme {
9
9
  metadata: ThemeMetadata
10
10
  }
11
11
 
12
- declare interface ThemeOptions {
12
+ export interface ThemeOptions {
13
13
  forceAbsoluteFontSizing?: boolean
14
14
  forceZIndex?: boolean
15
15
  }
16
16
 
17
17
  export interface BaseProviderProps {
18
18
  themeOptions?: ThemeOptions
19
- defaultTheme: DefaultTheme
19
+ defaultTheme: Theme
20
20
  children: React.ReactNode
21
21
  }
22
22
 
@@ -0,0 +1,41 @@
1
+ // TODO: Duplicate of the one in components-base. TS module resolution doesn't seem to be working from web -> base.
2
+ import type { ComponentType } from 'react'
3
+
4
+ export interface FileUploadTokens {
5
+ buttonBackgroundColor?: string
6
+ buttonBorderColor?: string
7
+ buttonBorderRadius?: number
8
+ buttonBorderWidth?: string
9
+ buttonTextColor?: string
10
+ buttonHeight?: string
11
+ buttonMinWidth?: string
12
+ buttonWidth?: string
13
+ notificationBackgroundColor?: string
14
+ notificationBorderColor?: string
15
+ notificationBorderRadius?: number
16
+ notificationTextColor?: string
17
+ notificationDismissButtonGap?: string
18
+ notificationDismissIcon?: string
19
+ notificationDismissIconColor?: string
20
+ notificationIcon?: string
21
+ notificationIconColor?: string
22
+ notificationIconGap?: string
23
+ notificationIconSize?: number
24
+ }
25
+
26
+ export interface FileUploadProps {
27
+ tokens?: FileUploadTokens
28
+ variant?: Record<string, any>
29
+ copy?: 'en' | 'fr'
30
+ fileTypes?: string[]
31
+ allowMultipleFiles?: boolean
32
+ maxFileSize?: number
33
+ maxFilesCount?: number
34
+ onUpload?: (files: any) => void
35
+ onDelete?: (file: any) => void
36
+ documentPicker?: Record<string, any>
37
+ }
38
+
39
+ declare const FileUpload: ComponentType<FileUploadProps>
40
+
41
+ export default FileUpload
package/types/index.d.ts CHANGED
@@ -8,6 +8,9 @@ export type { AutocompleteProps, AutocompleteItem } from './Autocomplete'
8
8
  export { default as Badge } from './Badge'
9
9
  export type { BadgeProps } from './Badge'
10
10
 
11
+ export { default as BaseProvider } from './BaseProvider'
12
+ export type { BaseProviderProps, ThemeOptions, Theme, ThemeMetadata } from './BaseProvider'
13
+
11
14
  export { default as BlockQuote } from './BlockQuote'
12
15
  export type { BlockQuoteProps } from './BlockQuote'
13
16
 
@@ -35,6 +38,10 @@ export type { DatePickerProps } from './DatePicker'
35
38
 
36
39
  declare const Disclaimer: ComponentType<Props>
37
40
  declare const ExpandCollapseMini: ComponentType<Props>
41
+
42
+ export { default as FileUpload } from './FileUpload'
43
+ export type { FileUploadProps, FileUploadTokens } from './FileUpload'
44
+
38
45
  declare const Footnote: ComponentType<Props> & {
39
46
  Link: ComponentType<Props>
40
47
  }
@@ -115,7 +122,7 @@ declare const Pagination: ComponentType<Props> & {
115
122
 
116
123
  export { default as QuantitySelector } from './QuantitySelector'
117
124
  export type { QuantitySelectorProps } from './QuantitySelector'
118
- export { default as BaseProvider } from './BaseProvider'
125
+
119
126
  declare const QuickLinks: ComponentType<Props> & {
120
127
  Item: ComponentType<Props>
121
128
  }