@contentful/field-editor-date 2.0.11 → 2.0.12

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.
@@ -56,6 +56,7 @@ function _interop_require_wildcard(obj, nodeInterop) {
56
56
  const validInputFormats = [
57
57
  'hh:mm a',
58
58
  'h:mm a',
59
+ 'HH:mm',
59
60
  'hh:mm',
60
61
  'kk:mm',
61
62
  'k:mm',
@@ -98,8 +99,9 @@ const TimepickerInput = ({ disabled, uses12hClock, time = '12:00', ampm = 'AM',
98
99
  const parsedTime = parseRawInput(selectedTime);
99
100
  const value = parsedTime ?? getDefaultTime();
100
101
  setSelectedTime(formatToString(uses12hClock, value));
102
+ const timeStr = uses12hClock ? (0, _datefns.format)(value, 'hh:mm') : (0, _datefns.format)(value, 'HH:mm');
101
103
  onChange({
102
- time: (0, _datefns.format)(value, 'hh:mm'),
104
+ time: timeStr,
103
105
  ampm: (0, _datefns.format)(value, 'a').toUpperCase()
104
106
  });
105
107
  }, [
@@ -5,7 +5,13 @@ Object.defineProperty(exports, "__esModule", {
5
5
  const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
6
6
  require("@testing-library/jest-dom/extend-expect");
7
7
  const _react1 = require("@testing-library/react");
8
+ const _userevent = /*#__PURE__*/ _interop_require_default(require("@testing-library/user-event"));
8
9
  const _TimepickerInput = require("./TimepickerInput");
10
+ function _interop_require_default(obj) {
11
+ return obj && obj.__esModule ? obj : {
12
+ default: obj
13
+ };
14
+ }
9
15
  function _getRequireWildcardCache(nodeInterop) {
10
16
  if (typeof WeakMap !== "function") return null;
11
17
  var cacheBabelInterop = new WeakMap();
@@ -72,4 +78,127 @@ describe('TimepickerInput', ()=>{
72
78
  }));
73
79
  expect(getByTestId('time-input')).toHaveValue('23:00');
74
80
  });
81
+ describe('onChange on blur — 24h mode', ()=>{
82
+ it('emits 19:00 (not 07:00) when user types 19:00 in 24h mode', ()=>{
83
+ const onChange = jest.fn();
84
+ const { getByTestId } = (0, _react1.render)(/*#__PURE__*/ _react.createElement(_TimepickerInput.TimepickerInput, {
85
+ disabled: false,
86
+ uses12hClock: false,
87
+ time: "19:00",
88
+ ampm: "PM",
89
+ onChange: onChange
90
+ }));
91
+ _userevent.default.clear(getByTestId('time-input'));
92
+ _userevent.default.type(getByTestId('time-input'), '19:00');
93
+ _userevent.default.tab();
94
+ expect(onChange).toHaveBeenCalledWith({
95
+ time: '19:00',
96
+ ampm: 'PM'
97
+ });
98
+ });
99
+ it('emits 23:59 (not 11:59) when user types 23:59 in 24h mode', ()=>{
100
+ const onChange = jest.fn();
101
+ const { getByTestId } = (0, _react1.render)(/*#__PURE__*/ _react.createElement(_TimepickerInput.TimepickerInput, {
102
+ disabled: false,
103
+ uses12hClock: false,
104
+ time: "23:59",
105
+ ampm: "PM",
106
+ onChange: onChange
107
+ }));
108
+ _userevent.default.clear(getByTestId('time-input'));
109
+ _userevent.default.type(getByTestId('time-input'), '23:59');
110
+ _userevent.default.tab();
111
+ expect(onChange).toHaveBeenCalledWith({
112
+ time: '23:59',
113
+ ampm: 'PM'
114
+ });
115
+ });
116
+ it('emits 00:00 when user types 00:00 in 24h mode', ()=>{
117
+ const onChange = jest.fn();
118
+ const { getByTestId } = (0, _react1.render)(/*#__PURE__*/ _react.createElement(_TimepickerInput.TimepickerInput, {
119
+ disabled: false,
120
+ uses12hClock: false,
121
+ time: "00:00",
122
+ ampm: "AM",
123
+ onChange: onChange
124
+ }));
125
+ _userevent.default.clear(getByTestId('time-input'));
126
+ _userevent.default.type(getByTestId('time-input'), '00:00');
127
+ _userevent.default.tab();
128
+ expect(onChange).toHaveBeenCalledWith({
129
+ time: '00:00',
130
+ ampm: 'AM'
131
+ });
132
+ });
133
+ it('emits 12:00 (noon) correctly in 24h mode', ()=>{
134
+ const onChange = jest.fn();
135
+ const { getByTestId } = (0, _react1.render)(/*#__PURE__*/ _react.createElement(_TimepickerInput.TimepickerInput, {
136
+ disabled: false,
137
+ uses12hClock: false,
138
+ time: "12:00",
139
+ ampm: "PM",
140
+ onChange: onChange
141
+ }));
142
+ _userevent.default.clear(getByTestId('time-input'));
143
+ _userevent.default.type(getByTestId('time-input'), '12:00');
144
+ _userevent.default.tab();
145
+ expect(onChange).toHaveBeenCalledWith({
146
+ time: '12:00',
147
+ ampm: 'PM'
148
+ });
149
+ });
150
+ it('emits 13:00 (not 01:00) when user types 13:00 in 24h mode', ()=>{
151
+ const onChange = jest.fn();
152
+ const { getByTestId } = (0, _react1.render)(/*#__PURE__*/ _react.createElement(_TimepickerInput.TimepickerInput, {
153
+ disabled: false,
154
+ uses12hClock: false,
155
+ time: "13:00",
156
+ ampm: "PM",
157
+ onChange: onChange
158
+ }));
159
+ _userevent.default.clear(getByTestId('time-input'));
160
+ _userevent.default.type(getByTestId('time-input'), '13:00');
161
+ _userevent.default.tab();
162
+ expect(onChange).toHaveBeenCalledWith({
163
+ time: '13:00',
164
+ ampm: 'PM'
165
+ });
166
+ });
167
+ });
168
+ describe('onChange on blur — 12h mode', ()=>{
169
+ it('emits 07:00 AM correctly in 12h mode', ()=>{
170
+ const onChange = jest.fn();
171
+ const { getByTestId } = (0, _react1.render)(/*#__PURE__*/ _react.createElement(_TimepickerInput.TimepickerInput, {
172
+ disabled: false,
173
+ uses12hClock: true,
174
+ time: "07:00",
175
+ ampm: "AM",
176
+ onChange: onChange
177
+ }));
178
+ _userevent.default.clear(getByTestId('time-input'));
179
+ _userevent.default.type(getByTestId('time-input'), '07:00 AM');
180
+ _userevent.default.tab();
181
+ expect(onChange).toHaveBeenCalledWith({
182
+ time: '07:00',
183
+ ampm: 'AM'
184
+ });
185
+ });
186
+ it('emits 07:00 PM correctly in 12h mode', ()=>{
187
+ const onChange = jest.fn();
188
+ const { getByTestId } = (0, _react1.render)(/*#__PURE__*/ _react.createElement(_TimepickerInput.TimepickerInput, {
189
+ disabled: false,
190
+ uses12hClock: true,
191
+ time: "07:00",
192
+ ampm: "PM",
193
+ onChange: onChange
194
+ }));
195
+ _userevent.default.clear(getByTestId('time-input'));
196
+ _userevent.default.type(getByTestId('time-input'), '07:00 PM');
197
+ _userevent.default.tab();
198
+ expect(onChange).toHaveBeenCalledWith({
199
+ time: '07:00',
200
+ ampm: 'PM'
201
+ });
202
+ });
203
+ });
75
204
  });
@@ -380,4 +380,42 @@ describe('date utils', ()=>{
380
380
  expect((0, _date.getDefaultAMPM)()).toBe('AM');
381
381
  });
382
382
  });
