openmct/src/plugins/timeConductor/timePopupFixed.vue
Jesse Mazzella 16e1ac2529
Fixes for e2e tests following the Vue 3 compat upgrade (#6837)
* clock, timeConductor and appActions fixes

* Ensure realtime uses upstream context when available
Eliminate ambiguity when looking for time conductor locator

* Fix log plot e2e tests

* Fix displayLayout e2e tests

* Specify global time conductor to fix issues with duplicate selectors with independent time contexts

* a11y: ARIA for conductor and independent time conductor

* a11y: fix label collisions, specify 'Menu' in label

* Add watch mode

* fix(e2e): update appActions and tests to use a11y locators for ITC

* Don't remove the itc popup from the DOM. Just show/hide it once it's added the first time.

* test(e2e): disable one imagery test due to known bug

* Add fixme to tagging tests, issue described in 6822

* Fix locator for time conductor popups

* Improve how time bounds are set in independent time conductor.
Fix tests for flexible layout and timestrip

* Fix some tests for itc for display layouts

* Fix Inspector tabs remounting on change

* fix autoscale test and snapshot

* Fix telemetry table test

* Fix timestrip test

* e2e: move test info annotations to within test

* 6826: Fixes padStart error due to using it on a number rather than a string

* fix(e2e): update snapshots

* fix(e2e): fix restricted notebook locator

* fix(restrictedNotebook): fix issue causing sections not to update on lock

* fix(restrictedNotebook): fix issue causing snapshots to not be able to be deleted from a locked page

- Using `this.$delete(arr, index)` does not update the `length` property on the underlying target object, so it can lead to bizarre issues where your array is of length 4 but it has 3 objects in it.

* fix: replace all instances of `$delete` with `Array.splice()` or `delete`

* fix(e2e): fix grand search test

* fix(#3117): can remove item from displayLayout via tree context menu while viewing another item

* fix: remove typo 

* Wait for background image to load

* fix(#6832): timelist events can tick down

* fix: ensure that menuitems have the raw objects so emits work

* fix: assign new arrays instead of editing state in-place

* refactor(timelist): use `getClock()` instead of `clock()`

* Revert "refactor(timelist): use `getClock()` instead of `clock()`"

This reverts commit d88855311289bcf9e0d94799cdeee25c8628f65d.

* refactor(timelist): use new timeAPI

* Stop ticking when the independent time context is disabled (#6833)

* Turn off the clock ticket for independent time conductor when it is disabled

* Fix linting issues

---------

Co-authored-by: Khalid Adil <khalidadil29@gmail.com>

* test: update couchdb notebook test

* fix: codeQL warnings

* fix(tree-item): infinite spinner issue

- Using `indexOf()` with an object was failing due to some items in the tree being Proxy-wrapped and others not. So instead, use `findIndex()` with a predicate that compares the navigationPaths of both objects

* [Timer] Remove "refresh" call, it is not needed (#6841)

* removing an unneccessary refresh that waas causing many get requests
* lets just pretend this never happened

* fix(mct-tree): maintain reactivity of all tree items

* Hide change role button in the indicator in cases where there is only… (#6840)

Hide change role button in the indicator in cases where there is only a single role available for the current user

---------

Co-authored-by: Shefali <simplyrender@gmail.com>
Co-authored-by: Khalid Adil <khalidadil29@gmail.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
Co-authored-by: David Tsay <david.e.tsay@nasa.gov>
Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
2023-07-28 02:06:41 +00:00

327 lines
9.7 KiB
Vue

<template>
<form ref="fixedDeltaInput">
<div class="c-tc-input-popup__input-grid">
<div class="pr-time-label"><em>Start</em> Date</div>
<div class="pr-time-label">Time</div>
<div class="pr-time-label"></div>
<div class="pr-time-label"><em>End</em> Date</div>
<div class="pr-time-label">Time</div>
<div class="pr-time-label"></div>
<div class="pr-time-input pr-time-input--date pr-time-input--input-and-button">
<input
ref="startDate"
v-model="formattedBounds.start"
class="c-input--datetime"
type="text"
autocorrect="off"
spellcheck="false"
aria-label="Start date"
@change="validateAllBounds('startDate')"
/>
<date-picker
v-if="isUTCBased"
class="c-ctrl-wrapper--menus-left"
:default-date-time="formattedBounds.start"
:formatter="timeFormatter"
@date-selected="startDateSelected"
/>
</div>
<div class="pr-time-input pr-time-input--time">
<input
ref="startTime"
v-model="formattedBounds.startTime"
class="c-input--datetime"
type="text"
autocorrect="off"
spellcheck="false"
aria-label="Start time"
@change="validateAllBounds('startDate')"
/>
</div>
<div class="pr-time-input pr-time-input__start-end-sep icon-arrows-right-left"></div>
<div class="pr-time-input pr-time-input--date pr-time-input--input-and-button">
<input
ref="endDate"
v-model="formattedBounds.end"
class="c-input--datetime"
type="text"
autocorrect="off"
spellcheck="false"
aria-label="End date"
@change="validateAllBounds('endDate')"
/>
<date-picker
v-if="isUTCBased"
class="c-ctrl-wrapper--menus-left"
:default-date-time="formattedBounds.end"
:formatter="timeFormatter"
@date-selected="endDateSelected"
/>
</div>
<div class="pr-time-input pr-time-input--time">
<input
ref="endTime"
v-model="formattedBounds.endTime"
class="c-input--datetime"
type="text"
autocorrect="off"
spellcheck="false"
aria-label="End time"
@change="validateAllBounds('endDate')"
/>
</div>
<div class="pr-time-input pr-time-input--buttons">
<button
class="c-button c-button--major icon-check"
:disabled="isDisabled"
aria-label="Submit time bounds"
@click.prevent="submit"
></button>
<button
class="c-button icon-x"
aria-label="Discard time bounds"
@click.prevent="hide"
></button>
</div>
</div>
</form>
</template>
<script>
import _ from 'lodash';
import DatePicker from './DatePicker.vue';
const DEFAULT_DURATION_FORMATTER = 'duration';
export default {
components: {
DatePicker
},
inject: ['openmct'],
props: {
inputBounds: {
type: Object,
required: true
},
inputTimeSystem: {
type: Object,
required: true
}
},
data() {
let timeSystem = this.openmct.time.getTimeSystem();
let durationFormatter = this.getFormatter(
timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER
);
let timeFormatter = this.getFormatter(timeSystem.timeFormat);
let bounds = this.bounds || this.openmct.time.getBounds();
return {
timeFormatter,
durationFormatter,
bounds: {
start: bounds.start,
end: bounds.end
},
formattedBounds: {
start: timeFormatter.format(bounds.start).split(' ')[0],
end: timeFormatter.format(bounds.end).split(' ')[0],
startTime: durationFormatter.format(Math.abs(bounds.start)),
endTime: durationFormatter.format(Math.abs(bounds.end))
},
isUTCBased: timeSystem.isUTCBased,
isDisabled: false
};
},
watch: {
inputBounds: {
handler(newBounds) {
this.handleNewBounds(newBounds);
},
deep: true
},
inputTimeSystem: {
handler(newTimeSystem) {
this.setTimeSystem(newTimeSystem);
},
deep: true
}
},
mounted() {
this.handleNewBounds = _.throttle(this.handleNewBounds, 300);
this.setTimeSystem(JSON.parse(JSON.stringify(this.openmct.time.getTimeSystem())));
},
beforeDestroy() {
this.clearAllValidation();
},
methods: {
handleNewBounds(bounds) {
this.setBounds(bounds);
this.setViewFromBounds(bounds);
},
clearAllValidation() {
[this.$refs.startDate, this.$refs.endDate].forEach(this.clearValidationForInput);
},
clearValidationForInput(input) {
input.setCustomValidity('');
input.title = '';
},
setBounds(bounds) {
this.bounds = bounds;
},
setViewFromBounds(bounds) {
this.formattedBounds.start = this.timeFormatter.format(bounds.start).split(' ')[0];
this.formattedBounds.end = this.timeFormatter.format(bounds.end).split(' ')[0];
this.formattedBounds.startTime = this.durationFormatter.format(Math.abs(bounds.start));
this.formattedBounds.endTime = this.durationFormatter.format(Math.abs(bounds.end));
},
setTimeSystem(timeSystem) {
this.timeSystem = timeSystem;
this.timeFormatter = this.getFormatter(timeSystem.timeFormat);
this.durationFormatter = this.getFormatter(
timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER
);
this.isUTCBased = timeSystem.isUTCBased;
},
getFormatter(key) {
return this.openmct.telemetry.getValueFormatter({
format: key
}).formatter;
},
setBoundsFromView(dismiss) {
if (this.$refs.fixedDeltaInput.checkValidity()) {
let start = this.timeFormatter.parse(
`${this.formattedBounds.start} ${this.formattedBounds.startTime}`
);
let end = this.timeFormatter.parse(
`${this.formattedBounds.end} ${this.formattedBounds.endTime}`
);
this.$emit('update', {
start: start,
end: end
});
}
if (dismiss) {
this.$emit('dismiss');
return false;
}
},
submit() {
this.validateAllBounds('startDate');
this.validateAllBounds('endDate');
this.submitForm(!this.isDisabled);
},
submitForm(dismiss) {
// Allow Vue model to catch up to user input.
// Submitting form will cause validation messages to display (but only if triggered by button click)
this.$nextTick(() => this.setBoundsFromView(dismiss));
},
validateAllBounds(ref) {
if (!this.areBoundsFormatsValid()) {
return false;
}
let validationResult = {
valid: true
};
const currentInput = this.$refs[ref];
return [this.$refs.startDate, this.$refs.endDate].every((input) => {
let boundsValues = {
start: this.timeFormatter.parse(
`${this.formattedBounds.start} ${this.formattedBounds.startTime}`
),
end: this.timeFormatter.parse(
`${this.formattedBounds.end} ${this.formattedBounds.endTime}`
)
};
//TODO: Do we need limits here? We have conductor limits disabled right now
// const limit = this.getBoundsLimit();
const limit = false;
if (this.timeSystem.isUTCBased && limit && boundsValues.end - boundsValues.start > limit) {
if (input === currentInput) {
validationResult = {
valid: false,
message: 'Start and end difference exceeds allowable limit'
};
}
} else {
if (input === currentInput) {
validationResult = this.openmct.time.validateBounds(boundsValues);
}
}
return this.handleValidationResults(input, validationResult);
});
},
areBoundsFormatsValid() {
let validationResult = {
valid: true
};
return [this.$refs.startDate, this.$refs.endDate].every((input) => {
const formattedDate =
input === this.$refs.startDate
? `${this.formattedBounds.start} ${this.formattedBounds.startTime}`
: `${this.formattedBounds.end} ${this.formattedBounds.endTime}`;
if (!this.timeFormatter.validate(formattedDate)) {
validationResult = {
valid: false,
message: 'Invalid date'
};
}
return this.handleValidationResults(input, validationResult);
});
},
getBoundsLimit() {
const configuration = this.configuration.menuOptions
.filter((option) => option.timeSystem === this.timeSystem.key)
.find((option) => option.limit);
const limit = configuration ? configuration.limit : undefined;
return limit;
},
handleValidationResults(input, validationResult) {
if (validationResult.valid !== true) {
input.setCustomValidity(validationResult.message);
input.title = validationResult.message;
this.isDisabled = true;
} else {
input.setCustomValidity('');
input.title = '';
this.isDisabled = false;
}
this.$refs.fixedDeltaInput.reportValidity();
return validationResult.valid;
},
startDateSelected(date) {
this.formattedBounds.start = this.timeFormatter.format(date).split(' ')[0];
this.validateAllBounds('startDate');
},
endDateSelected(date) {
this.formattedBounds.end = this.timeFormatter.format(date).split(' ')[0];
this.validateAllBounds('endDate');
},
hide($event) {
if ($event.target.className.indexOf('c-button icon-x') > -1) {
this.$emit('dismiss');
}
}
}
};
</script>