+ )
+ }
+}
+
+render()
+```
diff --git a/packages/ui-date-input/src/DateInput2/index.tsx b/packages/ui-date-input/src/DateInput2/index.tsx
new file mode 100644
index 0000000000..434f22e3cc
--- /dev/null
+++ b/packages/ui-date-input/src/DateInput2/index.tsx
@@ -0,0 +1,235 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 - present Instructure, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/** @jsx jsx */
+import { useState, useEffect, useContext } from 'react'
+import type { SyntheticEvent } from 'react'
+import moment from 'moment-timezone'
+import type { Moment } from '@instructure/ui-i18n'
+import { Calendar } from '@instructure/ui-calendar'
+import { IconButton } from '@instructure/ui-buttons'
+import {
+ IconCalendarMonthLine,
+ IconArrowOpenEndSolid,
+ IconArrowOpenStartSolid
+} from '@instructure/ui-icons'
+import { Popover } from '@instructure/ui-popover'
+import { TextInput } from '@instructure/ui-text-input'
+import { passthroughProps } from '@instructure/ui-react-utils'
+
+import { DateTime, ApplyLocaleContext, Locale } from '@instructure/ui-i18n'
+import { jsx } from '@instructure/emotion'
+
+import { propTypes } from './props'
+import type { DateInput2Props } from './props'
+import type { FormMessage } from '@instructure/ui-form-field'
+
+/**
+---
+category: components
+---
+**/
+const DateInput2 = ({
+ renderLabel,
+ screenReaderLabels,
+ isRequired = false,
+ interaction = 'enabled',
+ value = '',
+ size = 'medium',
+ isInline = false,
+ messages = [],
+ width,
+ onChange,
+ onBlur,
+ withYearPicker,
+ invalidDateErrorMessage,
+ locale,
+ timezone,
+ placeholder,
+ ...rest
+}: DateInput2Props) => {
+ const [selectedDate, setSelectedDate] = useState('')
+ const [inputMessages, setInputMessages] = useState(messages)
+ const [showPopover, setShowPopover] = useState(false)
+ const localeContext = useContext(ApplyLocaleContext)
+
+ useEffect(() => {
+ validateInput(true)
+ }, [value])
+
+ useEffect(() => {
+ setInputMessages(messages)
+ }, [messages])
+
+ const handleInputChange = (e: SyntheticEvent, value: string) => {
+ onChange?.(e, value)
+ }
+
+ const handleDateSelected = (
+ dateString: string,
+ momentDate: Moment,
+ e: SyntheticEvent
+ ) => {
+ setSelectedDate(dateString)
+ handleInputChange(
+ e,
+ `${momentDate.format('MMMM')} ${momentDate.format(
+ 'D'
+ )}, ${momentDate.format('YYYY')}`
+ )
+ setShowPopover(false)
+ }
+
+ const validateInput = (onlyRemoveError = false) => {
+ if (
+ moment
+ .tz(
+ value ? value : '',
+ [
+ DateTime.momentISOFormat,
+ 'llll',
+ 'LLLL',
+ 'lll',
+ 'LLL',
+ 'll',
+ 'LL',
+ 'l',
+ 'L'
+ ],
+ getLocale(),
+ true,
+ getTimezone()
+ )
+ .isValid() ||
+ value === ''
+ ) {
+ setSelectedDate(value || '')
+ setInputMessages(messages)
+ return
+ }
+ if (typeof invalidDateErrorMessage !== 'function' && !onlyRemoveError) {
+ setInputMessages([
+ {
+ type: 'error',
+ text: invalidDateErrorMessage
+ }
+ ])
+ }
+ }
+
+ const getLocale = () => {
+ if (locale) {
+ return locale
+ } else if (localeContext.locale) {
+ return localeContext.locale
+ }
+ return Locale.browserLocale()
+ }
+
+ const getTimezone = () => {
+ if (timezone) {
+ return timezone
+ } else if (localeContext.timezone) {
+ return localeContext.timezone
+ }
+ return DateTime.browserTimeZone()
+ }
+
+ const handleBlur = (e: SyntheticEvent) => {
+ onBlur?.(e)
+ validateInput(false)
+ }
+
+ return (
+
+
+
+ }
+ isShowingContent={showPopover}
+ onShowContent={() => setShowPopover(true)}
+ onHideContent={() => setShowPopover(false)}
+ on="click"
+ shouldContainFocus
+ shouldReturnFocus
+ shouldCloseOnDocumentClick
+ >
+ }
+ screenReaderLabel={screenReaderLabels.nextMonthButton}
+ />
+ }
+ renderPrevMonthButton={
+ }
+ screenReaderLabel={screenReaderLabels.prevMonthButton}
+ />
+ }
+ />
+
+ }
+ />
+ )
+}
+
+DateInput2.propTypes = propTypes
+
+export default DateInput2
+export { DateInput2 }
diff --git a/packages/ui-date-input/src/DateInput2/props.ts b/packages/ui-date-input/src/DateInput2/props.ts
new file mode 100644
index 0000000000..f103461587
--- /dev/null
+++ b/packages/ui-date-input/src/DateInput2/props.ts
@@ -0,0 +1,182 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 - present Instructure, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+import PropTypes from 'prop-types'
+import type { SyntheticEvent } from 'react'
+
+import { controllable } from '@instructure/ui-prop-types'
+import { FormPropTypes } from '@instructure/ui-form-field'
+import type { FormMessage } from '@instructure/ui-form-field'
+import type { Renderable, PropValidators } from '@instructure/shared-types'
+
+type DateInput2Props = {
+ /**
+ * Specifies the input label.
+ */
+ renderLabel: Renderable
+ screenReaderLabels: {
+ calendarIcon: string
+ prevMonthButton: string
+ nextMonthButton: string
+ }
+ /**
+ * Specifies the input value.
+ */
+ value?: string // TODO: controllable(PropTypes.string)
+ /**
+ * Specifies the input size.
+ */
+ size?: 'small' | 'medium' | 'large'
+ /**
+ * Html placeholder text to display when the input has no value. This should
+ * be hint text, not a label replacement.
+ */
+ placeholder?: string
+ /**
+ * Callback fired when the input changes.
+ */
+ onChange?: (event: React.SyntheticEvent, value: string) => void
+ /**
+ * Callback executed when the input fires a blur event.
+ */
+ onBlur?: (event: React.SyntheticEvent) => void
+ /**
+ * Specifies if interaction with the input is enabled, disabled, or readonly.
+ * When "disabled", the input changes visibly to indicate that it cannot
+ * receive user interactions. When "readonly" the input still cannot receive
+ * user interactions but it keeps the same styles as if it were enabled.
+ */
+ interaction?: 'enabled' | 'disabled' | 'readonly'
+ /**
+ * Specifies if the input is required.
+ */
+ isRequired?: boolean
+ /**
+ * Controls whether the input is rendered inline with other elements or if it
+ * is rendered as a block level element.
+ */
+ isInline?: boolean
+ /**
+ * Specifies the width of the input.
+ */
+ width?: string
+ /**
+ * Displays messages and validation for the input. It should be an object
+ * with the following shape:
+ * `{
+ * text: PropTypes.node,
+ * type: PropTypes.oneOf(['error', 'hint', 'success', 'screenreader-only'])
+ * }`
+ */
+ messages?: FormMessage[]
+ /**
+ * Callback fired requesting the calendar be shown.
+ */
+ onRequestShowCalendar?: (event: SyntheticEvent) => void
+ /**
+ * Callback fired requesting the calendar be hidden.
+ */
+ onRequestHideCalendar?: (event: SyntheticEvent) => void
+ /**
+ * The message shown to the user when the data is invalid.
+ * If a string, shown to the user anytime the input is invalid.
+ *
+ * If a function, receives a single parameter:
+ * - *rawDateValue*: the string entered as a date by the user.
+ **/
+ invalidDateErrorMessage?: string | ((rawDateValue: string) => FormMessage)
+ /**
+ * A standard language identifier.
+ *
+ * See [Moment.js](https://momentjs.com/timezone/docs/#/using-timezones/parsing-in-zone/) for
+ * more details.
+ *
+ * This property can also be set via a context property and if both are set
+ * then the component property takes precedence over the context property.
+ *
+ * The web browser's locale will be used if no value is set via a component
+ * property or a context property.
+ **/
+ locale?: string
+ /**
+ * A timezone identifier in the format: *Area/Location*
+ *
+ * See [List of tz database time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for the list
+ * of possible options.
+ *
+ * This property can also be set via a context property and if both are set
+ * then the component property takes precedence over the context property.
+ *
+ * The web browser's timezone will be used if no value is set via a component
+ * property or a context property.
+ **/
+ timezone?: string
+
+ /**
+ * If set, years can be picked from a dropdown.
+ * It accepts an object.
+ * screenReaderLabel: string // e.g.: i18n("pick a year")
+ *
+ * onRequestYearChange?:(e: React.MouseEvent,requestedYear: number): void // if set, on year change, only this will be called and no internal change will take place
+ *
+ * startYear: number // e.g.: 2001, sets the start year of the selectable list
+ *
+ * endYear: number // e.g.: 2030, sets the end year of the selectable list
+ */
+ withYearPicker?: {
+ screenReaderLabel: string
+ onRequestYearChange?: (e: SyntheticEvent, requestedYear: number) => void
+ startYear: number
+ endYear: number
+ }
+}
+
+type PropKeys = keyof DateInput2Props
+
+const propTypes: PropValidators = {
+ renderLabel: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
+ screenReaderLabels: PropTypes.object.isRequired,
+ value: controllable(PropTypes.string),
+ size: PropTypes.oneOf(['small', 'medium', 'large']),
+ placeholder: PropTypes.string,
+ onChange: PropTypes.func,
+ onBlur: PropTypes.func,
+ interaction: PropTypes.oneOf(['enabled', 'disabled', 'readonly']),
+ isRequired: PropTypes.bool,
+ isInline: PropTypes.bool,
+ width: PropTypes.string,
+ messages: PropTypes.arrayOf(FormPropTypes.message),
+ onRequestShowCalendar: PropTypes.func,
+ onRequestHideCalendar: PropTypes.func,
+ invalidDateErrorMessage: PropTypes.oneOfType([
+ PropTypes.func,
+ PropTypes.string
+ ]),
+ locale: PropTypes.string,
+ timezone: PropTypes.string,
+ withYearPicker: PropTypes.object
+}
+
+export type { DateInput2Props }
+export { propTypes }
diff --git a/packages/ui-date-input/src/index.ts b/packages/ui-date-input/src/index.ts
index 78bad96454..cbfea3e729 100644
--- a/packages/ui-date-input/src/index.ts
+++ b/packages/ui-date-input/src/index.ts
@@ -23,4 +23,5 @@
*/
export { DateInput } from './DateInput'
+export { DateInput2 } from './DateInput2'
export type { DateInputProps } from './DateInput/props'
diff --git a/packages/ui-date-input/tsconfig.build.json b/packages/ui-date-input/tsconfig.build.json
index 925c6de1fb..2c57876b7e 100644
--- a/packages/ui-date-input/tsconfig.build.json
+++ b/packages/ui-date-input/tsconfig.build.json
@@ -10,6 +10,9 @@
{
"path": "../ui-babel-preset/tsconfig.build.json"
},
+ {
+ "path": "../ui-buttons/tsconfig.build.json"
+ },
{
"path": "../ui-test-utils/tsconfig.build.json"
},
diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts
index bbe153ad5f..35ae2f0027 100644
--- a/packages/ui/src/index.ts
+++ b/packages/ui/src/index.ts
@@ -58,7 +58,7 @@ export {
ColorContrast,
ColorIndicator
} from '@instructure/ui-color-picker'
-export { DateInput } from '@instructure/ui-date-input'
+export { DateInput, DateInput2 } from '@instructure/ui-date-input'
export { DateTimeInput } from '@instructure/ui-date-time-input'
export { Dialog } from '@instructure/ui-dialog'
export { DrawerLayout, DrawerContent } from '@instructure/ui-drawer-layout'