Skip to content

Commit

Permalink
feat(ui-date-input,ui-text-input): add custom validation function and…
Browse files Browse the repository at this point in the history
… fix small layout issue
  • Loading branch information
balzss committed Jul 19, 2024
1 parent 2681980 commit dd459fb
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 27 deletions.
57 changes: 54 additions & 3 deletions packages/ui-date-input/src/DateInput2/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
describes: DateInput
describes: DateInput2
---

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.
Expand Down Expand Up @@ -74,7 +74,7 @@ This component is an updated version of [`DateInput`](/#DateInput) that's easier
invalidDateErrorMessage="Invalid date"
withYearPicker={{
screenReaderLabel: 'Year picker',
startYear: 1999,
startYear: 1900,
endYear: 2024
}}
/>
Expand Down Expand Up @@ -103,7 +103,7 @@ This component is an updated version of [`DateInput`](/#DateInput) that's easier
invalidDateErrorMessage="Invalid date"
withYearPicker={{
screenReaderLabel: 'Year picker',
startYear: 1999,
startYear: 1900,
endYear: 2024
}}
/>
Expand All @@ -112,3 +112,54 @@ This component is an updated version of [`DateInput`](/#DateInput) that's easier
render(<Example />)
```

### With custom validation

```js
---
type: example
---
const Example = () => {
const [value, setValue] = useState('')
const [messages, setMessages] = useState([])
const handleDateValidation = (dateString, isValidDate) => {
if (!isValidDate) {
setMessages([{
type: 'error',
text: 'This is not a valid date'
}])
} else if (new Date(dateString) < new Date('January 1, 1900')) {
setMessages([{
type: 'error',
text: 'Use date after January 1, 1900'
}])
} else {
setMessages([])
}
}
return (
<DateInput2
renderLabel="Choose a date"
screenReaderLabels={{
calendarIcon: 'Calendar',
nextMonthButton: 'Next month',
prevMonthButton: 'Previous month'
}}
width="20rem"
value={value}
messages={messages}
onRequestValidateDate={handleDateValidation}
onChange={(e, value) => setValue(value)}
withYearPicker={{
screenReaderLabel: 'Year picker',
startYear: 1900,
endYear: 2024
}}
/>
)
}
render(<Example />)
```
40 changes: 17 additions & 23 deletions packages/ui-date-input/src/DateInput2/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,7 @@ function isValidMomentDate(
return moment
.tz(
dateString,
[
moment.ISO_8601,
'llll',
'LLLL',
'lll',
'LLL',
'll',
'LL',
'l',
'L'
],
[moment.ISO_8601, 'llll', 'LLLL', 'lll', 'LLL', 'll', 'LL', 'l', 'L'],
locale,
true,
timezone
Expand All @@ -92,6 +82,7 @@ const DateInput2 = ({
onChange,
onBlur,
withYearPicker,
onRequestValidateDate,
invalidDateErrorMessage,
locale,
timezone,
Expand Down Expand Up @@ -145,14 +136,16 @@ const DateInput2 = ({
setInputMessages(messages || [])
return true
}
if (!onlyRemoveError) {
setInputMessages([
if (!onlyRemoveError && typeof invalidDateErrorMessage === 'string') {
setInputMessages((messages) => [
{
type: 'error',
text: invalidDateErrorMessage || '',
}
text: invalidDateErrorMessage
},
...messages
])
}

return false
}

Expand All @@ -176,17 +169,18 @@ const DateInput2 = ({
}

const handleBlur = (e: SyntheticEvent) => {
onBlur?.(e)
const isInputValid = validateInput(false)
if (isInputValid && value) {
const formattedDate = new Date(value).toLocaleDateString(getLocale(), {
month: 'long',
year: 'numeric',
day: 'numeric',
timeZone: getTimezone()
})
handleInputChange(e, formattedDate)
const formattedDate = new Date(value).toLocaleDateString(getLocale(), {
month: 'long',
year: 'numeric',
day: 'numeric',
timeZone: getTimezone()
})
handleInputChange(e, formattedDate)
}
onRequestValidateDate?.(value, isInputValid)
onBlur?.(e)
}

return (
Expand Down
14 changes: 13 additions & 1 deletion packages/ui-date-input/src/DateInput2/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,18 @@ type DateInput2Props = {
*/
onRequestHideCalendar?: (event: SyntheticEvent) => void
/**
* The message shown to the user when the date is invalid.
* Callback fired when the input is blurred. Feedback should be provided
* to the user when this function is called if the selected date or input
* value is invalid. The component has an internal check whether the date can
* be parsed to a valid date.
*/
onRequestValidateDate?: (
value?: string,
internalValidationPassed?: boolean
) => void | FormMessage[]
/**
* The message shown to the user when the date is invalid. If this prop is not set, validation is bypassed.
* If it's set to an empty string, validation happens and the input border changes to red if validation hasn't passed.
**/
invalidDateErrorMessage?: string
/**
Expand Down Expand Up @@ -165,6 +176,7 @@ const propTypes: PropValidators<PropKeys> = {
messages: PropTypes.arrayOf(FormPropTypes.message),
onRequestShowCalendar: PropTypes.func,
onRequestHideCalendar: PropTypes.func,
onRequestValidateDate: PropTypes.func,
invalidDateErrorMessage: PropTypes.oneOfType([
PropTypes.func,
PropTypes.string
Expand Down
5 changes: 5 additions & 0 deletions packages/ui-text-input/src/TextInput/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,11 @@ const generateStyle = (
...flexBase
},
afterElement: {
// TODO this is added for the case when there is an IconButton inside a TextInput (like in the DateInput2 component)
// and the button size makes the whole input 2px larger (because of the borders)
// this is not the best solution and in the long term we should work with the design team to figure out how to handle such cases
marginTop: '-1px',
marginBottom: '-1px',
label: 'textInput__afterElement',
...flexItemBase,
paddingInlineEnd: componentTheme.padding,
Expand Down

0 comments on commit dd459fb

Please sign in to comment.