2.0.4 merge into master (#5297)

* Release 2.0.3

* Fix tick values for plots ticks in log mode and null check (#5119)

* [2297] When there is no display range or range, skip setting the range value when auto scale is turned off.

* If the formatted value is a number and a float, set precision to 2 decimal points.

* Fix value assignment

* Use whole numbers in log mode

* Revert whole numbers fix - need floats for values between 0 and 1.

* Handle scrolling to focused image on resize/new data (#5121)

* Scroll to focused image when view resizes - this will force scrolling to focused image when going to/from view large mode

* Scroll to the right if there is no paused focused image

* [LAD Tables] Use Telemetry Collections (#5127)

* Use telemetry collections to handle bounds checks

* added telemetry collection to alphanumeric telemetry view (#5131)

* Added animation styling for POS and CAM; adjusted cutoff for isNewImage (#5116)

* Added animation styling for POS and CAM; adjusted cutoff for isNewImage

* Remove animation from POS and CAM

* Fix transactions overwriting latest objects with stale objects on save (#5132)

* use object (map) instead of set to track dirty objects
* fix tests due to internals change

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>

* Gauge edit enabled 2.0.3 (#5133)

* Gauge plugin #4896, add edit mode

* Dynamic dial-type Gauge sizing by height and width (#5129)

* Improve sizing strategy for gauges.
* Do not install gauge by default for now

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Jamie Vigliotta <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>

* [Telemetry Collections] Include data with start and end bounds (#5145)

* Reverts forced precision for log plots axis labels (#5147)

* Condition Widgets trigger hundreds of persistence calls (#5146)

Co-authored-by: unlikelyzero <jchill2@gmail.com>

* Update version for 2.0.4 (#5255)

* Eliminate NaN conditions and clear stale duration (#5248)

* Temp source map fix 2.0.4 (#5267)

* use dev mode for production

* mode -> production

* added extra devtool options

* wip

* Imagery Fixes for release/2.0.4 (#5282)

* Fallback for height

* Remove duplicated requestHistory call since setDataTimeContext already invokes it on mount

* Inverted datumIsNotValid and refactored requestHistory

* Remove old datumIsNotValid func

* Return false if datum is falsy

* Corrected brightness/contrast input

* Clone default values to avoid mutation

* Changed index of imageTelemetry to an item within bounds

* Implement clearData test for imagery differently

* x-out clearData tests

Co-authored-by: Joshi <simplyrender@gmail.com>

* Imagery test fixes (#5293)

* Fallback for height

* Remove duplicated requestHistory call since setDataTimeContext already invokes it on mount

* Inverted datumIsNotValid and refactored requestHistory

* Remove old datumIsNotValid func

* Return false if datum is falsy

* Corrected brightness/contrast input

* Clone default values to avoid mutation

* Changed index of imageTelemetry to an item within bounds

* Implement clearData test for imagery differently

* x-out clearData tests

* Set bounds on each test rather than the wrapper

Co-authored-by: Michael Rogers <contact@mhrogers.com>

* Imagery validation fix (#5295)

* Remove check for duplicate images
* Remove commented out code and add TODO

* lint fix

* Add missing tests

* Use the master version and ignore release/2.0.4 changes

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Michael Rogers <contact@mhrogers.com>
Co-authored-by: David Tsay <3614296+davetsay@users.noreply.github.com>
Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: unlikelyzero <jchill2@gmail.com>
This commit is contained in:
Shefali Joshi 2022-06-08 18:06:31 -07:00 committed by GitHub
parent 370e6a0c37
commit 0f0c6a7b17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 104 additions and 84 deletions

View File

@ -173,7 +173,7 @@ export default {
this.$emit('filtersUpdated', this.filters); this.$emit('filtersUpdated', this.filters);
}, },
handleResetFilters() { handleResetFilters() {
this.filters = DEFAULT_FILTER_VALUES; this.filters = {...DEFAULT_FILTER_VALUES};
this.notifyFiltersChanged(); this.notifyFiltersChanged();
}, },
limitZoomRange(factor) { limitZoomRange(factor) {

View File

@ -403,6 +403,9 @@ export default {
formattedDuration() { formattedDuration() {
let result = 'N/A'; let result = 'N/A';
let negativeAge = -1; let negativeAge = -1;
if (!Number.isInteger(this.numericDuration)) {
return result;
}
if (this.numericDuration > TWENTYFOUR_HOURS) { if (this.numericDuration > TWENTYFOUR_HOURS) {
negativeAge *= (this.numericDuration / TWENTYFOUR_HOURS); negativeAge *= (this.numericDuration / TWENTYFOUR_HOURS);
@ -905,8 +908,10 @@ export default {
let currentTime = this.timeContext.clock() && this.timeContext.clock().currentValue(); let currentTime = this.timeContext.clock() && this.timeContext.clock().currentValue();
if (currentTime === undefined) { if (currentTime === undefined) {
this.numericDuration = currentTime; this.numericDuration = currentTime;
} else { } else if (Number.isInteger(this.parsedSelectedTime)) {
this.numericDuration = currentTime - this.parsedSelectedTime; this.numericDuration = currentTime - this.parsedSelectedTime;
} else {
this.numericDuration = undefined;
} }
}, },
resetAgeCSS() { resetAgeCSS() {

View File

@ -72,6 +72,7 @@
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: contain; background-size: contain;
height: 100%; //fallback value
} }
&__image { &__image {
// Present to allow Save As... image // Present to allow Save As... image

View File

@ -70,22 +70,18 @@ export default {
this.timeContext.off('timeSystem', this.timeSystemChange); this.timeContext.off('timeSystem', this.timeSystemChange);
} }
}, },
datumIsNotValid(datum) { isDatumValid(datum) {
if (this.imageHistory.length === 0) { //TODO: Add a check to see if there are duplicate images (identical image timestamp and url subsequently)
if (!datum) {
return false; return false;
} }
const datumURL = this.formatImageUrl(datum);
const lastHistoryURL = this.formatImageUrl(this.imageHistory.slice(-1)[0]);
// datum is not valid if it matches the last datum in history,
// or it is before the last datum in the history
const datumTimeCheck = this.parseTime(datum); const datumTimeCheck = this.parseTime(datum);
const historyTimeCheck = this.parseTime(this.imageHistory.slice(-1)[0]); const bounds = this.timeContext.bounds();
const matchesLast = (datumTimeCheck === historyTimeCheck) && (datumURL === lastHistoryURL);
const isStale = datumTimeCheck < historyTimeCheck;
return matchesLast || isStale; const isOutOfBounds = datumTimeCheck < bounds.start || datumTimeCheck > bounds.end;
return !isOutOfBounds;
}, },
formatImageUrl(datum) { formatImageUrl(datum) {
if (!datum) { if (!datum) {
@ -132,25 +128,19 @@ export default {
return this.requestHistory(); return this.requestHistory();
}, },
async requestHistory() { async requestHistory() {
let bounds = this.timeContext.bounds();
this.requestCount++; this.requestCount++;
const requestId = this.requestCount; const requestId = this.requestCount;
this.imageHistory = []; const bounds = this.timeContext.bounds();
let data = await this.openmct.telemetry const data = await this.openmct.telemetry
.request(this.domainObject, bounds) || []; .request(this.domainObject, bounds) || [];
// wait until new request resolves to do comparison
if (this.requestCount === requestId) { if (this.requestCount !== requestId) {
let imagery = []; return this.imageHistory = [];
data.forEach((datum) => {
let image = this.normalizeDatum(datum);
if (image) {
imagery.push(image);
}
});
//this is to optimize anything that reacts to imageHistory length
this.imageHistory = imagery;
} }
const imagery = data.filter(this.isDatumValid).map(this.normalizeDatum);
this.imageHistory = imagery;
}, },
clearData(domainObjectToClear) { clearData(domainObjectToClear) {
// global clearData button is accepted therefore no truthy check on inputted param // global clearData button is accepted therefore no truthy check on inputted param
@ -180,27 +170,29 @@ export default {
.subscribe(this.domainObject, (datum) => { .subscribe(this.domainObject, (datum) => {
let parsedTimestamp = this.parseTime(datum); let parsedTimestamp = this.parseTime(datum);
let bounds = this.timeContext.bounds(); let bounds = this.timeContext.bounds();
if (!(parsedTimestamp >= bounds.start && parsedTimestamp <= bounds.end)) {
return;
}
if (parsedTimestamp >= bounds.start && parsedTimestamp <= bounds.end) { if (this.isDatumValid(datum)) {
let image = this.normalizeDatum(datum); this.imageHistory.push(this.normalizeDatum(datum));
if (image) {
this.imageHistory.push(image);
}
} }
}); });
}, },
normalizeDatum(datum) { normalizeDatum(datum) {
if (this.datumIsNotValid(datum)) {
return;
}
let image = { ...datum }; const formattedTime = this.formatTime(datum);
image.formattedTime = this.formatTime(datum); const url = this.formatImageUrl(datum);
image.url = this.formatImageUrl(datum); const time = this.parseTime(formattedTime);
image.time = this.parseTime(image.formattedTime); const imageDownloadName = this.getImageDownloadName(datum);
image.imageDownloadName = this.getImageDownloadName(datum);
return image; return {
...datum,
formattedTime,
url,
time,
imageDownloadName
};
}, },
getFormatter(key) { getFormatter(key) {
let metadataValue = this.metadata.value(key) || { format: key }; let metadataValue = this.metadata.value(key) || { format: key };

View File

@ -84,7 +84,6 @@ describe("The Imagery View Layouts", () => {
let telemetryPromise; let telemetryPromise;
let telemetryPromiseResolve; let telemetryPromiseResolve;
let cleanupFirst; let cleanupFirst;
let isClearDataTriggered;
let openmct; let openmct;
let parent; let parent;
@ -205,20 +204,12 @@ describe("The Imagery View Layouts", () => {
cleanupFirst = []; cleanupFirst = [];
openmct = createOpenMct(); openmct = createOpenMct();
openmct.time.timeSystem('utc', {
start: START - (5 * ONE_MINUTE),
end: START + (5 * ONE_MINUTE)
});
telemetryPromise = new Promise((resolve) => { telemetryPromise = new Promise((resolve) => {
telemetryPromiseResolve = resolve; telemetryPromiseResolve = resolve;
}); });
spyOn(openmct.telemetry, 'request').and.callFake(() => { spyOn(openmct.telemetry, 'request').and.callFake(() => {
if (isClearDataTriggered) {
return [];
}
telemetryPromiseResolve(imageTelemetry); telemetryPromiseResolve(imageTelemetry);
return telemetryPromise; return telemetryPromise;
@ -337,44 +328,93 @@ describe("The Imagery View Layouts", () => {
expect(imageryView).toBeDefined(); expect(imageryView).toBeDefined();
}); });
describe("imagery view", () => { describe("Clear data action for imagery", () => {
let applicableViews; let applicableViews;
let imageryViewProvider; let imageryViewProvider;
let imageryView; let imageryView;
let componentView;
let clearDataPlugin; let clearDataPlugin;
let clearDataAction; let clearDataAction;
beforeEach(() => { beforeEach(() => {
openmct.time.timeSystem('utc', {
start: START - (5 * ONE_MINUTE),
end: START + (5 * ONE_MINUTE)
});
applicableViews = openmct.objectViews.get(imageryObject, [imageryObject]); applicableViews = openmct.objectViews.get(imageryObject, [imageryObject]);
imageryViewProvider = applicableViews.find(viewProvider => viewProvider.key === imageryKey); imageryViewProvider = applicableViews.find(viewProvider => viewProvider.key === imageryKey);
imageryView = imageryViewProvider.view(imageryObject, [imageryObject]); imageryView = imageryViewProvider.view(imageryObject, [imageryObject]);
imageryView.show(child); imageryView.show(child);
componentView = imageryView._getInstance().$children[0];
clearDataPlugin = new ClearDataPlugin( clearDataPlugin = new ClearDataPlugin(
['example.imagery'], ['example.imagery'],
{indicator: true} {indicator: true}
); );
openmct.install(clearDataPlugin); openmct.install(clearDataPlugin);
clearDataAction = openmct.actions.getAction('clear-data-action'); clearDataAction = openmct.actions.getAction('clear-data-action');
return Vue.nextTick();
});
it('clear data action is installed', () => {
expect(clearDataAction).toBeDefined();
});
it('on clearData action should clear data for object is selected', (done) => {
// force show the thumbnails // force show the thumbnails
componentView.forceShowThumbnails = true;
Vue.nextTick(() => {
let clearDataResolve;
let telemetryRequestPromise = new Promise((resolve) => {
clearDataResolve = resolve;
});
expect(parent.querySelectorAll('.c-imagery__thumb').length).not.toBe(0);
openmct.objectViews.on('clearData', (_domainObject) => {
return Vue.nextTick(() => {
expect(parent.querySelectorAll('.c-imagery__thumb').length).toBe(0);
clearDataResolve();
});
});
clearDataAction.invoke(imageryObject);
telemetryRequestPromise.then(() => {
done();
});
});
});
});
describe("imagery view", () => {
let applicableViews;
let imageryViewProvider;
let imageryView;
beforeEach(() => {
openmct.time.timeSystem('utc', {
start: START - (5 * ONE_MINUTE),
end: START + (5 * ONE_MINUTE)
});
applicableViews = openmct.objectViews.get(imageryObject, [imageryObject]);
imageryViewProvider = applicableViews.find(viewProvider => viewProvider.key === imageryKey);
imageryView = imageryViewProvider.view(imageryObject, [imageryObject]);
imageryView.show(child);
imageryView._getInstance().$children[0].forceShowThumbnails = true; imageryView._getInstance().$children[0].forceShowThumbnails = true;
return Vue.nextTick(); return Vue.nextTick();
}); });
afterEach(() => {
isClearDataTriggered = false;
// openmct.time.stopClock();
// openmct.router.removeListener('change:hash', resolveFunction);
// imageryView.destroy();
});
it("on mount should show the the most recent image", (done) => { it("on mount should show the the most recent image", () => {
//Looks like we need Vue.nextTick here so that computed properties settle down //Looks like we need Vue.nextTick here so that computed properties settle down
Vue.nextTick(() => { return Vue.nextTick(() => {
const imageInfo = getImageInfo(parent); const imageInfo = getImageInfo(parent);
expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 1].timeId)).not.toEqual(-1); expect(imageInfo.url.indexOf(imageTelemetry[COUNT - 1].timeId)).not.toEqual(-1);
done();
}); });
}); });
@ -422,7 +462,7 @@ describe("The Imagery View Layouts", () => {
it("should show that an image is not new", (done) => { it("should show that an image is not new", (done) => {
Vue.nextTick(() => { Vue.nextTick(() => {
const target = imageTelemetry[2].url; const target = imageTelemetry[4].url;
parent.querySelectorAll(`img[src='${target}']`)[0].click(); parent.querySelectorAll(`img[src='${target}']`)[0].click();
Vue.nextTick(() => { Vue.nextTick(() => {
@ -544,25 +584,6 @@ describe("The Imagery View Layouts", () => {
expect(imageSizeAfter.width).toBeLessThan(imageSizeBefore.width); expect(imageSizeAfter.width).toBeLessThan(imageSizeBefore.width);
done(); done();
}); });
it('clear data action is installed', () => {
expect(clearDataAction).toBeDefined();
});
it('on clearData action should clear data for object is selected', async (done) => {
// force show the thumbnails
imageryView._getInstance().$children[0].forceShowThumbnails = true;
await Vue.nextTick();
expect(parent.querySelectorAll('.c-imagery__thumb').length).not.toBe(0);
openmct.objectViews.on('clearData', async (_domainObject) => {
await Vue.nextTick();
expect(parent.querySelectorAll('.c-imagery__thumb').length).toBe(0);
done();
});
// stubbed telemetry data will return empty array when true
isClearDataTriggered = true;
clearDataAction.invoke(imageryObject);
});
}); });
describe("imagery time strip view", () => { describe("imagery time strip view", () => {

View File

@ -26,9 +26,10 @@ const config = {
maelstromTheme: './src/plugins/themes/maelstrom-theme.scss' maelstromTheme: './src/plugins/themes/maelstrom-theme.scss'
}, },
output: { output: {
globalObject: "this", globalObject: 'this',
filename: '[name].js', filename: '[name].js',
library: '[name]', path: path.resolve(__dirname, 'dist'),
library: 'openmct',
libraryTarget: 'umd', libraryTarget: 'umd',
publicPath: '', publicPath: '',
hashFunction: 'xxhash64', hashFunction: 'xxhash64',

View File

@ -16,5 +16,5 @@ module.exports = merge(common, {
__OPENMCT_ROOT_RELATIVE__: '""' __OPENMCT_ROOT_RELATIVE__: '""'
}) })
], ],
devtool: 'source-map' devtool: 'eval-source-map'
}); });