@sb1/ffe-datepicker-react 100.12.0 → 100.12.3

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.
@@ -27,35 +27,60 @@ var Calendar = /** @class */ (function (_super) {
27
27
  _this.clickableDateRef = React.createRef();
28
28
  _this.prevMonthButtonElementRef = React.createRef();
29
29
  _this.nextMonthButtonElementRef = React.createRef();
30
+ _this.monthSelectRef = React.createRef();
31
+ _this.yearSelectRef = React.createRef();
30
32
  _this.focusTrap = function (event) {
31
- var _a, _b, _c, _d, _e, _f;
33
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
32
34
  var activeElement = document.activeElement;
33
35
  if (event.key === 'Tab') {
34
36
  event.preventDefault();
37
+ var dropdownCaption = _this.props.dropdownCaption;
35
38
  if (event.shiftKey) {
36
39
  if (activeElement === _this.clickableDateRef.current) {
37
40
  (_a = _this.nextMonthButtonElementRef.current) === null || _a === void 0 ? void 0 : _a.focus();
38
41
  _this.setState({ isFocusingHeader: true });
39
42
  }
40
- if (activeElement === _this.nextMonthButtonElementRef.current) {
41
- (_b = _this.prevMonthButtonElementRef.current) === null || _b === void 0 ? void 0 : _b.focus();
43
+ else if (activeElement === _this.nextMonthButtonElementRef.current) {
44
+ if (dropdownCaption) {
45
+ (_b = _this.yearSelectRef.current) === null || _b === void 0 ? void 0 : _b.focus();
46
+ }
47
+ else {
48
+ (_c = _this.prevMonthButtonElementRef.current) === null || _c === void 0 ? void 0 : _c.focus();
49
+ }
42
50
  }
43
- if (activeElement === _this.prevMonthButtonElementRef.current) {
44
- (_c = _this.clickableDateRef.current) === null || _c === void 0 ? void 0 : _c.focus();
51
+ else if (dropdownCaption && activeElement === _this.yearSelectRef.current) {
52
+ (_d = _this.monthSelectRef.current) === null || _d === void 0 ? void 0 : _d.focus();
53
+ }
54
+ else if (dropdownCaption && activeElement === _this.monthSelectRef.current) {
55
+ (_e = _this.prevMonthButtonElementRef.current) === null || _e === void 0 ? void 0 : _e.focus();
56
+ }
57
+ else if (activeElement === _this.prevMonthButtonElementRef.current) {
58
+ (_f = _this.clickableDateRef.current) === null || _f === void 0 ? void 0 : _f.focus();
45
59
  _this.setState({ isFocusingHeader: false });
46
60
  _this.forceUpdate();
47
61
  }
48
62
  }
49
63
  else {
50
64
  if (activeElement === _this.clickableDateRef.current) {
51
- (_d = _this.prevMonthButtonElementRef.current) === null || _d === void 0 ? void 0 : _d.focus();
65
+ (_g = _this.prevMonthButtonElementRef.current) === null || _g === void 0 ? void 0 : _g.focus();
52
66
  _this.setState({ isFocusingHeader: true });
53
67
  }
54
- if (activeElement === _this.prevMonthButtonElementRef.current) {
55
- (_e = _this.nextMonthButtonElementRef.current) === null || _e === void 0 ? void 0 : _e.focus();
68
+ else if (activeElement === _this.prevMonthButtonElementRef.current) {
69
+ if (dropdownCaption) {
70
+ (_h = _this.monthSelectRef.current) === null || _h === void 0 ? void 0 : _h.focus();
71
+ }
72
+ else {
73
+ (_j = _this.nextMonthButtonElementRef.current) === null || _j === void 0 ? void 0 : _j.focus();
74
+ }
56
75
  }
57
- if (activeElement === _this.nextMonthButtonElementRef.current) {
58
- (_f = _this.clickableDateRef.current) === null || _f === void 0 ? void 0 : _f.focus();
76
+ else if (dropdownCaption && activeElement === _this.monthSelectRef.current) {
77
+ (_k = _this.yearSelectRef.current) === null || _k === void 0 ? void 0 : _k.focus();
78
+ }
79
+ else if (dropdownCaption && activeElement === _this.yearSelectRef.current) {
80
+ (_l = _this.nextMonthButtonElementRef.current) === null || _l === void 0 ? void 0 : _l.focus();
81
+ }
82
+ else if (activeElement === _this.nextMonthButtonElementRef.current) {
83
+ (_m = _this.clickableDateRef.current) === null || _m === void 0 ? void 0 : _m.focus();
59
84
  _this.setState({ isFocusingHeader: false });
60
85
  _this.forceUpdate();
61
86
  }
@@ -222,7 +247,7 @@ var Calendar = /** @class */ (function (_super) {
222
247
  React.createElement("div", { className: this.props.calendarClassName || 'ffe-calendar', role: "application", onKeyDown: this.focusTrap },
223
248
  React.createElement(Header, { datepickerId: this.datepickerId, month: calendar.focusedMonth, nextMonthHandler: this.nextMonth, nextMonthLabel: calendar.nextName, previousMonthHandler: this.previousMonth, previousMonthLabel: calendar.previousName, year: calendar.focusedYear, prevMonthButtonElement: this.prevMonthButtonElementRef, nextMonthButtonElement: this.nextMonthButtonElementRef, monthNumber: calendar.focusedDate.month + 1, locale: this.props.locale, dropdownCaption: this.props.dropdownCaption, onMonthYearChange: function (month, year) {
224
249
  return _this.navigateToMonthYear(month, year);
225
- }, minDate: this.props.minDate, maxDate: this.props.maxDate }),
250
+ }, minDate: this.props.minDate, maxDate: this.props.maxDate, monthSelectRef: this.monthSelectRef, yearSelectRef: this.yearSelectRef }),
226
251
  React.createElement("table", { className: "ffe-calendar__grid", onKeyDown: this.keyDown, role: "presentation" },
227
252
  React.createElement("thead", null,
228
253
  React.createElement("tr", null, calendar.dayNames.map(this.renderDay))),
@@ -3,7 +3,7 @@ import { Icon } from '@sb1/ffe-icons-react';
3
3
  import { Dropdown } from '@sb1/ffe-dropdown-react';
4
4
  import { getMonthOptions, getYearOptions } from '../util/dateRangeUtils';
5
5
  export var Header = function (_a) {
6
- var datepickerId = _a.datepickerId, month = _a.month, nextMonthHandler = _a.nextMonthHandler, nextMonthLabel = _a.nextMonthLabel, previousMonthHandler = _a.previousMonthHandler, previousMonthLabel = _a.previousMonthLabel, year = _a.year, prevMonthButtonElement = _a.prevMonthButtonElement, nextMonthButtonElement = _a.nextMonthButtonElement, monthNumber = _a.monthNumber, _b = _a.dropdownCaption, dropdownCaption = _b === void 0 ? false : _b, locale = _a.locale, onMonthYearChange = _a.onMonthYearChange, minDate = _a.minDate, maxDate = _a.maxDate;
6
+ var datepickerId = _a.datepickerId, month = _a.month, nextMonthHandler = _a.nextMonthHandler, nextMonthLabel = _a.nextMonthLabel, previousMonthHandler = _a.previousMonthHandler, previousMonthLabel = _a.previousMonthLabel, year = _a.year, prevMonthButtonElement = _a.prevMonthButtonElement, nextMonthButtonElement = _a.nextMonthButtonElement, monthNumber = _a.monthNumber, _b = _a.dropdownCaption, dropdownCaption = _b === void 0 ? false : _b, locale = _a.locale, onMonthYearChange = _a.onMonthYearChange, minDate = _a.minDate, maxDate = _a.maxDate, monthSelectRef = _a.monthSelectRef, yearSelectRef = _a.yearSelectRef;
7
7
  var arrowBackIosIcon = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjQiPjxwYXRoIGQ9Im0zNjcuMzg0LTQ4MCAzMDEuMzA4IDMwMS4zMDhxMTEuOTIzIDExLjkyMyAxMS42MTUgMjguMDc3LS4zMDggMTYuMTUzLTEyLjIzMSAyOC4wNzZxLTExLjkyMiAxMS45MjMtMjguMDc2IDExLjkyM3QtMjguMDc2LTExLjkyM0wzMDUuMDc4LTQyOC43N3EtMTAuODQ3LTEwLjg0Ni0xNi4wNzctMjQuMzA3LTUuMjMxLTEzLjQ2Mi01LjIzMS0yNi45MjMgMC0xMy40NjEgNS4yMzEtMjYuOTIzIDUuMjMtMTMuNDYxIDE2LjA3Ny0yNC4zMDdsMzA2Ljg0Ni0zMDYuODQ2cTExLjkyMi0xMS45MjMgMjguMzg0LTExLjYxNiAxNi40NjEuMzA4IDI4LjM4NCAxMi4yMzEgMTEuOTIzIDExLjkyMyAxMS45MjMgMjguMDc2IDAgMTYuMTU0LTExLjkyMyAyOC4wNzdMMzY3LjM4NC00ODBaIi8+PC9zdmc+';
8
8
  var monthOptions = getMonthOptions(locale);
9
9
  var yearOptions = getYearOptions(minDate, maxDate);
@@ -31,9 +31,9 @@ export var Header = function (_a) {
31
31
  React.createElement(Icon, { fileUrl: arrowBackIosIcon, size: "md", className: "ffe-calendar__icon-prev" })),
32
32
  React.createElement("header", { "aria-live": "polite", className: "ffe-calendar__title", id: "".concat(datepickerId, "-title") }, dropdownCaption ? (React.createElement("div", { className: "ffe-calendar__dropdown-container" },
33
33
  React.createElement("div", { className: "ffe-calendar__dropdown ffe-calendar__month-dropdown" },
34
- React.createElement(Dropdown, { id: "".concat(datepickerId, "__month-select"), className: "ffe-calendar__month-select", value: monthNumber, onChange: handleMonthChange, "aria-label": "".concat(month, " ").concat(year), onClick: handleDropdownClick, onFocus: handleDropdownFocus }, monthOptions.map(function (option) { return (React.createElement("option", { key: option.value, value: option.value }, option.label)); }))),
34
+ React.createElement(Dropdown, { id: "".concat(datepickerId, "__month-select"), className: "ffe-calendar__month-select", value: monthNumber, onChange: handleMonthChange, "aria-label": "".concat(month, " ").concat(year), onClick: handleDropdownClick, onFocus: handleDropdownFocus, ref: monthSelectRef }, monthOptions.map(function (option) { return (React.createElement("option", { key: option.value, value: option.value }, option.label)); }))),
35
35
  React.createElement("div", { className: "ffe-calendar__dropdown ffe-calendar__year-dropdown" },
36
- React.createElement(Dropdown, { id: "".concat(datepickerId, "__year-select"), className: "ffe-calendar__year-select", value: year, onChange: handleYearChange, "aria-label": "".concat(year), onClick: handleDropdownClick, onFocus: handleDropdownFocus }, yearOptions.map(function (option) { return (React.createElement("option", { key: option.value, value: option.value }, option.label)); }))))) : (React.createElement("div", { id: "".concat(datepickerId, "__month-label"), "data-testid": "".concat(datepickerId, "__month-label") },
36
+ React.createElement(Dropdown, { id: "".concat(datepickerId, "__year-select"), className: "ffe-calendar__year-select", value: year, onChange: handleYearChange, "aria-label": "".concat(year), onClick: handleDropdownClick, onFocus: handleDropdownFocus, ref: yearSelectRef }, yearOptions.map(function (option) { return (React.createElement("option", { key: option.value, value: option.value }, option.label)); }))))) : (React.createElement("div", { id: "".concat(datepickerId, "__month-label"), "data-testid": "".concat(datepickerId, "__month-label") },
37
37
  React.createElement("span", { className: "ffe-calendar__month" }, month),
38
38
  React.createElement("span", { className: "ffe-calendar__year" }, year)))),
39
39
  React.createElement("button", { className: "ffe-calendar__month-nav ffe-calendar__next", onClick: nextMonthHandler, "aria-label": nextMonthLabel, type: "button", ref: nextMonthButtonElement, tabIndex: -1 },
@@ -20,10 +20,15 @@ var __rest = (this && this.__rest) || function (s, e) {
20
20
  }
21
21
  return t;
22
22
  };
23
- import React from 'react';
23
+ import React, { useMemo } from 'react';
24
24
  import classNames from 'classnames';
25
25
  import i18n from '../i18n/i18n';
26
+ import { isIOSSafari } from '../util/isIOSSafari';
26
27
  export var DateInput = React.forwardRef(function (_a, ref) {
27
28
  var ariaInvalid = _a.ariaInvalid, value = _a.value, className = _a.className, _b = _a.locale, locale = _b === void 0 ? 'nb' : _b, rest = __rest(_a, ["ariaInvalid", "value", "className", "locale"]);
28
- return (React.createElement("input", __assign({}, rest, { "aria-invalid": ariaInvalid, maxLength: 10, ref: ref, "aria-placeholder": i18n[locale].DATE_FORMAT, value: value, className: classNames('ffe-input-field ffe-dateinput__field', className) })));
29
+ // VoiceOver on iOS Safari blocks interaction with date-related inputs.
30
+ // Using role="textbox" as a workaround on that platform.
31
+ // https://dev.to/mfranzke/voiceover-bug-on-ios-safari-blocks-date-time-related-inputs-especially-in-react-4f61
32
+ var role = useMemo(function () { return (isIOSSafari() ? 'textbox' : undefined); }, []);
33
+ return (React.createElement("input", __assign({ type: "date", role: role, "aria-invalid": ariaInvalid, maxLength: 10, ref: ref, "aria-placeholder": i18n[locale].DATE_FORMAT, value: value, className: classNames('ffe-input-field ffe-dateinput__field', className) }, rest)));
29
34
  });
@@ -1 +1 @@
1
- {"root":["../src/index.ts","../src/types.ts","../src/button/Button.tsx","../src/button/index.ts","../src/calendar/Calendar.tsx","../src/calendar/ClickableDate.tsx","../src/calendar/Header.tsx","../src/calendar/NonClickableDate.tsx","../src/calendar/index.ts","../src/datelogic/simplecalendar.ts","../src/datelogic/simpledate.ts","../src/datelogic/types.ts","../src/datepicker/Datepicker.tsx","../src/datepicker/DatepickerComp.tsx","../src/datepicker/DatepickerContext.tsx","../src/datepicker/SpinButton.tsx","../src/datepicker/index.ts","../src/datepicker/padZero.ts","../src/datepicker/testHelper.ts","../src/datepicker/toNumber.ts","../src/i18n/en.ts","../src/i18n/i18n.ts","../src/i18n/nb.ts","../src/i18n/nn.ts","../src/input/DateInput.tsx","../src/input/index.ts","../src/util/dateRangeUtils.ts","../src/util/dateUtil.ts"],"version":"5.9.3"}
1
+ {"root":["../src/index.ts","../src/types.ts","../src/button/Button.tsx","../src/button/index.ts","../src/calendar/Calendar.tsx","../src/calendar/ClickableDate.tsx","../src/calendar/Header.tsx","../src/calendar/NonClickableDate.tsx","../src/calendar/index.ts","../src/datelogic/simplecalendar.ts","../src/datelogic/simpledate.ts","../src/datelogic/types.ts","../src/datepicker/Datepicker.tsx","../src/datepicker/DatepickerComp.tsx","../src/datepicker/DatepickerContext.tsx","../src/datepicker/SpinButton.tsx","../src/datepicker/index.ts","../src/datepicker/padZero.ts","../src/datepicker/testHelper.ts","../src/datepicker/toNumber.ts","../src/i18n/en.ts","../src/i18n/i18n.ts","../src/i18n/nb.ts","../src/i18n/nn.ts","../src/input/DateInput.tsx","../src/input/index.ts","../src/util/dateRangeUtils.ts","../src/util/dateUtil.ts","../src/util/isIOSSafari.ts"],"version":"5.9.3"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Detects iOS/iPadOS Safari to work around a VoiceOver bug that blocks
3
+ * interaction with date-related inputs.
4
+ * @see https://dev.to/mfranzke/voiceover-bug-on-ios-safari-blocks-date-time-related-inputs-especially-in-react-4f61
5
+ *
6
+ * Known limitations:
7
+ * - iOS WebViews (in-app browsers like Instagram, Facebook) use WebKit and may
8
+ * be affected by the same bug, but are not detected here since they don't
9
+ * include "Safari" in the user agent string.
10
+ */
11
+ export var isIOSSafari = function () {
12
+ if (typeof navigator === 'undefined') {
13
+ return false;
14
+ }
15
+ var ua = navigator.userAgent;
16
+ // iPadOS 13+ in desktop mode reports as Macintosh, but can be identified
17
+ // by having touch points (real Macs have maxTouchPoints === 0).
18
+ var isIOS = /iP(ad|hone|od)/.test(ua) ||
19
+ (/Macintosh/.test(ua) && navigator.maxTouchPoints > 1);
20
+ var isSafari = !!ua.match(/Safari/) && !ua.match(/CriOS|FxiOS|OPiOS|EdgiOS/);
21
+ return isIOS && isSafari;
22
+ };
@@ -63,35 +63,60 @@ var Calendar = /** @class */ (function (_super) {
63
63
  _this.clickableDateRef = react_1.default.createRef();
64
64
  _this.prevMonthButtonElementRef = react_1.default.createRef();
65
65
  _this.nextMonthButtonElementRef = react_1.default.createRef();
66
+ _this.monthSelectRef = react_1.default.createRef();
67
+ _this.yearSelectRef = react_1.default.createRef();
66
68
  _this.focusTrap = function (event) {
67
- var _a, _b, _c, _d, _e, _f;
69
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
68
70
  var activeElement = document.activeElement;
69
71
  if (event.key === 'Tab') {
70
72
  event.preventDefault();
73
+ var dropdownCaption = _this.props.dropdownCaption;
71
74
  if (event.shiftKey) {
72
75
  if (activeElement === _this.clickableDateRef.current) {
73
76
  (_a = _this.nextMonthButtonElementRef.current) === null || _a === void 0 ? void 0 : _a.focus();
74
77
  _this.setState({ isFocusingHeader: true });
75
78
  }
76
- if (activeElement === _this.nextMonthButtonElementRef.current) {
77
- (_b = _this.prevMonthButtonElementRef.current) === null || _b === void 0 ? void 0 : _b.focus();
79
+ else if (activeElement === _this.nextMonthButtonElementRef.current) {
80
+ if (dropdownCaption) {
81
+ (_b = _this.yearSelectRef.current) === null || _b === void 0 ? void 0 : _b.focus();
82
+ }
83
+ else {
84
+ (_c = _this.prevMonthButtonElementRef.current) === null || _c === void 0 ? void 0 : _c.focus();
85
+ }
78
86
  }
79
- if (activeElement === _this.prevMonthButtonElementRef.current) {
80
- (_c = _this.clickableDateRef.current) === null || _c === void 0 ? void 0 : _c.focus();
87
+ else if (dropdownCaption && activeElement === _this.yearSelectRef.current) {
88
+ (_d = _this.monthSelectRef.current) === null || _d === void 0 ? void 0 : _d.focus();
89
+ }
90
+ else if (dropdownCaption && activeElement === _this.monthSelectRef.current) {
91
+ (_e = _this.prevMonthButtonElementRef.current) === null || _e === void 0 ? void 0 : _e.focus();
92
+ }
93
+ else if (activeElement === _this.prevMonthButtonElementRef.current) {
94
+ (_f = _this.clickableDateRef.current) === null || _f === void 0 ? void 0 : _f.focus();
81
95
  _this.setState({ isFocusingHeader: false });
82
96
  _this.forceUpdate();
83
97
  }
84
98
  }
85
99
  else {
86
100
  if (activeElement === _this.clickableDateRef.current) {
87
- (_d = _this.prevMonthButtonElementRef.current) === null || _d === void 0 ? void 0 : _d.focus();
101
+ (_g = _this.prevMonthButtonElementRef.current) === null || _g === void 0 ? void 0 : _g.focus();
88
102
  _this.setState({ isFocusingHeader: true });
89
103
  }
90
- if (activeElement === _this.prevMonthButtonElementRef.current) {
91
- (_e = _this.nextMonthButtonElementRef.current) === null || _e === void 0 ? void 0 : _e.focus();
104
+ else if (activeElement === _this.prevMonthButtonElementRef.current) {
105
+ if (dropdownCaption) {
106
+ (_h = _this.monthSelectRef.current) === null || _h === void 0 ? void 0 : _h.focus();
107
+ }
108
+ else {
109
+ (_j = _this.nextMonthButtonElementRef.current) === null || _j === void 0 ? void 0 : _j.focus();
110
+ }
92
111
  }
93
- if (activeElement === _this.nextMonthButtonElementRef.current) {
94
- (_f = _this.clickableDateRef.current) === null || _f === void 0 ? void 0 : _f.focus();
112
+ else if (dropdownCaption && activeElement === _this.monthSelectRef.current) {
113
+ (_k = _this.yearSelectRef.current) === null || _k === void 0 ? void 0 : _k.focus();
114
+ }
115
+ else if (dropdownCaption && activeElement === _this.yearSelectRef.current) {
116
+ (_l = _this.nextMonthButtonElementRef.current) === null || _l === void 0 ? void 0 : _l.focus();
117
+ }
118
+ else if (activeElement === _this.nextMonthButtonElementRef.current) {
119
+ (_m = _this.clickableDateRef.current) === null || _m === void 0 ? void 0 : _m.focus();
95
120
  _this.setState({ isFocusingHeader: false });
96
121
  _this.forceUpdate();
97
122
  }
@@ -258,7 +283,7 @@ var Calendar = /** @class */ (function (_super) {
258
283
  react_1.default.createElement("div", { className: this.props.calendarClassName || 'ffe-calendar', role: "application", onKeyDown: this.focusTrap },
259
284
  react_1.default.createElement(Header_1.Header, { datepickerId: this.datepickerId, month: calendar.focusedMonth, nextMonthHandler: this.nextMonth, nextMonthLabel: calendar.nextName, previousMonthHandler: this.previousMonth, previousMonthLabel: calendar.previousName, year: calendar.focusedYear, prevMonthButtonElement: this.prevMonthButtonElementRef, nextMonthButtonElement: this.nextMonthButtonElementRef, monthNumber: calendar.focusedDate.month + 1, locale: this.props.locale, dropdownCaption: this.props.dropdownCaption, onMonthYearChange: function (month, year) {
260
285
  return _this.navigateToMonthYear(month, year);
261
- }, minDate: this.props.minDate, maxDate: this.props.maxDate }),
286
+ }, minDate: this.props.minDate, maxDate: this.props.maxDate, monthSelectRef: this.monthSelectRef, yearSelectRef: this.yearSelectRef }),
262
287
  react_1.default.createElement("table", { className: "ffe-calendar__grid", onKeyDown: this.keyDown, role: "presentation" },
263
288
  react_1.default.createElement("thead", null,
264
289
  react_1.default.createElement("tr", null, calendar.dayNames.map(this.renderDay))),
@@ -9,7 +9,7 @@ var ffe_icons_react_1 = require("@sb1/ffe-icons-react");
9
9
  var ffe_dropdown_react_1 = require("@sb1/ffe-dropdown-react");
10
10
  var dateRangeUtils_1 = require("../util/dateRangeUtils");
11
11
  var Header = function (_a) {
12
- var datepickerId = _a.datepickerId, month = _a.month, nextMonthHandler = _a.nextMonthHandler, nextMonthLabel = _a.nextMonthLabel, previousMonthHandler = _a.previousMonthHandler, previousMonthLabel = _a.previousMonthLabel, year = _a.year, prevMonthButtonElement = _a.prevMonthButtonElement, nextMonthButtonElement = _a.nextMonthButtonElement, monthNumber = _a.monthNumber, _b = _a.dropdownCaption, dropdownCaption = _b === void 0 ? false : _b, locale = _a.locale, onMonthYearChange = _a.onMonthYearChange, minDate = _a.minDate, maxDate = _a.maxDate;
12
+ var datepickerId = _a.datepickerId, month = _a.month, nextMonthHandler = _a.nextMonthHandler, nextMonthLabel = _a.nextMonthLabel, previousMonthHandler = _a.previousMonthHandler, previousMonthLabel = _a.previousMonthLabel, year = _a.year, prevMonthButtonElement = _a.prevMonthButtonElement, nextMonthButtonElement = _a.nextMonthButtonElement, monthNumber = _a.monthNumber, _b = _a.dropdownCaption, dropdownCaption = _b === void 0 ? false : _b, locale = _a.locale, onMonthYearChange = _a.onMonthYearChange, minDate = _a.minDate, maxDate = _a.maxDate, monthSelectRef = _a.monthSelectRef, yearSelectRef = _a.yearSelectRef;
13
13
  var arrowBackIosIcon = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjQiPjxwYXRoIGQ9Im0zNjcuMzg0LTQ4MCAzMDEuMzA4IDMwMS4zMDhxMTEuOTIzIDExLjkyMyAxMS42MTUgMjguMDc3LS4zMDggMTYuMTUzLTEyLjIzMSAyOC4wNzZxLTExLjkyMiAxMS45MjMtMjguMDc2IDExLjkyM3QtMjguMDc2LTExLjkyM0wzMDUuMDc4LTQyOC43N3EtMTAuODQ3LTEwLjg0Ni0xNi4wNzctMjQuMzA3LTUuMjMxLTEzLjQ2Mi01LjIzMS0yNi45MjMgMC0xMy40NjEgNS4yMzEtMjYuOTIzIDUuMjMtMTMuNDYxIDE2LjA3Ny0yNC4zMDdsMzA2Ljg0Ni0zMDYuODQ2cTExLjkyMi0xMS45MjMgMjguMzg0LTExLjYxNiAxNi40NjEuMzA4IDI4LjM4NCAxMi4yMzEgMTEuOTIzIDExLjkyMyAxMS45MjMgMjguMDc2IDAgMTYuMTU0LTExLjkyMyAyOC4wNzdMMzY3LjM4NC00ODBaIi8+PC9zdmc+';
14
14
  var monthOptions = (0, dateRangeUtils_1.getMonthOptions)(locale);
15
15
  var yearOptions = (0, dateRangeUtils_1.getYearOptions)(minDate, maxDate);
@@ -37,9 +37,9 @@ var Header = function (_a) {
37
37
  react_1.default.createElement(ffe_icons_react_1.Icon, { fileUrl: arrowBackIosIcon, size: "md", className: "ffe-calendar__icon-prev" })),
38
38
  react_1.default.createElement("header", { "aria-live": "polite", className: "ffe-calendar__title", id: "".concat(datepickerId, "-title") }, dropdownCaption ? (react_1.default.createElement("div", { className: "ffe-calendar__dropdown-container" },
39
39
  react_1.default.createElement("div", { className: "ffe-calendar__dropdown ffe-calendar__month-dropdown" },
40
- react_1.default.createElement(ffe_dropdown_react_1.Dropdown, { id: "".concat(datepickerId, "__month-select"), className: "ffe-calendar__month-select", value: monthNumber, onChange: handleMonthChange, "aria-label": "".concat(month, " ").concat(year), onClick: handleDropdownClick, onFocus: handleDropdownFocus }, monthOptions.map(function (option) { return (react_1.default.createElement("option", { key: option.value, value: option.value }, option.label)); }))),
40
+ react_1.default.createElement(ffe_dropdown_react_1.Dropdown, { id: "".concat(datepickerId, "__month-select"), className: "ffe-calendar__month-select", value: monthNumber, onChange: handleMonthChange, "aria-label": "".concat(month, " ").concat(year), onClick: handleDropdownClick, onFocus: handleDropdownFocus, ref: monthSelectRef }, monthOptions.map(function (option) { return (react_1.default.createElement("option", { key: option.value, value: option.value }, option.label)); }))),
41
41
  react_1.default.createElement("div", { className: "ffe-calendar__dropdown ffe-calendar__year-dropdown" },
42
- react_1.default.createElement(ffe_dropdown_react_1.Dropdown, { id: "".concat(datepickerId, "__year-select"), className: "ffe-calendar__year-select", value: year, onChange: handleYearChange, "aria-label": "".concat(year), onClick: handleDropdownClick, onFocus: handleDropdownFocus }, yearOptions.map(function (option) { return (react_1.default.createElement("option", { key: option.value, value: option.value }, option.label)); }))))) : (react_1.default.createElement("div", { id: "".concat(datepickerId, "__month-label"), "data-testid": "".concat(datepickerId, "__month-label") },
42
+ react_1.default.createElement(ffe_dropdown_react_1.Dropdown, { id: "".concat(datepickerId, "__year-select"), className: "ffe-calendar__year-select", value: year, onChange: handleYearChange, "aria-label": "".concat(year), onClick: handleDropdownClick, onFocus: handleDropdownFocus, ref: yearSelectRef }, yearOptions.map(function (option) { return (react_1.default.createElement("option", { key: option.value, value: option.value }, option.label)); }))))) : (react_1.default.createElement("div", { id: "".concat(datepickerId, "__month-label"), "data-testid": "".concat(datepickerId, "__month-label") },
43
43
  react_1.default.createElement("span", { className: "ffe-calendar__month" }, month),
44
44
  react_1.default.createElement("span", { className: "ffe-calendar__year" }, year)))),
45
45
  react_1.default.createElement("button", { className: "ffe-calendar__month-nav ffe-calendar__next", onClick: nextMonthHandler, "aria-label": nextMonthLabel, type: "button", ref: nextMonthButtonElement, tabIndex: -1 },
@@ -10,6 +10,39 @@ var __assign = (this && this.__assign) || function () {
10
10
  };
11
11
  return __assign.apply(this, arguments);
12
12
  };
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
13
46
  var __rest = (this && this.__rest) || function (s, e) {
14
47
  var t = {};
15
48
  for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
@@ -26,10 +59,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
59
  };
27
60
  Object.defineProperty(exports, "__esModule", { value: true });
28
61
  exports.DateInput = void 0;
29
- var react_1 = __importDefault(require("react"));
62
+ var react_1 = __importStar(require("react"));
30
63
  var classnames_1 = __importDefault(require("classnames"));
31
64
  var i18n_1 = __importDefault(require("../i18n/i18n"));
65
+ var isIOSSafari_1 = require("../util/isIOSSafari");
32
66
  exports.DateInput = react_1.default.forwardRef(function (_a, ref) {
33
67
  var ariaInvalid = _a.ariaInvalid, value = _a.value, className = _a.className, _b = _a.locale, locale = _b === void 0 ? 'nb' : _b, rest = __rest(_a, ["ariaInvalid", "value", "className", "locale"]);
34
- return (react_1.default.createElement("input", __assign({}, rest, { "aria-invalid": ariaInvalid, maxLength: 10, ref: ref, "aria-placeholder": i18n_1.default[locale].DATE_FORMAT, value: value, className: (0, classnames_1.default)('ffe-input-field ffe-dateinput__field', className) })));
68
+ // VoiceOver on iOS Safari blocks interaction with date-related inputs.
69
+ // Using role="textbox" as a workaround on that platform.
70
+ // https://dev.to/mfranzke/voiceover-bug-on-ios-safari-blocks-date-time-related-inputs-especially-in-react-4f61
71
+ var role = (0, react_1.useMemo)(function () { return ((0, isIOSSafari_1.isIOSSafari)() ? 'textbox' : undefined); }, []);
72
+ return (react_1.default.createElement("input", __assign({ type: "date", role: role, "aria-invalid": ariaInvalid, maxLength: 10, ref: ref, "aria-placeholder": i18n_1.default[locale].DATE_FORMAT, value: value, className: (0, classnames_1.default)('ffe-input-field ffe-dateinput__field', className) }, rest)));
35
73
  });
@@ -1 +1 @@
1
- {"root":["../src/index.ts","../src/types.ts","../src/button/Button.tsx","../src/button/index.ts","../src/calendar/Calendar.tsx","../src/calendar/ClickableDate.tsx","../src/calendar/Header.tsx","../src/calendar/NonClickableDate.tsx","../src/calendar/index.ts","../src/datelogic/simplecalendar.ts","../src/datelogic/simpledate.ts","../src/datelogic/types.ts","../src/datepicker/Datepicker.tsx","../src/datepicker/DatepickerComp.tsx","../src/datepicker/DatepickerContext.tsx","../src/datepicker/SpinButton.tsx","../src/datepicker/index.ts","../src/datepicker/padZero.ts","../src/datepicker/testHelper.ts","../src/datepicker/toNumber.ts","../src/i18n/en.ts","../src/i18n/i18n.ts","../src/i18n/nb.ts","../src/i18n/nn.ts","../src/input/DateInput.tsx","../src/input/index.ts","../src/util/dateRangeUtils.ts","../src/util/dateUtil.ts"],"version":"5.9.3"}
1
+ {"root":["../src/index.ts","../src/types.ts","../src/button/Button.tsx","../src/button/index.ts","../src/calendar/Calendar.tsx","../src/calendar/ClickableDate.tsx","../src/calendar/Header.tsx","../src/calendar/NonClickableDate.tsx","../src/calendar/index.ts","../src/datelogic/simplecalendar.ts","../src/datelogic/simpledate.ts","../src/datelogic/types.ts","../src/datepicker/Datepicker.tsx","../src/datepicker/DatepickerComp.tsx","../src/datepicker/DatepickerContext.tsx","../src/datepicker/SpinButton.tsx","../src/datepicker/index.ts","../src/datepicker/padZero.ts","../src/datepicker/testHelper.ts","../src/datepicker/toNumber.ts","../src/i18n/en.ts","../src/i18n/i18n.ts","../src/i18n/nb.ts","../src/i18n/nn.ts","../src/input/DateInput.tsx","../src/input/index.ts","../src/util/dateRangeUtils.ts","../src/util/dateUtil.ts","../src/util/isIOSSafari.ts"],"version":"5.9.3"}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isIOSSafari = void 0;
4
+ /**
5
+ * Detects iOS/iPadOS Safari to work around a VoiceOver bug that blocks
6
+ * interaction with date-related inputs.
7
+ * @see https://dev.to/mfranzke/voiceover-bug-on-ios-safari-blocks-date-time-related-inputs-especially-in-react-4f61
8
+ *
9
+ * Known limitations:
10
+ * - iOS WebViews (in-app browsers like Instagram, Facebook) use WebKit and may
11
+ * be affected by the same bug, but are not detected here since they don't
12
+ * include "Safari" in the user agent string.
13
+ */
14
+ var isIOSSafari = function () {
15
+ if (typeof navigator === 'undefined') {
16
+ return false;
17
+ }
18
+ var ua = navigator.userAgent;
19
+ // iPadOS 13+ in desktop mode reports as Macintosh, but can be identified
20
+ // by having touch points (real Macs have maxTouchPoints === 0).
21
+ var isIOS = /iP(ad|hone|od)/.test(ua) ||
22
+ (/Macintosh/.test(ua) && navigator.maxTouchPoints > 1);
23
+ var isSafari = !!ua.match(/Safari/) && !ua.match(/CriOS|FxiOS|OPiOS|EdgiOS/);
24
+ return isIOS && isSafari;
25
+ };
26
+ exports.isIOSSafari = isIOSSafari;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sb1/ffe-datepicker-react",
3
- "version": "100.12.0",
3
+ "version": "100.12.3",
4
4
  "license": "MIT",
5
5
  "author": "SpareBank 1",
6
6
  "files": [
@@ -24,11 +24,11 @@
24
24
  "test:watch": "ffe-buildtool jest --watch"
25
25
  },
26
26
  "dependencies": {
27
- "@sb1/ffe-datepicker": "^100.12.0",
28
- "@sb1/ffe-dropdown-react": "^100.12.0",
29
- "@sb1/ffe-form": "^100.12.0",
30
- "@sb1/ffe-form-react": "^100.12.0",
31
- "@sb1/ffe-icons-react": "^100.12.0",
27
+ "@sb1/ffe-datepicker": "^100.12.3",
28
+ "@sb1/ffe-dropdown-react": "^100.12.3",
29
+ "@sb1/ffe-form": "^100.12.3",
30
+ "@sb1/ffe-form-react": "^100.12.3",
31
+ "@sb1/ffe-icons-react": "^100.12.3",
32
32
  "@testing-library/react": "^16.0.0",
33
33
  "@types/lodash.debounce": "^4.0.9",
34
34
  "classnames": "^2.3.1",
@@ -36,7 +36,7 @@
36
36
  "uuid": "^9.0.0"
37
37
  },
38
38
  "devDependencies": {
39
- "@sb1/ffe-buildtool": "^100.12.0",
39
+ "@sb1/ffe-buildtool": "^100.12.3",
40
40
  "eslint": "^9.22.0",
41
41
  "react": "^18.2.0",
42
42
  "react-dom": "^18.2.0"
@@ -47,5 +47,5 @@
47
47
  "publishConfig": {
48
48
  "access": "public"
49
49
  },
50
- "gitHead": "78dd47a4064087480913e75ebd87b6c7deda6c54"
50
+ "gitHead": "c250edc3d20e5ed115c5c5951495d700e96a3be3"
51
51
  }
@@ -27,6 +27,8 @@ export declare class Calendar extends Component<CalendarProps, State> {
27
27
  clickableDateRef: React.RefObject<HTMLTableCellElement>;
28
28
  prevMonthButtonElementRef: React.RefObject<HTMLButtonElement>;
29
29
  nextMonthButtonElementRef: React.RefObject<HTMLButtonElement>;
30
+ monthSelectRef: React.RefObject<HTMLSelectElement>;
31
+ yearSelectRef: React.RefObject<HTMLSelectElement>;
30
32
  componentDidUpdate(prevProps: CalendarProps): void;
31
33
  shouldComponentUpdate(nextProps: CalendarProps): boolean;
32
34
  keyDown(event: React.KeyboardEvent<HTMLTableElement>): void;
@@ -10,6 +10,8 @@ interface HeaderProps {
10
10
  year: number;
11
11
  prevMonthButtonElement: React.RefObject<HTMLButtonElement>;
12
12
  nextMonthButtonElement: React.RefObject<HTMLButtonElement>;
13
+ monthSelectRef?: React.RefObject<HTMLSelectElement>;
14
+ yearSelectRef?: React.RefObject<HTMLSelectElement>;
13
15
  /** Nåværende månedsnummer (1-12) */
14
16
  monthNumber: number;
15
17
  /** Om måned- og år-dropdown skal vises i kalenderen */
@@ -1 +1 @@
1
- {"root":["../src/index.ts","../src/types.ts","../src/button/Button.tsx","../src/button/index.ts","../src/calendar/Calendar.tsx","../src/calendar/ClickableDate.tsx","../src/calendar/Header.tsx","../src/calendar/NonClickableDate.tsx","../src/calendar/index.ts","../src/datelogic/simplecalendar.ts","../src/datelogic/simpledate.ts","../src/datelogic/types.ts","../src/datepicker/Datepicker.tsx","../src/datepicker/DatepickerComp.tsx","../src/datepicker/DatepickerContext.tsx","../src/datepicker/SpinButton.tsx","../src/datepicker/index.ts","../src/datepicker/padZero.ts","../src/datepicker/testHelper.ts","../src/datepicker/toNumber.ts","../src/i18n/en.ts","../src/i18n/i18n.ts","../src/i18n/nb.ts","../src/i18n/nn.ts","../src/input/DateInput.tsx","../src/input/index.ts","../src/util/dateRangeUtils.ts","../src/util/dateUtil.ts"],"version":"5.9.3"}
1
+ {"root":["../src/index.ts","../src/types.ts","../src/button/Button.tsx","../src/button/index.ts","../src/calendar/Calendar.tsx","../src/calendar/ClickableDate.tsx","../src/calendar/Header.tsx","../src/calendar/NonClickableDate.tsx","../src/calendar/index.ts","../src/datelogic/simplecalendar.ts","../src/datelogic/simpledate.ts","../src/datelogic/types.ts","../src/datepicker/Datepicker.tsx","../src/datepicker/DatepickerComp.tsx","../src/datepicker/DatepickerContext.tsx","../src/datepicker/SpinButton.tsx","../src/datepicker/index.ts","../src/datepicker/padZero.ts","../src/datepicker/testHelper.ts","../src/datepicker/toNumber.ts","../src/i18n/en.ts","../src/i18n/i18n.ts","../src/i18n/nb.ts","../src/i18n/nn.ts","../src/input/DateInput.tsx","../src/input/index.ts","../src/util/dateRangeUtils.ts","../src/util/dateUtil.ts","../src/util/isIOSSafari.ts"],"version":"5.9.3"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Detects iOS/iPadOS Safari to work around a VoiceOver bug that blocks
3
+ * interaction with date-related inputs.
4
+ * @see https://dev.to/mfranzke/voiceover-bug-on-ios-safari-blocks-date-time-related-inputs-especially-in-react-4f61
5
+ *
6
+ * Known limitations:
7
+ * - iOS WebViews (in-app browsers like Instagram, Facebook) use WebKit and may
8
+ * be affected by the same bug, but are not detected here since they don't
9
+ * include "Safari" in the user agent string.
10
+ */
11
+ export declare const isIOSSafari: () => boolean;