diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-03 12:08:47 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-03 12:08:47 +0300 |
commit | 31a432e38a8b70d3ffb16afa8d7cfeee4f5f5921 (patch) | |
tree | b59f8b4e2ef7486f13adb01328a749f19b93a023 /app/assets/javascripts/vue_shared/components/date_time_picker | |
parent | 6b7b853dff4cb7e72d76bd501d75708111c95585 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/vue_shared/components/date_time_picker')
3 files changed, 117 insertions, 58 deletions
diff --git a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue index 07748482204..ddbb474bab6 100644 --- a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue +++ b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue @@ -1,20 +1,17 @@ <script> -import { GlDeprecatedButton, GlDropdown, GlDropdownItem, GlFormGroup } from '@gitlab/ui'; +import { GlIcon, GlDeprecatedButton, GlDropdown, GlDropdownItem, GlFormGroup } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; import { convertToFixedRange, isEqualTimeRanges, findTimeRange } from '~/lib/utils/datetime_range'; -import Icon from '~/vue_shared/components/icon.vue'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; import DateTimePickerInput from './date_time_picker_input.vue'; import { defaultTimeRanges, defaultTimeRange, - isValidDate, - stringToISODate, - ISODateToString, - truncateZerosInDateTime, - isDateTimePickerInputValid, + isValidInputString, + inputStringToIsoDate, + isoDateToInputString, } from './date_time_picker_lib'; const events = { @@ -24,13 +21,13 @@ const events = { export default { components: { - Icon, - TooltipOnTruncate, - DateTimePickerInput, - GlFormGroup, + GlIcon, GlDeprecatedButton, GlDropdown, GlDropdownItem, + GlFormGroup, + TooltipOnTruncate, + DateTimePickerInput, }, props: { value: { @@ -48,20 +45,41 @@ export default { required: false, default: true, }, + utc: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { timeRange: this.value, - startDate: '', - endDate: '', + + /** + * Valid start iso date string, null if not valid value + */ + startDate: null, + /** + * Invalid start date string as input by the user + */ + startFallbackVal: '', + + /** + * Valid end iso date string, null if not valid value + */ + endDate: null, + /** + * Invalid end date string as input by the user + */ + endFallbackVal: '', }; }, computed: { startInputValid() { - return isValidDate(this.startDate); + return isValidInputString(this.startDate); }, endInputValid() { - return isValidDate(this.endDate); + return isValidInputString(this.endDate); }, isValid() { return this.startInputValid && this.endInputValid; @@ -69,21 +87,31 @@ export default { startInput: { get() { - return this.startInputValid ? this.formatDate(this.startDate) : this.startDate; + return this.dateToInput(this.startDate) || this.startFallbackVal; }, set(val) { - // Attempt to set a formatted date if possible - this.startDate = isDateTimePickerInputValid(val) ? stringToISODate(val) : val; + try { + this.startDate = this.inputToDate(val); + this.startFallbackVal = null; + } catch (e) { + this.startDate = null; + this.startFallbackVal = val; + } this.timeRange = null; }, }, endInput: { get() { - return this.endInputValid ? this.formatDate(this.endDate) : this.endDate; + return this.dateToInput(this.endDate) || this.endFallbackVal; }, set(val) { - // Attempt to set a formatted date if possible - this.endDate = isDateTimePickerInputValid(val) ? stringToISODate(val) : val; + try { + this.endDate = this.inputToDate(val); + this.endFallbackVal = null; + } catch (e) { + this.endDate = null; + this.endFallbackVal = val; + } this.timeRange = null; }, }, @@ -96,10 +124,10 @@ export default { } const { start, end } = convertToFixedRange(this.value); - if (isValidDate(start) && isValidDate(end)) { + if (isValidInputString(start) && isValidInputString(end)) { return sprintf(__('%{start} to %{end}'), { - start: this.formatDate(start), - end: this.formatDate(end), + start: this.stripZerosInDateTime(this.dateToInput(start)), + end: this.stripZerosInDateTime(this.dateToInput(end)), }); } } catch { @@ -107,6 +135,13 @@ export default { } return ''; }, + + customLabel() { + if (this.utc) { + return __('Custom range (UTC)'); + } + return __('Custom range'); + }, }, watch: { value(newValue) { @@ -132,8 +167,17 @@ export default { } }, methods: { - formatDate(date) { - return truncateZerosInDateTime(ISODateToString(date)); + dateToInput(date) { + if (date === null) { + return null; + } + return isoDateToInputString(date, this.utc); + }, + inputToDate(value) { + return inputStringToIsoDate(value, this.utc); + }, + stripZerosInDateTime(str = '') { + return str.replace(' 00:00:00', ''); }, closeDropdown() { this.$refs.dropdown.hide(); @@ -169,10 +213,16 @@ export default { menu-class="date-time-picker-menu" toggle-class="date-time-picker-toggle text-truncate" > + <template #button-content> + <span class="gl-flex-grow-1 text-truncate">{{ timeWindowText }}</span> + <span v-if="utc" class="text-muted gl-font-weight-bold gl-font-sm">{{ __('UTC') }}</span> + <gl-icon class="gl-dropdown-caret" name="chevron-down" aria-hidden="true" /> + </template> + <div class="d-flex justify-content-between gl-p-2-deprecated-no-really-do-not-use-me"> <gl-form-group v-if="customEnabled" - :label="__('Custom range')" + :label="customLabel" label-for="custom-from-time" label-class="gl-pb-1-deprecated-no-really-do-not-use-me" class="custom-time-range-form-group col-md-7 gl-pl-1-deprecated-no-really-do-not-use-me gl-pr-0 m-0" @@ -214,7 +264,7 @@ export default { active-class="active" @click="setQuickRange(option)" > - <icon + <gl-icon name="mobile-issue-close" class="align-bottom" :class="{ invisible: !isOptionActive(option) }" diff --git a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_input.vue b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_input.vue index f19f8bd46b3..32a24844d71 100644 --- a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_input.vue +++ b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_input.vue @@ -6,9 +6,9 @@ import { dateFormats } from './date_time_picker_lib'; const inputGroupText = { invalidFeedback: sprintf(__('Format: %{dateFormat}'), { - dateFormat: dateFormats.stringDate, + dateFormat: dateFormats.inputFormat, }), - placeholder: dateFormats.stringDate, + placeholder: dateFormats.inputFormat, }; export default { diff --git a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_lib.js b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_lib.js index 673d981cf07..40708453d79 100644 --- a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_lib.js +++ b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_lib.js @@ -2,12 +2,6 @@ import dateformat from 'dateformat'; import { __ } from '~/locale'; /** - * Valid strings for this regex are - * 2019-10-01 and 2019-10-01 01:02:03 - */ -const dateTimePickerRegex = /^(\d{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])(?: (0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))?$/; - -/** * Default time ranges for the date picker. * @see app/assets/javascripts/lib/utils/datetime_range.js */ @@ -34,23 +28,33 @@ export const defaultTimeRanges = [ export const defaultTimeRange = defaultTimeRanges.find(tr => tr.default); export const dateFormats = { - ISODate: "yyyy-mm-dd'T'HH:MM:ss'Z'", - stringDate: 'yyyy-mm-dd HH:MM:ss', + /** + * Format used by users to input dates + * + * Note: Should be a format that can be parsed by Date.parse. + */ + inputFormat: 'yyyy-mm-dd HH:MM:ss', + /** + * Format used to strip timezone from inputs + */ + stripTimezoneFormat: "yyyy-mm-dd'T'HH:MM:ss'Z'", }; /** - * The URL params start and end need to be validated - * before passing them down to other components. + * Returns true if the date can be parsed succesfully after + * being typed by a user. * - * @param {string} dateString - * @returns true if the string is a valid date, false otherwise + * It allows some ambiguity so validation is not strict. + * + * @param {string} value - Value as typed by the user + * @returns true if the value can be parsed as a valid date, false otherwise */ -export const isValidDate = dateString => { +export const isValidInputString = value => { try { // dateformat throws error that can be caught. // This is better than using `new Date()` - if (dateString && dateString.trim()) { - dateformat(dateString, 'isoDateTime'); + if (value && value.trim()) { + dateformat(value, 'isoDateTime'); return true; } return false; @@ -60,25 +64,30 @@ export const isValidDate = dateString => { }; /** - * Convert the input in Time picker component to ISO date. + * Convert the input in time picker component to an ISO date. * - * @param {string} val - * @returns {string} + * @param {string} value + * @param {Boolean} utc - If true, it forces the date to by + * formatted using UTC format, ignoring the local time. + * @returns {Date} */ -export const stringToISODate = val => - dateformat(new Date(val.replace(/-/g, '/')), dateFormats.ISODate, true); +export const inputStringToIsoDate = (value, utc = false) => { + let date = new Date(value); + if (utc) { + // Forces date to be interpreted as UTC by stripping the timezone + // by formatting to a string with 'Z' and skipping timezone + date = dateformat(date, dateFormats.stripTimezoneFormat); + } + return dateformat(date, 'isoUtcDateTime'); +}; /** - * Convert the ISO date received from the URL to string - * for the Time picker component. + * Converts a iso date string to a formatted string for the Time picker component. * - * @param {Date} date + * @param {String} ISO Formatted date * @returns {string} */ -export const ISODateToString = date => dateformat(date, dateFormats.stringDate); - -export const truncateZerosInDateTime = datetime => datetime.replace(' 00:00:00', ''); - -export const isDateTimePickerInputValid = val => dateTimePickerRegex.test(val); +export const isoDateToInputString = (date, utc = false) => + dateformat(date, dateFormats.inputFormat, utc); export default {}; |