@deviceinsight/ng-ui-components 6.26.0 → 6.27.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 (25) hide show
  1. package/lib/cjs/components/input/DateInput.d.ts +3 -1
  2. package/lib/cjs/components/input/DateInput.d.ts.map +1 -1
  3. package/lib/cjs/components/input/DateInput.js +37 -16
  4. package/lib/cjs/components/input/DateTimeInput.d.ts.map +1 -1
  5. package/lib/cjs/components/input/DateTimeInput.js +16 -44
  6. package/lib/cjs/components/input/__tests__/DateInput.spec.js +7 -21
  7. package/lib/cjs/components/periodpicker/components/Inputs.d.ts.map +1 -1
  8. package/lib/cjs/components/periodpicker/components/Inputs.js +11 -4
  9. package/lib/esm/components/input/DateInput.d.ts +3 -1
  10. package/lib/esm/components/input/DateInput.d.ts.map +1 -1
  11. package/lib/esm/components/input/DateInput.js +37 -16
  12. package/lib/esm/components/input/DateTimeInput.d.ts.map +1 -1
  13. package/lib/esm/components/input/DateTimeInput.js +16 -44
  14. package/lib/esm/components/input/__tests__/DateInput.spec.js +7 -21
  15. package/lib/esm/components/periodpicker/components/Inputs.d.ts.map +1 -1
  16. package/lib/esm/components/periodpicker/components/Inputs.js +11 -4
  17. package/lib/esm/src/components/input/DateInput.d.ts +3 -1
  18. package/lib/esm/src/components/input/DateInput.d.ts.map +1 -1
  19. package/lib/esm/src/components/input/DateInput.js +37 -16
  20. package/lib/esm/src/components/input/DateTimeInput.d.ts.map +1 -1
  21. package/lib/esm/src/components/input/DateTimeInput.js +16 -44
  22. package/lib/esm/src/components/input/__tests__/DateInput.spec.js +7 -21
  23. package/lib/esm/src/components/periodpicker/components/Inputs.d.ts.map +1 -1
  24. package/lib/esm/src/components/periodpicker/components/Inputs.js +11 -4
  25. package/package.json +2 -2
@@ -5,6 +5,8 @@ import '../../assets/react-datepicker-custom.css';
5
5
  declare type DateInputProps = {
6
6
  /** the current value (millis since 1970-01-01 or formatted string) */
7
7
  value: number | string | null | undefined;
8
+ /** (optional) timezone */
9
+ timezone?: string;
8
10
  /** callback function that is executed when the user picks a date */
9
11
  onChange: (date: number | null | undefined, fromTextField?: boolean) => void;
10
12
  /** makes the input field clearable */
@@ -23,6 +25,6 @@ export declare type Props = Omit<ReactDatePickerProps, keyof DateInputProps> & D
23
25
  * DateInput is used to let users set a date conveniently.
24
26
  * Usually the user's locale is used for displaying the correct date format.
25
27
  */
26
- export default function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onChange, locale, ...otherProps }: Props): React.JSX.Element;
28
+ export default function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onChange, locale, timezone, ...otherProps }: Props): React.JSX.Element;
27
29
  export {};
28
30
  //# sourceMappingURL=DateInput.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DateInput.d.ts","sourceRoot":"","sources":["../../../../src/components/input/DateInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkD,MAAM,OAAO,CAAC;AACvE,OAAmB,EAAC,oBAAoB,EAAiB,MAAM,kBAAkB,CAAC;AAalF,OAAO,4CAA4C,CAAC;AACpD,OAAO,0CAA0C,CAAC;AAwClD,aAAK,cAAc,GAAG;IACrB,sEAAsE;IACtE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAE1C,oEAAoE;IACpE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAE7E,sCAAsC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,sCAAsC;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,oBAAY,KAAK,GAAG,IAAI,CAAC,oBAAoB,EAAE,MAAM,cAAc,CAAC,GAAG,cAAc,CAAC;AAqBtF;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EACjC,KAAK,EACL,SAAS,EACT,MAAM,EACN,QAAQ,EACR,gBAAgB,EAChB,QAAQ,EACR,MAAM,EACN,GAAG,UAAU,EACb,EAAE,KAAK,qBAmKP"}
1
+ {"version":3,"file":"DateInput.d.ts","sourceRoot":"","sources":["../../../../src/components/input/DateInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkD,MAAM,OAAO,CAAC;AAGvE,OAAmB,EAAC,oBAAoB,EAAiB,MAAM,kBAAkB,CAAC;AAalF,OAAO,4CAA4C,CAAC;AACpD,OAAO,0CAA0C,CAAC;AA0ClD,aAAK,cAAc,GAAG;IACrB,sEAAsE;IACtE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAE1C,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,oEAAoE;IACpE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAE7E,sCAAsC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,sCAAsC;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,oBAAY,KAAK,GAAG,IAAI,CAAC,oBAAoB,EAAE,MAAM,cAAc,CAAC,GAAG,cAAc,CAAC;AA2CtF;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EACjC,KAAK,EACL,SAAS,EACT,MAAM,EACN,QAAQ,EACR,gBAAgB,EAChB,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,GAAG,UAAU,EACb,EAAE,KAAK,qBAyKP"}
@@ -27,6 +27,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  const react_1 = __importStar(require("react"));
30
+ const timezone_1 = __importDefault(require("dayjs/plugin/timezone"));
31
+ const utc_1 = __importDefault(require("dayjs/plugin/utc"));
30
32
  const react_datepicker_1 = __importStar(require("react-datepicker"));
31
33
  const lodash_1 = require("lodash");
32
34
  const dayjs_1 = __importDefault(require("dayjs"));
@@ -41,6 +43,8 @@ const ng_ui_basic_components_1 = require("@deviceinsight/ng-ui-basic-components"
41
43
  require("react-datepicker/dist/react-datepicker.css");
42
44
  require("../../assets/react-datepicker-custom.css");
43
45
  const dayjsLocales_1 = require("../../utils/dayjsLocales");
46
+ dayjs_1.default.extend(utc_1.default);
47
+ dayjs_1.default.extend(timezone_1.default);
44
48
  dayjs_1.default.extend(localeData_1.default);
45
49
  dayjs_1.default.extend(localizedFormat_1.default);
46
50
  dayjs_1.default.extend(customParseFormat_1.default);
@@ -76,29 +80,40 @@ async function changeDayjsLocale(locale) {
76
80
  }
77
81
  }
78
82
  function isInputValid(value, pattern) {
79
- if (value == null || (typeof value === 'number' && !isNaN(value)) || value == '') {
83
+ if (value == null || value === '') {
80
84
  return true;
81
85
  }
86
+ if (typeof value === 'number') {
87
+ return !isNaN(value);
88
+ }
82
89
  if (typeof value === 'string') {
83
90
  return (0, dayjs_1.default)(value, pattern, true).isValid();
84
91
  }
85
92
  return false;
86
93
  }
87
- function inputToDate(value, pattern) {
88
- if (value == null || value === '' || !isInputValid(value, pattern))
89
- return null;
94
+ function inputToDate(value, pattern, timezone) {
90
95
  if (typeof value === 'number') {
91
96
  return new Date(value);
92
97
  }
93
- else {
94
- return (0, dayjs_1.default)(value, pattern, true).toDate();
98
+ if (typeof value === 'string' && (0, dayjs_1.default)(value, pattern, true).isValid()) {
99
+ return dayjs_1.default.tz(value, pattern, timezone).toDate();
100
+ }
101
+ return null;
102
+ }
103
+ function inputToTzDate(value, pattern, timezone) {
104
+ if (typeof value === 'number') {
105
+ return dayjs_1.default.tz(value, timezone);
106
+ }
107
+ if (typeof value === 'string' && (0, dayjs_1.default)(value, pattern, true).isValid()) {
108
+ return dayjs_1.default.tz(value, pattern, timezone);
95
109
  }
110
+ return null;
96
111
  }
97
112
  /**
98
113
  * DateInput is used to let users set a date conveniently.
99
114
  * Usually the user's locale is used for displaying the correct date format.
100
115
  */
101
- function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onChange, locale, ...otherProps }) {
116
+ function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onChange, locale, timezone, ...otherProps }) {
102
117
  const [pattern, setPattern] = (0, react_1.useState)(dayjs_1.default.localeData().longDateFormat('L'));
103
118
  const { t } = (0, i18n_1.useTranslation)();
104
119
  const [valid, setValid] = (0, react_1.useState)(false);
@@ -122,10 +137,9 @@ function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onCha
122
137
  });
123
138
  }
124
139
  else {
125
- const date = typeof value === 'number' ? (0, dayjs_1.default)(value) : (0, dayjs_1.default)(value, pattern, true);
126
- return date.format(pattern);
140
+ return typeof value === 'number' ? dayjs_1.default.tz(value, timezone).format(pattern) : value;
127
141
  }
