Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 12 additions & 38 deletions packages/ui-date-input/src/DateInput2/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import {
useState,
useEffect,
useContext,
forwardRef,
ForwardedRef,
ValidationMap
Expand All @@ -41,8 +40,7 @@ import {
import { Popover } from '@instructure/ui-popover'
import { TextInput } from '@instructure/ui-text-input'
import { callRenderProp, passthroughProps } from '@instructure/ui-react-utils'

import { ApplyLocaleContext, Locale } from '@instructure/ui-i18n'
import { getLocale, getTimezone } from '@instructure/ui-i18n'

import { propTypes } from './props'
import type { DateInput2Props } from './props'
Expand Down Expand Up @@ -93,11 +91,6 @@ function parseLocaleDate(
// create utc date from year, month (zero indexed) and day
const date = new Date(Date.UTC(year, month - 1, day))

if (date.getMonth() !== month - 1 || date.getDate() !== day) {
// Check if the Date object adjusts the values. If it does, the input is invalid.
return null
}

// Format date string in the provided timezone. The locale here is irrelevant, we only care about how to time is adjusted for the timezone.
const parts = new Intl.DateTimeFormat('en-US', {
timeZone,
Expand Down Expand Up @@ -166,27 +159,8 @@ const DateInput2 = forwardRef(
}: DateInput2Props,
ref: ForwardedRef<TextInput>
) => {
const localeContext = useContext(ApplyLocaleContext)

const getLocale = () => {
if (locale) {
return locale
} else if (localeContext.locale) {
return localeContext.locale
}
// default to the system's locale
return Locale.browserLocale()
}

const getTimezone = () => {
if (timezone) {
return timezone
} else if (localeContext.timezone) {
return localeContext.timezone
}
// default to the system's timezone
return Intl.DateTimeFormat().resolvedOptions().timeZone
}
const userLocale = locale || getLocale()
const userTimezone = timezone || getTimezone()

const [inputMessages, setInputMessages] = useState<FormMessage[]>(
messages || []
Expand All @@ -213,28 +187,28 @@ const DateInput2 = forwardRef(
if (dateFormat) {
if (typeof dateFormat === 'string') {
// use dateFormat instead of the user locale
date = parseLocaleDate(dateString, dateFormat, getTimezone())
date = parseLocaleDate(dateString, dateFormat, userTimezone)
} else if (dateFormat.parser) {
date = dateFormat.parser(dateString)
}
} else {
// no dateFormat prop passed, use locale for formatting
date = parseLocaleDate(dateString, getLocale(), getTimezone())
date = parseLocaleDate(dateString, userLocale, userTimezone)
}
return date ? [formatDate(date), date.toISOString()] : ['', '']
}

const formatDate = (
date: Date,
timeZone: string = getTimezone()
timeZone: string = userTimezone
): string => {
// use formatter function if provided
if (typeof dateFormat !== 'string' && dateFormat?.formatter) {
return dateFormat.formatter(date)
}
// if dateFormat set to a locale, use that, otherwise default to the user's locale
return date.toLocaleDateString(
typeof dateFormat === 'string' ? dateFormat : getLocale(),
typeof dateFormat === 'string' ? dateFormat : userLocale,
{
timeZone,
calendar: 'gregory',
Expand All @@ -253,9 +227,9 @@ const DateInput2 = forwardRef(
}

// Replace the matched number with the same number of dashes
const year = `${exampleDate.getFullYear()}`
const month = `${exampleDate.getMonth() + 1}`
const day = `${exampleDate.getDate()}`
const year = '2024'
const month = '9'
const day = '1'
return formattedDate
.replace(regex(year), (match) => 'Y'.repeat(match.length))
.replace(regex(month), (match) => 'M'.repeat(match.length))
Expand Down Expand Up @@ -340,8 +314,8 @@ const DateInput2 = forwardRef(
selectedDate={selectedDate}
disabledDates={disabledDates}
visibleMonth={selectedDate}
locale={getLocale()}
timezone={getTimezone()}
locale={userLocale}
timezone={userTimezone}
renderNextMonthButton={
<IconButton
size="small"
Expand Down
2 changes: 2 additions & 0 deletions packages/ui-i18n/src/Locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
* SOFTWARE.
*/

// TODO: there is an improved replacement for this helper at `ui-i18n/src/getLocale.ts`
// all uses of this should be updated and this helper deleted
import { canUseDOM } from '@instructure/ui-dom-utils'

const defaultLocale = 'en-US'
Expand Down
52 changes: 52 additions & 0 deletions packages/ui-i18n/src/getLocale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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 { useContext } from 'react'
import { ApplyLocaleContext } from '.'

// TODO: this is a better replacement for `ui-i18n/src/Locale.ts` which should be deleted in the future
export function getLocale(defaultLocale = 'en-US') {
const localeContext = useContext(ApplyLocaleContext)
if (localeContext.locale) {
return localeContext.locale
}

if (typeof navigator !== 'undefined') {
if (navigator.languages && navigator.languages.length) {
return navigator.languages[0]
}
if (navigator.language) {
return navigator.language
}
}
try {
// This is generally reliable if Intl is supported
return new Intl.DateTimeFormat().resolvedOptions().locale
} catch (e) {
console.warn(
'Intl.DateTimeFormat().resolvedOptions().locale failed, using fallback.'
)
// Fall through to default
}
return defaultLocale
}
35 changes: 35 additions & 0 deletions packages/ui-i18n/src/getTimezone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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 { useContext } from 'react'
import { ApplyLocaleContext } from '.'

// TODO: this is a better replacement for `ui-i18n/src/Locale.ts` which should be deleted in the future
export function getTimezone() {
const localeContext = useContext(ApplyLocaleContext)
if (localeContext.timezone) {
return localeContext.timezone
}

return Intl.DateTimeFormat().resolvedOptions().timeZone
}
6 changes: 5 additions & 1 deletion packages/ui-i18n/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ export { textDirectionContextConsumer } from './textDirectionContextConsumer'
export { DateTime } from './DateTime'
export { getTextDirection } from './getTextDirection'
export { I18nPropTypes } from './I18nPropTypes'
export { Locale } from './Locale'

export { Locale } from './Locale' // TODO delete this and only keep the ones below
export { getLocale } from './getLocale'
export { getTimezone } from './getTimezone'

export { DIRECTION, TextDirectionContext } from './TextDirectionContext'

export type { Moment } from './DateTime'
Expand Down
Loading