@instructure/ui-date-input 9.3.1-snapshot-4 → 9.3.1-snapshot-6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,9 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ## [9.3.1-snapshot-4](https://github.com/instructure/instructure-ui/compare/v9.3.0...v9.3.1-snapshot-4) (2024-07-25)
6
+ ## [9.3.1-snapshot-6](https://github.com/instructure/instructure-ui/compare/v9.3.0...v9.3.1-snapshot-6) (2024-07-25)
7
7
 
8
- **Note:** Version bump only for package @instructure/ui-date-input
8
+
9
+ ### Features
10
+
11
+ * **ui-date-input,ui-text-input:** add custom validation function and fix small layout issue ([77218be](https://github.com/instructure/instructure-ui/commit/77218be635611c1c674cc6c85d4dceaa76509117))
9
12
 
10
13
 
11
14
 
@@ -61,6 +61,7 @@ const DateInput2 = ({
61
61
  onChange,
62
62
  onBlur,
63
63
  withYearPicker,
64
+ onRequestValidateDate,
64
65
  invalidDateErrorMessage,
65
66
  locale,
66
67
  timezone,
@@ -98,6 +99,7 @@ const DateInput2 = ({
98
99
  });
99
100
  handleInputChange(e, formattedDate);
100
101
  setShowPopover(false);
102
+ onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(formattedDate, true);
101
103
  };
102
104
  const validateInput = (onlyRemoveError = false) => {
103
105
  // TODO `isValidDate` and `isValidMomentDate` basically have the same functionality but the latter is a bit more strict (e.g.: `33` is only valid in `isValidMomentDate`)
@@ -108,11 +110,11 @@ const DateInput2 = ({
108
110
  setInputMessages(messages || []);
109
111
  return true;
110
112
  }
111
- if (!onlyRemoveError) {
112
- setInputMessages([{
113
+ if (!onlyRemoveError && typeof invalidDateErrorMessage === 'string') {
114
+ setInputMessages(messages => [{
113
115
  type: 'error',
114
- text: invalidDateErrorMessage || ''
115
- }]);
116
+ text: invalidDateErrorMessage
117
+ }, ...messages]);
116
118
  }
117
119
  return false;
118
120
  };
@@ -134,7 +136,6 @@ const DateInput2 = ({
134
136
  return Intl.DateTimeFormat().resolvedOptions().timeZone;
135
137
  };
136
138
  const handleBlur = e => {
137
- onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
138
139
  const isInputValid = validateInput(false);
139
140
  if (isInputValid && value) {
140
141
  const formattedDate = new Date(value).toLocaleDateString(getLocale(), {
@@ -145,6 +146,8 @@ const DateInput2 = ({
145
146
  });
146
147
  handleInputChange(e, formattedDate);
147
148
  }
149
+ onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(value, isInputValid);
150
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
148
151
  };
149
152
  return jsx(TextInput, Object.assign({}, passthroughProps(rest), {
150
153
  renderLabel: renderLabel,
@@ -40,6 +40,7 @@ const propTypes = {
40
40
  messages: PropTypes.arrayOf(FormPropTypes.message),
41
41
  onRequestShowCalendar: PropTypes.func,
42
42
  onRequestHideCalendar: PropTypes.func,
43
+ onRequestValidateDate: PropTypes.func,
43
44
  invalidDateErrorMessage: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
44
45
  locale: PropTypes.string,
45
46
  timezone: PropTypes.string,
@@ -70,6 +70,7 @@ const DateInput2 = ({
70
70
  onChange,
71
71
  onBlur,
72
72
  withYearPicker,
73
+ onRequestValidateDate,
73
74
  invalidDateErrorMessage,
74
75
  locale,
75
76
  timezone,
@@ -107,6 +108,7 @@ const DateInput2 = ({
107
108
  });
108
109
  handleInputChange(e, formattedDate);
109
110
  setShowPopover(false);
111
+ onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(formattedDate, true);
110
112
  };
111
113
  const validateInput = (onlyRemoveError = false) => {
112
114
  // TODO `isValidDate` and `isValidMomentDate` basically have the same functionality but the latter is a bit more strict (e.g.: `33` is only valid in `isValidMomentDate`)
@@ -117,11 +119,11 @@ const DateInput2 = ({
117
119
  setInputMessages(messages || []);
118
120
  return true;
119
121
  }
120
- if (!onlyRemoveError) {
121
- setInputMessages([{
122
+ if (!onlyRemoveError && typeof invalidDateErrorMessage === 'string') {
123
+ setInputMessages(messages => [{
122
124
  type: 'error',
123
- text: invalidDateErrorMessage || ''
124
- }]);
125
+ text: invalidDateErrorMessage
126
+ }, ...messages]);
125
127
  }
126
128
  return false;
127
129
  };
@@ -143,7 +145,6 @@ const DateInput2 = ({
143
145
  return Intl.DateTimeFormat().resolvedOptions().timeZone;
144
146
  };
145
147
  const handleBlur = e => {
146
- onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
147
148
  const isInputValid = validateInput(false);
148
149
  if (isInputValid && value) {
149
150
  const formattedDate = new Date(value).toLocaleDateString(getLocale(), {
@@ -154,6 +155,8 @@ const DateInput2 = ({
154
155
  });
155
156
  handleInputChange(e, formattedDate);
156
157
  }
158
+ onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(value, isInputValid);
159
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
157
160
  };
158
161
  return (0, _emotion.jsx)(_TextInput.TextInput, Object.assign({}, (0, _passthroughProps.passthroughProps)(rest), {
159
162
  renderLabel: renderLabel,
@@ -47,6 +47,7 @@ const propTypes = exports.propTypes = {
47
47
  messages: _propTypes.default.arrayOf(_FormPropTypes.FormPropTypes.message),
48
48
  onRequestShowCalendar: _propTypes.default.func,
49
49
  onRequestHideCalendar: _propTypes.default.func,
50
+ onRequestValidateDate: _propTypes.default.func,
50
51
  invalidDateErrorMessage: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.string]),
51
52
  locale: _propTypes.default.string,
52
53
  timezone: _propTypes.default.string,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/ui-date-input",
3
- "version": "9.3.1-snapshot-4",
3
+ "version": "9.3.1-snapshot-6",
4
4
  "description": "A UI component library made by Instructure Inc.",
5
5
  "author": "Instructure, Inc. Engineering and Product Design",
6
6
  "module": "./es/index.js",
@@ -23,11 +23,11 @@
23
23
  },
24
24
  "license": "MIT",
25
25
  "devDependencies": {
26
- "@instructure/ui-axe-check": "9.3.1-snapshot-4",
27
- "@instructure/ui-babel-preset": "9.3.1-snapshot-4",
28
- "@instructure/ui-buttons": "9.3.1-snapshot-4",
29
- "@instructure/ui-scripts": "9.3.1-snapshot-4",
30
- "@instructure/ui-test-utils": "9.3.1-snapshot-4",
26
+ "@instructure/ui-axe-check": "9.3.1-snapshot-6",
27
+ "@instructure/ui-babel-preset": "9.3.1-snapshot-6",
28
+ "@instructure/ui-buttons": "9.3.1-snapshot-6",
29
+ "@instructure/ui-scripts": "9.3.1-snapshot-6",
30
+ "@instructure/ui-test-utils": "9.3.1-snapshot-6",
31
31
  "@testing-library/jest-dom": "^6.4.6",
32
32
  "@testing-library/react": "^15.0.7",
33
33
  "@testing-library/user-event": "^14.5.2",
@@ -35,20 +35,20 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@babel/runtime": "^7.24.5",
38
- "@instructure/emotion": "9.3.1-snapshot-4",
39
- "@instructure/shared-types": "9.3.1-snapshot-4",
40
- "@instructure/ui-calendar": "9.3.1-snapshot-4",
41
- "@instructure/ui-form-field": "9.3.1-snapshot-4",
42
- "@instructure/ui-i18n": "9.3.1-snapshot-4",
43
- "@instructure/ui-icons": "9.3.1-snapshot-4",
44
- "@instructure/ui-popover": "9.3.1-snapshot-4",
45
- "@instructure/ui-position": "9.3.1-snapshot-4",
46
- "@instructure/ui-prop-types": "9.3.1-snapshot-4",
47
- "@instructure/ui-react-utils": "9.3.1-snapshot-4",
48
- "@instructure/ui-selectable": "9.3.1-snapshot-4",
49
- "@instructure/ui-testable": "9.3.1-snapshot-4",
50
- "@instructure/ui-text-input": "9.3.1-snapshot-4",
51
- "@instructure/ui-utils": "9.3.1-snapshot-4",
38
+ "@instructure/emotion": "9.3.1-snapshot-6",
39
+ "@instructure/shared-types": "9.3.1-snapshot-6",
40
+ "@instructure/ui-calendar": "9.3.1-snapshot-6",
41
+ "@instructure/ui-form-field": "9.3.1-snapshot-6",
42
+ "@instructure/ui-i18n": "9.3.1-snapshot-6",
43
+ "@instructure/ui-icons": "9.3.1-snapshot-6",
44
+ "@instructure/ui-popover": "9.3.1-snapshot-6",
45
+ "@instructure/ui-position": "9.3.1-snapshot-6",
46
+ "@instructure/ui-prop-types": "9.3.1-snapshot-6",
47
+ "@instructure/ui-react-utils": "9.3.1-snapshot-6",
48
+ "@instructure/ui-selectable": "9.3.1-snapshot-6",
49
+ "@instructure/ui-testable": "9.3.1-snapshot-6",
50
+ "@instructure/ui-text-input": "9.3.1-snapshot-6",
51
+ "@instructure/ui-utils": "9.3.1-snapshot-6",
52
52
  "moment-timezone": "^0.5.45",
53
53
  "prop-types": "^15.8.1"
54
54
  },
@@ -1,5 +1,5 @@
1
1
  ---
2
- describes: DateInput
2
+ describes: DateInput2
3
3
  ---
4
4
 
5
5
  This component is an updated version of [`DateInput`](/#DateInput) that's easier to configure for developers, has a better UX, better accessibility features and a year picker. We recommend using this instead of `DateInput` which will be deprecated in the future.
@@ -74,7 +74,7 @@ This component is an updated version of [`DateInput`](/#DateInput) that's easier
74
74
  invalidDateErrorMessage="Invalid date"
75
75
  withYearPicker={{
76
76
  screenReaderLabel: 'Year picker',
77
- startYear: 1999,
77
+ startYear: 1900,
78
78
  endYear: 2024
79
79
  }}
80
80
  />
@@ -103,7 +103,7 @@ This component is an updated version of [`DateInput`](/#DateInput) that's easier
103
103
  invalidDateErrorMessage="Invalid date"
104
104
  withYearPicker={{
105
105
  screenReaderLabel: 'Year picker',
106
- startYear: 1999,
106
+ startYear: 1900,
107
107
  endYear: 2024
108
108
  }}
109
109
  />
@@ -112,3 +112,54 @@ This component is an updated version of [`DateInput`](/#DateInput) that's easier
112
112
 
113
113
  render(<Example />)
114
114
  ```
115
+
116
+ ### With custom validation
117
+
118
+ ```js
119
+ ---
120
+ type: example
121
+ ---
122
+ const Example = () => {
123
+ const [value, setValue] = useState('')
124
+ const [messages, setMessages] = useState([])
125
+
126
+ const handleDateValidation = (dateString, isValidDate) => {
127
+ if (!isValidDate) {
128
+ setMessages([{
129
+ type: 'error',
130
+ text: 'This is not a valid date'
131
+ }])
132
+ } else if (new Date(dateString) < new Date('January 1, 1900')) {
133
+ setMessages([{
134
+ type: 'error',
135
+ text: 'Use date after January 1, 1900'
136
+ }])
137
+ } else {
138
+ setMessages([])
139
+ }
140
+ }
141
+
142
+ return (
143
+ <DateInput2
144
+ renderLabel="Choose a date after January 1, 1900"
145
+ screenReaderLabels={{
146
+ calendarIcon: 'Calendar',
147
+ nextMonthButton: 'Next month',
148
+ prevMonthButton: 'Previous month'
149
+ }}
150
+ width="20rem"
151
+ value={value}
152
+ messages={messages}
153
+ onRequestValidateDate={handleDateValidation}
154
+ onChange={(e, value) => setValue(value)}
155
+ withYearPicker={{
156
+ screenReaderLabel: 'Year picker',
157
+ startYear: 1900,
158
+ endYear: 2024
159
+ }}
160
+ />
161
+ )
162
+ }
163
+
164
+ render(<Example />)
165
+ ```
@@ -56,17 +56,7 @@ function isValidMomentDate(
56
56
  return moment
57
57
  .tz(
58
58
  dateString,
59
- [
60
- moment.ISO_8601,
61
- 'llll',
62
- 'LLLL',
63
- 'lll',
64
- 'LLL',
65
- 'll',
66
- 'LL',
67
- 'l',
68
- 'L'
69
- ],
59
+ [moment.ISO_8601, 'llll', 'LLLL', 'lll', 'LLL', 'll', 'LL', 'l', 'L'],
70
60
  locale,
71
61
  true,
72
62
  timezone
@@ -92,6 +82,7 @@ const DateInput2 = ({
92
82
  onChange,
93
83
  onBlur,
94
84
  withYearPicker,
85
+ onRequestValidateDate,
95
86
  invalidDateErrorMessage,
96
87
  locale,
97
88
  timezone,
@@ -130,6 +121,7 @@ const DateInput2 = ({
130
121
  })
131
122
  handleInputChange(e, formattedDate)
132
123
  setShowPopover(false)
124
+ onRequestValidateDate?.(formattedDate, true)
133
125
  }
134
126
 
135
127
  const validateInput = (onlyRemoveError = false): boolean => {
@@ -145,14 +137,16 @@ const DateInput2 = ({
145
137
  setInputMessages(messages || [])
146
138
  return true
147
139
  }
148
- if (!onlyRemoveError) {
149
- setInputMessages([
140
+ if (!onlyRemoveError && typeof invalidDateErrorMessage === 'string') {
141
+ setInputMessages((messages) => [
150
142
  {
151
143
  type: 'error',
152
- text: invalidDateErrorMessage || '',
153
- }
144
+ text: invalidDateErrorMessage
145
+ },
146
+ ...messages
154
147
  ])
155
148
  }
149
+
156
150
  return false
157
151
  }
158
152
 
@@ -176,17 +170,18 @@ const DateInput2 = ({
176
170
  }
177
171
 
178
172
  const handleBlur = (e: SyntheticEvent) => {
179
- onBlur?.(e)
180
173
  const isInputValid = validateInput(false)
181
174
  if (isInputValid && value) {
182
- const formattedDate = new Date(value).toLocaleDateString(getLocale(), {
183
- month: 'long',
184
- year: 'numeric',
185
- day: 'numeric',
186
- timeZone: getTimezone()
187
- })
188
- handleInputChange(e, formattedDate)
175
+ const formattedDate = new Date(value).toLocaleDateString(getLocale(), {
176
+ month: 'long',
177
+ year: 'numeric',
178
+ day: 'numeric',
179
+ timeZone: getTimezone()
180
+ })
181
+ handleInputChange(e, formattedDate)
189
182
  }
183
+ onRequestValidateDate?.(value, isInputValid)
184
+ onBlur?.(e)
190
185
  }
191
186
 
192
187
  return (
@@ -99,7 +99,18 @@ type DateInput2Props = {
99
99
  */
100
100
  onRequestHideCalendar?: (event: SyntheticEvent) => void
101
101
  /**
102
- * The message shown to the user when the date is invalid.
102
+ * Callback fired when the input is blurred. Feedback should be provided
103
+ * to the user when this function is called if the selected date or input
104
+ * value is invalid. The component has an internal check whether the date can
105
+ * be parsed to a valid date.
106
+ */
107
+ onRequestValidateDate?: (
108
+ value?: string,
109
+ internalValidationPassed?: boolean
110
+ ) => void | FormMessage[]
111
+ /**
112
+ * The message shown to the user when the date is invalid. If this prop is not set, validation is bypassed.
113
+ * If it's set to an empty string, validation happens and the input border changes to red if validation hasn't passed.
103
114
  **/
104
115
  invalidDateErrorMessage?: string
105
116
  /**
@@ -165,6 +176,7 @@ const propTypes: PropValidators<PropKeys> = {
165
176
  messages: PropTypes.arrayOf(FormPropTypes.message),
166
177
  onRequestShowCalendar: PropTypes.func,
167
178
  onRequestHideCalendar: PropTypes.func,
179
+ onRequestValidateDate: PropTypes.func,
168
180
  invalidDateErrorMessage: PropTypes.oneOfType([
169
181
  PropTypes.func,
170
182
  PropTypes.string