128
- }, [pattern, t]);
142
+ }, [pattern, t, timezone]);
129
143
  (0, react_1.useEffect)(() => {
130
144
  if (value == null) {
131
145
  setInputText('');
@@ -144,10 +158,17 @@ function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onCha
144
158
  const handleInputTextChange = (text) => {
145
159
  setInputText(text);
146
160
  if (text) {
147
- const date = (0, dayjs_1.default)(text, pattern, true);
148
- const valid = date.isValid();
161
+ const valid = (0, dayjs_1.default)(text, pattern, true).isValid();
149
162
  if (valid) {
150
- onChange(date.valueOf(), true);
163
+ const prevDate = inputToTzDate(value, pattern, timezone);
164
+ const newDate = dayjs_1.default.tz(text, pattern, timezone ?? dayjs_1.default.tz.guess());
165
+ if (prevDate) {
166
+ newDate.set('hour', prevDate.hour());
167
+ newDate.set('minute', prevDate.minute());
168
+ newDate.set('second', prevDate.second());
169
+ newDate.set('millisecond', prevDate.millisecond());
170
+ }
171
+ onChange(newDate.valueOf(), true);
151
172
  }
152
173
  setValid(valid);
153
174
  }
@@ -165,10 +186,10 @@ function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onCha
165
186
  const handlePopupHide = () => setPopupActive(false);
166
187
  const handleClear = () => handleInputTextChange('');
167
188
  if (inline) {
168
- return (react_1.default.createElement(react_datepicker_1.default, { ...otherProps, onChange: handlePickerChange, selected: inputToDate(value, pattern), locale: locale ?? i18next_1.default.language, isClearable: clearable, placeholderText: pattern, className: (0, classnames_1.default)('date', { invalid: !valid }), inline: true }));
189
+ return (react_1.default.createElement(react_datepicker_1.default, { ...otherProps, onChange: handlePickerChange, selected: inputToDate(value, pattern, timezone), locale: locale ?? i18next_1.default.language, isClearable: clearable, placeholderText: pattern, className: (0, classnames_1.default)('date', { invalid: !valid }), inline: true }));
169
190
  }
170
191
  if (readOnly) {
171
- return (react_1.default.createElement(react_datepicker_1.default, { ...otherProps, onChange: () => { }, selected: inputToDate(value, pattern), locale: locale ?? i18next_1.default.language, customInput: react_1.default.createElement("span", null,
192
+ return (react_1.default.createElement(react_datepicker_1.default, { ...otherProps, onChange: () => { }, selected: inputToDate(value, pattern, timezone), locale: locale ?? i18next_1.default.language, customInput: react_1.default.createElement("span", null,
172
193
  react_1.default.createElement("input", { type: "text", value: formattedDate(value), readOnly: true })), placeholderText: pattern, readOnly: true }));
173
194
  }
174
195
  return (react_1.default.createElement(ng_ui_basic_components_1.Popup, { handler: react_1.default.createElement("span", { className: "di date-input-wrapper" },
@@ -181,6 +202,6 @@ function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onCha
181
202
  react_1.default.createElement("div", { className: "di date-picker-popup-wrapper", tabIndex: 0, onFocus: () => {
182
203
  debouncedSetPopupActive(true);
183
204
  } },
184
- react_1.default.createElement(react_datepicker_1.default, { ...otherProps, selected: inputToDate(value, pattern), onChange: handlePickerChange, locale: locale ?? i18next_1.default.language, isClearable: clearable, placeholderText: pattern, className: (0, classnames_1.default)('date', { invalid: !valid }), inline: true }))));
205
+ react_1.default.createElement(react_datepicker_1.default, { ...otherProps, selected: inputToDate(value, pattern, timezone), onChange: handlePickerChange, locale: locale ?? i18next_1.default.language, isClearable: clearable, placeholderText: pattern, className: (0, classnames_1.default)('date', { invalid: !valid }), inline: true }))));
185
206
  }
186
207
  exports.default = DateInput;
@@ -1 +1 @@
1
- {"version":3,"file":"DateTimeInput.d.ts","sourceRoot":"","sources":["../../../../src/components/input/DateTimeInput.tsx"],"names":[],"mappings":"AACA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAwBjD,oBAAY,KAAK,GAAG;IACnB,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAElB,oEAAoE;IACpE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAE7E,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,OAAO,CAAC,EAAE,IAAI,CAAC;IAEf,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EACrC,SAAS,EACT,QAAQ,EACR,OAAO,EACP,QAAgB,EAChB,QAAQ,EACR,GAAG,UAAU,EACb,EAAE,KAAK,qBAgFP"}
1
+ {"version":3,"file":"DateTimeInput.d.ts","sourceRoot":"","sources":["../../../../src/components/input/DateTimeInput.tsx"],"names":[],"mappings":"AACA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAcjD,oBAAY,KAAK,GAAG;IACnB,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAElB,oEAAoE;IACpE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAE7E,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,OAAO,CAAC,EAAE,IAAI,CAAC;IAEf,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EACrC,SAAS,EACT,QAAQ,EACR,OAAO,EACP,QAAgB,EAChB,QAAQ,EACR,GAAG,UAAU,EACb,EAAE,KAAK,qBA4DP"}
@@ -28,70 +28,42 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  const dayjs_1 = __importDefault(require("dayjs"));
30
30
  const react_1 = __importStar(require("react"));
31
- const periods_1 = require("../periodpicker/periods");
32
31
  const DateInput_1 = __importDefault(require("./DateInput"));
33
32
  const ValidatedTextInput_1 = __importDefault(require("./ValidatedTextInput"));
34
- const lodash_1 = require("lodash");
35
- const extractTime = (timezone, date) => {
33
+ const formatTime = (timezone, date) => {
36
34
  try {
37
- return (0, periods_1.dayjsTz)(timezone, date).format('HH:mm');
35
+ return dayjs_1.default.tz(date, timezone).format('HH:mm');
38
36
  }
39
37
  catch (error) {
40
38
  console.error('DateTimeInput: Error while extracting time from date', error);
41
39
  return '00:00';
42
40
  }
43
41
  };
44
- const extractDate = (timezone, date) => {
45
- try {
46
- return (0, dayjs_1.default)((0, periods_1.dayjsTz)(timezone, date).format('YYYY-MM-DD'));
47
- }
48
- catch (error) {
49
- console.error('DateTimeInput: Error while extracting time from date', error);
50
- return (0, dayjs_1.default)(new Date());
51
- }
52
- };
53
42
  /**
54
43
  * Extends DateInput but also displaying the current time.
55
44
  * Usually the user's locale is used for displaying the correct date format.
56
45
  */
57
46
  function DateTimeInput({ timestamp, timezone, warning, readOnly = false, onChange, ...otherProps }) {
58
- const [time, setTime] = (0, react_1.useState)(extractTime(timezone, timestamp));
59
- const timeValue = time == null ? extractTime(timezone, timestamp) : time;
47
+ const [time, setTime] = (0, react_1.useState)(formatTime(timezone, timestamp));
48
+ const timeValue = time == null ? formatTime(timezone, timestamp) : time;
60
49
  (0, react_1.useEffect)(() => {
61
- setTime(extractTime(timezone, timestamp));
50
+ setTime(formatTime(timezone, timestamp));
62
51
  }, [timezone, timestamp]);
63
52
  function handleChangeDate(date, fromTextField) {
64
- const oldDate = (0, periods_1.dayjsTz)(timezone, timestamp);
65
- const dateFix = (0, dayjs_1.default)(date);
66
- let newDate = (0, periods_1.dayjsTz)(timezone, date)
67
- .set('day', dateFix.day())
68
- .set('month', dateFix.month())
69
- .set('year', dateFix.year())
70
- .set('hour', (0, lodash_1.toInteger)(oldDate.hour()))
71
- .set('minute', (0, lodash_1.toInteger)(oldDate.minute()));
72
- const dstOffset = getLocalDstOffset(newDate);
73
- newDate = newDate.subtract(dstOffset, 'minutes');
53
+ const currentDate = dayjs_1.default.tz(timestamp, timezone);
54
+ const newDate = dayjs_1.default
55
+ .tz(date, timezone)
56
+ .set('hour', currentDate.hour())
57
+ .set('minute', currentDate.minute())
58
+ .set('second', currentDate.second())
59
+ .set('millisecond', currentDate.millisecond());
74
60
  onChange(newDate.valueOf(), fromTextField);
75
61
  }
76
- function getMaxUtcOffset(year) {
77
- const jan = new Date(`${year}-01-01`).getTimezoneOffset();
78
- const jul = new Date(`${year}-07-01`).getTimezoneOffset();
79
- return Math.min(jan, jul);
80
- }
81
- function getLocalDstOffset(newDate) {
82
- return newDate.toDate().getTimezoneOffset() - getMaxUtcOffset(newDate.year());
83
- }
84
62
  function handleChangeTime(time, valid) {
85
63
  if (valid) {
86
- const [hours, minutes] = time.split(':');
87
- const date = extractDate(timezone, timestamp);
88
- const dateTime = (0, periods_1.dayjsTz)(timezone, timestamp)
89
- .set('date', date.date())
90
- .set('hour', parseInt(hours, 10))
91
- .set('minute', parseInt(minutes, 10))
92
- .set('second', 0)
93
- .set('millisecond', 0);
94
- onChange(dateTime.valueOf());
64
+ const [hours, minutes] = time.split(':').map(Number);
65
+ const newDate = dayjs_1.default.tz(timestamp, timezone).set('hours', hours).set('minutes', minutes);
66
+ onChange(newDate.valueOf(), true);
95
67
  }
96
68
  setTime(time);
97
69
  }
@@ -101,7 +73,7 @@ function DateTimeInput({ timestamp, timezone, warning, readOnly = false, onChang
101
73
  }
102
74
  }
103
75
  return (react_1.default.createElement("span", { className: `di date-time-input ${warning ? 'with-warning' : ''}` },
104
- react_1.default.createElement(DateInput_1.default, { ...otherProps, value: extractDate(timezone, timestamp).valueOf(), onChange: handleChangeDate, readOnly: readOnly }),
76
+ react_1.default.createElement(DateInput_1.default, { ...otherProps, value: timestamp, timezone: timezone, onChange: handleChangeDate, readOnly: readOnly }),
105
77
  react_1.default.createElement(ValidatedTextInput_1.default, { "data-testid": "time-input", value: timeValue, onChange: handleChangeTime, pattern: /^(([01][0-9])|2[0-3]):[0-5][0-9]$/, required: true, invalidStyle: {
106
78
  borderColor: 'red'
107
79
  }, placeholder: "00:00", className: "time", onBlur: tryFixTime, disabled: readOnly })));
@@ -44,9 +44,7 @@ const DateStateProvider = ({ children, initialValue }) => {
44
44
  });
45
45
  (0, vitest_1.describe)('Changing values via text input', () => {
46
46
  (0, vitest_1.it)('should render correct date typed into text input without "invalid" class', async () => {
47
- await (0, react_2.act)(async () => {
48
- (0, react_2.render)(react_1.default.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => react_1.default.createElement(DateInput_1.default, { value: date, onChange: (date) => setDate(date) })));
49
- });
47
+ (0, react_2.render)(react_1.default.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => react_1.default.createElement(DateInput_1.default, { value: date, onChange: (date) => setDate(date) })));
50
48
  await (0, react_2.act)(async () => {
51
49
  react_2.fireEvent.change(react_2.screen.getByTestId('date-input'), { target: { value: '02.02.2019' } });
52
50
  });
@@ -54,9 +52,7 @@ const DateStateProvider = ({ children, initialValue }) => {
54
52
  (0, vitest_1.expect)(react_2.screen.getByTestId('date-input')).not.toHaveClass('invalid');
55
53
  });
56
54
  (0, vitest_1.it)('should render incorrect date typed into text input with "invalid" class', async () => {
57
- await (0, react_2.act)(async () => {
58
- (0, react_2.render)(react_1.default.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => react_1.default.createElement(DateInput_1.default, { value: date, onChange: (date) => setDate(date) })));
59
- });
55
+ (0, react_2.render)(react_1.default.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => react_1.default.createElement(DateInput_1.default, { value: date, onChange: (date) => setDate(date) })));
60
56
  await (0, react_2.act)(async () => {
61
57
  react_2.fireEvent.change(react_2.screen.getByTestId('date-input'), { target: { value: '02.02.201' } });
62
58
  });
@@ -64,9 +60,7 @@ const DateStateProvider = ({ children, initialValue }) => {
64
60
  });
65
61
  (0, vitest_1.it)('should call onChange callback with parsed value of the input during the input is being changed when the value is valid', async () => {
66
62
  const onChange = vitest_1.vi.fn();
67
- await (0, react_2.act)(async () => {
68
- (0, react_2.render)(react_1.default.createElement(DateInput_1.default, { value: new Date('2019-02-01').getTime(), onChange: onChange }));
69
- });
63
+ (0, react_2.render)(react_1.default.createElement(DateInput_1.default, { value: new Date('2019-02-01').getTime(), onChange: onChange }));
70
64
  await (0, react_2.act)(async () => {
71
65
  react_2.fireEvent.change(react_2.screen.getByTestId('date-input'), { target: { value: '02.02.2019' } });
72
66
  });
@@ -75,18 +69,14 @@ const DateStateProvider = ({ children, initialValue }) => {
75
69
  });
76
70
  (0, vitest_1.it)('should not call onChange callback while editing the input when the input value is invalid', async () => {
77
71
  const onChange = vitest_1.vi.fn();
78
- await (0, react_2.act)(async () => {
79
- (0, react_2.render)(react_1.default.createElement(DateInput_1.default, { value: new Date('2019-02-01').getTime(), onChange: onChange }));
80
- });
72
+ (0, react_2.render)(react_1.default.createElement(DateInput_1.default, { value: new Date('2019-02-01').getTime(), onChange: onChange }));
81
73
  await (0, react_2.act)(async () => {
82
74
  react_2.fireEvent.change(react_2.screen.getByTestId('date-input'), { target: { value: '02.02.201' } });
83
75
  });
84
76
  (0, vitest_1.expect)(onChange).not.toHaveBeenCalled();
85
77
  });
86
78
  (0, vitest_1.it)('should display the current value in the input field if the input value is invalid and the input became blurred', async () => {
87
- await (0, react_2.act)(async () => {
88
- (0, react_2.render)(react_1.default.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => react_1.default.createElement(DateInput_1.default, { value: date, onChange: (date) => setDate(date) })));
89
- });
79
+ (0, react_2.render)(react_1.default.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => react_1.default.createElement(DateInput_1.default, { value: date, onChange: (date) => setDate(date) })));
90
80
  await (0, react_2.act)(async () => {
91
81
  react_2.fireEvent.change(react_2.screen.getByTestId('date-input'), { target: { value: '02.02.201' } });
92
82
  react_2.fireEvent.blur(react_2.screen.getByTestId('date-input'));
@@ -94,16 +84,12 @@ const DateStateProvider = ({ children, initialValue }) => {
94
84
  (0, vitest_1.expect)(react_2.screen.getByTestId('date-input')).toHaveValue('01.02.2019');
95
85
  });
96
86
  (0, vitest_1.it)('should display invalid date text in the input when the value is NaN', async () => {
97
- await (0, react_2.act)(async () => {
98
- (0, react_2.render)(react_1.default.createElement(DateInput_1.default, { value: NaN, onChange: () => { } }));
99
- });
87
+ (0, react_2.render)(react_1.default.createElement(DateInput_1.default, { value: NaN, onChange: () => { } }));
100
88
  (0, vitest_1.expect)(react_2.screen.getByTestId('date-input')).toHaveValue('dateInput.invalidDate');
101
89
  });
102
90
  (0, vitest_1.it)('should call onChange callback with [null, true] when empty string is set to input field', async () => {
103
91
  const onChange = vitest_1.vi.fn();
104
- await (0, react_2.act)(async () => {
105
- (0, react_2.render)(react_1.default.createElement(DateInput_1.default, { value: Date.now(), onChange: onChange }));
106
- });
92
+ (0, react_2.render)(react_1.default.createElement(DateInput_1.default, { value: Date.now(), onChange: onChange }));
107
93
  await (0, react_2.act)(async () => {
108
94
  react_2.fireEvent.change(react_2.screen.getByTestId('date-input'), { target: { value: '' } });
109
95
  });
@@ -1 +1 @@
1
- {"version":3,"file":"Inputs.d.ts","sourceRoot":"","sources":["../../../../../src/components/periodpicker/components/Inputs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyB,MAAM,OAAO,CAAC;AAE9C,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAMhC,aAAK,KAAK,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAC,EAAE,KAAK,qBA2FnF"}
1
+ {"version":3,"file":"Inputs.d.ts","sourceRoot":"","sources":["../../../../../src/components/periodpicker/components/Inputs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyB,MAAM,OAAO,CAAC;AAG9C,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAMhC,aAAK,KAAK,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAC,EAAE,KAAK,qBAiGnF"}
@@ -27,6 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  const react_1 = __importStar(require("react"));
30
+ const lodash_1 = require("lodash");
30
31
  const DateTimeInput_1 = __importDefault(require("../../input/DateTimeInput"));
31
32
  const ONE_HOUR = 60 * 60 * 1000;
32
33
  const TOGGLE_TIMEOUT = 300;
@@ -53,10 +54,13 @@ function Inputs({ period, onChange, readOnly, locale, timezone }) {
53
54
  if (timestamp) {
54
55
  const incorrect = period.tsEnd && timestamp > period.tsEnd;
55
56
  const tsEnd = incorrect ? timestamp + ONE_HOUR : period.tsEnd;
56
- onChange({
57
+ const newPeriod = {
57
58
  tsStart: timestamp,
58
59
  tsEnd
59
- });
60
+ };
61
+ if (!(0, lodash_1.isEqual)(newPeriod, period)) {
62
+ onChange(newPeriod);
63
+ }
60
64
  if (!fromTextField) {
61
65
  openEndDate();
62
66
  }
@@ -69,10 +73,13 @@ function Inputs({ period, onChange, readOnly, locale, timezone }) {
69
73
  if (timestamp) {
70
74
  const incorrect = period.tsStart && timestamp < period.tsStart;
71
75
  const tsStart = incorrect ? timestamp - ONE_HOUR : period.tsStart;
72
- onChange({
76
+ const newPeriod = {
73
77
  tsStart,
74
78
  tsEnd: timestamp
75
- });
79
+ };
80
+ if (!(0, lodash_1.isEqual)(newPeriod, period)) {
81
+ onChange(newPeriod);
82
+ }
76
83
  if (incorrect) {
77
84
  showWarning('date-from');
78
85
  }
@@ -5,6 +5,8 @@ import '../../assets/react-datepicker-custom.css';
5
5
  declare type DateInputProps = {
6
6
  /** the current value (millis since 1970-01-01 or formatted string) */
7
7
  value: number | string | null | undefined;
8
+ /** (optional) timezone */
9
+ timezone?: string;
8
10
  /** callback function that is executed when the user picks a date */
9
11
  onChange: (date: number | null | undefined, fromTextField?: boolean) => void;
10
12
  /** makes the input field clearable */
@@ -23,6 +25,6 @@ export declare type Props = Omit<ReactDatePickerProps, keyof DateInputProps> & D
23
25
  * DateInput is used to let users set a date conveniently.
24
26
  * Usually the user's locale is used for displaying the correct date format.
25
27
  */
26
- export default function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onChange, locale, ...otherProps }: Props): React.JSX.Element;
28
+ export default function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onChange, locale, timezone, ...otherProps }: Props): React.JSX.Element;
27
29
  export {};
28
30
  //# sourceMappingURL=DateInput.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DateInput.d.ts","sourceRoot":"","sources":["../../../../src/components/input/DateInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkD,MAAM,OAAO,CAAC;AACvE,OAAmB,EAAC,oBAAoB,EAAiB,MAAM,kBAAkB,CAAC;AAalF,OAAO,4CAA4C,CAAC;AACpD,OAAO,0CAA0C,CAAC;AAwClD,aAAK,cAAc,GAAG;IACrB,sEAAsE;IACtE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAE1C,oEAAoE;IACpE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAE7E,sCAAsC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,sCAAsC;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,oBAAY,KAAK,GAAG,IAAI,CAAC,oBAAoB,EAAE,MAAM,cAAc,CAAC,GAAG,cAAc,CAAC;AAqBtF;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EACjC,KAAK,EACL,SAAS,EACT,MAAM,EACN,QAAQ,EACR,gBAAgB,EAChB,QAAQ,EACR,MAAM,EACN,GAAG,UAAU,EACb,EAAE,KAAK,qBAmKP"}
1
+ {"version":3,"file":"DateInput.d.ts","sourceRoot":"","sources":["../../../../src/components/input/DateInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkD,MAAM,OAAO,CAAC;AAGvE,OAAmB,EAAC,oBAAoB,EAAiB,MAAM,kBAAkB,CAAC;AAalF,OAAO,4CAA4C,CAAC;AACpD,OAAO,0CAA0C,CAAC;AA0ClD,aAAK,cAAc,GAAG;IACrB,sEAAsE;IACtE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAE1C,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,oEAAoE;IACpE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAE7E,sCAAsC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,sCAAsC;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,oBAAY,KAAK,GAAG,IAAI,CAAC,oBAAoB,EAAE,MAAM,cAAc,CAAC,GAAG,cAAc,CAAC;AA2CtF;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EACjC,KAAK,EACL,SAAS,EACT,MAAM,EACN,QAAQ,EACR,gBAAgB,EAChB,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,GAAG,UAAU,EACb,EAAE,KAAK,qBAyKP"}
@@ -1,4 +1,6 @@
1
1
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import timezone from 'dayjs/plugin/timezone';
3
+ import utc from 'dayjs/plugin/utc';
2
4
  import DatePicker, { registerLocale } from 'react-datepicker';
3
5
  import { debounce } from 'lodash';
4
6
  import dayjs from 'dayjs';
@@ -13,6 +15,8 @@ import { Button, Icon, Popup } from '@deviceinsight/ng-ui-basic-components';
13
15
  import 'react-datepicker/dist/react-datepicker.css';
14
16
  import '../../assets/react-datepicker-custom.css';
15
17
  import { isLocaleSupported } from '../../utils/dayjsLocales';
18
+ dayjs.extend(utc);
19
+ dayjs.extend(timezone);
16
20
  dayjs.extend(localeData);
17
21
  dayjs.extend(localizedFormat);
18
22
  dayjs.extend(customParseFormat);
@@ -48,29 +52,40 @@ async function changeDayjsLocale(locale) {
48
52
  }
49
53
  }
50
54
  function isInputValid(value, pattern) {
51
- if (value == null || (typeof value === 'number' && !isNaN(value)) || value == '') {
55
+ if (value == null || value === '') {
52
56
  return true;
53
57
  }
58
+ if (typeof value === 'number') {
59
+ return !isNaN(value);
60
+ }
54
61
  if (typeof value === 'string') {
55
62
  return dayjs(value, pattern, true).isValid();
56
63
  }
57
64
  return false;
58
65
  }
59
- function inputToDate(value, pattern) {
60
- if (value == null || value === '' || !isInputValid(value, pattern))
61
- return null;
66
+ function inputToDate(value, pattern, timezone) {
62
67
  if (typeof value === 'number') {
63
68
  return new Date(value);
64
69
  }
65
- else {
66
- return dayjs(value, pattern, true).toDate();
70
+ if (typeof value === 'string' && dayjs(value, pattern, true).isValid()) {
71
+ return dayjs.tz(value, pattern, timezone).toDate();
72
+ }
73
+ return null;
74
+ }
75
+ function inputToTzDate(value, pattern, timezone) {
76
+ if (typeof value === 'number') {
77
+ return dayjs.tz(value, timezone);
78
+ }
79
+ if (typeof value === 'string' && dayjs(value, pattern, true).isValid()) {
80
+ return dayjs.tz(value, pattern, timezone);
67
81
  }
82
+ return null;
68
83
  }
69
84
  /**
70
85
  * DateInput is used to let users set a date conveniently.
71
86
  * Usually the user's locale is used for displaying the correct date format.
72
87
  */
73
- export default function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onChange, locale, ...otherProps }) {
88
+ export default function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onChange, locale, timezone, ...otherProps }) {
74
89
  const [pattern, setPattern] = useState(dayjs.localeData().longDateFormat('L'));
75
90
  const { t } = useTranslation();
76
91
  const [valid, setValid] = useState(false);
@@ -94,10 +109,9 @@ export default function DateInput({ value, clearable, inline, readOnly, wrapperC
94
109
  });
95
110
  }
96
111
  else {
97
- const date = typeof value === 'number' ? dayjs(value) : dayjs(value, pattern, true);
98
- return date.format(pattern);
112
+ return typeof value === 'number' ? dayjs.tz(value, timezone).format(pattern) : value;
99
113
  }
100
- }, [pattern, t]);
114
+ }, [pattern, t, timezone]);
101
115
  useEffect(() => {
102
116
  if (value == null) {
103
117
  setInputText('');
@@ -116,10 +130,17 @@ export default function DateInput({ value, clearable, inline, readOnly, wrapperC
116
130
  const handleInputTextChange = (text) => {
117
131
  setInputText(text);
118
132
  if (text) {
119
- const date = dayjs(text, pattern, true);
120
- const valid = date.isValid();
133
+ const valid = dayjs(text, pattern, true).isValid();
121
134
  if (valid) {
122
- onChange(date.valueOf(), true);
135
+ const prevDate = inputToTzDate(value, pattern, timezone);
136
+ const newDate = dayjs.tz(text, pattern, timezone ?? dayjs.tz.guess());
137
+ if (prevDate) {
138
+ newDate.set('hour', prevDate.hour());
139
+ newDate.set('minute', prevDate.minute());
140
+ newDate.set('second', prevDate.second());
141
+ newDate.set('millisecond', prevDate.millisecond());
142
+ }
143
+ onChange(newDate.valueOf(), true);
123
144
  }
124
145
  setValid(valid);
125
146
  }
@@ -137,10 +158,10 @@ export default function DateInput({ value, clearable, inline, readOnly, wrapperC
137
158
  const handlePopupHide = () => setPopupActive(false);
138
159
  const handleClear = () => handleInputTextChange('');
139
160
  if (inline) {
140
- return (React.createElement(DatePicker, { ...otherProps, onChange: handlePickerChange, selected: inputToDate(value, pattern), locale: locale ?? i18n.language, isClearable: clearable, placeholderText: pattern, className: cx('date', { invalid: !valid }), inline: true }));
161
+ return (React.createElement(DatePicker, { ...otherProps, onChange: handlePickerChange, selected: inputToDate(value, pattern, timezone), locale: locale ?? i18n.language, isClearable: clearable, placeholderText: pattern, className: cx('date', { invalid: !valid }), inline: true }));
141
162
  }
142
163
  if (readOnly) {
143
- return (React.createElement(DatePicker, { ...otherProps, onChange: () => { }, selected: inputToDate(value, pattern), locale: locale ?? i18n.language, customInput: React.createElement("span", null,
164
+ return (React.createElement(DatePicker, { ...otherProps, onChange: () => { }, selected: inputToDate(value, pattern, timezone), locale: locale ?? i18n.language, customInput: React.createElement("span", null,
144
165
  React.createElement("input", { type: "text", value: formattedDate(value), readOnly: true })), placeholderText: pattern, readOnly: true }));
145
166
  }
146
167
  return (React.createElement(Popup, { handler: React.createElement("span", { className: "di date-input-wrapper" },
@@ -153,5 +174,5 @@ export default function DateInput({ value, clearable, inline, readOnly, wrapperC
153
174
  React.createElement("div", { className: "di date-picker-popup-wrapper", tabIndex: 0, onFocus: () => {
154
175
  debouncedSetPopupActive(true);
155
176
  } },
156
- React.createElement(DatePicker, { ...otherProps, selected: inputToDate(value, pattern), onChange: handlePickerChange, locale: locale ?? i18n.language, isClearable: clearable, placeholderText: pattern, className: cx('date', { invalid: !valid }), inline: true }))));
177
+ React.createElement(DatePicker, { ...otherProps, selected: inputToDate(value, pattern, timezone), onChange: handlePickerChange, locale: locale ?? i18n.language, isClearable: clearable, placeholderText: pattern, className: cx('date', { invalid: !valid }), inline: true }))));
157
178
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DateTimeInput.d.ts","sourceRoot":"","sources":["../../../../src/components/input/DateTimeInput.tsx"],"names":[],"mappings":"AACA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAwBjD,oBAAY,KAAK,GAAG;IACnB,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAElB,oEAAoE;IACpE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAE7E,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,OAAO,CAAC,EAAE,IAAI,CAAC;IAEf,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EACrC,SAAS,EACT,QAAQ,EACR,OAAO,EACP,QAAgB,EAChB,QAAQ,EACR,GAAG,UAAU,EACb,EAAE,KAAK,qBAgFP"}
1
+ {"version":3,"file":"DateTimeInput.d.ts","sourceRoot":"","sources":["../../../../src/components/input/DateTimeInput.tsx"],"names":[],"mappings":"AACA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAcjD,oBAAY,KAAK,GAAG;IACnB,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAElB,oEAAoE;IACpE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAE7E,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,OAAO,CAAC,EAAE,IAAI,CAAC;IAEf,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EACrC,SAAS,EACT,QAAQ,EACR,OAAO,EACP,QAAgB,EAChB,QAAQ,EACR,GAAG,UAAU,EACb,EAAE,KAAK,qBA4DP"}
@@ -1,69 +1,41 @@
1
1
  import dayjs from 'dayjs';
2
2
  import React, { useEffect, useState } from 'react';
3
- import { dayjsTz } from '../periodpicker/periods';
4
3
  import DateInput from './DateInput';
5
4
  import ValidatedTextInput from './ValidatedTextInput';
6
- import { toInteger } from 'lodash';
7
- const extractTime = (timezone, date) => {
5
+ const formatTime = (timezone, date) => {
8
6
  try {
9
- return dayjsTz(timezone, date).format('HH:mm');
7
+ return dayjs.tz(date, timezone).format('HH:mm');
10
8
  }
11
9
  catch (error) {
12
10
  console.error('DateTimeInput: Error while extracting time from date', error);
13
11
  return '00:00';
14
12
  }
15
13
  };
16
- const extractDate = (timezone, date) => {
17
- try {
18
- return dayjs(dayjsTz(timezone, date).format('YYYY-MM-DD'));
19
- }
20
- catch (error) {
21
- console.error('DateTimeInput: Error while extracting time from date', error);
22
- return dayjs(new Date());
23
- }
24
- };
25
14
  /**
26
15
  * Extends DateInput but also displaying the current time.
27
16
  * Usually the user's locale is used for displaying the correct date format.
28
17
  */
29
18
  export default function DateTimeInput({ timestamp, timezone, warning, readOnly = false, onChange, ...otherProps }) {
30
- const [time, setTime] = useState(extractTime(timezone, timestamp));
31
- const timeValue = time == null ? extractTime(timezone, timestamp) : time;
19
+ const [time, setTime] = useState(formatTime(timezone, timestamp));
20
+ const timeValue = time == null ? formatTime(timezone, timestamp) : time;
32
21
  useEffect(() => {
33
- setTime(extractTime(timezone, timestamp));
22
+ setTime(formatTime(timezone, timestamp));
34
23
  }, [timezone, timestamp]);
35
24
  function handleChangeDate(date, fromTextField) {
36
- const oldDate = dayjsTz(timezone, timestamp);
37
- const dateFix = dayjs(date);
38
- let newDate = dayjsTz(timezone, date)
39
- .set('day', dateFix.day())
40
- .set('month', dateFix.month())
41
- .set('year', dateFix.year())
42
- .set('hour', toInteger(oldDate.hour()))
43
- .set('minute', toInteger(oldDate.minute()));
44
- const dstOffset = getLocalDstOffset(newDate);
45
- newDate = newDate.subtract(dstOffset, 'minutes');
25
+ const currentDate = dayjs.tz(timestamp, timezone);
26
+ const newDate = dayjs
27
+ .tz(date, timezone)
28
+ .set('hour', currentDate.hour())
29
+ .set('minute', currentDate.minute())
30
+ .set('second', currentDate.second())
31
+ .set('millisecond', currentDate.millisecond());
46
32
  onChange(newDate.valueOf(), fromTextField);
47
33
  }
48
- function getMaxUtcOffset(year) {
49
- const jan = new Date(`${year}-01-01`).getTimezoneOffset();
50
- const jul = new Date(`${year}-07-01`).getTimezoneOffset();
51
- return Math.min(jan, jul);
52
- }
53
- function getLocalDstOffset(newDate) {
54
- return newDate.toDate().getTimezoneOffset() - getMaxUtcOffset(newDate.year());
55
- }
56
34
  function handleChangeTime(time, valid) {
57
35
  if (valid) {
58
- const [hours, minutes] = time.split(':');
59
- const date = extractDate(timezone, timestamp);
60
- const dateTime = dayjsTz(timezone, timestamp)
61
- .set('date', date.date())
62
- .set('hour', parseInt(hours, 10))
63
- .set('minute', parseInt(minutes, 10))
64
- .set('second', 0)
65
- .set('millisecond', 0);
66
- onChange(dateTime.valueOf());
36
+ const [hours, minutes] = time.split(':').map(Number);
37
+ const newDate = dayjs.tz(timestamp, timezone).set('hours', hours).set('minutes', minutes);
38
+ onChange(newDate.valueOf(), true);
67
39
  }
68
40
  setTime(time);
69
41
  }
@@ -73,7 +45,7 @@ export default function DateTimeInput({ timestamp, timezone, warning, readOnly =
73
45
  }
74
46
  }
75
47
  return (React.createElement("span", { className: `di date-time-input ${warning ? 'with-warning' : ''}` },
76
- React.createElement(DateInput, { ...otherProps, value: extractDate(timezone, timestamp).valueOf(), onChange: handleChangeDate, readOnly: readOnly }),
48
+ React.createElement(DateInput, { ...otherProps, value: timestamp, timezone: timezone, onChange: handleChangeDate, readOnly: readOnly }),
77
49
  React.createElement(ValidatedTextInput, { "data-testid": "time-input", value: timeValue, onChange: handleChangeTime, pattern: /^(([01][0-9])|2[0-3]):[0-5][0-9]$/, required: true, invalidStyle: {
78
50
  borderColor: 'red'
79
51
  }, placeholder: "00:00", className: "time", onBlur: tryFixTime, disabled: readOnly })));
@@ -16,9 +16,7 @@ describe('DateInput.js', () => {
16
16
  });
17
17
  describe('Changing values via text input', () => {
18
18
  it('should render correct date typed into text input without "invalid" class', async () => {
19
- await act(async () => {
20
- render(React.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => React.createElement(DateInput, { value: date, onChange: (date) => setDate(date) })));
21
- });
19
+ render(React.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => React.createElement(DateInput, { value: date, onChange: (date) => setDate(date) })));
22
20
  await act(async () => {
23
21
  fireEvent.change(screen.getByTestId('date-input'), { target: { value: '02.02.2019' } });
24
22
  });
@@ -26,9 +24,7 @@ describe('DateInput.js', () => {
26
24
  expect(screen.getByTestId('date-input')).not.toHaveClass('invalid');
27
25
  });
28
26
  it('should render incorrect date typed into text input with "invalid" class', async () => {
29
- await act(async () => {
30
- render(React.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => React.createElement(DateInput, { value: date, onChange: (date) => setDate(date) })));
31
- });
27
+ render(React.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => React.createElement(DateInput, { value: date, onChange: (date) => setDate(date) })));
32
28
  await act(async () => {
33
29
  fireEvent.change(screen.getByTestId('date-input'), { target: { value: '02.02.201' } });
34
30
  });
@@ -36,9 +32,7 @@ describe('DateInput.js', () => {
36
32
  });
37
33
  it('should call onChange callback with parsed value of the input during the input is being changed when the value is valid', async () => {
38
34
  const onChange = vi.fn();
39
- await act(async () => {
40
- render(React.createElement(DateInput, { value: new Date('2019-02-01').getTime(), onChange: onChange }));
41
- });
35
+ render(React.createElement(DateInput, { value: new Date('2019-02-01').getTime(), onChange: onChange }));
42
36
  await act(async () => {
43
37
  fireEvent.change(screen.getByTestId('date-input'), { target: { value: '02.02.2019' } });
44
38
  });
@@ -47,18 +41,14 @@ describe('DateInput.js', () => {
47
41
  });
48
42
  it('should not call onChange callback while editing the input when the input value is invalid', async () => {
49
43
  const onChange = vi.fn();
50
- await act(async () => {
51
- render(React.createElement(DateInput, { value: new Date('2019-02-01').getTime(), onChange: onChange }));
52
- });
44
+ render(React.createElement(DateInput, { value: new Date('2019-02-01').getTime(), onChange: onChange }));
53
45
  await act(async () => {
54
46
  fireEvent.change(screen.getByTestId('date-input'), { target: { value: '02.02.201' } });
55
47
  });
56
48
  expect(onChange).not.toHaveBeenCalled();
57
49
  });
58
50
  it('should display the current value in the input field if the input value is invalid and the input became blurred', async () => {
59
- await act(async () => {
60
- render(React.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => React.createElement(DateInput, { value: date, onChange: (date) => setDate(date) })));
61
- });
51
+ render(React.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => React.createElement(DateInput, { value: date, onChange: (date) => setDate(date) })));
62
52
  await act(async () => {
63
53
  fireEvent.change(screen.getByTestId('date-input'), { target: { value: '02.02.201' } });
64
54
  fireEvent.blur(screen.getByTestId('date-input'));
@@ -66,16 +56,12 @@ describe('DateInput.js', () => {
66
56
  expect(screen.getByTestId('date-input')).toHaveValue('01.02.2019');
67
57
  });
68
58
  it('should display invalid date text in the input when the value is NaN', async () => {
69
- await act(async () => {
70
- render(React.createElement(DateInput, { value: NaN, onChange: () => { } }));
71
- });
59
+ render(React.createElement(DateInput, { value: NaN, onChange: () => { } }));
72
60
  expect(screen.getByTestId('date-input')).toHaveValue('dateInput.invalidDate');
73
61
  });
74
62
  it('should call onChange callback with [null, true] when empty string is set to input field', async () => {
75
63
  const onChange = vi.fn();
76
- await act(async () => {
77
- render(React.createElement(DateInput, { value: Date.now(), onChange: onChange }));
78
- });
64
+ render(React.createElement(DateInput, { value: Date.now(), onChange: onChange }));
79
65
  await act(async () => {
80
66
  fireEvent.change(screen.getByTestId('date-input'), { target: { value: '' } });
81
67
  });
@@ -1 +1 @@
1
- {"version":3,"file":"Inputs.d.ts","sourceRoot":"","sources":["../../../../../src/components/periodpicker/components/Inputs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyB,MAAM,OAAO,CAAC;AAE9C,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAMhC,aAAK,KAAK,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAC,EAAE,KAAK,qBA2FnF"}
1
+ {"version":3,"file":"Inputs.d.ts","sourceRoot":"","sources":["../../../../../src/components/periodpicker/components/Inputs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyB,MAAM,OAAO,CAAC;AAG9C,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAMhC,aAAK,KAAK,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAC,EAAE,KAAK,qBAiGnF"}
@@ -1,4 +1,5 @@
1
1
  import React, { useRef, useState } from 'react';
2
+ import { isEqual } from 'lodash';
2
3
  import DateTimeInput from '../../input/DateTimeInput';
3
4
  const ONE_HOUR = 60 * 60 * 1000;
4
5
  const TOGGLE_TIMEOUT = 300;
@@ -25,10 +26,13 @@ export default function Inputs({ period, onChange, readOnly, locale, timezone })
25
26
  if (timestamp) {
26
27
  const incorrect = period.tsEnd && timestamp > period.tsEnd;
27
28
  const tsEnd = incorrect ? timestamp + ONE_HOUR : period.tsEnd;
28
- onChange({
29
+ const newPeriod = {
29
30
  tsStart: timestamp,
30
31
  tsEnd
31
- });
32
+ };
33
+ if (!isEqual(newPeriod, period)) {
34
+ onChange(newPeriod);
35
+ }
32
36
  if (!fromTextField) {
33
37
  openEndDate();
34
38
  }
@@ -41,10 +45,13 @@ export default function Inputs({ period, onChange, readOnly, locale, timezone })
41
45
  if (timestamp) {
42
46
  const incorrect = period.tsStart && timestamp < period.tsStart;
43
47
  const tsStart = incorrect ? timestamp - ONE_HOUR : period.tsStart;
44
- onChange({
48
+ const newPeriod = {
45
49
  tsStart,
46
50
  tsEnd: timestamp
47
- });
51
+ };
52
+ if (!isEqual(newPeriod, period)) {
53
+ onChange(newPeriod);
54
+ }
48
55
  if (incorrect) {
49
56
  showWarning('date-from');
50
57
  }
@@ -5,6 +5,8 @@ import '../../assets/react-datepicker-custom.css';
5
5
  declare type DateInputProps = {
6
6
  /** the current value (millis since 1970-01-01 or formatted string) */
7
7
  value: number | string | null | undefined;
8
+ /** (optional) timezone */
9
+ timezone?: string;
8
10
  /** callback function that is executed when the user picks a date */
9
11
  onChange: (date: number | null | undefined, fromTextField?: boolean) => void;
10
12
  /** makes the input field clearable */
@@ -23,6 +25,6 @@ export declare type Props = Omit<ReactDatePickerProps, keyof DateInputProps> & D
23
25
  * DateInput is used to let users set a date conveniently.
24
26
  * Usually the user's locale is used for displaying the correct date format.
25
27
  */
26
- export default function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onChange, locale, ...otherProps }: Props): React.JSX.Element;
28
+ export default function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onChange, locale, timezone, ...otherProps }: Props): React.JSX.Element;
27
29
  export {};
28
30
  //# sourceMappingURL=DateInput.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DateInput.d.ts","sourceRoot":"","sources":["../../../../../src/components/input/DateInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkD,MAAM,OAAO,CAAC;AACvE,OAAmB,EAAC,oBAAoB,EAAiB,MAAM,kBAAkB,CAAC;AAalF,OAAO,4CAA4C,CAAC;AACpD,OAAO,0CAA0C,CAAC;AAwClD,aAAK,cAAc,GAAG;IACrB,sEAAsE;IACtE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAE1C,oEAAoE;IACpE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAE7E,sCAAsC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,sCAAsC;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,oBAAY,KAAK,GAAG,IAAI,CAAC,oBAAoB,EAAE,MAAM,cAAc,CAAC,GAAG,cAAc,CAAC;AAqBtF;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EACjC,KAAK,EACL,SAAS,EACT,MAAM,EACN,QAAQ,EACR,gBAAgB,EAChB,QAAQ,EACR,MAAM,EACN,GAAG,UAAU,EACb,EAAE,KAAK,qBAmKP"}
1
+ {"version":3,"file":"DateInput.d.ts","sourceRoot":"","sources":["../../../../../src/components/input/DateInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkD,MAAM,OAAO,CAAC;AAGvE,OAAmB,EAAC,oBAAoB,EAAiB,MAAM,kBAAkB,CAAC;AAalF,OAAO,4CAA4C,CAAC;AACpD,OAAO,0CAA0C,CAAC;AA0ClD,aAAK,cAAc,GAAG;IACrB,sEAAsE;IACtE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAE1C,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,oEAAoE;IACpE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAE7E,sCAAsC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,sCAAsC;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,oBAAY,KAAK,GAAG,IAAI,CAAC,oBAAoB,EAAE,MAAM,cAAc,CAAC,GAAG,cAAc,CAAC;AA2CtF;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EACjC,KAAK,EACL,SAAS,EACT,MAAM,EACN,QAAQ,EACR,gBAAgB,EAChB,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,GAAG,UAAU,EACb,EAAE,KAAK,qBAyKP"}
@@ -1,4 +1,6 @@
1
1
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import timezone from 'dayjs/plugin/timezone';
3
+ import utc from 'dayjs/plugin/utc';
2
4
  import DatePicker, { registerLocale } from 'react-datepicker';
3
5
  import { debounce } from 'lodash';
4
6
  import dayjs from 'dayjs';
@@ -13,6 +15,8 @@ import { Button, Icon, Popup } from '@deviceinsight/ng-ui-basic-components';
13
15
  import 'react-datepicker/dist/react-datepicker.css';
14
16
  import '../../assets/react-datepicker-custom.css';
15
17
  import { isLocaleSupported } from '../../utils/dayjsLocales';
18
+ dayjs.extend(utc);
19
+ dayjs.extend(timezone);
16
20
  dayjs.extend(localeData);
17
21
  dayjs.extend(localizedFormat);
18
22
  dayjs.extend(customParseFormat);
@@ -48,29 +52,40 @@ async function changeDayjsLocale(locale) {
48
52
  }
49
53
  }
50
54
  function isInputValid(value, pattern) {
51
- if (value == null || (typeof value === 'number' && !isNaN(value)) || value == '') {
55
+ if (value == null || value === '') {
52
56
  return true;
53
57
  }
58
+ if (typeof value === 'number') {
59
+ return !isNaN(value);
60
+ }
54
61
  if (typeof value === 'string') {
55
62
  return dayjs(value, pattern, true).isValid();
56
63
  }
57
64
  return false;
58
65
  }
59
- function inputToDate(value, pattern) {
60
- if (value == null || value === '' || !isInputValid(value, pattern))
61
- return null;
66
+ function inputToDate(value, pattern, timezone) {
62
67
  if (typeof value === 'number') {
63
68
  return new Date(value);
64
69
  }
65
- else {
66
- return dayjs(value, pattern, true).toDate();
70
+ if (typeof value === 'string' && dayjs(value, pattern, true).isValid()) {
71
+ return dayjs.tz(value, pattern, timezone).toDate();
72
+ }
73
+ return null;
74
+ }
75
+ function inputToTzDate(value, pattern, timezone) {
76
+ if (typeof value === 'number') {
77
+ return dayjs.tz(value, timezone);
78
+ }
79
+ if (typeof value === 'string' && dayjs(value, pattern, true).isValid()) {
80
+ return dayjs.tz(value, pattern, timezone);
67
81
  }
82
+ return null;
68
83
  }
69
84
  /**
70
85
  * DateInput is used to let users set a date conveniently.
71
86
  * Usually the user's locale is used for displaying the correct date format.
72
87
  */
73
- export default function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onChange, locale, ...otherProps }) {
88
+ export default function DateInput({ value, clearable, inline, readOnly, wrapperClassName, onChange, locale, timezone, ...otherProps }) {
74
89
  const [pattern, setPattern] = useState(dayjs.localeData().longDateFormat('L'));
75
90
  const { t } = useTranslation();
76
91
  const [valid, setValid] = useState(false);
@@ -94,10 +109,9 @@ export default function DateInput({ value, clearable, inline, readOnly, wrapperC
94
109
  });
95
110
  }
96
111
  else {
97
- const date = typeof value === 'number' ? dayjs(value) : dayjs(value, pattern, true);
98
- return date.format(pattern);
112
+ return typeof value === 'number' ? dayjs.tz(value, timezone).format(pattern) : value;
99
113
  }
100
- }, [pattern, t]);
114
+ }, [pattern, t, timezone]);
101
115
  useEffect(() => {
102
116
  if (value == null) {
103
117
  setInputText('');
@@ -116,10 +130,17 @@ export default function DateInput({ value, clearable, inline, readOnly, wrapperC
116
130
  const handleInputTextChange = (text) => {
117
131
  setInputText(text);
118
132
  if (text) {
119
- const date = dayjs(text, pattern, true);
120
- const valid = date.isValid();
133
+ const valid = dayjs(text, pattern, true).isValid();
121
134
  if (valid) {
122
- onChange(date.valueOf(), true);
135
+ const prevDate = inputToTzDate(value, pattern, timezone);
136
+ const newDate = dayjs.tz(text, pattern, timezone ?? dayjs.tz.guess());
137
+ if (prevDate) {
138
+ newDate.set('hour', prevDate.hour());
139
+ newDate.set('minute', prevDate.minute());
140
+ newDate.set('second', prevDate.second());
141
+ newDate.set('millisecond', prevDate.millisecond());
142
+ }
143
+ onChange(newDate.valueOf(), true);
123
144
  }
124
145
  setValid(valid);
125
146
  }
@@ -137,10 +158,10 @@ export default function DateInput({ value, clearable, inline, readOnly, wrapperC
137
158
  const handlePopupHide = () => setPopupActive(false);
138
159
  const handleClear = () => handleInputTextChange('');
139
160
  if (inline) {
140
- return (React.createElement(DatePicker, { ...otherProps, onChange: handlePickerChange, selected: inputToDate(value, pattern), locale: locale ?? i18n.language, isClearable: clearable, placeholderText: pattern, className: cx('date', { invalid: !valid }), inline: true }));
161
+ return (React.createElement(DatePicker, { ...otherProps, onChange: handlePickerChange, selected: inputToDate(value, pattern, timezone), locale: locale ?? i18n.language, isClearable: clearable, placeholderText: pattern, className: cx('date', { invalid: !valid }), inline: true }));
141
162
  }
142
163
  if (readOnly) {
143
- return (React.createElement(DatePicker, { ...otherProps, onChange: () => { }, selected: inputToDate(value, pattern), locale: locale ?? i18n.language, customInput: React.createElement("span", null,
164
+ return (React.createElement(DatePicker, { ...otherProps, onChange: () => { }, selected: inputToDate(value, pattern, timezone), locale: locale ?? i18n.language, customInput: React.createElement("span", null,
144
165
  React.createElement("input", { type: "text", value: formattedDate(value), readOnly: true })), placeholderText: pattern, readOnly: true }));
145
166
  }
146
167
  return (React.createElement(Popup, { handler: React.createElement("span", { className: "di date-input-wrapper" },
@@ -153,5 +174,5 @@ export default function DateInput({ value, clearable, inline, readOnly, wrapperC
153
174
  React.createElement("div", { className: "di date-picker-popup-wrapper", tabIndex: 0, onFocus: () => {
154
175
  debouncedSetPopupActive(true);
155
176
  } },
156
- React.createElement(DatePicker, { ...otherProps, selected: inputToDate(value, pattern), onChange: handlePickerChange, locale: locale ?? i18n.language, isClearable: clearable, placeholderText: pattern, className: cx('date', { invalid: !valid }), inline: true }))));
177
+ React.createElement(DatePicker, { ...otherProps, selected: inputToDate(value, pattern, timezone), onChange: handlePickerChange, locale: locale ?? i18n.language, isClearable: clearable, placeholderText: pattern, className: cx('date', { invalid: !valid }), inline: true }))));
157
178
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DateTimeInput.d.ts","sourceRoot":"","sources":["../../../../../src/components/input/DateTimeInput.tsx"],"names":[],"mappings":"AACA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAwBjD,oBAAY,KAAK,GAAG;IACnB,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAElB,oEAAoE;IACpE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAE7E,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,OAAO,CAAC,EAAE,IAAI,CAAC;IAEf,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EACrC,SAAS,EACT,QAAQ,EACR,OAAO,EACP,QAAgB,EAChB,QAAQ,EACR,GAAG,UAAU,EACb,EAAE,KAAK,qBAgFP"}
1
+ {"version":3,"file":"DateTimeInput.d.ts","sourceRoot":"","sources":["../../../../../src/components/input/DateTimeInput.tsx"],"names":[],"mappings":"AACA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAcjD,oBAAY,KAAK,GAAG;IACnB,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAElB,oEAAoE;IACpE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAE7E,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,OAAO,CAAC,EAAE,IAAI,CAAC;IAEf,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EACrC,SAAS,EACT,QAAQ,EACR,OAAO,EACP,QAAgB,EAChB,QAAQ,EACR,GAAG,UAAU,EACb,EAAE,KAAK,qBA4DP"}
@@ -1,69 +1,41 @@
1
1
  import dayjs from 'dayjs';
2
2
  import React, { useEffect, useState } from 'react';
3
- import { dayjsTz } from '../periodpicker/periods';
4
3
  import DateInput from './DateInput';
5
4
  import ValidatedTextInput from './ValidatedTextInput';
6
- import { toInteger } from 'lodash';
7
- const extractTime = (timezone, date) => {
5
+ const formatTime = (timezone, date) => {
8
6
  try {
9
- return dayjsTz(timezone, date).format('HH:mm');
7
+ return dayjs.tz(date, timezone).format('HH:mm');
10
8
  }
11
9
  catch (error) {
12
10
  console.error('DateTimeInput: Error while extracting time from date', error);
13
11
  return '00:00';
14
12
  }
15
13
  };
16
- const extractDate = (timezone, date) => {
17
- try {
18
- return dayjs(dayjsTz(timezone, date).format('YYYY-MM-DD'));
19
- }
20
- catch (error) {
21
- console.error('DateTimeInput: Error while extracting time from date', error);
22
- return dayjs(new Date());
23
- }
24
- };
25
14
  /**
26
15
  * Extends DateInput but also displaying the current time.
27
16
  * Usually the user's locale is used for displaying the correct date format.
28
17
  */
29
18
  export default function DateTimeInput({ timestamp, timezone, warning, readOnly = false, onChange, ...otherProps }) {
30
- const [time, setTime] = useState(extractTime(timezone, timestamp));
31
- const timeValue = time == null ? extractTime(timezone, timestamp) : time;
19
+ const [time, setTime] = useState(formatTime(timezone, timestamp));
20
+ const timeValue = time == null ? formatTime(timezone, timestamp) : time;
32
21
  useEffect(() => {
33
- setTime(extractTime(timezone, timestamp));
22
+ setTime(formatTime(timezone, timestamp));
34
23
  }, [timezone, timestamp]);
35
24
  function handleChangeDate(date, fromTextField) {
36
- const oldDate = dayjsTz(timezone, timestamp);
37
- const dateFix = dayjs(date);
38
- let newDate = dayjsTz(timezone, date)
39
- .set('day', dateFix.day())
40
- .set('month', dateFix.month())
41
- .set('year', dateFix.year())
42
- .set('hour', toInteger(oldDate.hour()))
43
- .set('minute', toInteger(oldDate.minute()));
44
- const dstOffset = getLocalDstOffset(newDate);
45
- newDate = newDate.subtract(dstOffset, 'minutes');
25
+ const currentDate = dayjs.tz(timestamp, timezone);
26
+ const newDate = dayjs
27
+ .tz(date, timezone)
28
+ .set('hour', currentDate.hour())
29
+ .set('minute', currentDate.minute())
30
+ .set('second', currentDate.second())
31
+ .set('millisecond', currentDate.millisecond());
46
32
  onChange(newDate.valueOf(), fromTextField);
47
33
  }
48
- function getMaxUtcOffset(year) {
49
- const jan = new Date(`${year}-01-01`).getTimezoneOffset();
50
- const jul = new Date(`${year}-07-01`).getTimezoneOffset();
51
- return Math.min(jan, jul);
52
- }
53
- function getLocalDstOffset(newDate) {
54
- return newDate.toDate().getTimezoneOffset() - getMaxUtcOffset(newDate.year());
55
- }
56
34
  function handleChangeTime(time, valid) {
57
35
  if (valid) {
58
- const [hours, minutes] = time.split(':');
59
- const date = extractDate(timezone, timestamp);
60
- const dateTime = dayjsTz(timezone, timestamp)
61
- .set('date', date.date())
62
- .set('hour', parseInt(hours, 10))
63
- .set('minute', parseInt(minutes, 10))
64
- .set('second', 0)
65
- .set('millisecond', 0);
66
- onChange(dateTime.valueOf());
36
+ const [hours, minutes] = time.split(':').map(Number);
37
+ const newDate = dayjs.tz(timestamp, timezone).set('hours', hours).set('minutes', minutes);
38
+ onChange(newDate.valueOf(), true);
67
39
  }
68
40
  setTime(time);
69
41
  }
@@ -73,7 +45,7 @@ export default function DateTimeInput({ timestamp, timezone, warning, readOnly =
73
45
  }
74
46
  }
75
47
  return (React.createElement("span", { className: `di date-time-input ${warning ? 'with-warning' : ''}` },
76
- React.createElement(DateInput, { ...otherProps, value: extractDate(timezone, timestamp).valueOf(), onChange: handleChangeDate, readOnly: readOnly }),
48
+ React.createElement(DateInput, { ...otherProps, value: timestamp, timezone: timezone, onChange: handleChangeDate, readOnly: readOnly }),
77
49
  React.createElement(ValidatedTextInput, { "data-testid": "time-input", value: timeValue, onChange: handleChangeTime, pattern: /^(([01][0-9])|2[0-3]):[0-5][0-9]$/, required: true, invalidStyle: {
78
50
  borderColor: 'red'
79
51
  }, placeholder: "00:00", className: "time", onBlur: tryFixTime, disabled: readOnly })));
@@ -16,9 +16,7 @@ describe('DateInput.js', () => {
16
16
  });
17
17
  describe('Changing values via text input', () => {
18
18
  it('should render correct date typed into text input without "invalid" class', async () => {
19
- await act(async () => {
20
- render(React.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => React.createElement(DateInput, { value: date, onChange: (date) => setDate(date) })));
21
- });
19
+ render(React.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => React.createElement(DateInput, { value: date, onChange: (date) => setDate(date) })));
22
20
  await act(async () => {
23
21
  fireEvent.change(screen.getByTestId('date-input'), { target: { value: '02.02.2019' } });
24
22
  });
@@ -26,9 +24,7 @@ describe('DateInput.js', () => {
26
24
  expect(screen.getByTestId('date-input')).not.toHaveClass('invalid');
27
25
  });
28
26
  it('should render incorrect date typed into text input with "invalid" class', async () => {
29
- await act(async () => {
30
- render(React.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => React.createElement(DateInput, { value: date, onChange: (date) => setDate(date) })));
31
- });
27
+ render(React.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => React.createElement(DateInput, { value: date, onChange: (date) => setDate(date) })));
32
28
  await act(async () => {
33
29
  fireEvent.change(screen.getByTestId('date-input'), { target: { value: '02.02.201' } });
34
30
  });
@@ -36,9 +32,7 @@ describe('DateInput.js', () => {
36
32
  });
37
33
  it('should call onChange callback with parsed value of the input during the input is being changed when the value is valid', async () => {
38
34
  const onChange = vi.fn();
39
- await act(async () => {
40
- render(React.createElement(DateInput, { value: new Date('2019-02-01').getTime(), onChange: onChange }));
41
- });
35
+ render(React.createElement(DateInput, { value: new Date('2019-02-01').getTime(), onChange: onChange }));
42
36
  await act(async () => {
43
37
  fireEvent.change(screen.getByTestId('date-input'), { target: { value: '02.02.2019' } });
44
38
  });
@@ -47,18 +41,14 @@ describe('DateInput.js', () => {
47
41
  });
48
42
  it('should not call onChange callback while editing the input when the input value is invalid', async () => {
49
43
  const onChange = vi.fn();
50
- await act(async () => {
51
- render(React.createElement(DateInput, { value: new Date('2019-02-01').getTime(), onChange: onChange }));
52
- });
44
+ render(React.createElement(DateInput, { value: new Date('2019-02-01').getTime(), onChange: onChange }));
53
45
  await act(async () => {
54
46
  fireEvent.change(screen.getByTestId('date-input'), { target: { value: '02.02.201' } });
55
47
  });
56
48
  expect(onChange).not.toHaveBeenCalled();
57
49
  });
58
50
  it('should display the current value in the input field if the input value is invalid and the input became blurred', async () => {
59
- await act(async () => {
60
- render(React.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => React.createElement(DateInput, { value: date, onChange: (date) => setDate(date) })));
61
- });
51
+ render(React.createElement(DateStateProvider, { initialValue: new Date('2019-02-01').getTime() }, (date, setDate) => React.createElement(DateInput, { value: date, onChange: (date) => setDate(date) })));
62
52
  await act(async () => {
63
53
  fireEvent.change(screen.getByTestId('date-input'), { target: { value: '02.02.201' } });
64
54
  fireEvent.blur(screen.getByTestId('date-input'));
@@ -66,16 +56,12 @@ describe('DateInput.js', () => {
66
56
  expect(screen.getByTestId('date-input')).toHaveValue('01.02.2019');
67
57
  });
68
58
  it('should display invalid date text in the input when the value is NaN', async () => {
69
- await act(async () => {
70
- render(React.createElement(DateInput, { value: NaN, onChange: () => { } }));
71
- });
59
+ render(React.createElement(DateInput, { value: NaN, onChange: () => { } }));
72
60
  expect(screen.getByTestId('date-input')).toHaveValue('dateInput.invalidDate');
73
61
  });
74
62
  it('should call onChange callback with [null, true] when empty string is set to input field', async () => {
75
63
  const onChange = vi.fn();
76
- await act(async () => {
77
- render(React.createElement(DateInput, { value: Date.now(), onChange: onChange }));
78
- });
64
+ render(React.createElement(DateInput, { value: Date.now(), onChange: onChange }));
79
65
  await act(async () => {
80
66
  fireEvent.change(screen.getByTestId('date-input'), { target: { value: '' } });
81
67
  });
@@ -1 +1 @@
1
- {"version":3,"file":"Inputs.d.ts","sourceRoot":"","sources":["../../../../../../src/components/periodpicker/components/Inputs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyB,MAAM,OAAO,CAAC;AAE9C,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAMhC,aAAK,KAAK,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAC,EAAE,KAAK,qBA2FnF"}
1
+ {"version":3,"file":"Inputs.d.ts","sourceRoot":"","sources":["../../../../../../src/components/periodpicker/components/Inputs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyB,MAAM,OAAO,CAAC;AAG9C,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAMhC,aAAK,KAAK,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAC,EAAE,KAAK,qBAiGnF"}
@@ -1,4 +1,5 @@
1
1
  import React, { useRef, useState } from 'react';
2
+ import { isEqual } from 'lodash';
2
3
  import DateTimeInput from '../../input/DateTimeInput';
3
4
  const ONE_HOUR = 60 * 60 * 1000;
4
5
  const TOGGLE_TIMEOUT = 300;
@@ -25,10 +26,13 @@ export default function Inputs({ period, onChange, readOnly, locale, timezone })
25
26
  if (timestamp) {
26
27
  const incorrect = period.tsEnd && timestamp > period.tsEnd;
27
28
  const tsEnd = incorrect ? timestamp + ONE_HOUR : period.tsEnd;
28
- onChange({
29
+ const newPeriod = {
29
30
  tsStart: timestamp,
30
31
  tsEnd
31
- });
32
+ };
33
+ if (!isEqual(newPeriod, period)) {
34
+ onChange(newPeriod);
35
+ }
32
36
  if (!fromTextField) {
33
37
  openEndDate();
34
38
  }
@@ -41,10 +45,13 @@ export default function Inputs({ period, onChange, readOnly, locale, timezone })
41
45
  if (timestamp) {
42
46
  const incorrect = period.tsStart && timestamp < period.tsStart;
43
47
  const tsStart = incorrect ? timestamp - ONE_HOUR : period.tsStart;
44
- onChange({
48
+ const newPeriod = {
45
49
  tsStart,
46
50
  tsEnd: timestamp
47
- });
51
+ };
52
+ if (!isEqual(newPeriod, period)) {
53
+ onChange(newPeriod);
54
+ }
48
55
  if (incorrect) {
49
56
  showWarning('date-from');
50
57
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deviceinsight/ng-ui-components",
3
- "version": "6.26.0",
3
+ "version": "6.27.0",
4
4
  "private": false,
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "repository": {
@@ -43,7 +43,7 @@
43
43
  "@dnd-kit/sortable": "^7.0.1",
44
44
  "@svgdotjs/svg.js": "^3.0.16",
45
45
  "classnames": "^2.3.1",
46
- "dayjs": "^1.11.0",
46
+ "dayjs": "^1.11.18",
47
47
  "highcharts": "^10.2.1",
48
48
  "highcharts-react-official": "^3.1.0",
49
49
  "lodash": "^4.17.21",