Differentiate aggregate telenmetry in LAD tables (#6406)

* allow lad tables rows to be selected

* rows now clickable and previewable

* trying to add type column

* aggregate and telemetry

* if aggregate, use blank for value and timestamps

* remove extraneous path lookup

* cleanup css

* add tests

* allow hiding of type column

* adjust tests to include type column
This commit is contained in:
Scott Bell 2023-03-15 05:31:15 +04:00 committed by GitHub
parent 600890c4a6
commit 1f30706d27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 120 additions and 15 deletions

View File

@ -53,16 +53,20 @@ test.describe('Testing LAD table configuration', () => {
// make sure headers are visible initially
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible();
// hide timestamp column
await page.getByLabel('Timestamp').uncheck();
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible();
// hide units column
// hide units & type column
await page.getByLabel('Units').uncheck();
await page.getByLabel('Type').uncheck();
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden();
// save and reload and verify they columns are still hidden
await page.locator('button[title="Save"]').click();
@ -70,6 +74,7 @@ test.describe('Testing LAD table configuration', () => {
await page.reload();
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden();
// Edit LAD table
await page.locator('[title="Edit"]').click();
@ -78,6 +83,7 @@ test.describe('Testing LAD table configuration', () => {
// show timestamp column
await page.getByLabel('Timestamp').check();
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
// save and reload and make sure only timestamp is still visible
@ -85,23 +91,27 @@ test.describe('Testing LAD table configuration', () => {
await page.locator('text=Save and Finish Editing').click();
await page.reload();
await expect(page.getByRole('cell', { name: 'Units' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Type' })).toBeHidden();
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
// Edit LAD table
await page.locator('[title="Edit"]').click();
await page.getByRole('tab', { name: 'LAD Table Configuration' }).getByText('LAD Table Configuration').click();
// show units column
// show units and type columns
await page.getByLabel('Units').check();
await page.getByLabel('Type').check();
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible();
// save and reload and make sure both columns are still visible
// save and reload and make sure all columns are still visible
await page.locator('button[title="Save"]').click();
await page.locator('text=Save and Finish Editing').click();
await page.reload();
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
await expect(page.getByRole('cell', { name: 'Units' })).toBeVisible();
await expect(page.getByRole('cell', { name: 'Type' })).toBeVisible();
});
});

View File

@ -22,7 +22,8 @@
<template>
<tr
class="js-lad-table__body__row"
class="js-lad-table__body__row c-table__selectable-row"
@click="clickedRow"
@contextmenu.prevent="showContextMenu"
>
<td class="js-first-data">{{ domainObject.name }}</td>
@ -40,6 +41,10 @@
>
{{ unit }}
</td>
<td
v-if="showType"
class="js-type-data"
>{{ typeLabel }}</td>
</tr>
</template>
@ -52,6 +57,9 @@ const CONTEXT_MENU_ACTIONS = [
];
const BLANK_VALUE = '---';
import identifierToString from '/src/tools/url';
import PreviewAction from "@/ui/preview/PreviewAction.js";
export default {
inject: ['openmct', 'currentView'],
props: {
@ -83,17 +91,28 @@ export default {
datum: undefined,
timestamp: undefined,
timestampKey: undefined,
composition: [],
unit: ''
};
},
computed: {
value() {
if (!this.datum) {
if (!this.datum || this.isAggregate) {
return BLANK_VALUE;
}
return this.formats[this.valueKey].format(this.datum);
},
typeLabel() {
if (this.isAggregate) {
return 'Aggregate';
}
return "Telemetry";
},
isAggregate() {
return this.composition && this.composition.length > 0;
},
valueClasses() {
let classes = [];
@ -113,7 +132,7 @@ export default {
},
formattedTimestamp() {
if (!this.timestamp) {
if (!this.timestamp || this.isAggregate) {
return BLANK_VALUE;
}
@ -131,12 +150,19 @@ export default {
},
showTimestamp() {
return !this.configuration?.hiddenColumns?.timestamp;
},
showType() {
return !this.configuration?.hiddenColumns?.type;
}
},
mounted() {
async mounted() {
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
const compositionCollection = this.openmct.composition.get(this.domainObject);
if (compositionCollection) {
this.composition = await compositionCollection.load();
}
this.timeContext = this.openmct.time.getContextForView(this.objectPath);
@ -170,11 +196,15 @@ export default {
if (this.hasUnits) {
this.setUnit();
}
this.previewAction = new PreviewAction(this.openmct);
this.previewAction.on('isVisible', this.togglePreviewState);
},
destroyed() {
this.openmct.time.off('timeSystem', this.updateTimeSystem);
this.telemetryCollection.off('add', this.setLatestValues);
this.telemetryCollection.off('clear', this.resetValues);
this.previewAction.off('isVisible', this.togglePreviewState);
this.telemetryCollection.destroy();
},
@ -189,6 +219,20 @@ export default {
});
}
},
clickedRow(event) {
if (this.openmct.editor.isEditing()) {
event.preventDefault();
this.preview(this.objectPath);
} else {
const resultUrl = identifierToString(this.openmct, this.objectPath);
this.openmct.router.navigate(resultUrl);
}
},
preview(objectPath) {
if (this.previewAction.appliesTo(objectPath)) {
this.previewAction.invoke(objectPath);
}
},
setLatestValues(data) {
this.latestDatum = data[data.length - 1];
this.updateView();

View File

@ -32,6 +32,7 @@
<th v-if="showTimestamp">Timestamp</th>
<th>Value</th>
<th v-if="hasUnits">Units</th>
<th v-if="showType">Type</th>
</tr>
</thead>
<tbody>
@ -93,6 +94,9 @@ export default {
showTimestamp() {
return !this.configuration?.hiddenColumns?.timestamp;
},
showType() {
return !this.configuration?.hiddenColumns?.type;
},
staleClass() {
if (this.staleObjects.length !== 0) {
return 'is-stale';

View File

@ -74,7 +74,8 @@ export default {
return {
headers: {
timestamp: 'Timestamp',
units: 'Units'
units: 'Units',
type: 'Type'
},
ladTableConfiguration,
isEditing: this.openmct.editor.isEditing(),

View File

@ -31,6 +31,7 @@
<th>Name</th>
<th v-if="showTimestamp">Timestamp</th>
<th>Value</th>
<th v-if="showType">Type</th>
<th v-if="hasUnits">Units</th>
</tr>
</thead>
@ -112,6 +113,9 @@ export default {
showTimestamp() {
return !this.configuration?.hiddenColumns?.timestamp;
},
showType() {
return !this.configuration?.hiddenColumns?.type;
},
staleClass() {
if (this.staleObjects.length !== 0) {
return 'is-stale';

View File

@ -152,23 +152,42 @@ describe("The LAD Table", () => {
}
}).telemetry;
// add another telemetry object as composition in lad table to test multi rows
mockObj.ladTable.composition.push(anotherTelemetryObj.identifier);
const aggregateTelemetryObj = getMockObjects({
objectKeyStrings: ['telemetry'],
overwrite: {
telemetry: {
name: "Aggregate Telemetry Object",
identifier: {
namespace: "",
key: "aggregate-telemetry-object"
}
}
}
}).telemetry;
// add another aggregate telemetry object as composition in lad table to test multi rows
aggregateTelemetryObj.composition = [anotherTelemetryObj.identifier];
mockObj.ladTable.composition.push(aggregateTelemetryObj.identifier);
beforeEach(async () => {
let telemetryRequestResolve;
let telemetryObjectResolve;
let anotherTelemetryObjectResolve;
let telemetryRequestPromise = new Promise((resolve) => {
let aggregateTelemetryObjectResolve;
const telemetryRequestPromise = new Promise((resolve) => {
telemetryRequestResolve = resolve;
});
let telemetryObjectPromise = new Promise((resolve) => {
const telemetryObjectPromise = new Promise((resolve) => {
telemetryObjectResolve = resolve;
});
let anotherTelemetryObjectPromise = new Promise((resolve) => {
const anotherTelemetryObjectPromise = new Promise((resolve) => {
anotherTelemetryObjectResolve = resolve;
});
const aggregateTelemetryObjectPromise = new Promise((resolve) => {
aggregateTelemetryObjectResolve = resolve;
});
spyOnBuiltins(['requestAnimationFrame']);
window.requestAnimationFrame.and.callFake((callBack) => {
callBack();
@ -185,10 +204,14 @@ describe("The LAD Table", () => {
telemetryObjectResolve(mockObj.telemetry);
return telemetryObjectPromise;
} else {
} else if (obj.key === 'another-telemetry-object') {
anotherTelemetryObjectResolve(anotherTelemetryObj);
return anotherTelemetryObjectPromise;
} else {
aggregateTelemetryObjectResolve(aggregateTelemetryObj);
return aggregateTelemetryObjectPromise;
}
});
@ -202,7 +225,7 @@ describe("The LAD Table", () => {
ladTableView = ladTableViewProvider.view(mockObj.ladTable, [mockObj.ladTable]);
ladTableView.show(child, true);
await Promise.all([telemetryRequestPromise, telemetryObjectPromise, anotherTelemetryObjectPromise]);
await Promise.all([telemetryRequestPromise, telemetryObjectPromise, anotherTelemetryObjectPromise, aggregateTelemetryObjectResolve]);
await Vue.nextTick();
});
@ -217,6 +240,16 @@ describe("The LAD Table", () => {
await Vue.nextTick();
const latestDate = parent.querySelector(TABLE_BODY_FIRST_ROW_SECOND_DATA).innerText;
expect(latestDate).toBe(expectedDate);
const dataType = parent.querySelector(TABLE_BODY_ROWS).querySelector('.js-type-data').innerText;
expect(dataType).toBe('Telemetry');
});
it("should show aggregate telemetry type with blank data", async () => {
await Vue.nextTick();
const lastestData = parent.querySelectorAll(TABLE_BODY_ROWS)[1].querySelectorAll('td')[2].innerText;
expect(lastestData).toBe('---');
const dataType = parent.querySelectorAll(TABLE_BODY_ROWS)[1].querySelector('.js-type-data').innerText;
expect(dataType).toBe('Aggregate');
});
it("should show the name provided for the the telemetry producing object", () => {

View File

@ -150,6 +150,15 @@ div.c-table {
tr:not(.c-table__group-header) + tr:not(.c-table__group-header) {
border-top: 1px solid $colorTabBorder;
}
transition: $transOut;
}
&__selectable-row {cursor: pointer;
&:hover {
background: $colorListItemBgHov;
filter: $filterHov;
transition: $transIn;
}
}
&__group-header {