383
+ describe('buildFieldValue — date does not shift due to timezone offset', ()=>{
384
+ it('April 27 23:00-02:00 round-trips as April 27 (UTC would parse as April 28 01:00Z)', ()=>{
385
+ const result = (0, _date.userInputFromDatetime)({
386
+ value: '2026-04-27T23:00-02:00',
387
+ uses12hClock: false
388
+ });
389
+ const built = (0, _date.buildFieldValue)({
390
+ data: result,
391
+ usesTime: true,
392
+ usesTimezone: true
393
+ });
394
+ expect(built.valid).toBe('2026-04-27T23:00-02:00');
395
+ });
396
+ it('April 28 01:00+02:00 round-trips as April 28 (UTC would parse as April 27 23:00Z)', ()=>{
397
+ const result = (0, _date.userInputFromDatetime)({
398
+ value: '2026-04-28T01:00+02:00',
399
+ uses12hClock: false
400
+ });
401
+ const built = (0, _date.buildFieldValue)({
402
+ data: result,
403
+ usesTime: true,
404
+ usesTimezone: true
405
+ });
406
+ expect(built.valid).toBe('2026-04-28T01:00+02:00');
407
+ });
408
+ it('preserves date for 23:59-01:00 (UTC: April 28 00:59Z → would show as April 28)', ()=>{
409
+ const result = (0, _date.userInputFromDatetime)({
410
+ value: '2026-06-15T23:59-01:00',
411
+ uses12hClock: false
412
+ });
413
+ const built = (0, _date.buildFieldValue)({
414
+ data: result,
415
+ usesTime: true,
416
+ usesTimezone: true
417
+ });
418
+ expect(built.valid).toBe('2026-06-15T23:59-01:00');
419
+ });
420
+ });
383
421
  });
