mirror of
https://github.com/nasa/openmct.git
synced 2025-05-06 02:28:21 +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",
|
"WCAG",
|
||||||
"stackedplot",
|
"stackedplot",
|
||||||
"Andale",
|
"Andale",
|
||||||
"unnnormalized",
|
"unnormalized",
|
||||||
"checksnapshots",
|
"checksnapshots",
|
||||||
"specced",
|
"specced",
|
||||||
"composables",
|
"composables",
|
||||||
|
@ -63,6 +63,66 @@ test.describe('Overlay Plot', () => {
|
|||||||
await expect(seriesColorSwatch).toHaveCSS('background-color', 'rgb(255, 166, 61)');
|
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 ({
|
test('Limit lines persist when series is moved to another Y Axis and on refresh', async ({
|
||||||
page
|
page
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -257,6 +257,56 @@ test.describe('Stacked Plot', () => {
|
|||||||
|
|
||||||
await assertAggregateLegendIsVisible(page);
|
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';
|
const LEGACY_SPACE = 'mct';
|
||||||
|
|
||||||
export default function CouchPlugin(options) {
|
export default function CouchPlugin(options) {
|
||||||
function normalizeOptions(unnnormalizedOptions) {
|
function normalizeOptions(unnormalizedOptions) {
|
||||||
const normalizedOptions = {};
|
const normalizedOptions = {};
|
||||||
if (typeof unnnormalizedOptions === 'string') {
|
if (typeof unnormalizedOptions === 'string') {
|
||||||
normalizedOptions.databases = [
|
normalizedOptions.databases = [
|
||||||
{
|
{
|
||||||
url: options,
|
url: options,
|
||||||
@ -41,19 +41,19 @@ export default function CouchPlugin(options) {
|
|||||||
indicator: true
|
indicator: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
} else if (!unnnormalizedOptions.databases) {
|
} else if (!unnormalizedOptions.databases) {
|
||||||
normalizedOptions.databases = [
|
normalizedOptions.databases = [
|
||||||
{
|
{
|
||||||
url: unnnormalizedOptions.url,
|
url: unnormalizedOptions.url,
|
||||||
namespace: DEFAULT_NAMESPACE,
|
namespace: DEFAULT_NAMESPACE,
|
||||||
additionalNamespaces: [LEGACY_SPACE],
|
additionalNamespaces: [LEGACY_SPACE],
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
useDesignDocuments: unnnormalizedOptions.useDesignDocuments,
|
useDesignDocuments: unnormalizedOptions.useDesignDocuments,
|
||||||
indicator: true
|
indicator: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
normalizedOptions.databases = unnnormalizedOptions.databases;
|
normalizedOptions.databases = unnormalizedOptions.databases;
|
||||||
}
|
}
|
||||||
|
|
||||||
// final sanity check, ensure we have all options
|
// final sanity check, ensure we have all options
|
||||||
|
@ -94,7 +94,7 @@ export default class XAxisModel extends Model {
|
|||||||
*/
|
*/
|
||||||
defaultModel(options) {
|
defaultModel(options) {
|
||||||
const bounds = options.openmct.time.bounds();
|
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);
|
const format = options.openmct.telemetry.getFormatter(timeSystem.timeFormat);
|
||||||
|
|
||||||
/** @type {XAxisModelType} */
|
/** @type {XAxisModelType} */
|
||||||
|
@ -79,7 +79,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="grid-cell value">{{ showLegendsForChildren ? 'Yes' : 'No' }}</div>
|
<div class="grid-cell value">{{ showLegendsForChildren ? 'Yes' : 'No' }}</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="grid-row">
|
<li v-if="showLegendDetails" class="grid-row">
|
||||||
<div
|
<div
|
||||||
class="grid-cell label"
|
class="grid-cell label"
|
||||||
title="The position of the legend relative to the plot display area."
|
title="The position of the legend relative to the plot display area."
|
||||||
@ -88,25 +88,27 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="grid-cell value capitalize">{{ position }}</div>
|
<div class="grid-cell value capitalize">{{ position }}</div>
|
||||||
</li>
|
</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">
|
<div class="grid-cell label" title="Hide the legend when the plot is small">
|
||||||
Hide when plot small
|
Hide when plot small
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-cell value">{{ hideLegendWhenSmall ? 'Yes' : 'No' }}</div>
|
<div class="grid-cell value">{{ hideLegendWhenSmall ? 'Yes' : 'No' }}</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="grid-row">
|
<li v-if="showLegendDetails" class="grid-row">
|
||||||
<div class="grid-cell label" title="Show the legend expanded by default">
|
<div class="grid-cell label" title="Show the legend expanded by default">
|
||||||
Expand by Default
|
Expand by Default
|
||||||
</div>
|
</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>
|
||||||
<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.">
|
<div class="grid-cell label" title="What to display in the legend when it's collapsed.">
|
||||||
Show when collapsed:
|
Show when collapsed:
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-cell value">{{ valueToShowWhenCollapsed.replace('nearest', '') }}</div>
|
<div class="grid-cell value">{{ valueToShowWhenCollapsed.replace('nearest', '') }}</div>
|
||||||
</li>
|
</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.">
|
<div class="grid-cell label" title="What to display in the legend when it's expanded.">
|
||||||
Show when expanded:
|
Show when expanded:
|
||||||
</div>
|
</div>
|
||||||
@ -164,6 +166,11 @@ export default {
|
|||||||
pathObjIndex === 0 && pathObject.type === 'telemetry.plot.stacked'
|
pathObjIndex === 0 && pathObject.type === 'telemetry.plot.stacked'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
showLegendDetails() {
|
||||||
|
return (
|
||||||
|
!this.isStackedPlotObject || (this.isStackedPlotObject && !this.showLegendsForChildren)
|
||||||
|
);
|
||||||
|
},
|
||||||
yAxesWithSeries() {
|
yAxesWithSeries() {
|
||||||
return this.yAxes.filter((yAxis) => yAxis.seriesCount > 0);
|
return this.yAxes.filter((yAxis) => yAxis.seriesCount > 0);
|
||||||
}
|
}
|
||||||
@ -174,9 +181,8 @@ export default {
|
|||||||
if (!this.isStackedPlotObject) {
|
if (!this.isStackedPlotObject) {
|
||||||
this.initYAxesConfiguration();
|
this.initYAxesConfiguration();
|
||||||
this.registerListeners();
|
this.registerListeners();
|
||||||
} else {
|
|
||||||
this.initLegendConfiguration();
|
|
||||||
}
|
}
|
||||||
|
this.initLegendConfiguration();
|
||||||
|
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
},
|
},
|
||||||
|
@ -132,7 +132,8 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
expanded: false
|
expanded: false,
|
||||||
|
status: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -26,12 +26,13 @@
|
|||||||
<div class="grid-cell value">
|
<div class="grid-cell value">
|
||||||
<input
|
<input
|
||||||
v-model="showLegendsForChildren"
|
v-model="showLegendsForChildren"
|
||||||
|
aria-label="Show Legends For Children"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@change="updateForm('showLegendsForChildren')"
|
@change="updateForm('showLegendsForChildren')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="grid-row">
|
<li v-if="showLegendDetails" class="grid-row">
|
||||||
<div
|
<div
|
||||||
class="grid-cell label"
|
class="grid-cell label"
|
||||||
title="The position of the legend relative to the plot display area."
|
title="The position of the legend relative to the plot display area."
|
||||||
@ -47,7 +48,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</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">
|
<div class="grid-cell label" title="Hide the legend when the plot is small">
|
||||||
Hide when plot small
|
Hide when plot small
|
||||||
</div>
|
</div>
|
||||||
@ -59,15 +60,20 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="grid-row">
|
<li v-if="showLegendDetails" class="grid-row">
|
||||||
<div class="grid-cell label" title="Show the legend expanded by default">
|
<div class="grid-cell label" title="Show the legend expanded by default">
|
||||||
Expand by default
|
Expand by default
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-cell value">
|
<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>
|
</div>
|
||||||
</li>
|
</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.">
|
<div class="grid-cell label" title="What to display in the legend when it's collapsed.">
|
||||||
When collapsed show
|
When collapsed show
|
||||||
</div>
|
</div>
|
||||||
@ -82,7 +88,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</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.">
|
<div class="grid-cell label" title="What to display in the legend when it's expanded.">
|
||||||
When expanded show
|
When expanded show
|
||||||
</div>
|
</div>
|
||||||
@ -169,6 +175,11 @@ export default {
|
|||||||
(pathObject, pathObjIndex) =>
|
(pathObject, pathObjIndex) =>
|
||||||
pathObjIndex === 0 && pathObject?.type === 'telemetry.plot.stacked'
|
pathObjIndex === 0 && pathObject?.type === 'telemetry.plot.stacked'
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
showLegendDetails() {
|
||||||
|
return (
|
||||||
|
!this.isStackedPlotObject || (this.isStackedPlotObject && !this.showLegendsForChildren)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -157,7 +157,8 @@ export default {
|
|||||||
limitLines: this.series.get('limitLines'),
|
limitLines: this.series.get('limitLines'),
|
||||||
markerSize: this.series.get('markerSize'),
|
markerSize: this.series.get('markerSize'),
|
||||||
validation: {},
|
validation: {},
|
||||||
swatchActive: false
|
swatchActive: false,
|
||||||
|
status: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -30,12 +30,17 @@
|
|||||||
<div
|
<div
|
||||||
class="c-plot-legend__view-control gl-plot-legend__view-control c-disclosure-triangle is-enabled"
|
class="c-plot-legend__view-control gl-plot-legend__view-control c-disclosure-triangle is-enabled"
|
||||||
:class="{ 'c-disclosure-triangle--expanded': isLegendExpanded }"
|
:class="{ 'c-disclosure-triangle--expanded': isLegendExpanded }"
|
||||||
@click="expandLegend"
|
@click="toggleLegend"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<div class="c-plot-legend__wrapper" :class="{ 'is-cursor-locked': cursorLocked }">
|
<div class="c-plot-legend__wrapper" :class="{ 'is-cursor-locked': cursorLocked }">
|
||||||
<!-- COLLAPSED PLOT LEGEND -->
|
<!-- 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
|
<div
|
||||||
class="c-state-indicator__alert-cursor-lock icon-cursor-lock"
|
class="c-state-indicator__alert-cursor-lock icon-cursor-lock"
|
||||||
title="Cursor is point locked. Click anywhere in the plot to unlock."
|
title="Cursor is point locked. Click anywhere in the plot to unlock."
|
||||||
@ -50,7 +55,12 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- EXPANDED PLOT LEGEND -->
|
<!-- 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
|
<div
|
||||||
class="c-state-indicator__alert-cursor-lock--verbose icon-cursor-lock"
|
class="c-state-indicator__alert-cursor-lock--verbose icon-cursor-lock"
|
||||||
title="Click anywhere in the plot to unlock."
|
title="Click anywhere in the plot to unlock."
|
||||||
@ -145,11 +155,22 @@ export default {
|
|||||||
this.legend = this.config.legend;
|
this.legend = this.config.legend;
|
||||||
this.seriesModels = [];
|
this.seriesModels = [];
|
||||||
this.listenTo(this.config.legend, 'change:position', this.updatePosition, this);
|
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();
|
this.initialize();
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
this.isLegendExpanded = this.legend.get('expanded') === true;
|
this.isLegendExpanded = this.legend.get('expanded') === true;
|
||||||
|
this.$emit('expanded', this.isLegendExpanded);
|
||||||
this.updatePosition();
|
this.updatePosition();
|
||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
@ -171,6 +192,11 @@ export default {
|
|||||||
this.registerListeners(this.config);
|
this.registerListeners(this.config);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
changeExpandDefault() {
|
||||||
|
this.isLegendExpanded = this.config.legend.model.expandByDefault;
|
||||||
|
this.legend.set('expanded', this.isLegendExpanded);
|
||||||
|
this.$emit('expanded', this.isLegendExpanded);
|
||||||
|
},
|
||||||
getConfig() {
|
getConfig() {
|
||||||
const configId = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
const configId = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||||
|
|
||||||
@ -198,9 +224,12 @@ export default {
|
|||||||
config.series.forEach(this.addSeries, this);
|
config.series.forEach(this.addSeries, this);
|
||||||
},
|
},
|
||||||
addSeries(series) {
|
addSeries(series) {
|
||||||
this.seriesModels[this.seriesModels.length] = series;
|
const existingSeries = this.getSeries(series.keyString);
|
||||||
|
if (existingSeries) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.seriesModels.push(series);
|
||||||
},
|
},
|
||||||
|
|
||||||
removeSeries(plotSeries) {
|
removeSeries(plotSeries) {
|
||||||
this.stopListening(plotSeries);
|
this.stopListening(plotSeries);
|
||||||
|
|
||||||
@ -209,7 +238,13 @@ export default {
|
|||||||
);
|
);
|
||||||
this.seriesModels.splice(seriesIndex, 1);
|
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.isLegendExpanded = !this.isLegendExpanded;
|
||||||
this.legend.set('expanded', this.isLegendExpanded);
|
this.legend.set('expanded', this.isLegendExpanded);
|
||||||
this.$emit('expanded', this.isLegendExpanded);
|
this.$emit('expanded', this.isLegendExpanded);
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="plot-legend-item"
|
class="plot-legend-item"
|
||||||
|
:aria-label="`Plot Legend Item for ${domainObject?.name}`"
|
||||||
:class="{
|
:class="{
|
||||||
'is-stale': isStale,
|
'is-stale': isStale,
|
||||||
'is-status--missing': isMissing
|
'is-status--missing': isMissing
|
||||||
@ -123,9 +124,14 @@ export default {
|
|||||||
this.seriesModels = [];
|
this.seriesModels = [];
|
||||||
eventHelpers.extend(this);
|
eventHelpers.extend(this);
|
||||||
this.config = this.getConfig();
|
this.config = this.getConfig();
|
||||||
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
|
if (this.domainObject.type === 'telemetry.plot.stacked') {
|
||||||
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
|
this.objectComposition = this.openmct.composition.get(this.domainObject);
|
||||||
this.config.series.forEach(this.onSeriesAdd, this);
|
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.legend = this.config.legend;
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
this.setupClockChangedEvent((domainObject) => {
|
this.setupClockChangedEvent((domainObject) => {
|
||||||
@ -135,6 +141,11 @@ export default {
|
|||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
this.stopListening();
|
this.stopListening();
|
||||||
|
|
||||||
|
if (this.objectComposition) {
|
||||||
|
this.objectComposition.off('add', this.addTelemetryObject);
|
||||||
|
this.objectComposition.off('remove', this.removeTelemetryObject);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getConfig() {
|
getConfig() {
|
||||||
@ -142,9 +153,36 @@ export default {
|
|||||||
|
|
||||||
return configStore.get(configId);
|
return configStore.get(configId);
|
||||||
},
|
},
|
||||||
onSeriesAdd(series, index) {
|
registerListeners(config) {
|
||||||
this.seriesModels[index] = series;
|
//listen to any changes to the telemetry endpoints that are associated with the child
|
||||||
if (series.keyString === this.seriesKeyString) {
|
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(
|
this.listenTo(
|
||||||
series,
|
series,
|
||||||
'change:color',
|
'change:color',
|
||||||
@ -163,7 +201,6 @@ export default {
|
|||||||
);
|
);
|
||||||
this.subscribeToStaleness(series.domainObject);
|
this.subscribeToStaleness(series.domainObject);
|
||||||
this.initialize();
|
this.initialize();
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onSeriesRemove(seriesToRemove) {
|
onSeriesRemove(seriesToRemove) {
|
||||||
const seriesIndexToRemove = this.seriesModels.findIndex(
|
const seriesIndexToRemove = this.seriesModels.findIndex(
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<tr
|
<tr
|
||||||
class="plot-legend-item"
|
class="plot-legend-item"
|
||||||
|
:aria-label="`Plot Legend Item for ${domainObject?.name}`"
|
||||||
:class="{
|
:class="{
|
||||||
'is-stale': isStale,
|
'is-stale': isStale,
|
||||||
'is-status--missing': isMissing
|
'is-status--missing': isMissing
|
||||||
@ -103,6 +104,7 @@ export default {
|
|||||||
isMissing: false,
|
isMissing: false,
|
||||||
colorAsHexString: '',
|
colorAsHexString: '',
|
||||||
name: '',
|
name: '',
|
||||||
|
nameWithUnit: '',
|
||||||
unit: '',
|
unit: '',
|
||||||
formattedYValue: '',
|
formattedYValue: '',
|
||||||
formattedXValue: '',
|
formattedXValue: '',
|
||||||
@ -146,9 +148,16 @@ export default {
|
|||||||
this.seriesModels = [];
|
this.seriesModels = [];
|
||||||
eventHelpers.extend(this);
|
eventHelpers.extend(this);
|
||||||
this.config = this.getConfig();
|
this.config = this.getConfig();
|
||||||
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
|
|
||||||
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
|
if (this.domainObject.type === 'telemetry.plot.stacked') {
|
||||||
this.config.series.forEach(this.onSeriesAdd, this);
|
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.legend = this.config.legend;
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
this.setupClockChangedEvent((domainObject) => {
|
this.setupClockChangedEvent((domainObject) => {
|
||||||
@ -158,11 +167,43 @@ export default {
|
|||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
this.stopListening();
|
this.stopListening();
|
||||||
|
|
||||||
|
if (this.objectComposition) {
|
||||||
|
this.objectComposition.off('add', this.addTelemetryObject);
|
||||||
|
this.objectComposition.off('remove', this.removeTelemetryObject);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onSeriesAdd(series, index) {
|
registerListeners(config) {
|
||||||
this.seriesModels[index] = series;
|
//listen to any changes to the telemetry endpoints that are associated with the child
|
||||||
if (series.keyString === this.seriesKeyString) {
|
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(
|
this.listenTo(
|
||||||
series,
|
series,
|
||||||
'change:color',
|
'change:color',
|
||||||
@ -181,7 +222,6 @@ export default {
|
|||||||
);
|
);
|
||||||
this.subscribeToStaleness(series.domainObject);
|
this.subscribeToStaleness(series.domainObject);
|
||||||
this.initialize();
|
this.initialize();
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onSeriesRemove(seriesToRemove) {
|
onSeriesRemove(seriesToRemove) {
|
||||||
const seriesIndexToRemove = this.seriesModels.findIndex(
|
const seriesIndexToRemove = this.seriesModels.findIndex(
|
||||||
|
@ -401,6 +401,7 @@ describe('the plugin', function () {
|
|||||||
const clickEvent = createMouseEvent('click');
|
const clickEvent = createMouseEvent('click');
|
||||||
|
|
||||||
legendControl.dispatchEvent(clickEvent);
|
legendControl.dispatchEvent(clickEvent);
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
let legend = element.querySelectorAll('.plot-wrapper-expanded-legend .plot-legend-item td');
|
let legend = element.querySelectorAll('.plot-wrapper-expanded-legend .plot-legend-item td');
|
||||||
expect(legend.length).toBe(6);
|
expect(legend.length).toBe(6);
|
||||||
|
@ -163,13 +163,13 @@ export default {
|
|||||||
},
|
},
|
||||||
updateView() {
|
updateView() {
|
||||||
//If this object is not persistable, then package it with it's parent
|
//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)) {
|
if (this.openmct.telemetry.isTelemetryObject(plotObject)) {
|
||||||
this.subscribeToStaleness(object);
|
this.subscribeToStaleness(plotObject);
|
||||||
} else {
|
} else {
|
||||||
// possibly overlay or other composition based plot
|
// 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('add', this.subscribeToStaleness);
|
||||||
this.composition.on('remove', this.triggerUnsubscribeFromStaleness);
|
this.composition.on('remove', this.triggerUnsubscribeFromStaleness);
|
||||||
@ -209,29 +209,34 @@ export default {
|
|||||||
this.removeSelectable = this.openmct.selection.selectable(this.$el, this.context);
|
this.removeSelectable = this.openmct.selection.selectable(this.$el, this.context);
|
||||||
},
|
},
|
||||||
getPlotObject() {
|
getPlotObject() {
|
||||||
if (this.childObject.configuration?.series) {
|
this.checkPlotConfiguration();
|
||||||
//If the object has a configuration (like an overlay plot), allow initialization of the config from it's persisted config
|
// If object is missing, warn
|
||||||
return this.childObject;
|
|
||||||
} else {
|
|
||||||
//If object is missing, warn and return object
|
|
||||||
if (this.openmct.objects.isMissing(this.childObject)) {
|
if (this.openmct.objects.isMissing(this.childObject)) {
|
||||||
console.warn('Missing domain object');
|
console.warn('Missing domain object for stacked plot', this.childObject);
|
||||||
|
|
||||||
return this.childObject;
|
|
||||||
}
|
}
|
||||||
|
return this.childObject;
|
||||||
// If the object does not have configuration, initialize the series config with the persisted config from the stacked plot
|
},
|
||||||
|
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 configId = this.openmct.objects.makeKeyString(this.childObject.identifier);
|
||||||
let config = configStore.get(configId);
|
const existingConfig = configStore.get(configId);
|
||||||
if (!config) {
|
if (!existingConfig) {
|
||||||
let persistedSeriesConfig = this.domainObject.configuration.series.find(
|
let persistedSeriesConfig = this.domainObject.configuration.series.find((seriesConfig) => {
|
||||||
(seriesConfig) => {
|
|
||||||
return this.openmct.objects.areIdsEqual(
|
return this.openmct.objects.areIdsEqual(
|
||||||
seriesConfig.identifier,
|
seriesConfig.identifier,
|
||||||
this.childObject.identifier
|
this.childObject.identifier
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (!persistedSeriesConfig) {
|
if (!persistedSeriesConfig) {
|
||||||
persistedSeriesConfig = {
|
persistedSeriesConfig = {
|
||||||
@ -240,7 +245,7 @@ export default {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
config = new PlotConfigurationModel({
|
const newConfig = new PlotConfigurationModel({
|
||||||
id: configId,
|
id: configId,
|
||||||
domainObject: {
|
domainObject: {
|
||||||
...this.childObject,
|
...this.childObject,
|
||||||
@ -260,10 +265,7 @@ export default {
|
|||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
configStore.add(configId, config);
|
configStore.add(configId, newConfig);
|
||||||
}
|
|
||||||
|
|
||||||
return this.childObject;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -360,7 +360,7 @@ describe('the plugin', function () {
|
|||||||
expect(legend[0].innerHTML).toEqual('Test Object');
|
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(
|
let legendControl = element.querySelector(
|
||||||
'.c-plot-legend__view-control.gl-plot-legend__view-control.c-disclosure-triangle'
|
'.c-plot-legend__view-control.gl-plot-legend__view-control.c-disclosure-triangle'
|
||||||
);
|
);
|
||||||
@ -368,6 +368,8 @@ describe('the plugin', function () {
|
|||||||
|
|
||||||
legendControl.dispatchEvent(clickEvent);
|
legendControl.dispatchEvent(clickEvent);
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
let legend = element.querySelectorAll('.plot-wrapper-expanded-legend .plot-legend-item td');
|
let legend = element.querySelectorAll('.plot-wrapper-expanded-legend .plot-legend-item td');
|
||||||
expect(legend.length).toBe(6);
|
expect(legend.length).toBe(6);
|
||||||
});
|
});
|
||||||
|
@ -658,13 +658,6 @@ mct-plot {
|
|||||||
|
|
||||||
.gl-plot,
|
.gl-plot,
|
||||||
.c-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 {
|
&.plot-legend-collapsed .icon-cursor-lock::before {
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user