mirror of
https://github.com/nasa/openmct.git
synced 2025-05-02 16:53:24 +00:00
* 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>
327 lines
9.7 KiB
Vue
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>
|