@@ -33,10 +33,8 @@ function parseUtcOffset(datetimeString) {
33
33
  return match[1] === 'Z' ? '+00:00' : match[1];
34
34
  }
35
35
  function fieldValueToDate(datetimeString) {
36
- if (!datetimeString) {
37
- return null;
38
- }
39
- const date = (0, _datefns.parseISO)(datetimeString);
36
+ if (!datetimeString) return null;
37
+ const date = (0, _datefns.parse)(datetimeString.slice(0, 10), 'yyyy-MM-dd', new Date(0));
40
38
  return (0, _datefns.isValid)(date) ? date : null;
41
39
  }
42
40
  function timeFromUserInput(input) {
@@ -5,6 +5,7 @@ import { parse, format, isValid } from 'date-fns';
5
5
  const validInputFormats = [
6
6
  'hh:mm a',
7
7
  'h:mm a',
8
+ 'HH:mm',
8
9
  'hh:mm',
9
10
  'kk:mm',
10
11
  'k:mm',
@@ -47,8 +48,9 @@ export const TimepickerInput = ({ disabled, uses12hClock, time = '12:00', ampm =
47
48
  const parsedTime = parseRawInput(selectedTime);
48
49
  const value = parsedTime ?? getDefaultTime();
49
50
  setSelectedTime(formatToString(uses12hClock, value));
51
+ const timeStr = uses12hClock ? format(value, 'hh:mm') : format(value, 'HH:mm');
50
52
  onChange({
51
- time: format(value, 'hh:mm'),
53
+ time: timeStr,
52
54
  ampm: format(value, 'a').toUpperCase()
53
55
  });
54
56
  }, [
@@ -1,6 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import '@testing-library/jest-dom/extend-expect';
3
3
  import { cleanup, configure, render } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
4
5
  import { TimepickerInput } from './TimepickerInput';
5
6
  configure({
6
7
  testIdAttribute: 'data-test-id'
@@ -27,4 +28,127 @@ describe('TimepickerInput', ()=>{
27
28
  }));
28
29
  expect(getByTestId('time-input')).toHaveValue('23:00');
29
30
  });
31
+ describe('onChange on blur — 24h mode', ()=>{
32
+ it('emits 19:00 (not 07:00) when user types 19:00 in 24h mode', ()=>{
33
+ const onChange = jest.fn();
34
+ const { getByTestId } = render(/*#__PURE__*/ React.createElement(TimepickerInput, {
35
+ disabled: false,
36
+ uses12hClock: false,
37
+ time: "19:00",
38
+ ampm: "PM",
39
+ onChange: onChange
40
+ }));
41
+ userEvent.clear(getByTestId('time-input'));
42
+ userEvent.type(getByTestId('time-input'), '19:00');
43
+ userEvent.tab();
44
+ expect(onChange).toHaveBeenCalledWith({
45
+ time: '19:00',
46
+ ampm: 'PM'
47
+ });
48
+ });
49
+ it('emits 23:59 (not 11:59) when user types 23:59 in 24h mode', ()=>{
50
+ const onChange = jest.fn();
51
+ const { getByTestId } = render(/*#__PURE__*/ React.createElement(TimepickerInput, {
52
+ disabled: false,
53
+ uses12hClock: false,
54
+ time: "23:59",
55
+ ampm: "PM",
56
+ onChange: onChange
57
+ }));
58
+ userEvent.clear(getByTestId('time-input'));
59
+ userEvent.type(getByTestId('time-input'), '23:59');
60
+ userEvent.tab();
61
+ expect(onChange).toHaveBeenCalledWith({
62
+ time: '23:59',
63
+ ampm: 'PM'
64
+ });
65
+ });
66
+ it('emits 00:00 when user types 00:00 in 24h mode', ()=>{
67
+ const onChange = jest.fn();
68
+ const { getByTestId } = render(/*#__PURE__*/ React.createElement(TimepickerInput, {
69
+ disabled: false,
70
+ uses12hClock: false,
71
+ time: "00:00",
72
+ ampm: "AM",
73
+ onChange: onChange
74
+ }));
75
+ userEvent.clear(getByTestId('time-input'));
76
+ userEvent.type(getByTestId('time-input'), '00:00');
77
+ userEvent.tab();
78
+ expect(onChange).toHaveBeenCalledWith({
79
+ time: '00:00',
80
+ ampm: 'AM'
81
+ });
82
+ });
83
+ it('emits 12:00 (noon) correctly in 24h mode', ()=>{
84
+ const onChange = jest.fn();
85
+ const { getByTestId } = render(/*#__PURE__*/ React.createElement(TimepickerInput, {
86
+ disabled: false,
87
+ uses12hClock: false,
88
+ time: "12:00",
89
+ ampm: "PM",
90
+ onChange: onChange
91
+ }));
92
+ userEvent.clear(getByTestId('time-input'));
93
+ userEvent.type(getByTestId('time-input'), '12:00');
94
+ userEvent.tab();
95
+ expect(onChange).toHaveBeenCalledWith({
96
+ time: '12:00',
97
+ ampm: 'PM'
98
+ });
99
+ });
100
+ it('emits 13:00 (not 01:00) when user types 13:00 in 24h mode', ()=>{
101
+ const onChange = jest.fn();
102
+ const { getByTestId } = render(/*#__PURE__*/ React.createElement(TimepickerInput, {
103
+ disabled: false,
104
+ uses12hClock: false,
105
+ time: "13:00",
106
+ ampm: "PM",
107
+ onChange: onChange
108
+ }));
109
+ userEvent.clear(getByTestId('time-input'));
110
+ userEvent.type(getByTestId('time-input'), '13:00');
111
+ userEvent.tab();
112
+ expect(onChange).toHaveBeenCalledWith({
113
+ time: '13:00',
114
+ ampm: 'PM'
115
+ });
116
+ });
117
+ });
118
+ describe('onChange on blur — 12h mode', ()=>{
119
+ it('emits 07:00 AM correctly in 12h mode', ()=>{
120
+ const onChange = jest.fn();
121
+ const { getByTestId } = render(/*#__PURE__*/ React.createElement(TimepickerInput, {
122
+ disabled: false,
123
+ uses12hClock: true,
124
+ time: "07:00",
125
+ ampm: "AM",
126
+ onChange: onChange
127
+ }));
128
+ userEvent.clear(getByTestId('time-input'));
129
+ userEvent.type(getByTestId('time-input'), '07:00 AM');
130
+ userEvent.tab();
131
+ expect(onChange).toHaveBeenCalledWith({
132
+ time: '07:00',
133
+ ampm: 'AM'
134
+ });
135
+ });
136
+ it('emits 07:00 PM correctly in 12h mode', ()=>{
137
+ const onChange = jest.fn();
138
+ const { getByTestId } = render(/*#__PURE__*/ React.createElement(TimepickerInput, {
139
+ disabled: false,
140
+ uses12hClock: true,
141
+ time: "07:00",
142
+ ampm: "PM",
143
+ onChange: onChange
144
+ }));
145
+ userEvent.clear(getByTestId('time-input'));
146
+ userEvent.type(getByTestId('time-input'), '07:00 PM');
147
+ userEvent.tab();
148
+ expect(onChange).toHaveBeenCalledWith({
149
+ time: '07:00',
150
+ ampm: 'PM'
151
+ });
152
+ });
153
+ });
30
154
  });
@@ -376,4 +376,42 @@ describe('date utils', ()=>{
376
376
  expect(getDefaultAMPM()).toBe('AM');
377
377
  });
378
378
  });
