mirror of
https://github.com/nasa/openmct.git
synced 2024-12-24 07:16:39 +00:00
Plot legends expand by default when enabled (#7453)
* expanded legend showing, but malformed * fix legends * add e2e test and aria labels for controls * fix tests * remove focused test * make plot legend items dynamic * expand legend immediately when changing default * Ensure stacked plots show cumulative legend (#7481) * simplify config loading logic * wip * fixed stacked plot legend issue * fix legend * remove console.debugs * remove extraneous prop * add test * fix legend * use props
This commit is contained in:
parent
cd6adbadde
commit
5f8d6899d2
@ -493,7 +493,7 @@
|
||||
"WCAG",
|
||||
"stackedplot",
|
||||
"Andale",
|
||||
"unnnormalized",
|
||||
"unnormalized",
|
||||
"checksnapshots",
|
||||
"specced",
|
||||
"composables",
|
||||
|
@ -63,6 +63,66 @@ test.describe('Overlay Plot', () => {
|
||||
await expect(seriesColorSwatch).toHaveCSS('background-color', 'rgb(255, 166, 61)');
|
||||
});
|
||||
|
||||
test('Plot legend expands by default', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7403'
|
||||
});
|
||||
const overlayPlot = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Overlay Plot'
|
||||
});
|
||||
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
parent: overlayPlot.uuid
|
||||
});
|
||||
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
parent: overlayPlot.uuid
|
||||
});
|
||||
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
parent: overlayPlot.uuid
|
||||
});
|
||||
|
||||
await page.goto(overlayPlot.url);
|
||||
|
||||
await page.getByRole('tab', { name: 'Config' }).click();
|
||||
|
||||
// Assert that the legend is collapsed by default
|
||||
await expect(page.getByLabel('Plot Legend Collapsed')).toBeVisible();
|
||||
await expect(page.getByLabel('Plot Legend Expanded')).toBeHidden();
|
||||
await expect(page.getByLabel('Expand by Default')).toHaveText('No');
|
||||
|
||||
expect(await page.getByLabel('Plot Legend Item').count()).toBe(3);
|
||||
|
||||
// Change the legend to expand by default
|
||||
await page.getByLabel('Edit Object').click();
|
||||
await page.getByLabel('Expand By Default').check();
|
||||
await page.getByLabel('Save').click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
// Assert that the legend is now open
|
||||
await expect(page.getByLabel('Plot Legend Collapsed')).toBeHidden();
|
||||
await expect(page.getByLabel('Plot Legend Expanded')).toBeVisible();
|
||||
await expect(page.getByRole('cell', { name: 'Name' })).toBeVisible();
|
||||
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
|
||||
await expect(page.getByRole('cell', { name: 'Value' })).toBeVisible();
|
||||
await expect(page.getByLabel('Expand by Default')).toHaveText('Yes');
|
||||
await expect(page.getByLabel('Plot Legend Item')).toHaveCount(3);
|
||||
|
||||
// Assert that the legend is expanded on page load
|
||||
await page.reload();
|
||||
await expect(page.getByLabel('Plot Legend Collapsed')).toBeHidden();
|
||||
await expect(page.getByLabel('Plot Legend Expanded')).toBeVisible();
|
||||
await expect(page.getByRole('cell', { name: 'Name' })).toBeVisible();
|
||||
await expect(page.getByRole('cell', { name: 'Timestamp' })).toBeVisible();
|
||||
await expect(page.getByRole('cell', { name: 'Value' })).toBeVisible();
|
||||
await expect(page.getByLabel('Expand by Default')).toHaveText('Yes');
|
||||
await expect(page.getByLabel('Plot Legend Item')).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('Limit lines persist when series is moved to another Y Axis and on refresh', async ({
|
||||
page
|
||||
}) => {
|
||||
|
@ -257,6 +257,56 @@ test.describe('Stacked Plot', () => {
|
||||
|
||||
await assertAggregateLegendIsVisible(page);
|
||||
});
|
||||
|
||||
test('can toggle between aggregate and per child legends', async ({ page }) => {
|
||||
// make some an overlay plot
|
||||
const overlayPlot = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Overlay Plot',
|
||||
parent: stackedPlot.uuid
|
||||
});
|
||||
|
||||
// make some SWGs for the overlay plot
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
parent: overlayPlot.uuid
|
||||
});
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
parent: overlayPlot.uuid
|
||||
});
|
||||
|
||||
await page.goto(stackedPlot.url);
|
||||
await page.getByLabel('Edit Object').click();
|
||||
await page.getByRole('tab', { name: 'Config' }).click();
|
||||
await page.getByLabel('Inspector Views').getByRole('checkbox').uncheck();
|
||||
await page.getByLabel('Expand By Default').check();
|
||||
await page.getByLabel('Save').click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
await expect(page.getByLabel('Plot Legend Expanded')).toHaveCount(1);
|
||||
await expect(page.getByLabel('Plot Legend Item')).toHaveCount(5);
|
||||
|
||||
// reload and ensure the legend is still expanded
|
||||
await page.reload();
|
||||
await expect(page.getByLabel('Plot Legend Expanded')).toHaveCount(1);
|
||||
await expect(page.getByLabel('Plot Legend Item')).toHaveCount(5);
|
||||
|
||||
// change to collapsed by default
|
||||
await page.getByLabel('Edit Object').click();
|
||||
await page.getByLabel('Expand By Default').uncheck();
|
||||
await page.getByLabel('Save').click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
await expect(page.getByLabel('Plot Legend Collapsed')).toHaveCount(1);
|
||||
await expect(page.getByLabel('Plot Legend Item')).toHaveCount(5);
|
||||
|
||||
// change it to individual legends
|
||||
await page.getByLabel('Edit Object').click();
|
||||
await page.getByRole('tab', { name: 'Config' }).click();
|
||||
await page.getByLabel('Show Legends For Children').check();
|
||||
await page.getByLabel('Save').click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
await expect(page.getByLabel('Plot Legend Collapsed')).toHaveCount(4);
|
||||
await expect(page.getByLabel('Plot Legend Item')).toHaveCount(5);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -28,9 +28,9 @@ const DEFAULT_NAMESPACE = '';
|
||||
const LEGACY_SPACE = 'mct';
|
||||
|
||||
export default function CouchPlugin(options) {
|
||||
function normalizeOptions(unnnormalizedOptions) {
|
||||
function normalizeOptions(unnormalizedOptions) {
|
||||
const normalizedOptions = {};
|
||||
if (typeof unnnormalizedOptions === 'string') {
|
||||
if (typeof unnormalizedOptions === 'string') {
|
||||
normalizedOptions.databases = [
|
||||
{
|
||||
url: options,
|
||||
@ -41,19 +41,19 @@ export default function CouchPlugin(options) {
|
||||
indicator: true
|
||||
}
|
||||
];
|
||||
} else if (!unnnormalizedOptions.databases) {
|
||||
} else if (!unnormalizedOptions.databases) {
|
||||
normalizedOptions.databases = [
|
||||
{
|
||||
url: unnnormalizedOptions.url,
|
||||
url: unnormalizedOptions.url,
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
additionalNamespaces: [LEGACY_SPACE],
|
||||
readOnly: false,
|
||||
useDesignDocuments: unnnormalizedOptions.useDesignDocuments,
|
||||
useDesignDocuments: unnormalizedOptions.useDesignDocuments,
|
||||
indicator: true
|
||||
}
|
||||
];
|
||||
} else {
|
||||
normalizedOptions.databases = unnnormalizedOptions.databases;
|
||||
normalizedOptions.databases = unnormalizedOptions.databases;
|
||||
}
|
||||
|
||||
// final sanity check, ensure we have all options
|
||||
|
@ -94,7 +94,7 @@ export default class XAxisModel extends Model {
|
||||
*/
|
||||
defaultModel(options) {
|
||||
const bounds = options.openmct.time.bounds();
|
||||
const timeSystem = options.openmct.time.timeSystem();
|
||||
const timeSystem = options.openmct.time.getTimeSystem();
|
||||
const format = options.openmct.telemetry.getFormatter(timeSystem.timeFormat);
|
||||
|
||||
/** @type {XAxisModelType} */
|
||||
|
@ -79,7 +79,7 @@
|
||||
</div>
|
||||
<div class="grid-cell value">{{ showLegendsForChildren ? 'Yes' : 'No' }}</div>
|
||||
</li>
|
||||
<li class="grid-row">
|
||||
<li v-if="showLegendDetails" class="grid-row">
|
||||
<div
|
||||
class="grid-cell label"
|
||||
title="The position of the legend relative to the plot display area."
|
||||
@ -88,25 +88,27 @@
|
||||
</div>
|
||||
<div class="grid-cell value capitalize">{{ position }}</div>
|
||||
</li>
|
||||
<li class="grid-row">
|
||||
<li v-if="showLegendDetails" class="grid-row">
|
||||
<div class="grid-cell label" title="Hide the legend when the plot is small">
|
||||
Hide when plot small
|
||||
</div>
|
||||
<div class="grid-cell value">{{ hideLegendWhenSmall ? 'Yes' : 'No' }}</div>
|
||||
</li>
|
||||
<li class="grid-row">
|
||||
<li v-if="showLegendDetails" class="grid-row">
|
||||
<div class="grid-cell label" title="Show the legend expanded by default">
|
||||
Expand by Default
|
||||
</div>
|
||||
<div class="grid-cell value">{{ expandByDefault ? 'Yes' : 'No' }}</div>
|
||||
<div aria-label="Expand by Default" class="grid-cell value">
|
||||
{{ expandByDefault ? 'Yes' : 'No' }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="grid-row">
|
||||
<li v-if="showLegendDetails" class="grid-row">
|
||||
<div class="grid-cell label" title="What to display in the legend when it's collapsed.">
|
||||
Show when collapsed:
|
||||
</div>
|
||||
<div class="grid-cell value">{{ valueToShowWhenCollapsed.replace('nearest', '') }}</div>
|
||||
</li>
|
||||
<li class="grid-row">
|
||||
<li v-if="showLegendDetails" class="grid-row">
|
||||
<div class="grid-cell label" title="What to display in the legend when it's expanded.">
|
||||
Show when expanded:
|
||||
</div>
|
||||
@ -164,6 +166,11 @@ export default {
|
||||
pathObjIndex === 0 && pathObject.type === 'telemetry.plot.stacked'
|
||||
);
|
||||
},
|
||||
showLegendDetails() {
|
||||
return (
|
||||
!this.isStackedPlotObject || (this.isStackedPlotObject && !this.showLegendsForChildren)
|
||||
);
|
||||
},
|
||||
yAxesWithSeries() {
|
||||
return this.yAxes.filter((yAxis) => yAxis.seriesCount > 0);
|
||||
}
|
||||
@ -174,9 +181,8 @@ export default {
|
||||
if (!this.isStackedPlotObject) {
|
||||
this.initYAxesConfiguration();
|
||||
this.registerListeners();
|
||||
} else {
|
||||
this.initLegendConfiguration();
|
||||
}
|
||||
this.initLegendConfiguration();
|
||||
|
||||
this.loaded = true;
|
||||
},
|
||||
|
@ -132,7 +132,8 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expanded: false
|
||||
expanded: false,
|
||||
status: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -26,12 +26,13 @@
|
||||
<div class="grid-cell value">
|
||||
<input
|
||||
v-model="showLegendsForChildren"
|
||||
aria-label="Show Legends For Children"
|
||||
type="checkbox"
|
||||
@change="updateForm('showLegendsForChildren')"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li class="grid-row">
|
||||
<li v-if="showLegendDetails" class="grid-row">
|
||||
<div
|
||||
class="grid-cell label"
|
||||
title="The position of the legend relative to the plot display area."
|
||||
@ -47,7 +48,7 @@
|
||||
</select>
|
||||
</div>
|
||||
</li>
|
||||
<li class="grid-row">
|
||||
<li v-if="showLegendDetails" class="grid-row">
|
||||
<div class="grid-cell label" title="Hide the legend when the plot is small">
|
||||
Hide when plot small
|
||||
</div>
|
||||
@ -59,15 +60,20 @@
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li class="grid-row">
|
||||
<li v-if="showLegendDetails" class="grid-row">
|
||||
<div class="grid-cell label" title="Show the legend expanded by default">
|
||||
Expand by default
|
||||
</div>
|
||||
<div class="grid-cell value">
|
||||
<input v-model="expandByDefault" type="checkbox" @change="updateForm('expandByDefault')" />
|
||||
<input
|
||||
v-model="expandByDefault"
|
||||
aria-label="Expand By Default"
|
||||
type="checkbox"
|
||||
@change="updateForm('expandByDefault')"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li class="grid-row">
|
||||
<li v-if="showLegendDetails" class="grid-row">
|
||||
<div class="grid-cell label" title="What to display in the legend when it's collapsed.">
|
||||
When collapsed show
|
||||
</div>
|
||||
@ -82,7 +88,7 @@
|
||||
</select>
|
||||
</div>
|
||||
</li>
|
||||
<li class="grid-row">
|
||||
<li v-if="showLegendDetails" class="grid-row">
|
||||
<div class="grid-cell label" title="What to display in the legend when it's expanded.">
|
||||
When expanded show
|
||||
</div>
|
||||
@ -169,6 +175,11 @@ export default {
|
||||
(pathObject, pathObjIndex) =>
|
||||
pathObjIndex === 0 && pathObject?.type === 'telemetry.plot.stacked'
|
||||
);
|
||||
},
|
||||
showLegendDetails() {
|
||||
return (
|
||||
!this.isStackedPlotObject || (this.isStackedPlotObject && !this.showLegendsForChildren)
|
||||
);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@ -157,7 +157,8 @@ export default {
|
||||
limitLines: this.series.get('limitLines'),
|
||||
markerSize: this.series.get('markerSize'),
|
||||
validation: {},
|
||||
swatchActive: false
|
||||
swatchActive: false,
|
||||
status: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -30,12 +30,17 @@
|
||||
<div
|
||||
class="c-plot-legend__view-control gl-plot-legend__view-control c-disclosure-triangle is-enabled"
|
||||
:class="{ 'c-disclosure-triangle--expanded': isLegendExpanded }"
|
||||
@click="expandLegend"
|
||||
@click="toggleLegend"
|
||||
></div>
|
||||
|
||||
<div class="c-plot-legend__wrapper" :class="{ 'is-cursor-locked': cursorLocked }">
|
||||
<!-- COLLAPSED PLOT LEGEND -->
|
||||
<div class="plot-wrapper-collapsed-legend" :class="{ 'is-cursor-locked': cursorLocked }">
|
||||
<div
|
||||
v-if="!isLegendExpanded"
|
||||
class="plot-wrapper-collapsed-legend"
|
||||
aria-label="Plot Legend Collapsed"
|
||||
:class="{ 'is-cursor-locked': cursorLocked }"
|
||||
>
|
||||
<div
|
||||
class="c-state-indicator__alert-cursor-lock icon-cursor-lock"
|
||||
title="Cursor is point locked. Click anywhere in the plot to unlock."
|
||||
@ -50,7 +55,12 @@
|
||||
/>
|
||||
</div>
|
||||
<!-- EXPANDED PLOT LEGEND -->
|
||||
<div class="plot-wrapper-expanded-legend" :class="{ 'is-cursor-locked': cursorLocked }">
|
||||
<div
|
||||
v-else
|
||||
class="plot-wrapper-expanded-legend"
|
||||
aria-label="Plot Legend Expanded"
|
||||
:class="{ 'is-cursor-locked': cursorLocked }"
|
||||
>
|
||||
<div
|
||||
class="c-state-indicator__alert-cursor-lock--verbose icon-cursor-lock"
|
||||
title="Click anywhere in the plot to unlock."
|
||||
@ -145,11 +155,22 @@ export default {
|
||||
this.legend = this.config.legend;
|
||||
this.seriesModels = [];
|
||||
this.listenTo(this.config.legend, 'change:position', this.updatePosition, this);
|
||||
|
||||
if (this.domainObject.type === 'telemetry.plot.stacked') {
|
||||
this.objectComposition = this.openmct.composition.get(this.domainObject);
|
||||
this.objectComposition.on('add', this.addTelemetryObject);
|
||||
this.objectComposition.on('remove', this.removeTelemetryObject);
|
||||
this.objectComposition.load();
|
||||
} else {
|
||||
this.registerListeners(this.config);
|
||||
}
|
||||
this.listenTo(this.config.legend, 'change:expandByDefault', this.changeExpandDefault, this);
|
||||
this.initialize();
|
||||
},
|
||||
mounted() {
|
||||
this.loaded = true;
|
||||
this.isLegendExpanded = this.legend.get('expanded') === true;
|
||||
this.$emit('expanded', this.isLegendExpanded);
|
||||
this.updatePosition();
|
||||
},
|
||||
beforeUnmount() {
|
||||
@ -171,6 +192,11 @@ export default {
|
||||
this.registerListeners(this.config);
|
||||
}
|
||||
},
|
||||
changeExpandDefault() {
|
||||
this.isLegendExpanded = this.config.legend.model.expandByDefault;
|
||||
this.legend.set('expanded', this.isLegendExpanded);
|
||||
this.$emit('expanded', this.isLegendExpanded);
|
||||
},
|
||||
getConfig() {
|
||||
const configId = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
|
||||
@ -198,9 +224,12 @@ export default {
|
||||
config.series.forEach(this.addSeries, this);
|
||||
},
|
||||
addSeries(series) {
|
||||
this.seriesModels[this.seriesModels.length] = series;
|
||||
const existingSeries = this.getSeries(series.keyString);
|
||||
if (existingSeries) {
|
||||
return;
|
||||
}
|
||||
this.seriesModels.push(series);
|
||||
},
|
||||
|
||||
removeSeries(plotSeries) {
|
||||
this.stopListening(plotSeries);
|
||||
|
||||
@ -209,7 +238,13 @@ export default {
|
||||
);
|
||||
this.seriesModels.splice(seriesIndex, 1);
|
||||
},
|
||||
expandLegend() {
|
||||
getSeries(keyStringToFind) {
|
||||
const foundSeries = this.seriesModels.find((series) => {
|
||||
return series.keyString === keyStringToFind;
|
||||
});
|
||||
return foundSeries;
|
||||
},
|
||||
toggleLegend() {
|
||||
this.isLegendExpanded = !this.isLegendExpanded;
|
||||
this.legend.set('expanded', this.isLegendExpanded);
|
||||
this.$emit('expanded', this.isLegendExpanded);
|
||||
|
@ -22,6 +22,7 @@
|
||||
<template>
|
||||
<div
|
||||
class="plot-legend-item"
|
||||
:aria-label="`Plot Legend Item for ${domainObject?.name}`"
|
||||
:class="{
|
||||
'is-stale': isStale,
|
||||
'is-status--missing': isMissing
|
||||
@ -123,9 +124,14 @@ export default {
|
||||
this.seriesModels = [];
|
||||
eventHelpers.extend(this);
|
||||
this.config = this.getConfig();
|
||||
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
|
||||
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
|
||||
this.config.series.forEach(this.onSeriesAdd, this);
|
||||
if (this.domainObject.type === 'telemetry.plot.stacked') {
|
||||
this.objectComposition = this.openmct.composition.get(this.domainObject);
|
||||
this.objectComposition.on('add', this.addTelemetryObject);
|
||||
this.objectComposition.on('remove', this.removeTelemetryObject);
|
||||
this.objectComposition.load();
|
||||
} else {
|
||||
this.registerListeners(this.config);
|
||||
}
|
||||
this.legend = this.config.legend;
|
||||
this.loaded = true;
|
||||
this.setupClockChangedEvent((domainObject) => {
|
||||
@ -135,6 +141,11 @@ export default {
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.stopListening();
|
||||
|
||||
if (this.objectComposition) {
|
||||
this.objectComposition.off('add', this.addTelemetryObject);
|
||||
this.objectComposition.off('remove', this.removeTelemetryObject);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getConfig() {
|
||||
@ -142,29 +153,55 @@ export default {
|
||||
|
||||
return configStore.get(configId);
|
||||
},
|
||||
onSeriesAdd(series, index) {
|
||||
this.seriesModels[index] = series;
|
||||
if (series.keyString === this.seriesKeyString) {
|
||||
this.listenTo(
|
||||
series,
|
||||
'change:color',
|
||||
(newColor) => {
|
||||
this.updateColor(newColor);
|
||||
},
|
||||
this
|
||||
);
|
||||
this.listenTo(
|
||||
series,
|
||||
'change:name',
|
||||
() => {
|
||||
this.updateName();
|
||||
},
|
||||
this
|
||||
);
|
||||
this.subscribeToStaleness(series.domainObject);
|
||||
this.initialize();
|
||||
registerListeners(config) {
|
||||
//listen to any changes to the telemetry endpoints that are associated with the child
|
||||
this.listenTo(config.series, 'add', this.onSeriesAdd, this);
|
||||
this.listenTo(config.series, 'remove', this.onSeriesRemove, this);
|
||||
config.series.forEach(this.onSeriesAdd, this);
|
||||
},
|
||||
addTelemetryObject(object) {
|
||||
//get the config for each child
|
||||
const configId = this.openmct.objects.makeKeyString(object.identifier);
|
||||
const config = configStore.get(configId);
|
||||
if (config) {
|
||||
this.registerListeners(config);
|
||||
}
|
||||
},
|
||||
removeTelemetryObject(identifier) {
|
||||
const configId = this.openmct.objects.makeKeyString(identifier);
|
||||
const config = configStore.get(configId);
|
||||
if (config) {
|
||||
config.series.forEach(this.onSeriesRemove, this);
|
||||
}
|
||||
},
|
||||
onSeriesAdd(series) {
|
||||
if (series.keyString !== this.seriesKeyString) {
|
||||
return;
|
||||
}
|
||||
const existingSeries = this.getSeries(series.keyString);
|
||||
if (existingSeries) {
|
||||
return;
|
||||
}
|
||||
this.seriesModels.push(series);
|
||||
this.listenTo(
|
||||
series,
|
||||
'change:color',
|
||||
(newColor) => {
|
||||
this.updateColor(newColor);
|
||||
},
|
||||
this
|
||||
);
|
||||
this.listenTo(
|
||||
series,
|
||||
'change:name',
|
||||
() => {
|
||||
this.updateName();
|
||||
},
|
||||
this
|
||||
);
|
||||
this.subscribeToStaleness(series.domainObject);
|
||||
this.initialize();
|
||||
},
|
||||
onSeriesRemove(seriesToRemove) {
|
||||
const seriesIndexToRemove = this.seriesModels.findIndex(
|
||||
(series) => series.keyString === seriesToRemove.keyString
|
||||
|
@ -22,6 +22,7 @@
|
||||
<template>
|
||||
<tr
|
||||
class="plot-legend-item"
|
||||
:aria-label="`Plot Legend Item for ${domainObject?.name}`"
|
||||
:class="{
|
||||
'is-stale': isStale,
|
||||
'is-status--missing': isMissing
|
||||
@ -103,6 +104,7 @@ export default {
|
||||
isMissing: false,
|
||||
colorAsHexString: '',
|
||||
name: '',
|
||||
nameWithUnit: '',
|
||||
unit: '',
|
||||
formattedYValue: '',
|
||||
formattedXValue: '',
|
||||
@ -146,9 +148,16 @@ export default {
|
||||
this.seriesModels = [];
|
||||
eventHelpers.extend(this);
|
||||
this.config = this.getConfig();
|
||||
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
|
||||
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
|
||||
this.config.series.forEach(this.onSeriesAdd, this);
|
||||
|
||||
if (this.domainObject.type === 'telemetry.plot.stacked') {
|
||||
this.objectComposition = this.openmct.composition.get(this.domainObject);
|
||||
this.objectComposition.on('add', this.addTelemetryObject);
|
||||
this.objectComposition.on('remove', this.removeTelemetryObject);
|
||||
this.objectComposition.load();
|
||||
} else {
|
||||
this.registerListeners(this.config);
|
||||
}
|
||||
|
||||
this.legend = this.config.legend;
|
||||
this.loaded = true;
|
||||
this.setupClockChangedEvent((domainObject) => {
|
||||
@ -158,31 +167,62 @@ export default {
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.stopListening();
|
||||
|
||||
if (this.objectComposition) {
|
||||
this.objectComposition.off('add', this.addTelemetryObject);
|
||||
this.objectComposition.off('remove', this.removeTelemetryObject);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSeriesAdd(series, index) {
|
||||
this.seriesModels[index] = series;
|
||||
if (series.keyString === this.seriesKeyString) {
|
||||
this.listenTo(
|
||||
series,
|
||||
'change:color',
|
||||
(newColor) => {
|
||||
this.updateColor(newColor);
|
||||
},
|
||||
this
|
||||
);
|
||||
this.listenTo(
|
||||
series,
|
||||
'change:name',
|
||||
() => {
|
||||
this.updateName();
|
||||
},
|
||||
this
|
||||
);
|
||||
this.subscribeToStaleness(series.domainObject);
|
||||
this.initialize();
|
||||
registerListeners(config) {
|
||||
//listen to any changes to the telemetry endpoints that are associated with the child
|
||||
this.listenTo(config.series, 'add', this.onSeriesAdd, this);
|
||||
this.listenTo(config.series, 'remove', this.onSeriesRemove, this);
|
||||
config.series.forEach(this.onSeriesAdd, this);
|
||||
},
|
||||
addTelemetryObject(object) {
|
||||
//get the config for each child
|
||||
const configId = this.openmct.objects.makeKeyString(object.identifier);
|
||||
const config = configStore.get(configId);
|
||||
if (config) {
|
||||
this.registerListeners(config);
|
||||
}
|
||||
},
|
||||
removeTelemetryObject(identifier) {
|
||||
const configId = this.openmct.objects.makeKeyString(identifier);
|
||||
const config = configStore.get(configId);
|
||||
if (config) {
|
||||
config.series.forEach(this.onSeriesRemove, this);
|
||||
}
|
||||
},
|
||||
onSeriesAdd(series) {
|
||||
if (series.keyString !== this.seriesKeyString) {
|
||||
return;
|
||||
}
|
||||
const existingSeries = this.getSeries(series.keyString);
|
||||
if (existingSeries) {
|
||||
return;
|
||||
}
|
||||
this.seriesModels.push(series);
|
||||
this.listenTo(
|
||||
series,
|
||||
'change:color',
|
||||
(newColor) => {
|
||||
this.updateColor(newColor);
|
||||
},
|
||||
this
|
||||
);
|
||||
this.listenTo(
|
||||
series,
|
||||
'change:name',
|
||||
() => {
|
||||
this.updateName();
|
||||
},
|
||||
this
|
||||
);
|
||||
this.subscribeToStaleness(series.domainObject);
|
||||
this.initialize();
|
||||
},
|
||||
onSeriesRemove(seriesToRemove) {
|
||||
const seriesIndexToRemove = this.seriesModels.findIndex(
|
||||
(series) => series.keyString === seriesToRemove.keyString
|
||||
|
@ -401,6 +401,7 @@ describe('the plugin', function () {
|
||||
const clickEvent = createMouseEvent('click');
|
||||
|
||||
legendControl.dispatchEvent(clickEvent);
|
||||
await nextTick();
|
||||
|
||||
let legend = element.querySelectorAll('.plot-wrapper-expanded-legend .plot-legend-item td');
|
||||
expect(legend.length).toBe(6);
|
||||
|
@ -163,13 +163,13 @@ export default {
|
||||
},
|
||||
updateView() {
|
||||
//If this object is not persistable, then package it with it's parent
|
||||
const object = this.getPlotObject();
|
||||
const plotObject = this.getPlotObject();
|
||||
|
||||
if (this.openmct.telemetry.isTelemetryObject(object)) {
|
||||
this.subscribeToStaleness(object);
|
||||
if (this.openmct.telemetry.isTelemetryObject(plotObject)) {
|
||||
this.subscribeToStaleness(plotObject);
|
||||
} else {
|
||||
// possibly overlay or other composition based plot
|
||||
this.composition = this.openmct.composition.get(object);
|
||||
this.composition = this.openmct.composition.get(plotObject);
|
||||
|
||||
this.composition.on('add', this.subscribeToStaleness);
|
||||
this.composition.on('remove', this.triggerUnsubscribeFromStaleness);
|
||||
@ -209,61 +209,63 @@ export default {
|
||||
this.removeSelectable = this.openmct.selection.selectable(this.$el, this.context);
|
||||
},
|
||||
getPlotObject() {
|
||||
if (this.childObject.configuration?.series) {
|
||||
//If the object has a configuration (like an overlay plot), allow initialization of the config from it's persisted config
|
||||
return this.childObject;
|
||||
} else {
|
||||
//If object is missing, warn and return object
|
||||
if (this.openmct.objects.isMissing(this.childObject)) {
|
||||
console.warn('Missing domain object');
|
||||
|
||||
return this.childObject;
|
||||
}
|
||||
|
||||
// If the object does not have configuration, initialize the series config with the persisted config from the stacked plot
|
||||
const configId = this.openmct.objects.makeKeyString(this.childObject.identifier);
|
||||
let config = configStore.get(configId);
|
||||
if (!config) {
|
||||
let persistedSeriesConfig = this.domainObject.configuration.series.find(
|
||||
(seriesConfig) => {
|
||||
return this.openmct.objects.areIdsEqual(
|
||||
seriesConfig.identifier,
|
||||
this.childObject.identifier
|
||||
);
|
||||
}
|
||||
this.checkPlotConfiguration();
|
||||
// If object is missing, warn
|
||||
if (this.openmct.objects.isMissing(this.childObject)) {
|
||||
console.warn('Missing domain object for stacked plot', this.childObject);
|
||||
}
|
||||
return this.childObject;
|
||||
},
|
||||
checkPlotConfiguration() {
|
||||
// If the object has its own configuration (like an overlay plot), don't initialize a stacked plot configuration
|
||||
// and instead use its configuration directly.
|
||||
// Otherwise ensure we've got a stacked plot item configuration ready for us.
|
||||
if (
|
||||
!this.openmct.objects.isMissing(this.childObject) &&
|
||||
!this.childObject.configuration?.series
|
||||
) {
|
||||
this.ensureStackedSeriesConfigInitialization();
|
||||
}
|
||||
},
|
||||
ensureStackedSeriesConfigInitialization() {
|
||||
const configId = this.openmct.objects.makeKeyString(this.childObject.identifier);
|
||||
const existingConfig = configStore.get(configId);
|
||||
if (!existingConfig) {
|
||||
let persistedSeriesConfig = this.domainObject.configuration.series.find((seriesConfig) => {
|
||||
return this.openmct.objects.areIdsEqual(
|
||||
seriesConfig.identifier,
|
||||
this.childObject.identifier
|
||||
);
|
||||
});
|
||||
|
||||
if (!persistedSeriesConfig) {
|
||||
persistedSeriesConfig = {
|
||||
series: {},
|
||||
yAxis: {}
|
||||
};
|
||||
}
|
||||
|
||||
config = new PlotConfigurationModel({
|
||||
id: configId,
|
||||
domainObject: {
|
||||
...this.childObject,
|
||||
configuration: {
|
||||
series: [
|
||||
{
|
||||
identifier: this.childObject.identifier,
|
||||
...persistedSeriesConfig.series
|
||||
}
|
||||
],
|
||||
yAxis: persistedSeriesConfig.yAxis
|
||||
}
|
||||
},
|
||||
openmct: this.openmct,
|
||||
palette: this.colorPalette,
|
||||
callback: (data) => {
|
||||
this.data = data;
|
||||
}
|
||||
});
|
||||
configStore.add(configId, config);
|
||||
if (!persistedSeriesConfig) {
|
||||
persistedSeriesConfig = {
|
||||
series: {},
|
||||
yAxis: {}
|
||||
};
|
||||
}
|
||||
|
||||
return this.childObject;
|
||||
const newConfig = new PlotConfigurationModel({
|
||||
id: configId,
|
||||
domainObject: {
|
||||
...this.childObject,
|
||||
configuration: {
|
||||
series: [
|
||||
{
|
||||
identifier: this.childObject.identifier,
|
||||
...persistedSeriesConfig.series
|
||||
}
|
||||
],
|
||||
yAxis: persistedSeriesConfig.yAxis
|
||||
}
|
||||
},
|
||||
openmct: this.openmct,
|
||||
palette: this.colorPalette,
|
||||
callback: (data) => {
|
||||
this.data = data;
|
||||
}
|
||||
});
|
||||
configStore.add(configId, newConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -360,7 +360,7 @@ describe('the plugin', function () {
|
||||
expect(legend[0].innerHTML).toEqual('Test Object');
|
||||
});
|
||||
|
||||
it('Renders an expanded legend for every telemetry', () => {
|
||||
it('Renders an expanded legend for every telemetry', async () => {
|
||||
let legendControl = element.querySelector(
|
||||
'.c-plot-legend__view-control.gl-plot-legend__view-control.c-disclosure-triangle'
|
||||
);
|
||||
@ -368,6 +368,8 @@ describe('the plugin', function () {
|
||||
|
||||
legendControl.dispatchEvent(clickEvent);
|
||||
|
||||
await nextTick();
|
||||
|
||||
let legend = element.querySelectorAll('.plot-wrapper-expanded-legend .plot-legend-item td');
|
||||
expect(legend.length).toBe(6);
|
||||
});
|
||||
|
@ -658,13 +658,6 @@ mct-plot {
|
||||
|
||||
.gl-plot,
|
||||
.c-plot {
|
||||
&.plot-legend-collapsed .plot-wrapper-expanded-legend {
|
||||
display: none;
|
||||
}
|
||||
&.plot-legend-expanded .plot-wrapper-collapsed-legend {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.plot-legend-collapsed .icon-cursor-lock::before {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user