/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. * * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import React, { Component, MouseEventHandler, Ref } from 'react'; import classNames from 'classnames'; import { Moment } from 'moment'; // eslint-disable-line import/named import { OuiFormControlLayout, OuiValidatableControl } from '../form'; import { OuiFormControlLayoutIconsProps } from '../form/form_control_layout/form_control_layout_icons'; import { OuiErrorBoundary } from '../error_boundary'; import { OuiI18nConsumer } from '../context'; import { ApplyClassComponentDefaults, CommonProps } from '../common'; // @ts-ignore the type is provided by react-datepicker.d.ts import { ReactDatePicker as _ReactDatePicker } from '../../../packages'; import ReactDatePicker, { ReactDatePickerProps } from './react-datepicker'; // eslint-disable-line import/no-unresolved export const ouiDatePickerDefaultDateFormat = 'MM/DD/YYYY'; export const ouiDatePickerDefaultTimeFormat = 'hh:mm A'; const DatePicker = _ReactDatePicker as typeof ReactDatePicker; interface OuiExtendedDatePickerProps extends ReactDatePickerProps { /** * Applies classes to the numbered days provided. Check docs for example. */ dayClassName?: (date: Moment) => string | null; /** * Makes the input full width */ fullWidth?: boolean; /** * ref for the ReactDatePicker instance */ inputRef: Ref>; /** * Provides styling to the input when invalid */ isInvalid?: boolean; /** * Provides styling to the input when loading */ isLoading?: boolean; /** * What to do when the input is cleared by the x icon */ onClear?: MouseEventHandler; /** * Opens to this date (in moment format) on first press, regardless of selection */ openToDate?: Moment; /** * Shows only when no date is selected */ placeholder?: string; /** * Can turn the shadow off if using the inline prop */ shadow?: boolean; /** * Show the icon in input */ showIcon?: boolean; /** * Pass an icon type to change the default `calendar` or `clock` icon */ iconType?: OuiFormControlLayoutIconsProps['icon']; /** * Sets the placement of the popover. It accepts: `"bottom"`, `"bottom-end"`, `"bottom-start"`, `"left"`, `"left-end"`, `"left-start"`, `"right"`, `"right-end"`, `"right-start"`, `"top"`, `"top-end"`, `"top-start"` */ popoverPlacement?: ReactDatePickerProps['popperPlacement']; } type _OuiDatePickerProps = CommonProps & OuiExtendedDatePickerProps; export type OuiDatePickerProps = ApplyClassComponentDefaults< typeof OuiDatePicker >; export class OuiDatePicker extends Component<_OuiDatePickerProps> { static defaultProps = { adjustDateOnChange: true, dateFormat: ouiDatePickerDefaultDateFormat, fullWidth: false, inputRef: () => {}, isLoading: false, shadow: true, shouldCloseOnSelect: true, showIcon: true, showTimeSelect: false, timeFormat: ouiDatePickerDefaultTimeFormat, popoverPlacement: 'bottom-start', }; render() { const { adjustDateOnChange, calendarClassName, className, customInput, dateFormat, dayClassName, disabled, excludeDates, filterDate, fullWidth, iconType, injectTimes, inline, inputRef, isInvalid, isLoading, locale, maxDate, maxTime, minDate, minTime, onChange, onClear, openToDate, placeholder, popperClassName, popoverPlacement, selected, shadow, shouldCloseOnSelect, showIcon, showTimeSelect, showTimeSelectOnly, timeFormat, utcOffset, ...rest } = this.props; const classes = classNames('ouiDatePicker', { 'ouiDatePicker--shadow': shadow, 'ouiDatePicker--inline': inline, }); const datePickerClasses = classNames( 'ouiDatePicker', 'ouiFieldText', { 'ouiFieldText--fullWidth': fullWidth, 'ouiFieldText-isLoading': isLoading, 'ouiFieldText--withIcon': !inline && showIcon, 'ouiFieldText-isInvalid': isInvalid, }, className ); let optionalIcon: OuiFormControlLayoutIconsProps['icon']; if (inline || customInput || !showIcon) { optionalIcon = undefined; } else if (iconType) { optionalIcon = iconType; } else if (showTimeSelectOnly) { optionalIcon = 'clock'; } else { optionalIcon = 'calendar'; } // In case the consumer did not alter the default date format but wants // to add the time select, we append the default time format let fullDateFormat = dateFormat; if (showTimeSelect && dateFormat === ouiDatePickerDefaultDateFormat) { fullDateFormat = `${dateFormat} ${timeFormat}`; } // OuiDatePicker only supports a subset of props from react-datepicker. Using any of // the unsupported props below will spit out an error. const PropNotSupported = () => { throw new Error(`You are using a prop from react-datepicker that OuiDatePicker does not support. Please check the OUI documentation for more information.`); }; if ( // We don't want to show multiple months next to each other this.props.monthsShown || // There is no need to show week numbers this.props.showWeekNumbers || // Our css adapts to height, no need to fix it this.props.fixedHeight || // We force the month / year selection UI. No need to configure it this.props.dropdownMode || // Short month is unnecessary. Our UI has plenty of room for full months this.props.useShortMonthInDropdown || // The today button is not needed. This should always be external to the calendar this.props.todayButton || // We hide the time caption, so there is no need to overwrite its text this.props.timeCaption || // We always want keyboard accessibility on this.props.disabledKeyboardNavigation || // This is easy enough to do. It can conflict with isLoading state this.props.isClearable || // There is no reason to launch the datepicker in its own modal. Can always build these ourselves this.props.withPortal ) { return ( ); } return ( {({ locale: contextLocale }) => { return ( ); }} ); } }