379
+ describe('buildFieldValue — date does not shift due to timezone offset', ()=>{
380
+ it('April 27 23:00-02:00 round-trips as April 27 (UTC would parse as April 28 01:00Z)', ()=>{
381
+ const result = userInputFromDatetime({
382
+ value: '2026-04-27T23:00-02:00',
383
+ uses12hClock: false
384
+ });
385
+ const built = buildFieldValue({
386
+ data: result,
387
+ usesTime: true,
388
+ usesTimezone: true
389
+ });
390
+ expect(built.valid).toBe('2026-04-27T23:00-02:00');
391
+ });
392
+ it('April 28 01:00+02:00 round-trips as April 28 (UTC would parse as April 27 23:00Z)', ()=>{
393
+ const result = userInputFromDatetime({
394
+ value: '2026-04-28T01:00+02:00',
395
+ uses12hClock: false
396
+ });
397
+ const built = buildFieldValue({
398
+ data: result,
399
+ usesTime: true,
400
+ usesTimezone: true
401
+ });
402
+ expect(built.valid).toBe('2026-04-28T01:00+02:00');
403
+ });
404
+ it('preserves date for 23:59-01:00 (UTC: April 28 00:59Z → would show as April 28)', ()=>{
405
+ const result = userInputFromDatetime({
406
+ value: '2026-06-15T23:59-01:00',
407
+ uses12hClock: false
408
+ });
409
+ const built = buildFieldValue({
410
+ data: result,
411
+ usesTime: true,
412
+ usesTimezone: true
413
+ });
414
+ expect(built.valid).toBe('2026-06-15T23:59-01:00');
415
+ });
416
+ });
379
417
  });
