When auto scale is turned off, handle user specified range correctly (#6258)
* Fix typo when saving the user specified range * Ensure range is specified when autoscale is turned off * Don't redraw unless absolutely necessary * Add 'stats' to the handled attributes for redrawing plots * Handle x axis displayRange, marker shape and size to redraw * If there are is no closest data point for a plot, skip annotation gathering * Ensure that min and max user defined ranges are valid when autoscale is disabled. Otherwise, enable autoscale to true. * Fix autoscale e2e test * updated snapshot * Update e2e/README.md Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com> Co-authored-by: John Hill <john.c.hill@nasa.gov> Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com>
@ -89,7 +89,7 @@ Read more about [Playwright Snapshots](https://playwright.dev/docs/test-snapshot
|
|||||||
#### Open MCT's implementation
|
#### Open MCT's implementation
|
||||||
|
|
||||||
- Our Snapshot tests receive a `@snapshot` tag.
|
- Our Snapshot tests receive a `@snapshot` tag.
|
||||||
- Snapshots need to be executed within the official Playwright container to ensure we're using the exact rendering platform in CI and locally.
|
- Snapshots need to be executed within the official Playwright container to ensure we're using the exact rendering platform in CI and locally. To do a valid comparison locally:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:[GET THIS VERSION FROM OUR CIRCLECI CONFIG FILE]-focal /bin/bash
|
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:[GET THIS VERSION FROM OUR CIRCLECI CONFIG FILE]-focal /bin/bash
|
||||||
@ -97,9 +97,24 @@ npm install
|
|||||||
npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @snapshot
|
npx playwright test --config=e2e/playwright-ci.config.js --project=chrome --grep @snapshot
|
||||||
```
|
```
|
||||||
|
|
||||||
### (WIP) Updating Snapshots
|
### Updating Snapshots
|
||||||
|
|
||||||
When the `@snapshot` tests fail, they will need to be evaluated to see if the failure is an acceptable change or
|
When the `@snapshot` tests fail, they will need to be evaluated to determine if the failure is an acceptable and desireable or an unintended regression.
|
||||||
|
|
||||||
|
To compare a snapshot, run a test and open the html report with the 'Expected' vs 'Actual' screenshot. If the actual screenshot is preferred, then the source-controlled 'Expected' snapshots will need to be updated with the following scripts.
|
||||||
|
|
||||||
|
MacOS
|
||||||
|
```
|
||||||
|
npm run test:e2e:updatesnapshots
|
||||||
|
```
|
||||||
|
|
||||||
|
Linux/CI
|
||||||
|
```sh
|
||||||
|
// Replace {X.X.X} with the current Playwright version
|
||||||
|
// from our package.json or circleCI configuration file
|
||||||
|
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v{X.X.X}-focal /bin/bash
|
||||||
|
npm install
|
||||||
|
npm run test:e2e:updatesnapshots
|
||||||
|
|
||||||
## Performance Testing
|
## Performance Testing
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ test.use({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('ExportAsJSON', () => {
|
test.describe('Autoscale', () => {
|
||||||
test('User can set autoscale with a valid range @snapshot', async ({ page, openmctConfig }) => {
|
test('User can set autoscale with a valid range @snapshot', async ({ page, openmctConfig }) => {
|
||||||
const { myItemsFolderName } = openmctConfig;
|
const { myItemsFolderName } = openmctConfig;
|
||||||
|
|
||||||
@ -47,16 +47,32 @@ test.describe('ExportAsJSON', () => {
|
|||||||
|
|
||||||
await testYTicks(page, ['-1.00', '-0.50', '0.00', '0.50', '1.00']);
|
await testYTicks(page, ['-1.00', '-0.50', '0.00', '0.50', '1.00']);
|
||||||
|
|
||||||
|
// enter edit mode
|
||||||
|
await page.click('button[title="Edit"]');
|
||||||
|
|
||||||
await turnOffAutoscale(page);
|
await turnOffAutoscale(page);
|
||||||
|
|
||||||
// Make sure that after turning off autoscale, the user selected range values start at the same values the plot had prior.
|
await setUserDefinedMinAndMax(page, '-2', '2');
|
||||||
await testYTicks(page, ['-1.00', '-0.50', '0.00', '0.50', '1.00']);
|
|
||||||
|
// save
|
||||||
|
await page.click('button[title="Save"]');
|
||||||
|
await Promise.all([
|
||||||
|
page.locator('li[title = "Save and Finish Editing"]').click(),
|
||||||
|
//Wait for Save Banner to appear
|
||||||
|
page.waitForSelector('.c-message-banner__message')
|
||||||
|
]);
|
||||||
|
//Wait until Save Banner is gone
|
||||||
|
await page.locator('.c-message-banner__close-button').click();
|
||||||
|
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
|
||||||
|
|
||||||
|
// Make sure that after turning off autoscale, the user entered range values are reflexted in the ticks.
|
||||||
|
await testYTicks(page, ['-2.00', '-1.50', '-1.00', '-0.50', '0.00', '0.50', '1.00', '1.50', '2.00']);
|
||||||
|
|
||||||
const canvas = page.locator('canvas').nth(1);
|
const canvas = page.locator('canvas').nth(1);
|
||||||
|
|
||||||
await canvas.hover({trial: true});
|
await canvas.hover({trial: true});
|
||||||
|
|
||||||
expect(await canvas.screenshot()).toMatchSnapshot('autoscale-canvas-prepan.png', { animations: 'disabled' });
|
expect.soft(await canvas.screenshot()).toMatchSnapshot('autoscale-canvas-prepan.png', { animations: 'disabled' });
|
||||||
|
|
||||||
//Alt Drag Start
|
//Alt Drag Start
|
||||||
await page.keyboard.down('Alt');
|
await page.keyboard.down('Alt');
|
||||||
@ -76,11 +92,12 @@ test.describe('ExportAsJSON', () => {
|
|||||||
await page.keyboard.up('Alt');
|
await page.keyboard.up('Alt');
|
||||||
|
|
||||||
// Ensure the drag worked.
|
// Ensure the drag worked.
|
||||||
await testYTicks(page, ['0.00', '0.50', '1.00', '1.50', '2.00']);
|
await testYTicks(page, ['0.00', '0.50', '1.00', '1.50', '2.00', '2.50', '3.00', '3.50']);
|
||||||
|
|
||||||
|
//Wait for canvas to stablize.
|
||||||
await canvas.hover({trial: true});
|
await canvas.hover({trial: true});
|
||||||
|
|
||||||
expect(await canvas.screenshot()).toMatchSnapshot('autoscale-canvas-panned.png', { animations: 'disabled' });
|
expect.soft(await canvas.screenshot()).toMatchSnapshot('autoscale-canvas-panned.png', { animations: 'disabled' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -152,22 +169,25 @@ async function createSinewaveOverlayPlot(page, myItemsFolderName) {
|
|||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
async function turnOffAutoscale(page) {
|
async function turnOffAutoscale(page) {
|
||||||
// enter edit mode
|
|
||||||
await page.locator('text=Unnamed Overlay Plot Snapshot >> button').nth(3).click();
|
|
||||||
|
|
||||||
// uncheck autoscale
|
// uncheck autoscale
|
||||||
await page.getByRole('listitem').filter({ hasText: 'Auto scale' }).getByRole('checkbox').uncheck();
|
await page.getByRole('listitem').filter({ hasText: 'Auto scale' }).getByRole('checkbox').uncheck();
|
||||||
|
}
|
||||||
|
|
||||||
// save
|
/**
|
||||||
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
|
* @param {import('@playwright/test').Page} page
|
||||||
await Promise.all([
|
* @param {string} min
|
||||||
page.locator('text=Save and Finish Editing').click(),
|
* @param {string} max
|
||||||
//Wait for Save Banner to appear
|
*/
|
||||||
page.waitForSelector('.c-message-banner__message')
|
async function setUserDefinedMinAndMax(page, min, max) {
|
||||||
]);
|
// set minimum value
|
||||||
//Wait until Save Banner is gone
|
const minRangeInput = page.getByRole('listitem').filter({ hasText: 'Minimum Value' }).locator('input[type="number"]');
|
||||||
await page.locator('.c-message-banner__close-button').click();
|
await minRangeInput.click();
|
||||||
await page.waitForSelector('.c-message-banner__message', { state: 'detached'});
|
await minRangeInput.fill(min);
|
||||||
|
|
||||||
|
// set maximum value
|
||||||
|
const maxRangeInput = page.getByRole('listitem').filter({ hasText: 'Maximum Value' }).locator('input[type="number"]');
|
||||||
|
await maxRangeInput.click();
|
||||||
|
await maxRangeInput.fill(max);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -179,7 +199,7 @@ async function testYTicks(page, values) {
|
|||||||
let promises = [yTicks.count().then(c => expect(c).toBe(values.length))];
|
let promises = [yTicks.count().then(c => expect(c).toBe(values.length))];
|
||||||
|
|
||||||
for (let i = 0, l = values.length; i < l; i += 1) {
|
for (let i = 0, l = values.length; i < l; i += 1) {
|
||||||
promises.push(expect(yTicks.nth(i)).toHaveText(values[i])); // eslint-disable-line
|
promises.push(expect.soft(yTicks.nth(i)).toHaveText(values[i])); // eslint-disable-line
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 16 KiB |
@ -855,7 +855,7 @@ export default {
|
|||||||
gatherNearbyAnnotations() {
|
gatherNearbyAnnotations() {
|
||||||
const nearbyAnnotations = [];
|
const nearbyAnnotations = [];
|
||||||
this.config.series.models.forEach(series => {
|
this.config.series.models.forEach(series => {
|
||||||
if (series.closest.annotationsById) {
|
if (series?.closest?.annotationsById) {
|
||||||
Object.values(series.closest.annotationsById).forEach(closeAnnotation => {
|
Object.values(series.closest.annotationsById).forEach(closeAnnotation => {
|
||||||
const addedAnnotationAlready = nearbyAnnotations.some(annotation => {
|
const addedAnnotationAlready = nearbyAnnotations.some(annotation => {
|
||||||
return _.isEqual(annotation.targets, closeAnnotation.targets)
|
return _.isEqual(annotation.targets, closeAnnotation.targets)
|
||||||
|
@ -52,6 +52,41 @@ const MARKER_SIZE = 6.0;
|
|||||||
const HIGHLIGHT_SIZE = MARKER_SIZE * 2.0;
|
const HIGHLIGHT_SIZE = MARKER_SIZE * 2.0;
|
||||||
const ANNOTATION_SIZE = MARKER_SIZE * 3.0;
|
const ANNOTATION_SIZE = MARKER_SIZE * 3.0;
|
||||||
const CLEARANCE = 15;
|
const CLEARANCE = 15;
|
||||||
|
// These attributes are changed in the plot model, but we don't need to react to the changes.
|
||||||
|
const NO_HANDLING_NEEDED_ATTRIBUTES = {
|
||||||
|
label: 'label',
|
||||||
|
values: 'values',
|
||||||
|
format: 'format',
|
||||||
|
color: 'color',
|
||||||
|
name: 'name',
|
||||||
|
unit: 'unit'
|
||||||
|
};
|
||||||
|
// These attributes in turn set one of HANDLED_ATTRIBUTES, so we don't need specific listeners for them - this prevents excessive redraws.
|
||||||
|
const IMPLICIT_HANDLED_ATTRIBUTES = {
|
||||||
|
range: 'range',
|
||||||
|
//series stats update y axis stats
|
||||||
|
stats: 'stats',
|
||||||
|
frozen: 'frozen',
|
||||||
|
autoscale: 'autoscale',
|
||||||
|
autoscalePadding: 'autoscalePadding',
|
||||||
|
logMode: 'logMode',
|
||||||
|
yKey: 'yKey'
|
||||||
|
};
|
||||||
|
// Attribute changes that we are specifically handling with listeners
|
||||||
|
const HANDLED_ATTRIBUTES = {
|
||||||
|
//X and Y Axis attributes
|
||||||
|
key: 'key',
|
||||||
|
displayRange: 'displayRange',
|
||||||
|
//series attributes
|
||||||
|
xKey: 'xKey',
|
||||||
|
interpolate: 'interpolate',
|
||||||
|
markers: 'markers',
|
||||||
|
markerShape: 'markerShape',
|
||||||
|
markerSize: 'markerSize',
|
||||||
|
alarmMarkers: 'alarmMarkers',
|
||||||
|
limitLines: 'limitLines',
|
||||||
|
yAxisId: 'yAxisId'
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct', 'domainObject', 'path'],
|
inject: ['openmct', 'domainObject', 'path'],
|
||||||
@ -138,14 +173,16 @@ export default {
|
|||||||
this.offset = {
|
this.offset = {
|
||||||
[yAxisId]: {}
|
[yAxisId]: {}
|
||||||
};
|
};
|
||||||
this.listenTo(this.config.yAxis, 'change:key', this.resetYOffsetAndSeriesDataForYAxis.bind(this, yAxisId), this);
|
this.listenTo(this.config.yAxis, `change:${HANDLED_ATTRIBUTES.displayRange}`, this.scheduleDraw);
|
||||||
this.listenTo(this.config.yAxis, 'change', this.updateLimitsAndDraw);
|
this.listenTo(this.config.yAxis, `change:${HANDLED_ATTRIBUTES.key}`, this.resetYOffsetAndSeriesDataForYAxis.bind(this, yAxisId), this);
|
||||||
|
this.listenTo(this.config.yAxis, 'change', this.redrawIfNotAlreadyHandled);
|
||||||
if (this.config.additionalYAxes.length) {
|
if (this.config.additionalYAxes.length) {
|
||||||
this.config.additionalYAxes.forEach(yAxis => {
|
this.config.additionalYAxes.forEach(yAxis => {
|
||||||
const id = yAxis.get('id');
|
const id = yAxis.get('id');
|
||||||
this.offset[id] = {};
|
this.offset[id] = {};
|
||||||
this.listenTo(yAxis, 'change', this.updateLimitsAndDraw);
|
this.listenTo(yAxis, `change:${HANDLED_ATTRIBUTES.displayRange}`, this.scheduleDraw);
|
||||||
this.listenTo(yAxis, 'change:key', this.resetYOffsetAndSeriesDataForYAxis.bind(this, id), this);
|
this.listenTo(yAxis, `change:${HANDLED_ATTRIBUTES.key}`, this.resetYOffsetAndSeriesDataForYAxis.bind(this, id), this);
|
||||||
|
this.listenTo(yAxis, 'change', this.redrawIfNotAlreadyHandled);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +199,8 @@ export default {
|
|||||||
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
|
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
|
||||||
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
|
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
|
||||||
|
|
||||||
this.listenTo(this.config.xAxis, 'change', this.updateLimitsAndDraw);
|
this.listenTo(this.config.xAxis, 'change:displayRange', this.scheduleDraw);
|
||||||
|
this.listenTo(this.config.xAxis, 'change', this.redrawIfNotAlreadyHandled);
|
||||||
this.config.series.forEach(this.onSeriesAdd, this);
|
this.config.series.forEach(this.onSeriesAdd, this);
|
||||||
this.$emit('chartLoaded');
|
this.$emit('chartLoaded');
|
||||||
},
|
},
|
||||||
@ -191,14 +229,15 @@ export default {
|
|||||||
this.changeLimitLines(mode, o, series);
|
this.changeLimitLines(mode, o, series);
|
||||||
},
|
},
|
||||||
onSeriesAdd(series) {
|
onSeriesAdd(series) {
|
||||||
this.listenTo(series, 'change:xKey', this.reDraw, this);
|
this.listenTo(series, `change:${HANDLED_ATTRIBUTES.xKey}`, this.reDraw, this);
|
||||||
this.listenTo(series, 'change:interpolate', this.changeInterpolate, this);
|
this.listenTo(series, `change:${HANDLED_ATTRIBUTES.interpolate}`, this.changeInterpolate, this);
|
||||||
this.listenTo(series, 'change:markers', this.changeMarkers, this);
|
this.listenTo(series, `change:${HANDLED_ATTRIBUTES.markers}`, this.changeMarkers, this);
|
||||||
this.listenTo(series, 'change:alarmMarkers', this.changeAlarmMarkers, this);
|
this.listenTo(series, `change:${HANDLED_ATTRIBUTES.alarmMarkers}`, this.changeAlarmMarkers, this);
|
||||||
this.listenTo(series, 'change:limitLines', this.changeLimitLines, this);
|
this.listenTo(series, `change:${HANDLED_ATTRIBUTES.limitLines}`, this.changeLimitLines, this);
|
||||||
this.listenTo(series, 'change:yAxisId', this.resetAxisAndRedraw, this);
|
this.listenTo(series, `change:${HANDLED_ATTRIBUTES.yAxisId}`, this.resetAxisAndRedraw, this);
|
||||||
// TODO: Which other changes is the listener below reacting to?
|
this.listenTo(series, `change:${HANDLED_ATTRIBUTES.markerShape}`, this.scheduleDraw, this);
|
||||||
this.listenTo(series, 'change', this.scheduleDraw);
|
this.listenTo(series, `change:${HANDLED_ATTRIBUTES.markerSize}`, this.scheduleDraw, this);
|
||||||
|
this.listenTo(series, 'change', this.redrawIfNotAlreadyHandled);
|
||||||
this.listenTo(series, 'add', this.onAddPoint);
|
this.listenTo(series, 'add', this.onAddPoint);
|
||||||
this.makeChartElement(series);
|
this.makeChartElement(series);
|
||||||
this.makeLimitLines(series);
|
this.makeLimitLines(series);
|
||||||
@ -531,8 +570,25 @@ export default {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
redrawIfNotAlreadyHandled(attribute, value, oldValue) {
|
||||||
|
if (Object.keys(HANDLED_ATTRIBUTES).includes(attribute) && oldValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(IMPLICIT_HANDLED_ATTRIBUTES).includes(attribute) && oldValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(NO_HANDLING_NEEDED_ATTRIBUTES).includes(attribute) && oldValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn('Unhandled change:', attribute);
|
||||||
|
this.updateLimitsAndDraw();
|
||||||
|
},
|
||||||
updateLimitsAndDraw() {
|
updateLimitsAndDraw() {
|
||||||
this.drawLimitLines();
|
this.drawLimitLines();
|
||||||
|
this.scheduleDraw();
|
||||||
},
|
},
|
||||||
scheduleDraw() {
|
scheduleDraw() {
|
||||||
if (!this.drawScheduled) {
|
if (!this.drawScheduled) {
|
||||||
|
@ -252,6 +252,7 @@ export default class PlotSeries extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const valueMetadata = this.metadata.value(newKey);
|
const valueMetadata = this.metadata.value(newKey);
|
||||||
|
//TODO: Should we do this even if there is a persisted config?
|
||||||
if (!this.persistedConfig || !this.persistedConfig.interpolate) {
|
if (!this.persistedConfig || !this.persistedConfig.interpolate) {
|
||||||
if (valueMetadata.format === 'enum') {
|
if (valueMetadata.format === 'enum') {
|
||||||
this.set('interpolate', 'stepAfter');
|
this.set('interpolate', 'stepAfter');
|
||||||
|
@ -57,7 +57,14 @@ export default class YAxisModel extends Model {
|
|||||||
this.listenTo(this, 'change:logMode', this.onLogModeChange, this);
|
this.listenTo(this, 'change:logMode', this.onLogModeChange, this);
|
||||||
this.listenTo(this, 'change:frozen', this.toggleFreeze, this);
|
this.listenTo(this, 'change:frozen', this.toggleFreeze, this);
|
||||||
this.listenTo(this, 'change:range', this.updateDisplayRange, this);
|
this.listenTo(this, 'change:range', this.updateDisplayRange, this);
|
||||||
this.updateDisplayRange(this.get('range'));
|
const range = this.get('range');
|
||||||
|
this.updateDisplayRange(range);
|
||||||
|
//This is an edge case and should not happen
|
||||||
|
const invalidRange = !range || (range?.min === undefined || range?.max === undefined);
|
||||||
|
const invalidAutoScaleOff = (options.model.autoscale === false) && invalidRange;
|
||||||
|
if (invalidAutoScaleOff) {
|
||||||
|
this.set('autoscale', true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @param {import('./SeriesCollection').default} seriesCollection
|
* @param {import('./SeriesCollection').default} seriesCollection
|
||||||
@ -250,23 +257,6 @@ export default class YAxisModel extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.set('displayRange', _range);
|
this.set('displayRange', _range);
|
||||||
} else {
|
|
||||||
// Otherwise use the last known displayRange as the initial
|
|
||||||
// values for the user-defined range, so that we don't end up
|
|
||||||
// with any error from an undefined user range.
|
|
||||||
|
|
||||||
const _range = this.get('displayRange');
|
|
||||||
|
|
||||||
if (!_range) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.get('logMode')) {
|
|
||||||
_range.min = antisymlog(_range.min, 10);
|
|
||||||
_range.max = antisymlog(_range.max, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set('range', _range);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,11 +367,8 @@ export default class YAxisModel extends Model {
|
|||||||
autoscale: true,
|
autoscale: true,
|
||||||
logMode: options.model?.logMode ?? false,
|
logMode: options.model?.logMode ?? false,
|
||||||
autoscalePadding: 0.1,
|
autoscalePadding: 0.1,
|
||||||
id: options.id
|
id: options.id,
|
||||||
|
range: options.model?.range
|
||||||
// 'range' is not specified here, it is undefined at first. When the
|
|
||||||
// user turns off autoscale, the current 'displayRange' is used for
|
|
||||||
// the initial value of 'range'.
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="!yAxis.autoscale && yAxis.rangeMin"
|
v-if="!yAxis.autoscale && yAxis.rangeMin !== ''"
|
||||||
class="grid-row"
|
class="grid-row"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -83,7 +83,7 @@
|
|||||||
<div class="grid-cell value">{{ yAxis.rangeMin }}</div>
|
<div class="grid-cell value">{{ yAxis.rangeMin }}</div>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="!yAxis.autoscale && yAxis.rangeMax"
|
v-if="!yAxis.autoscale && yAxis.rangeMax !== ''"
|
||||||
class="grid-row"
|
class="grid-row"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -217,8 +217,8 @@ export default {
|
|||||||
autoscale: this.config.yAxis.get('autoscale'),
|
autoscale: this.config.yAxis.get('autoscale'),
|
||||||
logMode: this.config.yAxis.get('logMode'),
|
logMode: this.config.yAxis.get('logMode'),
|
||||||
autoscalePadding: this.config.yAxis.get('autoscalePadding'),
|
autoscalePadding: this.config.yAxis.get('autoscalePadding'),
|
||||||
rangeMin: range ? range.min : '',
|
rangeMin: range?.min ?? '',
|
||||||
rangeMax: range ? range.max : ''
|
rangeMax: range?.max ?? ''
|
||||||
});
|
});
|
||||||
this.config.additionalYAxes.forEach(yAxis => {
|
this.config.additionalYAxes.forEach(yAxis => {
|
||||||
range = yAxis.get('range');
|
range = yAxis.get('range');
|
||||||
@ -230,8 +230,8 @@ export default {
|
|||||||
autoscale: yAxis.get('autoscale'),
|
autoscale: yAxis.get('autoscale'),
|
||||||
logMode: yAxis.get('logMode'),
|
logMode: yAxis.get('logMode'),
|
||||||
autoscalePadding: yAxis.get('autoscalePadding'),
|
autoscalePadding: yAxis.get('autoscalePadding'),
|
||||||
rangeMin: range ? range.min : '',
|
rangeMin: range?.min ?? '',
|
||||||
rangeMax: range ? range.max : ''
|
rangeMax: range?.max ?? ''
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@
|
|||||||
>Minimum Value</div>
|
>Minimum Value</div>
|
||||||
<div class="grid-cell value">
|
<div class="grid-cell value">
|
||||||
<input
|
<input
|
||||||
v-model="rangeMin"
|
v-model.lazy="rangeMin"
|
||||||
class="c-input--flex"
|
class="c-input--flex"
|
||||||
type="number"
|
type="number"
|
||||||
@change="updateForm('range')"
|
@change="updateForm('range')"
|
||||||
@ -94,7 +94,7 @@
|
|||||||
title="Maximum Y axis value."
|
title="Maximum Y axis value."
|
||||||
>Maximum Value</div>
|
>Maximum Value</div>
|
||||||
<div class="grid-cell value"><input
|
<div class="grid-cell value"><input
|
||||||
v-model="rangeMax"
|
v-model.lazy="rangeMax"
|
||||||
class="c-input--flex"
|
class="c-input--flex"
|
||||||
type="number"
|
type="number"
|
||||||
@change="updateForm('range')"
|
@change="updateForm('range')"
|
||||||
@ -131,6 +131,12 @@ export default {
|
|||||||
loaded: false
|
loaded: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.autoscale === false && this.validationErrors.range) {
|
||||||
|
this.autoscale = true;
|
||||||
|
this.updateForm('autoscale');
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
eventHelpers.extend(this);
|
eventHelpers.extend(this);
|
||||||
this.getConfig();
|
this.getConfig();
|
||||||
@ -172,12 +178,9 @@ export default {
|
|||||||
objectPath: `${prefix}.logMode`
|
objectPath: `${prefix}.logMode`
|
||||||
},
|
},
|
||||||
range: {
|
range: {
|
||||||
objectPath: `${prefix}.range'`,
|
objectPath: `${prefix}.range`,
|
||||||
coerce: function coerceRange(range) {
|
coerce: function coerceRange(range) {
|
||||||
const newRange = {
|
const newRange = {};
|
||||||
min: -1,
|
|
||||||
max: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
if (range && typeof range.min !== 'undefined' && range.min !== null) {
|
if (range && typeof range.min !== 'undefined' && range.min !== null) {
|
||||||
newRange.min = Number(range.min);
|
newRange.min = Number(range.min);
|
||||||
@ -222,9 +225,11 @@ export default {
|
|||||||
this.autoscale = this.yAxis.get('autoscale');
|
this.autoscale = this.yAxis.get('autoscale');
|
||||||
this.logMode = this.yAxis.get('logMode');
|
this.logMode = this.yAxis.get('logMode');
|
||||||
this.autoscalePadding = this.yAxis.get('autoscalePadding');
|
this.autoscalePadding = this.yAxis.get('autoscalePadding');
|
||||||
const range = this.yAxis.get('range') ?? this.yAxis.get('displayRange');
|
const range = this.yAxis.get('range');
|
||||||
this.rangeMin = range?.min;
|
if (range && range.min !== undefined && range.max !== undefined) {
|
||||||
this.rangeMax = range?.max;
|
this.rangeMin = range.min;
|
||||||
|
this.rangeMax = range.max;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getPrefix() {
|
getPrefix() {
|
||||||
let prefix = 'yAxis';
|
let prefix = 'yAxis';
|
||||||
@ -311,6 +316,15 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//If autoscale is turned off, we must know what the user defined min and max ranges are
|
||||||
|
if (formKey === 'autoscale' && this.autoscale === false) {
|
||||||
|
const rangeFormField = this.fields.range;
|
||||||
|
this.validationErrors.range = rangeFormField.validate?.({
|
||||||
|
min: this.rangeMin,
|
||||||
|
max: this.rangeMax
|
||||||
|
}, this.yAxis);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|