@@ -1,4 +1,4 @@
1
- import { format, getHours, getMinutes, isValid, parse, parseISO, set } from 'date-fns';
1
+ import { format, getHours, getMinutes, isValid, parse, set } from 'date-fns';
2
2
  const ZONE_RX = /(Z|[+-]\d{2}[:+]?\d{2})$/;
3
3
  function startOfTodayOffset() {
4
4
  return format(new Date(), 'xxx');
@@ -9,10 +9,8 @@ function parseUtcOffset(datetimeString) {
9
9
  return match[1] === 'Z' ? '+00:00' : match[1];
10
10
  }
11
11
  function fieldValueToDate(datetimeString) {
12
- if (!datetimeString) {
13
- return null;
14
- }
15
- const date = parseISO(datetimeString);
12
+ if (!datetimeString) return null;
13
+ const date = parse(datetimeString.slice(0, 10), 'yyyy-MM-dd', new Date(0));
16
14
  return isValid(date) ? date : null;
17
15
  }
18
16
  function timeFromUserInput(input) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/field-editor-date",
3
- "version": "2.0.11",
3
+ "version": "2.0.12",
4
4
  "main": "dist/cjs/index.js",
5
5
  "module": "dist/esm/index.js",
6
6
  "types": "dist/types/index.d.ts",
@@ -57,5 +57,5 @@
57
57
  "publishConfig": {
58
58
  "registry": "https://npm.pkg.github.com/"
59
59
  },
60
- "gitHead": "039b71a95db2959bed3977a571b512233a5e375c"
60
+ "gitHead": "011bb99013e7bb1c58fbc5f870b81bd3aba38b95"
61
61
  }