mirror of
https://github.com/nasa/openmct.git
synced 2024-12-19 21:27:52 +00:00
test(e2e): stabilize flaky imagery tests (#7765)
This commit is contained in:
parent
1fae0a6ad5
commit
689f7cc815
@ -39,6 +39,7 @@ const config = {
|
|||||||
'vue/no-deprecated-events-api': 'warn',
|
'vue/no-deprecated-events-api': 'warn',
|
||||||
'vue/no-v-for-template-key': 'off',
|
'vue/no-v-for-template-key': 'off',
|
||||||
'vue/no-v-for-template-key-on-child': 'error',
|
'vue/no-v-for-template-key-on-child': 'error',
|
||||||
|
'vue/component-name-in-template-casing': ['error', 'PascalCase'],
|
||||||
'prettier/prettier': 'error',
|
'prettier/prettier': 'error',
|
||||||
'you-dont-need-lodash-underscore/omit': 'off',
|
'you-dont-need-lodash-underscore/omit': 'off',
|
||||||
'you-dont-need-lodash-underscore/throttle': 'off',
|
'you-dont-need-lodash-underscore/throttle': 'off',
|
||||||
|
@ -39,7 +39,7 @@ export default merge(common, {
|
|||||||
return shouldWrite;
|
return shouldWrite;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watchFiles: ['**/*.css'],
|
watchFiles: ['src/**/*.css', 'example/**/*.css'],
|
||||||
static: {
|
static: {
|
||||||
directory: fileURLToPath(new URL('../dist', import.meta.url)),
|
directory: fileURLToPath(new URL('../dist', import.meta.url)),
|
||||||
publicPath: '/dist',
|
publicPath: '/dist',
|
||||||
|
@ -78,13 +78,13 @@ async function createDomainObjectWithDefaults(
|
|||||||
|
|
||||||
// Navigate to the parent object. This is necessary to create the object
|
// Navigate to the parent object. This is necessary to create the object
|
||||||
// in the correct location, such as a folder, layout, or plot.
|
// in the correct location, such as a folder, layout, or plot.
|
||||||
await page.goto(`${parentUrl}`);
|
await page.goto(parentUrl);
|
||||||
|
|
||||||
//Click the Create button
|
// Click the Create button
|
||||||
await page.getByRole('button', { name: 'Create' }).click();
|
await page.getByRole('button', { name: 'Create', exact: true }).click();
|
||||||
|
|
||||||
// Click the object specified by 'type'
|
// Click the object specified by 'type'-- case insensitive
|
||||||
await page.click(`li[role='menuitem']:text("${type}")`);
|
await page.getByRole('menuitem', { name: new RegExp(`^${type}$`, 'i') }).click();
|
||||||
|
|
||||||
// Modify the name input field of the domain object to accept 'name'
|
// Modify the name input field of the domain object to accept 'name'
|
||||||
const nameInput = page.locator('form[name="mctForm"] .first input[type="text"]');
|
const nameInput = page.locator('form[name="mctForm"] .first input[type="text"]');
|
||||||
@ -353,7 +353,7 @@ async function getFocusedObjectUuid(page) {
|
|||||||
* @returns {Promise<string>} the url of the object
|
* @returns {Promise<string>} the url of the object
|
||||||
*/
|
*/
|
||||||
async function getHashUrlToDomainObject(page, identifier) {
|
async function getHashUrlToDomainObject(page, identifier) {
|
||||||
await page.waitForLoadState('load');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
const hashUrl = await page.evaluate(async (objectIdentifier) => {
|
const hashUrl = await page.evaluate(async (objectIdentifier) => {
|
||||||
const path = await window.openmct.objects.getOriginalPath(objectIdentifier);
|
const path = await window.openmct.objects.getOriginalPath(objectIdentifier);
|
||||||
let url =
|
let url =
|
||||||
|
@ -25,14 +25,21 @@ This test suite is dedicated to tests which verify the basic operations surround
|
|||||||
but only assume that example imagery is present.
|
but only assume that example imagery is present.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createDomainObjectWithDefaults, setRealTimeMode } from '../../../../appActions.js';
|
import {
|
||||||
import { waitForAnimations } from '../../../../baseFixtures.js';
|
createDomainObjectWithDefaults,
|
||||||
|
navigateToObjectWithRealTime,
|
||||||
|
setRealTimeMode
|
||||||
|
} from '../../../../appActions.js';
|
||||||
|
import { MISSION_TIME } from '../../../../constants.js';
|
||||||
import { expect, test } from '../../../../pluginFixtures.js';
|
import { expect, test } from '../../../../pluginFixtures.js';
|
||||||
const backgroundImageSelector = '.c-imagery__main-image__background-image';
|
|
||||||
const panHotkey = process.platform === 'linux' ? ['Shift', 'Alt'] : ['Alt'];
|
const panHotkey = process.platform === 'linux' ? ['Shift', 'Alt'] : ['Alt'];
|
||||||
const tagHotkey = ['Shift', 'Alt'];
|
const tagHotkey = ['Shift', 'Alt'];
|
||||||
const expectedAltText = process.platform === 'linux' ? 'Shift+Alt drag to pan' : 'Alt drag to pan';
|
const expectedAltText = process.platform === 'linux' ? 'Shift+Alt drag to pan' : 'Alt drag to pan';
|
||||||
const thumbnailUrlParamsRegexp = /\?w=100&h=100/;
|
const thumbnailUrlParamsRegexp = /\?w=100&h=100/;
|
||||||
|
const IMAGE_LOAD_DELAY = 5 * 1000;
|
||||||
|
const MOUSE_WHEEL_DELTA_Y = 120;
|
||||||
|
const FIVE_MINUTES = 1000 * 60 * 5;
|
||||||
|
const THIRTY_SECONDS = 1000 * 30;
|
||||||
|
|
||||||
//The following block of tests verifies the basic functionality of example imagery and serves as a template for Imagery objects embedded in other objects.
|
//The following block of tests verifies the basic functionality of example imagery and serves as a template for Imagery objects embedded in other objects.
|
||||||
test.describe('Example Imagery Object', () => {
|
test.describe('Example Imagery Object', () => {
|
||||||
@ -45,8 +52,7 @@ test.describe('Example Imagery Object', () => {
|
|||||||
|
|
||||||
// Verify that the created object is focused
|
// Verify that the created object is focused
|
||||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(exampleImagery.name);
|
await expect(page.locator('.l-browse-bar__object-name')).toContainText(exampleImagery.name);
|
||||||
await page.locator('.c-imagery__main-image__bg').hover({ trial: true });
|
await page.getByLabel('Focused Image Element').hover({ trial: true });
|
||||||
await page.locator(backgroundImageSelector).waitFor();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can use Mouse Wheel to zoom in and out of latest image', async ({ page }) => {
|
test('Can use Mouse Wheel to zoom in and out of latest image', async ({ page }) => {
|
||||||
@ -63,7 +69,7 @@ test.describe('Example Imagery Object', () => {
|
|||||||
|
|
||||||
test('Can right click on image and open it in a new tab @2p', async ({ page, context }) => {
|
test('Can right click on image and open it in a new tab @2p', async ({ page, context }) => {
|
||||||
// try to right click on image
|
// try to right click on image
|
||||||
const backgroundImage = await page.locator(backgroundImageSelector);
|
const backgroundImage = page.getByLabel('Focused Image Element');
|
||||||
await backgroundImage.click({
|
await backgroundImage.click({
|
||||||
button: 'right',
|
button: 'right',
|
||||||
// eslint-disable-next-line playwright/no-force-option
|
// eslint-disable-next-line playwright/no-force-option
|
||||||
@ -80,7 +86,7 @@ test.describe('Example Imagery Object', () => {
|
|||||||
const newPage = await pagePromise;
|
const newPage = await pagePromise;
|
||||||
await newPage.waitForLoadState();
|
await newPage.waitForLoadState();
|
||||||
// expect new tab url to have jpg in it
|
// expect new tab url to have jpg in it
|
||||||
await expect(newPage.url()).toContain('.jpg');
|
expect(newPage.url()).toContain('.jpg');
|
||||||
});
|
});
|
||||||
|
|
||||||
// this requires CORS to be enabled in some fashion
|
// this requires CORS to be enabled in some fashion
|
||||||
@ -105,27 +111,36 @@ test.describe('Example Imagery Object', () => {
|
|||||||
type: 'issue',
|
type: 'issue',
|
||||||
description: 'https://github.com/nasa/openmct/issues/6821'
|
description: 'https://github.com/nasa/openmct/issues/6821'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test independent fixed time with global fixed time
|
// Test independent fixed time with global fixed time
|
||||||
// flip on independent time conductor
|
// flip on independent time conductor
|
||||||
await page.getByRole('switch', { name: 'Enable Independent Time Conductor' }).click();
|
await page.getByLabel('Enable Independent Time Conductor').click();
|
||||||
|
|
||||||
// Adding in delay to address flakiness of ITC test-- button event handlers not registering in time
|
|
||||||
await expect(page.locator('#independentTCToggle')).toBeChecked();
|
await expect(page.locator('#independentTCToggle')).toBeChecked();
|
||||||
await expect(page.locator('.c-compact-tc').first()).toBeVisible();
|
await expect(page.locator('.c-compact-tc').first()).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Independent Time Conductor Settings' })
|
||||||
|
).toBeEnabled();
|
||||||
await page.getByRole('button', { name: 'Independent Time Conductor Settings' }).click();
|
await page.getByRole('button', { name: 'Independent Time Conductor Settings' }).click();
|
||||||
|
await expect(page.getByLabel('Time Conductor Options')).toBeVisible();
|
||||||
|
await page.getByLabel('Time Conductor Options').hover({ trial: true });
|
||||||
|
|
||||||
|
await page.getByRole('textbox', { name: 'Start date' }).hover({ trial: true });
|
||||||
await page.getByRole('textbox', { name: 'Start date' }).fill('2021-12-30');
|
await page.getByRole('textbox', { name: 'Start date' }).fill('2021-12-30');
|
||||||
await page.keyboard.press('Tab');
|
await page.keyboard.press('Tab');
|
||||||
|
await page.getByRole('textbox', { name: 'Start time' }).hover({ trial: true });
|
||||||
await page.getByRole('textbox', { name: 'Start time' }).fill('01:01:00');
|
await page.getByRole('textbox', { name: 'Start time' }).fill('01:01:00');
|
||||||
await page.keyboard.press('Tab');
|
await page.keyboard.press('Tab');
|
||||||
|
await page.getByRole('textbox', { name: 'End date' }).hover({ trial: true });
|
||||||
await page.getByRole('textbox', { name: 'End date' }).fill('2021-12-30');
|
await page.getByRole('textbox', { name: 'End date' }).fill('2021-12-30');
|
||||||
await page.keyboard.press('Tab');
|
await page.keyboard.press('Tab');
|
||||||
|
await page.getByRole('textbox', { name: 'End time' }).hover({ trial: true });
|
||||||
await page.getByRole('textbox', { name: 'End time' }).fill('01:11:00');
|
await page.getByRole('textbox', { name: 'End time' }).fill('01:11:00');
|
||||||
await page.keyboard.press('Tab');
|
await page.getByRole('textbox', { name: 'End time' }).fill('01:11:00');
|
||||||
await page.keyboard.press('Enter');
|
await page.getByLabel('Submit time bounds').click();
|
||||||
|
|
||||||
// check image date
|
// wait for image thumbnails to stabilize
|
||||||
|
await page.getByLabel('Image Thumbnails', { exact: true }).hover({ trial: true });
|
||||||
await expect(page.getByText('2021-12-30 01:01:00.000Z').first()).toBeVisible();
|
await expect(page.getByText('2021-12-30 01:01:00.000Z').first()).toBeVisible();
|
||||||
|
|
||||||
// flip it off
|
// flip it off
|
||||||
@ -166,14 +181,11 @@ test.describe('Example Imagery Object', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Can use alt+drag to move around image once zoomed in', async ({ page }) => {
|
test('Can use alt+drag to move around image once zoomed in', async ({ page }) => {
|
||||||
const deltaYStep = 100; //equivalent to 1x zoom
|
|
||||||
|
|
||||||
await page.locator('.c-imagery__main-image__bg').hover({ trial: true });
|
await page.locator('.c-imagery__main-image__bg').hover({ trial: true });
|
||||||
|
|
||||||
// zoom in
|
// zoom in
|
||||||
await page.mouse.wheel(0, deltaYStep * 2);
|
await page.mouse.wheel(0, MOUSE_WHEEL_DELTA_Y * 2);
|
||||||
await page.locator('.c-imagery__main-image__bg').hover({ trial: true });
|
const zoomedBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
|
||||||
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
|
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
|
||||||
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
|
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
|
||||||
// move to the right
|
// move to the right
|
||||||
@ -195,7 +207,7 @@ test.describe('Example Imagery Object', () => {
|
|||||||
await page.mouse.move(imageCenterX - 200, imageCenterY, 10);
|
await page.mouse.move(imageCenterX - 200, imageCenterY, 10);
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
||||||
const afterRightPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const afterRightPanBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
expect(zoomedBoundingBox.x).toBeGreaterThan(afterRightPanBoundingBox.x);
|
expect(zoomedBoundingBox.x).toBeGreaterThan(afterRightPanBoundingBox.x);
|
||||||
|
|
||||||
// pan left
|
// pan left
|
||||||
@ -204,7 +216,7 @@ test.describe('Example Imagery Object', () => {
|
|||||||
await page.mouse.move(imageCenterX, imageCenterY, 10);
|
await page.mouse.move(imageCenterX, imageCenterY, 10);
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
||||||
const afterLeftPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const afterLeftPanBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
expect(afterRightPanBoundingBox.x).toBeLessThan(afterLeftPanBoundingBox.x);
|
expect(afterRightPanBoundingBox.x).toBeLessThan(afterLeftPanBoundingBox.x);
|
||||||
|
|
||||||
// pan up
|
// pan up
|
||||||
@ -214,7 +226,7 @@ test.describe('Example Imagery Object', () => {
|
|||||||
await page.mouse.move(imageCenterX, imageCenterY + 200, 10);
|
await page.mouse.move(imageCenterX, imageCenterY + 200, 10);
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
||||||
const afterUpPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const afterUpPanBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
expect(afterUpPanBoundingBox.y).toBeGreaterThan(afterLeftPanBoundingBox.y);
|
expect(afterUpPanBoundingBox.y).toBeGreaterThan(afterLeftPanBoundingBox.y);
|
||||||
|
|
||||||
// pan down
|
// pan down
|
||||||
@ -223,7 +235,7 @@ test.describe('Example Imagery Object', () => {
|
|||||||
await page.mouse.move(imageCenterX, imageCenterY - 200, 10);
|
await page.mouse.move(imageCenterX, imageCenterY - 200, 10);
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
||||||
const afterDownPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const afterDownPanBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
expect(afterDownPanBoundingBox.y).toBeLessThan(afterUpPanBoundingBox.y);
|
expect(afterDownPanBoundingBox.y).toBeLessThan(afterUpPanBoundingBox.y);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -282,26 +294,43 @@ test.describe('Example Imagery Object', () => {
|
|||||||
await expect(page.getByText('Drilling')).toBeVisible();
|
await expect(page.getByText('Drilling')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can use + - buttons to zoom on the image @unstable', async ({ page }) => {
|
test('Can use + - buttons to zoom on the image', async ({ page }) => {
|
||||||
await buttonZoomOnImageAndAssert(page);
|
await buttonZoomOnImageAndAssert(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can use the reset button to reset the image @unstable', async ({ page }, testInfo) => {
|
test('Can use the reset button to reset the image', async ({ page }) => {
|
||||||
|
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||||
|
'style.transform',
|
||||||
|
'scale(1) translate(0px, 0px)'
|
||||||
|
);
|
||||||
|
|
||||||
// Get initial image dimensions
|
// Get initial image dimensions
|
||||||
const initialBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const initialBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
|
|
||||||
// Zoom in twice via button
|
// Zoom in twice via button
|
||||||
await zoomIntoImageryByButton(page);
|
await zoomIntoImageryByButton(page);
|
||||||
|
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||||
|
'style.transform',
|
||||||
|
'scale(2) translate(0px, 0px)'
|
||||||
|
);
|
||||||
await zoomIntoImageryByButton(page);
|
await zoomIntoImageryByButton(page);
|
||||||
|
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||||
|
'style.transform',
|
||||||
|
'scale(3) translate(0px, 0px)'
|
||||||
|
);
|
||||||
|
|
||||||
// Get and assert zoomed in image dimensions
|
// Get and assert zoomed in image dimensions
|
||||||
const zoomedInBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const zoomedInBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
expect.soft(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
|
expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
|
||||||
expect.soft(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
|
expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
|
||||||
|
|
||||||
// Reset pan and zoom and assert against initial image dimensions
|
// Reset pan and zoom and assert against initial image dimensions
|
||||||
await resetImageryPanAndZoom(page);
|
await resetImageryPanAndZoom(page);
|
||||||
const finalBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||||
|
'style.transform',
|
||||||
|
'scale(1) translate(0px, 0px)'
|
||||||
|
);
|
||||||
|
const finalBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
expect(finalBoundingBox).toEqual(initialBoundingBox);
|
expect(finalBoundingBox).toEqual(initialBoundingBox);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -324,20 +353,25 @@ test.describe('Example Imagery Object', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('Example Imagery in Display Layout', () => {
|
test.describe('Example Imagery in Display Layout @clock', () => {
|
||||||
let displayLayout;
|
let displayLayout;
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
|
// We mock the clock so that we don't need to wait for time driven events
|
||||||
|
// to verify functionality.
|
||||||
|
await page.clock.setSystemTime(MISSION_TIME);
|
||||||
|
await page.clock.resume();
|
||||||
|
|
||||||
// Go to baseURL
|
// Go to baseURL
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
displayLayout = await createDomainObjectWithDefaults(page, { type: 'Display Layout' });
|
displayLayout = await createDomainObjectWithDefaults(page, { type: 'Display Layout' });
|
||||||
await page.goto(displayLayout.url);
|
|
||||||
|
|
||||||
await createImageryView(page);
|
// Create Example Imagery inside Display Layout
|
||||||
|
await createImageryViewWithShortDelay(page, {
|
||||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(
|
name: 'Unnamed Example Imagery',
|
||||||
'Unnamed Example Imagery'
|
parent: displayLayout.uuid
|
||||||
);
|
});
|
||||||
|
|
||||||
await page.goto(displayLayout.url);
|
await page.goto(displayLayout.url);
|
||||||
});
|
});
|
||||||
@ -390,7 +424,7 @@ test.describe('Example Imagery in Display Layout', () => {
|
|||||||
await expect.soft(pausePlayButton).toHaveClass(/is-paused/);
|
await expect.soft(pausePlayButton).toHaveClass(/is-paused/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Imagery View operations @unstable', async ({ page }) => {
|
test('Imagery View operations @clock', async ({ page }) => {
|
||||||
test.info().annotations.push({
|
test.info().annotations.push({
|
||||||
type: 'issue',
|
type: 'issue',
|
||||||
description: 'https://github.com/nasa/openmct/issues/5265'
|
description: 'https://github.com/nasa/openmct/issues/5265'
|
||||||
@ -410,7 +444,7 @@ test.describe('Example Imagery in Display Layout', () => {
|
|||||||
await page.locator('div[title="Resize object width"] > input').click();
|
await page.locator('div[title="Resize object width"] > input').click();
|
||||||
await page.locator('div[title="Resize object width"] > input').fill('50');
|
await page.locator('div[title="Resize object width"] > input').fill('50');
|
||||||
|
|
||||||
await performImageryViewOperationsAndAssert(page);
|
await performImageryViewOperationsAndAssert(page, displayLayout);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Resizing the layout changes thumbnail visibility and size', async ({ page }) => {
|
test('Resizing the layout changes thumbnail visibility and size', async ({ page }) => {
|
||||||
@ -454,7 +488,10 @@ test.describe('Example Imagery in Display Layout', () => {
|
|||||||
type: 'issue',
|
type: 'issue',
|
||||||
description: 'https://github.com/nasa/openmct/issues/6709'
|
description: 'https://github.com/nasa/openmct/issues/6709'
|
||||||
});
|
});
|
||||||
await createImageryView(page);
|
await createImageryViewWithShortDelay(page, {
|
||||||
|
name: 'Unnamed Example Imagery',
|
||||||
|
parent: displayLayout.uuid
|
||||||
|
});
|
||||||
await page.goto(displayLayout.url);
|
await page.goto(displayLayout.url);
|
||||||
|
|
||||||
const imageElements = page.locator('.c-imagery__main-image-wrapper');
|
const imageElements = page.locator('.c-imagery__main-image-wrapper');
|
||||||
@ -483,16 +520,21 @@ test.describe('Example Imagery in Display Layout', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('Example Imagery in Flexible layout', () => {
|
test.describe('Example Imagery in Flexible layout @clock', () => {
|
||||||
let flexibleLayout;
|
let flexibleLayout;
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
|
// We mock the clock so that we don't need to wait for time driven events
|
||||||
|
// to verify functionality.
|
||||||
|
await page.clock.setSystemTime(MISSION_TIME);
|
||||||
|
await page.clock.resume();
|
||||||
|
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
flexibleLayout = await createDomainObjectWithDefaults(page, { type: 'Flexible Layout' });
|
flexibleLayout = await createDomainObjectWithDefaults(page, { type: 'Flexible Layout' });
|
||||||
|
|
||||||
// Create Example Imagery inside the Flexible Layout
|
// Create Example Imagery inside the Flexible Layout
|
||||||
await createDomainObjectWithDefaults(page, {
|
await createImageryViewWithShortDelay(page, {
|
||||||
type: 'Example Imagery',
|
name: 'Unnamed Example Imagery',
|
||||||
parent: flexibleLayout.uuid
|
parent: flexibleLayout.uuid
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -502,61 +544,35 @@ test.describe('Example Imagery in Flexible layout', () => {
|
|||||||
|
|
||||||
test('Can double-click on the image to view large image', async ({ page }) => {
|
test('Can double-click on the image to view large image', async ({ page }) => {
|
||||||
// Double-click on the image to open large view
|
// Double-click on the image to open large view
|
||||||
const imageElement = await page.getByRole('button', { name: 'Image Wrapper' });
|
const imageElement = page.getByRole('button', { name: 'Image Wrapper' });
|
||||||
await imageElement.dblclick();
|
await imageElement.dblclick();
|
||||||
|
|
||||||
// Check if the large view is visible
|
// Check if the large view is visible
|
||||||
await page.getByRole('button', { name: 'Background Image', state: 'visible' });
|
page.getByRole('button', { name: 'Focused Image Element', state: 'visible' });
|
||||||
|
|
||||||
// Close the large view
|
// Close the large view
|
||||||
await page.getByRole('button', { name: 'Close' }).click();
|
await page.getByRole('button', { name: 'Close' }).click();
|
||||||
});
|
});
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test('Imagery View operations @clock', async ({ page, browserName }) => {
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
|
||||||
|
|
||||||
flexibleLayout = await createDomainObjectWithDefaults(page, { type: 'Flexible Layout' });
|
|
||||||
await page.goto(flexibleLayout.url);
|
|
||||||
|
|
||||||
/* Create Sine Wave Generator with minimum Image Load Delay */
|
|
||||||
// Click the Create button
|
|
||||||
await page.getByRole('button', { name: 'Create' }).click();
|
|
||||||
|
|
||||||
// Click text=Example Imagery
|
|
||||||
await page.click('li[role="menuitem"]:has-text("Example Imagery")');
|
|
||||||
|
|
||||||
// Clear and set Image load delay to minimum value
|
|
||||||
await page.locator('input[type="number"]').fill('');
|
|
||||||
await page.locator('input[type="number"]').fill('5000');
|
|
||||||
|
|
||||||
// Click text=OK
|
|
||||||
await Promise.all([
|
|
||||||
page.waitForNavigation({ waitUntil: 'networkidle' }),
|
|
||||||
page.click('button:has-text("OK")'),
|
|
||||||
//Wait for Save Banner to appear
|
|
||||||
page.waitForSelector('.c-message-banner__message')
|
|
||||||
]);
|
|
||||||
|
|
||||||
await expect(page.locator('.l-browse-bar__object-name')).toContainText(
|
|
||||||
'Unnamed Example Imagery'
|
|
||||||
);
|
|
||||||
|
|
||||||
await page.goto(flexibleLayout.url);
|
|
||||||
});
|
|
||||||
test('Imagery View operations @unstable', async ({ page, browserName }) => {
|
|
||||||
test.fixme(browserName === 'firefox', 'This test needs to be updated to work with firefox');
|
test.fixme(browserName === 'firefox', 'This test needs to be updated to work with firefox');
|
||||||
test.info().annotations.push({
|
test.info().annotations.push({
|
||||||
type: 'issue',
|
type: 'issue',
|
||||||
description: 'https://github.com/nasa/openmct/issues/5326'
|
description: 'https://github.com/nasa/openmct/issues/5326'
|
||||||
});
|
});
|
||||||
|
|
||||||
await performImageryViewOperationsAndAssert(page);
|
await performImageryViewOperationsAndAssert(page, flexibleLayout);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('Example Imagery in Tabs View', () => {
|
test.describe('Example Imagery in Tabs View @clock', () => {
|
||||||
let tabsView;
|
let tabsView;
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
|
// We mock the clock so that we don't need to wait for time driven events
|
||||||
|
// to verify functionality.
|
||||||
|
await page.clock.setSystemTime(MISSION_TIME);
|
||||||
|
await page.clock.resume();
|
||||||
|
|
||||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
tabsView = await createDomainObjectWithDefaults(page, { type: 'Tabs View' });
|
tabsView = await createDomainObjectWithDefaults(page, { type: 'Tabs View' });
|
||||||
@ -570,8 +586,8 @@ test.describe('Example Imagery in Tabs View', () => {
|
|||||||
await page.click('li[role="menuitem"]:has-text("Example Imagery")');
|
await page.click('li[role="menuitem"]:has-text("Example Imagery")');
|
||||||
|
|
||||||
// Clear and set Image load delay to minimum value
|
// Clear and set Image load delay to minimum value
|
||||||
await page.locator('input[type="number"]').fill('');
|
await page.locator('input[type="number"]').clear();
|
||||||
await page.locator('input[type="number"]').fill('5000');
|
await page.locator('input[type="number"]').fill(`${IMAGE_LOAD_DELAY}`);
|
||||||
|
|
||||||
// Click text=OK
|
// Click text=OK
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@ -587,8 +603,8 @@ test.describe('Example Imagery in Tabs View', () => {
|
|||||||
|
|
||||||
await page.goto(tabsView.url);
|
await page.goto(tabsView.url);
|
||||||
});
|
});
|
||||||
test('Imagery View operations @unstable', async ({ page }) => {
|
test('Imagery View operations @clock', async ({ page }) => {
|
||||||
await performImageryViewOperationsAndAssert(page);
|
await performImageryViewOperationsAndAssert(page, tabsView);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -652,20 +668,21 @@ test.describe('Example Imagery in Time Strip', () => {
|
|||||||
* 7. Image brightness/contrast can be adjusted by dragging the sliders
|
* 7. Image brightness/contrast can be adjusted by dragging the sliders
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
async function performImageryViewOperationsAndAssert(page) {
|
async function performImageryViewOperationsAndAssert(page, layoutObject) {
|
||||||
// Verify that imagery thumbnails use a thumbnail url
|
// Verify that imagery thumbnails use a thumbnail url
|
||||||
const thumbnailImages = page.locator('.c-thumb__image');
|
const thumbnailImages = page.getByLabel('Image thumbnail from').locator('.c-thumb__image');
|
||||||
const mainImage = page.locator('.c-imagery__main-image__image');
|
const mainImage = page.locator('.c-imagery__main-image__image');
|
||||||
await expect(thumbnailImages.first()).toHaveAttribute('src', thumbnailUrlParamsRegexp);
|
await expect(thumbnailImages.first()).toHaveAttribute('src', thumbnailUrlParamsRegexp);
|
||||||
await expect(mainImage).not.toHaveAttribute('src', thumbnailUrlParamsRegexp);
|
await expect(mainImage).not.toHaveAttribute('src', thumbnailUrlParamsRegexp);
|
||||||
|
|
||||||
// Click previous image button
|
// Click previous image button
|
||||||
const previousImageButton = page.locator('.c-nav--prev');
|
const previousImageButton = page.getByLabel('Previous image');
|
||||||
await previousImageButton.click();
|
await expect(previousImageButton).toBeVisible();
|
||||||
|
await page.getByLabel('Image Wrapper').hover({ trial: true });
|
||||||
|
|
||||||
// Verify previous image
|
// Need to force click as the annotation canvas lies on top of the image
|
||||||
const selectedImage = page.locator('.selected');
|
// and fails the accessibility checks
|
||||||
await expect(selectedImage).toBeVisible();
|
// eslint-disable-next-line playwright/no-force-option
|
||||||
|
await previousImageButton.click({ force: true });
|
||||||
|
|
||||||
// Use the zoom buttons to zoom in and out
|
// Use the zoom buttons to zoom in and out
|
||||||
await buttonZoomOnImageAndAssert(page);
|
await buttonZoomOnImageAndAssert(page);
|
||||||
@ -680,42 +697,51 @@ async function performImageryViewOperationsAndAssert(page) {
|
|||||||
await mouseZoomOnImageAndAssert(page, -2);
|
await mouseZoomOnImageAndAssert(page, -2);
|
||||||
|
|
||||||
// Click next image button
|
// Click next image button
|
||||||
const nextImageButton = page.locator('.c-nav--next');
|
const nextImageButton = page.getByLabel('Next image');
|
||||||
await nextImageButton.click();
|
await expect(nextImageButton).toBeVisible();
|
||||||
|
await page.getByLabel('Image Wrapper').hover({ trial: true });
|
||||||
|
// eslint-disable-next-line playwright/no-force-option
|
||||||
|
await nextImageButton.click({ force: true });
|
||||||
// set realtime mode
|
// set realtime mode
|
||||||
await setRealTimeMode(page);
|
await navigateToObjectWithRealTime(
|
||||||
|
page,
|
||||||
|
layoutObject.url,
|
||||||
|
`${FIVE_MINUTES}`,
|
||||||
|
`${THIRTY_SECONDS}`
|
||||||
|
);
|
||||||
|
// Verify previous image
|
||||||
|
await expect(previousImageButton).toBeVisible();
|
||||||
|
await page.getByLabel('Image Wrapper').hover({ trial: true });
|
||||||
|
// eslint-disable-next-line playwright/no-force-option
|
||||||
|
await previousImageButton.click({ force: true });
|
||||||
|
await page.locator('.active').click();
|
||||||
|
const selectedImage = page.locator('.selected');
|
||||||
|
await expect(selectedImage).toBeVisible();
|
||||||
|
|
||||||
// Zoom in on next image
|
// Zoom in on next image
|
||||||
await mouseZoomOnImageAndAssert(page, 2);
|
await mouseZoomOnImageAndAssert(page, 2);
|
||||||
|
|
||||||
// Clicking on the left arrow should pause the imagery and go to previous image
|
// Clicking on the left arrow should pause the imagery and go to previous image
|
||||||
await previousImageButton.click();
|
await previousImageButton.click();
|
||||||
await expect(page.locator('.c-button.pause-play')).toHaveClass(/is-paused/);
|
await expect(page.getByLabel('Pause automatic scrolling of image thumbnails')).toBeVisible();
|
||||||
await expect(selectedImage).toBeVisible();
|
await expect(selectedImage).toBeVisible();
|
||||||
|
|
||||||
// The imagery view should be updated when new images come in
|
|
||||||
const imageCount = await page.locator('.c-imagery__thumb').count();
|
|
||||||
await expect
|
|
||||||
.poll(
|
|
||||||
async () => {
|
|
||||||
const newImageCount = await page.locator('.c-imagery__thumb').count();
|
|
||||||
|
|
||||||
return newImageCount;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
message: 'verify that old images are discarded',
|
|
||||||
timeout: 7 * 1000
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.toBe(imageCount);
|
|
||||||
|
|
||||||
// Verify selected image is still displayed
|
// Verify selected image is still displayed
|
||||||
await expect(selectedImage).toBeVisible();
|
await expect(selectedImage).toBeVisible();
|
||||||
|
|
||||||
// Unpause imagery
|
// Unpause imagery
|
||||||
await page.locator('.pause-play').click();
|
await page.locator('.pause-play').click();
|
||||||
|
|
||||||
|
// verify that old images are discarded
|
||||||
|
const lastImageInBounds = page.getByLabel('Image thumbnail from').first();
|
||||||
|
const lastImageTimestamp = await lastImageInBounds.getAttribute('title');
|
||||||
|
expect(lastImageTimestamp).not.toBeNull();
|
||||||
|
|
||||||
|
// go forward in time to ensure old images are discarded
|
||||||
|
await page.clock.fastForward(IMAGE_LOAD_DELAY);
|
||||||
|
await page.clock.resume();
|
||||||
|
await expect(page.getByLabel(lastImageTimestamp)).toBeHidden();
|
||||||
|
|
||||||
//Get background-image url from background-image css prop
|
//Get background-image url from background-image css prop
|
||||||
await assertBackgroundImageUrlFromBackgroundCss(page);
|
await assertBackgroundImageUrlFromBackgroundCss(page);
|
||||||
|
|
||||||
@ -789,38 +815,18 @@ async function assertBackgroundImageBrightness(page, expected) {
|
|||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
async function assertBackgroundImageUrlFromBackgroundCss(page) {
|
async function assertBackgroundImageUrlFromBackgroundCss(page) {
|
||||||
const backgroundImage = page.locator('.c-imagery__main-image__background-image');
|
const backgroundImage = page.getByLabel('Focused Image Element');
|
||||||
let backgroundImageUrl = await backgroundImage.evaluate((el) => {
|
const backgroundImageUrl = await backgroundImage.evaluate((el) => {
|
||||||
return window
|
return window
|
||||||
.getComputedStyle(el)
|
.getComputedStyle(el)
|
||||||
.getPropertyValue('background-image')
|
.getPropertyValue('background-image')
|
||||||
.match(/url\(([^)]+)\)/)[1];
|
.match(/url\(([^)]+)\)/)[1];
|
||||||
});
|
});
|
||||||
let backgroundImageUrl1 = backgroundImageUrl.slice(1, -1); //forgive me, padre
|
|
||||||
console.log('backgroundImageUrl1 ' + backgroundImageUrl1);
|
|
||||||
|
|
||||||
let backgroundImageUrl2;
|
// go forward in time to ensure old images are discarded
|
||||||
await expect
|
await page.clock.fastForward(IMAGE_LOAD_DELAY);
|
||||||
.poll(
|
await page.clock.resume();
|
||||||
async () => {
|
await expect(backgroundImage).not.toHaveJSProperty('background-image', backgroundImageUrl);
|
||||||
// Verify next image has updated
|
|
||||||
let backgroundImageUrlNext = await backgroundImage.evaluate((el) => {
|
|
||||||
return window
|
|
||||||
.getComputedStyle(el)
|
|
||||||
.getPropertyValue('background-image')
|
|
||||||
.match(/url\(([^)]+)\)/)[1];
|
|
||||||
});
|
|
||||||
backgroundImageUrl2 = backgroundImageUrlNext.slice(1, -1); //forgive me, padre
|
|
||||||
|
|
||||||
return backgroundImageUrl2;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
message: 'verify next image has updated',
|
|
||||||
timeout: 7 * 1000
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.not.toBe(backgroundImageUrl1);
|
|
||||||
console.log('backgroundImageUrl2 ' + backgroundImageUrl2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -829,7 +835,7 @@ async function assertBackgroundImageUrlFromBackgroundCss(page) {
|
|||||||
async function panZoomAndAssertImageProperties(page) {
|
async function panZoomAndAssertImageProperties(page) {
|
||||||
const imageryHintsText = await page.locator('.c-imagery__hints').innerText();
|
const imageryHintsText = await page.locator('.c-imagery__hints').innerText();
|
||||||
expect(expectedAltText).toEqual(imageryHintsText);
|
expect(expectedAltText).toEqual(imageryHintsText);
|
||||||
const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const zoomedBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
|
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
|
||||||
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
|
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
|
||||||
|
|
||||||
@ -839,7 +845,7 @@ async function panZoomAndAssertImageProperties(page) {
|
|||||||
await page.mouse.move(imageCenterX - 200, imageCenterY, 10);
|
await page.mouse.move(imageCenterX - 200, imageCenterY, 10);
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
||||||
const afterRightPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const afterRightPanBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
expect(zoomedBoundingBox.x).toBeGreaterThan(afterRightPanBoundingBox.x);
|
expect(zoomedBoundingBox.x).toBeGreaterThan(afterRightPanBoundingBox.x);
|
||||||
|
|
||||||
// Pan left
|
// Pan left
|
||||||
@ -848,7 +854,7 @@ async function panZoomAndAssertImageProperties(page) {
|
|||||||
await page.mouse.move(imageCenterX, imageCenterY, 10);
|
await page.mouse.move(imageCenterX, imageCenterY, 10);
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
||||||
const afterLeftPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const afterLeftPanBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
expect(afterRightPanBoundingBox.x).toBeLessThan(afterLeftPanBoundingBox.x);
|
expect(afterRightPanBoundingBox.x).toBeLessThan(afterLeftPanBoundingBox.x);
|
||||||
|
|
||||||
// Pan up
|
// Pan up
|
||||||
@ -858,7 +864,7 @@ async function panZoomAndAssertImageProperties(page) {
|
|||||||
await page.mouse.move(imageCenterX, imageCenterY + 200, 10);
|
await page.mouse.move(imageCenterX, imageCenterY + 200, 10);
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
||||||
const afterUpPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const afterUpPanBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
expect(afterUpPanBoundingBox.y).toBeGreaterThanOrEqual(afterLeftPanBoundingBox.y);
|
expect(afterUpPanBoundingBox.y).toBeGreaterThanOrEqual(afterLeftPanBoundingBox.y);
|
||||||
|
|
||||||
// Pan down
|
// Pan down
|
||||||
@ -867,7 +873,7 @@ async function panZoomAndAssertImageProperties(page) {
|
|||||||
await page.mouse.move(imageCenterX, imageCenterY - 200, 10);
|
await page.mouse.move(imageCenterX, imageCenterY - 200, 10);
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
await Promise.all(panHotkey.map((x) => page.keyboard.up(x)));
|
||||||
const afterDownPanBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const afterDownPanBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
expect(afterDownPanBoundingBox.y).toBeLessThanOrEqual(afterUpPanBoundingBox.y);
|
expect(afterDownPanBoundingBox.y).toBeLessThanOrEqual(afterUpPanBoundingBox.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -879,19 +885,20 @@ async function panZoomAndAssertImageProperties(page) {
|
|||||||
*/
|
*/
|
||||||
async function mouseZoomOnImageAndAssert(page, factor = 2) {
|
async function mouseZoomOnImageAndAssert(page, factor = 2) {
|
||||||
// Zoom in
|
// Zoom in
|
||||||
const originalImageDimensions = await page.locator(backgroundImageSelector).boundingBox();
|
await page.getByLabel('Focused Image Element').hover({ trial: true });
|
||||||
const deltaYStep = 100; // equivalent to 1x zoom
|
const originalImageDimensions = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
await page.mouse.wheel(0, deltaYStep * factor);
|
await page.mouse.wheel(0, MOUSE_WHEEL_DELTA_Y * factor);
|
||||||
const zoomedBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
await waitForZoomAndPanTransitions(page);
|
||||||
|
|
||||||
|
const zoomedBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
|
const imageCenterX = zoomedBoundingBox.x + zoomedBoundingBox.width / 2;
|
||||||
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
|
const imageCenterY = zoomedBoundingBox.y + zoomedBoundingBox.height / 2;
|
||||||
|
|
||||||
// center the mouse pointer
|
// center the mouse pointer
|
||||||
await page.mouse.move(imageCenterX, imageCenterY);
|
await page.mouse.move(imageCenterX, imageCenterY);
|
||||||
|
|
||||||
// Wait for zoom animation to finish
|
// Wait for zoom animation to finish and get the new image dimensions
|
||||||
await page.locator('.c-imagery__main-image__bg').hover({ trial: true });
|
const imageMouseZoomed = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
const imageMouseZoomed = await page.locator(backgroundImageSelector).boundingBox();
|
|
||||||
|
|
||||||
if (factor > 0) {
|
if (factor > 0) {
|
||||||
expect(imageMouseZoomed.height).toBeGreaterThan(originalImageDimensions.height);
|
expect(imageMouseZoomed.height).toBeGreaterThan(originalImageDimensions.height);
|
||||||
@ -908,29 +915,61 @@ async function mouseZoomOnImageAndAssert(page, factor = 2) {
|
|||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
async function buttonZoomOnImageAndAssert(page) {
|
async function buttonZoomOnImageAndAssert(page) {
|
||||||
|
// Lock the zoom and pan so it doesn't reset if a new image comes in
|
||||||
|
await page.getByLabel('Focused Image Element').hover({ trial: true });
|
||||||
|
const lockButton = page.getByRole('button', {
|
||||||
|
name: 'Lock current zoom and pan across all images'
|
||||||
|
});
|
||||||
|
if (!(await lockButton.isVisible())) {
|
||||||
|
await page.getByLabel('Focused Image Element').hover({ trial: true });
|
||||||
|
}
|
||||||
|
await lockButton.click();
|
||||||
|
|
||||||
|
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||||
|
'style.transform',
|
||||||
|
'scale(1) translate(0px, 0px)'
|
||||||
|
);
|
||||||
|
|
||||||
// Get initial image dimensions
|
// Get initial image dimensions
|
||||||
const initialBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const initialBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
|
|
||||||
// Zoom in twice via button
|
// Zoom in twice via button
|
||||||
await zoomIntoImageryByButton(page);
|
await zoomIntoImageryByButton(page);
|
||||||
|
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||||
|
'style.transform',
|
||||||
|
'scale(2) translate(0px, 0px)'
|
||||||
|
);
|
||||||
await zoomIntoImageryByButton(page);
|
await zoomIntoImageryByButton(page);
|
||||||
|
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||||
|
'style.transform',
|
||||||
|
'scale(3) translate(0px, 0px)'
|
||||||
|
);
|
||||||
|
|
||||||
// Get and assert zoomed in image dimensions
|
// Get and assert zoomed in image dimensions
|
||||||
const zoomedInBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const zoomedInBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
|
expect(zoomedInBoundingBox.height).toBeGreaterThan(initialBoundingBox.height);
|
||||||
expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
|
expect(zoomedInBoundingBox.width).toBeGreaterThan(initialBoundingBox.width);
|
||||||
|
|
||||||
// Zoom out once via button
|
// Zoom out once via button
|
||||||
await zoomOutOfImageryByButton(page);
|
await zoomOutOfImageryByButton(page);
|
||||||
|
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||||
|
'style.transform',
|
||||||
|
'scale(2) translate(0px, 0px)'
|
||||||
|
);
|
||||||
|
|
||||||
// Get and assert zoomed out image dimensions
|
// Get and assert zoomed out image dimensions
|
||||||
const zoomedOutBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
const zoomedOutBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
expect(zoomedOutBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height);
|
expect(zoomedOutBoundingBox.height).toBeLessThan(zoomedInBoundingBox.height);
|
||||||
expect(zoomedOutBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width);
|
expect(zoomedOutBoundingBox.width).toBeLessThan(zoomedInBoundingBox.width);
|
||||||
|
|
||||||
// Zoom out again via button, assert against the initial image dimensions
|
// Zoom out again via button, assert against the initial image dimensions
|
||||||
await zoomOutOfImageryByButton(page);
|
await zoomOutOfImageryByButton(page);
|
||||||
const finalBoundingBox = await page.locator(backgroundImageSelector).boundingBox();
|
await expect(page.getByLabel('Focused Image Element')).toHaveJSProperty(
|
||||||
|
'style.transform',
|
||||||
|
'scale(1) translate(0px, 0px)'
|
||||||
|
);
|
||||||
|
|
||||||
|
const finalBoundingBox = await page.getByLabel('Focused Image Element').boundingBox();
|
||||||
expect(finalBoundingBox).toEqual(initialBoundingBox);
|
expect(finalBoundingBox).toEqual(initialBoundingBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -957,16 +996,11 @@ async function assertBackgroundImageContrast(page, expected) {
|
|||||||
*/
|
*/
|
||||||
async function zoomIntoImageryByButton(page) {
|
async function zoomIntoImageryByButton(page) {
|
||||||
// FIXME: There should only be one set of imagery buttons, but there are two?
|
// FIXME: There should only be one set of imagery buttons, but there are two?
|
||||||
const zoomInBtn = page
|
const zoomInBtn = page.getByRole('button', { name: 'Zoom in' });
|
||||||
.locator("[role='toolbar'][aria-label='Image controls'] .t-btn-zoom-in")
|
const backgroundImage = page.getByLabel('Focused Image Element');
|
||||||
.nth(0);
|
await backgroundImage.hover({ trial: true });
|
||||||
const backgroundImage = page.locator(backgroundImageSelector);
|
|
||||||
if (!(await zoomInBtn.isVisible())) {
|
|
||||||
await backgroundImage.hover({ trial: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
await zoomInBtn.click();
|
await zoomInBtn.click();
|
||||||
await waitForAnimations(backgroundImage);
|
await waitForZoomAndPanTransitions(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -975,17 +1009,11 @@ async function zoomIntoImageryByButton(page) {
|
|||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
async function zoomOutOfImageryByButton(page) {
|
async function zoomOutOfImageryByButton(page) {
|
||||||
// FIXME: There should only be one set of imagery buttons, but there are two?
|
const zoomOutBtn = page.getByRole('button', { name: 'Zoom out' });
|
||||||
const zoomOutBtn = page
|
const backgroundImage = page.getByLabel('Focused Image Element');
|
||||||
.locator("[role='toolbar'][aria-label='Image controls'] .t-btn-zoom-out")
|
await backgroundImage.hover({ trial: true });
|
||||||
.nth(0);
|
|
||||||
const backgroundImage = page.locator(backgroundImageSelector);
|
|
||||||
if (!(await zoomOutBtn.isVisible())) {
|
|
||||||
await backgroundImage.hover({ trial: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
await zoomOutBtn.click();
|
await zoomOutBtn.click();
|
||||||
await waitForAnimations(backgroundImage);
|
await waitForZoomAndPanTransitions(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -994,38 +1022,43 @@ async function zoomOutOfImageryByButton(page) {
|
|||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
async function resetImageryPanAndZoom(page) {
|
async function resetImageryPanAndZoom(page) {
|
||||||
// FIXME: There should only be one set of imagery buttons, but there are two?
|
const panZoomResetBtn = page.getByRole('button', { name: 'Remove zoom and pan' });
|
||||||
const panZoomResetBtn = page
|
await expect(panZoomResetBtn).toBeVisible();
|
||||||
.locator("[role='toolbar'][aria-label='Image controls'] .t-btn-zoom-reset")
|
await panZoomResetBtn.hover({ trial: true });
|
||||||
.nth(0);
|
|
||||||
const backgroundImage = page.locator(backgroundImageSelector);
|
|
||||||
if (!(await panZoomResetBtn.isVisible())) {
|
|
||||||
await backgroundImage.hover({ trial: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
await panZoomResetBtn.click();
|
await panZoomResetBtn.click();
|
||||||
await waitForAnimations(backgroundImage);
|
|
||||||
|
await waitForZoomAndPanTransitions(page);
|
||||||
|
await expect(page.getByText('Alt drag to pan')).toBeHidden();
|
||||||
|
await expect(page.locator('.c-thumb__viewable-area')).toBeHidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
async function createImageryView(page) {
|
async function createImageryViewWithShortDelay(page, { name, parent }) {
|
||||||
// Click the Create button
|
await createDomainObjectWithDefaults(page, {
|
||||||
await page.getByRole('button', { name: 'Create' }).click();
|
name,
|
||||||
|
type: 'Example Imagery',
|
||||||
// Click text=Example Imagery
|
parent
|
||||||
await page.click('li[role="menuitem"]:has-text("Example Imagery")');
|
});
|
||||||
|
|
||||||
|
await expect(page.locator('.l-browse-bar__object-name')).toContainText('Unnamed Example Imagery');
|
||||||
|
await page.getByLabel('More actions').click();
|
||||||
|
await page.getByLabel('Edit Properties').click();
|
||||||
// Clear and set Image load delay to minimum value
|
// Clear and set Image load delay to minimum value
|
||||||
await page.locator('input[type="number"]').fill('');
|
await page.locator('input[type="number"]').fill(`${IMAGE_LOAD_DELAY}`);
|
||||||
await page.locator('input[type="number"]').fill('5000');
|
await page.getByLabel('Save').click();
|
||||||
|
}
|
||||||
// Click text=OK
|
|
||||||
await Promise.all([
|
/**
|
||||||
page.waitForNavigation({ waitUntil: 'networkidle' }),
|
* @param {import('@playwright/test').Page} page
|
||||||
page.click('button:has-text("OK")'),
|
*/
|
||||||
//Wait for Save Banner to appear
|
// eslint-disable-next-line require-await
|
||||||
page.waitForSelector('.c-message-banner__message')
|
async function waitForZoomAndPanTransitions(page) {
|
||||||
]);
|
// Wait for image to stabilize
|
||||||
|
await page.getByLabel('Focused Image Element').hover({ trial: true });
|
||||||
|
// Wait for zoom to end
|
||||||
|
await expect(page.getByLabel('Focused Image Element')).not.toHaveClass(/is-zooming|is-panning/);
|
||||||
|
// Wait for image to stabilize
|
||||||
|
await page.getByLabel('Focused Image Element').hover({ trial: true });
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<mct-tree
|
<MctTree
|
||||||
:is-selector-tree="true"
|
:is-selector-tree="true"
|
||||||
:initial-selection="model.parent"
|
:initial-selection="model.parent"
|
||||||
@tree-item-selection="handleItemSelection"
|
@tree-item-selection="handleItemSelection"
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<lad-row
|
<LadRow
|
||||||
v-for="ladRow in items"
|
v-for="ladRow in items"
|
||||||
:key="ladRow.key"
|
:key="ladRow.key"
|
||||||
:domain-object="ladRow.domainObject"
|
:domain-object="ladRow.domainObject"
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
{{ ladTable.domainObject.name }}
|
{{ ladTable.domainObject.name }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<lad-row
|
<LadRow
|
||||||
v-for="ladRow in ladTelemetryObjects[ladTable.key]"
|
v-for="ladRow in ladTelemetryObjects[ladTable.key]"
|
||||||
:key="combineKeys(ladTable.key, ladRow.key)"
|
:key="combineKeys(ladTable.key, ladRow.key)"
|
||||||
:domain-object="ladRow.domainObject"
|
:domain-object="ladRow.domainObject"
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<ul class="c-tree">
|
<ul class="c-tree">
|
||||||
<h2 title="Display properties for this object">Bar Graph Series</h2>
|
<h2 title="Display properties for this object">Bar Graph Series</h2>
|
||||||
<li>
|
<li>
|
||||||
<series-options
|
<SeriesOptions
|
||||||
v-for="series in plotSeries"
|
v-for="series in plotSeries"
|
||||||
:key="series.keyString"
|
:key="series.keyString"
|
||||||
:item="series"
|
:item="series"
|
||||||
|
@ -22,10 +22,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="canEdit">
|
<div v-if="canEdit">
|
||||||
<plot-options-edit />
|
<PlotOptionsEdit />
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<plot-options-browse />
|
<PlotOptionsBrowse />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -57,7 +57,7 @@
|
|||||||
<span class="c-condition__summary">
|
<span class="c-condition__summary">
|
||||||
<template v-if="!condition.isDefault && !canEvaluateCriteria"> Define criteria </template>
|
<template v-if="!condition.isDefault && !canEvaluateCriteria"> Define criteria </template>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
<condition-description :show-label="false" :condition="condition" />
|
<ConditionDescription :show-label="false" :condition="condition" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@ -184,7 +184,7 @@
|
|||||||
<span class="c-condition__output"> Output: {{ condition.configuration.output }} </span>
|
<span class="c-condition__output"> Output: {{ condition.configuration.output }} </span>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-condition__summary">
|
<div class="c-condition__summary">
|
||||||
<condition-description :show-label="false" :condition="condition" />
|
<ConditionDescription :show-label="false" :condition="condition" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,31 +43,31 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<toolbar-color-picker
|
<ToolbarColorPicker
|
||||||
v-if="hasProperty(styleItem.style.border)"
|
v-if="hasProperty(styleItem.style.border)"
|
||||||
class="c-style__toolbar-button--border-color u-menu-to--center"
|
class="c-style__toolbar-button--border-color u-menu-to--center"
|
||||||
:options="borderColorOption"
|
:options="borderColorOption"
|
||||||
@change="updateStyleValue"
|
@change="updateStyleValue"
|
||||||
/>
|
/>
|
||||||
<toolbar-color-picker
|
<ToolbarColorPicker
|
||||||
v-if="hasProperty(styleItem.style.backgroundColor)"
|
v-if="hasProperty(styleItem.style.backgroundColor)"
|
||||||
class="c-style__toolbar-button--background-color u-menu-to--center"
|
class="c-style__toolbar-button--background-color u-menu-to--center"
|
||||||
:options="backgroundColorOption"
|
:options="backgroundColorOption"
|
||||||
@change="updateStyleValue"
|
@change="updateStyleValue"
|
||||||
/>
|
/>
|
||||||
<toolbar-color-picker
|
<ToolbarColorPicker
|
||||||
v-if="hasProperty(styleItem.style.color)"
|
v-if="hasProperty(styleItem.style.color)"
|
||||||
class="c-style__toolbar-button--color u-menu-to--center"
|
class="c-style__toolbar-button--color u-menu-to--center"
|
||||||
:options="colorOption"
|
:options="colorOption"
|
||||||
@change="updateStyleValue"
|
@change="updateStyleValue"
|
||||||
/>
|
/>
|
||||||
<toolbar-button
|
<ToolbarButton
|
||||||
v-if="hasProperty(styleItem.style.imageUrl)"
|
v-if="hasProperty(styleItem.style.imageUrl)"
|
||||||
class="c-style__toolbar-button--image-url"
|
class="c-style__toolbar-button--image-url"
|
||||||
:options="imageUrlOption"
|
:options="imageUrlOption"
|
||||||
@change="updateStyleValue"
|
@change="updateStyleValue"
|
||||||
/>
|
/>
|
||||||
<toolbar-toggle-button
|
<ToolbarToggleButton
|
||||||
v-if="hasProperty(styleItem.style.isStyleInvisible)"
|
v-if="hasProperty(styleItem.style.isStyleInvisible)"
|
||||||
class="c-style__toolbar-button--toggle-visible"
|
class="c-style__toolbar-button--toggle-visible"
|
||||||
:options="isStyleInvisibleOption"
|
:options="isStyleInvisibleOption"
|
||||||
@ -76,7 +76,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Save Styles -->
|
<!-- Save Styles -->
|
||||||
<toolbar-button
|
<ToolbarButton
|
||||||
v-if="canSaveStyle"
|
v-if="canSaveStyle"
|
||||||
ref="saveStyleButton"
|
ref="saveStyleButton"
|
||||||
class="c-style__toolbar-button--save c-local-controls--show-on-hover c-icon-button c-icon-button--major"
|
class="c-style__toolbar-button--save c-local-controls--show-on-hover c-icon-button c-icon-button--major"
|
||||||
|
@ -112,11 +112,11 @@
|
|||||||
:class="{ 'is-current': conditionStyle.conditionId === selectedConditionId }"
|
:class="{ 'is-current': conditionStyle.conditionId === selectedConditionId }"
|
||||||
@click="applySelectedConditionStyle(conditionStyle.conditionId)"
|
@click="applySelectedConditionStyle(conditionStyle.conditionId)"
|
||||||
>
|
>
|
||||||
<condition-error
|
<ConditionError
|
||||||
:show-label="true"
|
:show-label="true"
|
||||||
:condition="getCondition(conditionStyle.conditionId)"
|
:condition="getCondition(conditionStyle.conditionId)"
|
||||||
/>
|
/>
|
||||||
<condition-description
|
<ConditionDescription
|
||||||
:show-label="true"
|
:show-label="true"
|
||||||
:condition="getCondition(conditionStyle.conditionId)"
|
:condition="getCondition(conditionStyle.conditionId)"
|
||||||
/>
|
/>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<layout-frame
|
<LayoutFrame
|
||||||
:item="item"
|
:item="item"
|
||||||
:grid-size="gridSize"
|
:grid-size="gridSize"
|
||||||
:is-editing="isEditing"
|
:is-editing="isEditing"
|
||||||
@ -38,7 +38,7 @@
|
|||||||
aria-label="Box"
|
aria-label="Box"
|
||||||
></div>
|
></div>
|
||||||
</template>
|
</template>
|
||||||
</layout-frame>
|
</LayoutFrame>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<layout-frame
|
<LayoutFrame
|
||||||
:item="item"
|
:item="item"
|
||||||
:grid-size="gridSize"
|
:grid-size="gridSize"
|
||||||
:is-editing="isEditing"
|
:is-editing="isEditing"
|
||||||
@ -38,7 +38,7 @@
|
|||||||
aria-label="Ellipse"
|
aria-label="Ellipse"
|
||||||
></div>
|
></div>
|
||||||
</template>
|
</template>
|
||||||
</layout-frame>
|
</LayoutFrame>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<layout-frame
|
<LayoutFrame
|
||||||
:item="item"
|
:item="item"
|
||||||
:grid-size="gridSize"
|
:grid-size="gridSize"
|
||||||
:is-editing="isEditing"
|
:is-editing="isEditing"
|
||||||
@ -31,7 +31,7 @@
|
|||||||
<template #content>
|
<template #content>
|
||||||
<div class="c-image-view" :style="style"></div>
|
<div class="c-image-view" :style="style"></div>
|
||||||
</template>
|
</template>
|
||||||
</layout-frame>
|
</LayoutFrame>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<layout-frame
|
<LayoutFrame
|
||||||
:item="item"
|
:item="item"
|
||||||
:grid-size="gridSize"
|
:grid-size="gridSize"
|
||||||
:is-editing="isEditing"
|
:is-editing="isEditing"
|
||||||
@ -39,7 +39,7 @@
|
|||||||
:layout-font="item.font"
|
:layout-font="item.font"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</layout-frame>
|
</LayoutFrame>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<layout-frame
|
<LayoutFrame
|
||||||
:item="item"
|
:item="item"
|
||||||
:grid-size="gridSize"
|
:grid-size="gridSize"
|
||||||
:is-editing="isEditing"
|
:is-editing="isEditing"
|
||||||
@ -68,7 +68,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</layout-frame>
|
</LayoutFrame>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<layout-frame
|
<LayoutFrame
|
||||||
:item="item"
|
:item="item"
|
||||||
:grid-size="gridSize"
|
:grid-size="gridSize"
|
||||||
:is-editing="isEditing"
|
:is-editing="isEditing"
|
||||||
@ -42,7 +42,7 @@
|
|||||||
<div class="c-text-view__text">{{ item.text }}</div>
|
<div class="c-text-view__text">{{ item.text }}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</layout-frame>
|
</LayoutFrame>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -50,14 +50,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="isEditing" class="c-inspect-properties__label span-all">
|
<div v-if="isEditing" class="c-inspect-properties__label span-all">
|
||||||
<toggle-switch
|
<ToggleSwitch
|
||||||
:id="keyString"
|
:id="keyString"
|
||||||
:checked="persistedFilters.useGlobal"
|
:checked="persistedFilters.useGlobal"
|
||||||
@change="useGlobalFilter"
|
@change="useGlobalFilter"
|
||||||
/>
|
/>
|
||||||
Use global filter
|
Use global filter
|
||||||
</div>
|
</div>
|
||||||
<filter-field
|
<FilterField
|
||||||
v-for="metadatum in activeFilters"
|
v-for="metadatum in activeFilters"
|
||||||
:key="metadatum.key"
|
:key="metadatum.key"
|
||||||
:filter-field="metadatum"
|
:filter-field="metadatum"
|
||||||
|
@ -25,12 +25,12 @@
|
|||||||
<div v-if="hasActiveFilters" class="c-filter-indication">
|
<div v-if="hasActiveFilters" class="c-filter-indication">
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</div>
|
</div>
|
||||||
<global-filters
|
<GlobalFilters
|
||||||
:global-filters="globalFilters"
|
:global-filters="globalFilters"
|
||||||
:global-metadata="globalMetadata"
|
:global-metadata="globalMetadata"
|
||||||
@persist-global-filters="persistGlobalFilters"
|
@persist-global-filters="persistGlobalFilters"
|
||||||
/>
|
/>
|
||||||
<filter-object
|
<FilterObject
|
||||||
v-for="(child, key) in children"
|
v-for="(child, key) in children"
|
||||||
:key="key"
|
:key="key"
|
||||||
:filter-object="child"
|
:filter-object="child"
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul v-if="expanded" class="c-inspect-properties">
|
<ul v-if="expanded" class="c-inspect-properties">
|
||||||
<filter-field
|
<FilterField
|
||||||
v-for="metadatum in globalMetadata"
|
v-for="metadatum in globalMetadata"
|
||||||
:key="metadatum.key"
|
:key="metadatum.key"
|
||||||
:filter-field="metadatum"
|
:filter-field="metadatum"
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
<span class="c-fl-container__size-indicator">{{ sizeString }}</span>
|
<span class="c-fl-container__size-indicator">{{ sizeString }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<drop-hint
|
<DropHint
|
||||||
class="c-fl-frame__drop-hint"
|
class="c-fl-frame__drop-hint"
|
||||||
:index="-1"
|
:index="-1"
|
||||||
:allow-drop="allowDrop"
|
:allow-drop="allowDrop"
|
||||||
@ -47,7 +47,7 @@
|
|||||||
|
|
||||||
<div role="row" class="c-fl-container__frames-holder" :class="flexLayoutCssClass">
|
<div role="row" class="c-fl-container__frames-holder" :class="flexLayoutCssClass">
|
||||||
<template v-for="(frame, i) in frames" :key="frame.id">
|
<template v-for="(frame, i) in frames" :key="frame.id">
|
||||||
<frame-component
|
<FrameComponent
|
||||||
class="c-fl-container__frame"
|
class="c-fl-container__frame"
|
||||||
:frame="frame"
|
:frame="frame"
|
||||||
:index="i"
|
:index="i"
|
||||||
@ -58,14 +58,14 @@
|
|||||||
:object-path="objectPath"
|
:object-path="objectPath"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<drop-hint
|
<DropHint
|
||||||
class="c-fl-frame__drop-hint"
|
class="c-fl-frame__drop-hint"
|
||||||
:index="i"
|
:index="i"
|
||||||
:allow-drop="allowDrop"
|
:allow-drop="allowDrop"
|
||||||
@object-drop-to="moveOrCreateNewFrame"
|
@object-drop-to="moveOrCreateNewFrame"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<resize-handle
|
<ResizeHandle
|
||||||
v-if="i !== frames.length - 1"
|
v-if="i !== frames.length - 1"
|
||||||
:index="i"
|
:index="i"
|
||||||
:drag-orientation="rowsLayout ? 'horizontal' : 'vertical'"
|
:drag-orientation="rowsLayout ? 'horizontal' : 'vertical'"
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
:aria-label="`Flexible Layout ${rowsLayout ? 'Rows' : 'Columns'}`"
|
:aria-label="`Flexible Layout ${rowsLayout ? 'Rows' : 'Columns'}`"
|
||||||
>
|
>
|
||||||
<template v-for="(container, index) in containers" :key="`component-${container.id}`">
|
<template v-for="(container, index) in containers" :key="`component-${container.id}`">
|
||||||
<drop-hint
|
<DropHint
|
||||||
v-if="index === 0 && containers.length > 1"
|
v-if="index === 0 && containers.length > 1"
|
||||||
class="c-fl-frame__drop-hint"
|
class="c-fl-frame__drop-hint"
|
||||||
:index="-1"
|
:index="-1"
|
||||||
@ -42,7 +42,7 @@
|
|||||||
@object-drop-to="moveContainer"
|
@object-drop-to="moveContainer"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<container-component
|
<ContainerComponent
|
||||||
:index="index"
|
:index="index"
|
||||||
:container="container"
|
:container="container"
|
||||||
:rows-layout="rowsLayout"
|
:rows-layout="rowsLayout"
|
||||||
@ -54,7 +54,7 @@
|
|||||||
@persist="persist"
|
@persist="persist"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<resize-handle
|
<ResizeHandle
|
||||||
v-if="index !== containers.length - 1"
|
v-if="index !== containers.length - 1"
|
||||||
:index="index"
|
:index="index"
|
||||||
:drag-orientation="rowsLayout ? 'vertical' : 'horizontal'"
|
:drag-orientation="rowsLayout ? 'vertical' : 'horizontal'"
|
||||||
@ -64,7 +64,7 @@
|
|||||||
@end-move="endContainerResizing"
|
@end-move="endContainerResizing"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<drop-hint
|
<DropHint
|
||||||
v-if="containers.length > 1"
|
v-if="containers.length > 1"
|
||||||
class="c-fl-frame__drop-hint"
|
class="c-fl-frame__drop-hint"
|
||||||
:index="index"
|
:index="index"
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
role="group"
|
role="group"
|
||||||
@dragstart="initDrag"
|
@dragstart="initDrag"
|
||||||
>
|
>
|
||||||
<object-frame
|
<ObjectFrame
|
||||||
v-if="domainObject"
|
v-if="domainObject"
|
||||||
ref="objectFrame"
|
ref="objectFrame"
|
||||||
:domain-object="domainObject"
|
:domain-object="domainObject"
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div class="l-grid-view" role="grid" :aria-label="`${domainObject.name} Grid View`">
|
<div class="l-grid-view" role="grid" :aria-label="`${domainObject.name} Grid View`">
|
||||||
<grid-item
|
<GridItem
|
||||||
v-for="(item, index) in items"
|
v-for="(item, index) in items"
|
||||||
:key="index"
|
:key="index"
|
||||||
:item="item"
|
:item="item"
|
||||||
|
@ -73,7 +73,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<list-item
|
<ListItem
|
||||||
v-for="item in sortedItems"
|
v-for="item in sortedItems"
|
||||||
:key="item.objectKeyString"
|
:key="item.objectKeyString"
|
||||||
:item="item"
|
:item="item"
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="c-compass" :style="`width: 100%; height: 100%`">
|
<div class="c-compass" :style="`width: 100%; height: 100%`">
|
||||||
<compass-hud
|
<CompassHud
|
||||||
v-if="showCompassHUD"
|
v-if="showCompassHUD"
|
||||||
:camera-angle-of-view="cameraAngleOfView"
|
:camera-angle-of-view="cameraAngleOfView"
|
||||||
:heading="heading"
|
:heading="heading"
|
||||||
|
@ -26,50 +26,42 @@
|
|||||||
role="toolbar"
|
role="toolbar"
|
||||||
aria-label="Image controls"
|
aria-label="Image controls"
|
||||||
>
|
>
|
||||||
<imagery-view-menu-switcher
|
<ImageryViewMenuSwitcher
|
||||||
:icon-class="'icon-brightness'"
|
:icon-class="'icon-brightness'"
|
||||||
:aria-label="'Brightness and contrast'"
|
:aria-label="'Brightness and contrast'"
|
||||||
:title="'Brightness and contrast'"
|
:title="'Brightness and contrast'"
|
||||||
>
|
>
|
||||||
<filter-settings @filter-changed="updateFilterValues" />
|
<FilterSettings @filter-changed="updateFilterValues" />
|
||||||
</imagery-view-menu-switcher>
|
</ImageryViewMenuSwitcher>
|
||||||
|
|
||||||
<imagery-view-menu-switcher
|
<ImageryViewMenuSwitcher
|
||||||
v-if="layers.length"
|
v-if="layers.length"
|
||||||
icon-class="icon-layers"
|
icon-class="icon-layers"
|
||||||
aria-label="Layers"
|
aria-label="Layers"
|
||||||
title="Layers"
|
title="Layers"
|
||||||
>
|
>
|
||||||
<layer-settings :layers="layers" @toggle-layer-visibility="toggleLayerVisibility" />
|
<LayerSettings :layers="layers" @toggle-layer-visibility="toggleLayerVisibility" />
|
||||||
</imagery-view-menu-switcher>
|
</ImageryViewMenuSwitcher>
|
||||||
|
|
||||||
<zoom-settings
|
<ZoomSettings
|
||||||
class="--hide-if-less-than-220"
|
class="--hide-if-less-than-220"
|
||||||
:pan-zoom-locked="panZoomLocked"
|
:pan-zoom-locked="panZoomLocked"
|
||||||
:zoom-factor="zoomFactor"
|
:zoom-factor="zoomFactor"
|
||||||
@zoom-out="zoomOut"
|
|
||||||
@zoom-in="zoomIn"
|
|
||||||
@toggle-zoom-lock="toggleZoomLock"
|
|
||||||
@handle-reset-image="handleResetImage"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<imagery-view-menu-switcher
|
<ImageryViewMenuSwitcher
|
||||||
class="--show-if-less-than-220"
|
class="--show-if-less-than-220"
|
||||||
:icon-class="'icon-magnify'"
|
:icon-class="'icon-magnify'"
|
||||||
:aria-label="'Zoom settings'"
|
:aria-label="'Zoom settings'"
|
||||||
:title="'Zoom settings'"
|
:title="'Zoom settings'"
|
||||||
>
|
>
|
||||||
<zoom-settings
|
<ZoomSettings
|
||||||
:pan-zoom-locked="panZoomLocked"
|
:pan-zoom-locked="panZoomLocked"
|
||||||
:class="'c-control-menu c-menu--has-close-btn'"
|
:class="'c-control-menu c-menu--has-close-btn'"
|
||||||
:zoom-factor="zoomFactor"
|
:zoom-factor="zoomFactor"
|
||||||
:is-menu="true"
|
:is-menu="true"
|
||||||
@zoom-out="zoomOut"
|
|
||||||
@zoom-in="zoomIn"
|
|
||||||
@toggle-zoom-lock="toggleZoomLock"
|
|
||||||
@handle-reset-image="handleResetImage"
|
|
||||||
/>
|
/>
|
||||||
</imagery-view-menu-switcher>
|
</ImageryViewMenuSwitcher>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -98,7 +90,15 @@ export default {
|
|||||||
ImageryViewMenuSwitcher,
|
ImageryViewMenuSwitcher,
|
||||||
ZoomSettings
|
ZoomSettings
|
||||||
},
|
},
|
||||||
inject: ['openmct', 'domainObject'],
|
inject: ['openmct', 'domainObject', 'resetImage', 'handlePanZoomUpdate'],
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
resetImage: this.resetImage,
|
||||||
|
zoomIn: this.zoomIn,
|
||||||
|
zoomOut: this.zoomOut,
|
||||||
|
toggleZoomLock: this.toggleZoomLock
|
||||||
|
};
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
layers: {
|
layers: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@ -117,7 +117,6 @@ export default {
|
|||||||
},
|
},
|
||||||
emits: [
|
emits: [
|
||||||
'cursors-updated',
|
'cursors-updated',
|
||||||
'reset-image',
|
|
||||||
'pan-zoom-updated',
|
'pan-zoom-updated',
|
||||||
'filters-updated',
|
'filters-updated',
|
||||||
'start-pan',
|
'start-pan',
|
||||||
@ -155,7 +154,7 @@ export default {
|
|||||||
imageUrl(newUrl, oldUrl) {
|
imageUrl(newUrl, oldUrl) {
|
||||||
// reset image pan/zoom if newUrl only if not locked
|
// reset image pan/zoom if newUrl only if not locked
|
||||||
if (newUrl && !this.panZoomLocked) {
|
if (newUrl && !this.panZoomLocked) {
|
||||||
this.handleResetImage();
|
this.resetImage();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cursorStates(states) {
|
cursorStates(states) {
|
||||||
@ -172,12 +171,6 @@ export default {
|
|||||||
document.removeEventListener('keyup', this.handleKeyUp);
|
document.removeEventListener('keyup', this.handleKeyUp);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleResetImage() {
|
|
||||||
this.$emit('reset-image');
|
|
||||||
},
|
|
||||||
handleUpdatePanZoom(options) {
|
|
||||||
this.$emit('pan-zoom-updated', options);
|
|
||||||
},
|
|
||||||
toggleZoomLock() {
|
toggleZoomLock() {
|
||||||
this.panZoomLocked = !this.panZoomLocked;
|
this.panZoomLocked = !this.panZoomLocked;
|
||||||
},
|
},
|
||||||
@ -208,10 +201,10 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (newScaleFactor <= 0 || newScaleFactor <= ZOOM_LIMITS_MIN_DEFAULT) {
|
if (newScaleFactor <= 0 || newScaleFactor <= ZOOM_LIMITS_MIN_DEFAULT) {
|
||||||
return this.handleResetImage();
|
return this.resetImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleUpdatePanZoom({
|
this.handlePanZoomUpdate({
|
||||||
newScaleFactor,
|
newScaleFactor,
|
||||||
screenClientX,
|
screenClientX,
|
||||||
screenClientY
|
screenClientY
|
||||||
|
@ -28,8 +28,10 @@
|
|||||||
selected: selected,
|
selected: selected,
|
||||||
'real-time': realTime
|
'real-time': realTime
|
||||||
}"
|
}"
|
||||||
:aria-label="image.formattedTime"
|
:aria-label="ariaLabel"
|
||||||
:title="image.formattedTime"
|
:title="image.formattedTime"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
@click="handleClick"
|
@click="handleClick"
|
||||||
>
|
>
|
||||||
<a class="c-thumb__image-wrapper" href="" :download="image.imageDownloadName" @click.prevent>
|
<a class="c-thumb__image-wrapper" href="" :download="image.imageDownloadName" @click.prevent>
|
||||||
@ -94,6 +96,9 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
ariaLabel() {
|
||||||
|
return `Image thumbnail from ${this.image.formattedTime}${this.showAnnotationIndicator ? ', has annotations' : ''}`;
|
||||||
|
},
|
||||||
viewableAreaStyle() {
|
viewableAreaStyle() {
|
||||||
if (!this.viewableArea || !this.imgWidth || !this.imgHeight) {
|
if (!this.viewableArea || !this.imgWidth || !this.imgHeight) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -39,8 +39,6 @@
|
|||||||
:zoom-factor="zoomFactor"
|
:zoom-factor="zoomFactor"
|
||||||
:image-url="imageUrl"
|
:image-url="imageUrl"
|
||||||
:layers="layers"
|
:layers="layers"
|
||||||
@reset-image="resetImage"
|
|
||||||
@pan-zoom-updated="handlePanZoomUpdate"
|
|
||||||
@filters-updated="setFilters"
|
@filters-updated="setFilters"
|
||||||
@cursors-updated="setCursorStates"
|
@cursors-updated="setCursorStates"
|
||||||
@start-pan="startPan"
|
@start-pan="startPan"
|
||||||
@ -87,9 +85,11 @@
|
|||||||
fetchpriority="low"
|
fetchpriority="low"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="imageUrl"
|
v-show="imageUrl"
|
||||||
ref="focusedImageElement"
|
ref="focusedImageElement"
|
||||||
|
aria-label="Focused Image Element"
|
||||||
class="c-imagery__main-image__background-image"
|
class="c-imagery__main-image__background-image"
|
||||||
|
:class="{ 'is-zooming': isZooming, 'is-panning': isPanning }"
|
||||||
:draggable="!isSelectable"
|
:draggable="!isSelectable"
|
||||||
:style="focusImageStyles"
|
:style="focusImageStyles"
|
||||||
></div>
|
></div>
|
||||||
@ -112,6 +112,7 @@
|
|||||||
<button
|
<button
|
||||||
class="c-local-controls c-local-controls--show-on-hover c-imagery__prev-next-button c-nav c-nav--prev"
|
class="c-local-controls c-local-controls--show-on-hover c-imagery__prev-next-button c-nav c-nav--prev"
|
||||||
title="Previous image"
|
title="Previous image"
|
||||||
|
aria-label="Previous image"
|
||||||
:disabled="isPrevDisabled"
|
:disabled="isPrevDisabled"
|
||||||
@click="prevImage()"
|
@click="prevImage()"
|
||||||
></button>
|
></button>
|
||||||
@ -119,6 +120,7 @@
|
|||||||
<button
|
<button
|
||||||
class="c-local-controls c-local-controls--show-on-hover c-imagery__prev-next-button c-nav c-nav--next"
|
class="c-local-controls c-local-controls--show-on-hover c-imagery__prev-next-button c-nav c-nav--next"
|
||||||
title="Next image"
|
title="Next image"
|
||||||
|
aria-label="Next image"
|
||||||
:disabled="isNextDisabled"
|
:disabled="isNextDisabled"
|
||||||
@click="nextImage()"
|
@click="nextImage()"
|
||||||
></button>
|
></button>
|
||||||
@ -161,6 +163,7 @@
|
|||||||
v-if="!isFixed"
|
v-if="!isFixed"
|
||||||
class="c-button icon-pause pause-play"
|
class="c-button icon-pause pause-play"
|
||||||
:class="{ 'is-paused': isPaused }"
|
:class="{ 'is-paused': isPaused }"
|
||||||
|
aria-label="Pause automatic scrolling of image thumbnails"
|
||||||
@click="handlePauseButton(!isPaused)"
|
@click="handlePauseButton(!isPaused)"
|
||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
@ -184,6 +187,7 @@
|
|||||||
'animate-scroll': animateThumbScroll
|
'animate-scroll': animateThumbScroll
|
||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
|
aria-label="Image Thumbnails"
|
||||||
@scroll="handleScroll"
|
@scroll="handleScroll"
|
||||||
>
|
>
|
||||||
<ImageThumbnail
|
<ImageThumbnail
|
||||||
@ -192,7 +196,7 @@
|
|||||||
:image="image"
|
:image="image"
|
||||||
:active="focusedImageIndex === index"
|
:active="focusedImageIndex === index"
|
||||||
:imagery-annotations="imageryAnnotations[image.time]"
|
:imagery-annotations="imageryAnnotations[image.time]"
|
||||||
:selected="focusedImageIndex === index && isPaused"
|
:selected="isSelected(index)"
|
||||||
:real-time="!isFixed"
|
:real-time="!isFixed"
|
||||||
:viewable-area="focusedImageIndex === index ? viewableArea : null"
|
:viewable-area="focusedImageIndex === index ? viewableArea : null"
|
||||||
@click="thumbnailClicked(index)"
|
@click="thumbnailClicked(index)"
|
||||||
@ -202,6 +206,7 @@
|
|||||||
<button
|
<button
|
||||||
class="c-imagery__auto-scroll-resume-button c-icon-button icon-play"
|
class="c-imagery__auto-scroll-resume-button c-icon-button icon-play"
|
||||||
title="Resume automatic scrolling of image thumbnails"
|
title="Resume automatic scrolling of image thumbnails"
|
||||||
|
aria-label="Resume automatic scrolling of image thumbnails"
|
||||||
@click="scrollToRight"
|
@click="scrollToRight"
|
||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
@ -267,6 +272,13 @@ export default {
|
|||||||
'imageFreshnessOptions',
|
'imageFreshnessOptions',
|
||||||
'showCompassHUD'
|
'showCompassHUD'
|
||||||
],
|
],
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
toggleZoomLock: this.toggleZoomLock,
|
||||||
|
resetImage: this.resetImage,
|
||||||
|
handlePanZoomUpdate: this.handlePanZoomUpdate
|
||||||
|
};
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
focusedImageTimestamp: {
|
focusedImageTimestamp: {
|
||||||
type: Number,
|
type: Number,
|
||||||
@ -282,58 +294,62 @@ export default {
|
|||||||
this.requestCount = 0;
|
this.requestCount = 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
timeFormat: '',
|
animateThumbScroll: false,
|
||||||
layers: [],
|
animateZoom: true,
|
||||||
visibleLayers: [],
|
annotationsBeingMarqueed: false,
|
||||||
durationFormatter: undefined,
|
|
||||||
imageHistory: [],
|
|
||||||
bounds: {},
|
|
||||||
timeSystem: timeSystem,
|
|
||||||
keyString: undefined,
|
|
||||||
autoScroll: true,
|
autoScroll: true,
|
||||||
thumbnailClick: THUMBNAIL_CLICKED,
|
bounds: {},
|
||||||
isPaused: false,
|
|
||||||
isFixed: false,
|
|
||||||
canTrackDuration: false,
|
canTrackDuration: false,
|
||||||
refreshCSS: false,
|
cursorStates: {
|
||||||
focusedImageIndex: undefined,
|
isPannable: false,
|
||||||
focusedImageRelatedTelemetry: {},
|
modifierKeyPressed: false,
|
||||||
numericDuration: undefined,
|
showCursorZoomIn: false,
|
||||||
relatedTelemetry: {},
|
showCursorZoomOut: false
|
||||||
latestRelatedTelemetry: {},
|
},
|
||||||
focusedImageNaturalAspectRatio: undefined,
|
durationFormatter: undefined,
|
||||||
imageContainerWidth: undefined,
|
|
||||||
imageContainerHeight: undefined,
|
|
||||||
sizedImageWidth: 0,
|
|
||||||
sizedImageHeight: 0,
|
|
||||||
viewHeight: 0,
|
|
||||||
lockCompass: true,
|
|
||||||
resizingWindow: false,
|
|
||||||
zoomFactor: ZOOM_SCALE_DEFAULT,
|
|
||||||
filters: {
|
filters: {
|
||||||
brightness: 100,
|
brightness: 100,
|
||||||
contrast: 100
|
contrast: 100
|
||||||
},
|
},
|
||||||
cursorStates: {
|
focusedImageIndex: undefined,
|
||||||
isPannable: false,
|
focusedImageNaturalAspectRatio: undefined,
|
||||||
showCursorZoomIn: false,
|
focusedImageRelatedTelemetry: {},
|
||||||
showCursorZoomOut: false,
|
forceShowThumbnails: false,
|
||||||
modifierKeyPressed: false
|
imageContainerHeight: undefined,
|
||||||
},
|
imageContainerWidth: undefined,
|
||||||
|
imageHistory: [],
|
||||||
|
imagePanned: false,
|
||||||
imageTranslateX: 0,
|
imageTranslateX: 0,
|
||||||
imageTranslateY: 0,
|
imageTranslateY: 0,
|
||||||
imageViewportWidth: 0,
|
|
||||||
imageViewportHeight: 0,
|
imageViewportHeight: 0,
|
||||||
pan: undefined,
|
imageViewportWidth: 0,
|
||||||
animateZoom: true,
|
|
||||||
imagePanned: false,
|
|
||||||
forceShowThumbnails: false,
|
|
||||||
animateThumbScroll: false,
|
|
||||||
imageryAnnotations: {},
|
imageryAnnotations: {},
|
||||||
annotationsBeingMarqueed: false
|
isFixed: false,
|
||||||
|
isPaused: false,
|
||||||
|
isZooming: false,
|
||||||
|
keyString: undefined,
|
||||||
|
latestRelatedTelemetry: {},
|
||||||
|
layers: [],
|
||||||
|
lockCompass: true,
|
||||||
|
numericDuration: undefined,
|
||||||
|
pan: null,
|
||||||
|
refreshCSS: false,
|
||||||
|
relatedTelemetry: {},
|
||||||
|
resizingWindow: false,
|
||||||
|
sizedImageHeight: 0,
|
||||||
|
sizedImageWidth: 0,
|
||||||
|
thumbnailClick: THUMBNAIL_CLICKED,
|
||||||
|
timeFormat: '',
|
||||||
|
timeSystem: timeSystem,
|
||||||
|
viewHeight: 0,
|
||||||
|
visibleLayers: [],
|
||||||
|
zoomFactor: ZOOM_SCALE_DEFAULT
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
isPanning() {
|
||||||
|
return Boolean(this.pan);
|
||||||
|
},
|
||||||
displayThumbnails() {
|
displayThumbnails() {
|
||||||
return this.forceShowThumbnails || this.viewHeight >= SHOW_THUMBS_THRESHOLD_HEIGHT;
|
return this.forceShowThumbnails || this.viewHeight >= SHOW_THUMBS_THRESHOLD_HEIGHT;
|
||||||
},
|
},
|
||||||
@ -667,6 +683,9 @@ export default {
|
|||||||
this.focusedImageWrapper = this.$refs.focusedImageWrapper;
|
this.focusedImageWrapper = this.$refs.focusedImageWrapper;
|
||||||
this.focusedImageElement = this.$refs.focusedImageElement;
|
this.focusedImageElement = this.$refs.focusedImageElement;
|
||||||
|
|
||||||
|
this.focusedImageElement.addEventListener('transitionstart', this.handleZoomTransitionStart);
|
||||||
|
this.focusedImageElement.addEventListener('transitionend', this.handleZoomTransitionEnd);
|
||||||
|
|
||||||
//We only need to use this till the user focuses an image manually
|
//We only need to use this till the user focuses an image manually
|
||||||
if (this.focusedImageTimestamp !== undefined) {
|
if (this.focusedImageTimestamp !== undefined) {
|
||||||
this.isPaused = true;
|
this.isPaused = true;
|
||||||
@ -724,6 +743,9 @@ export default {
|
|||||||
this.openmct.selection.on('change', this.updateSelection);
|
this.openmct.selection.on('change', this.updateSelection);
|
||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
|
this.focusedImageElement.removeEventListener('transitionstart', this.handleZoomTransitionStart);
|
||||||
|
this.focusedImageElement.removeEventListener('transitionend', this.handleZoomTransitionEnd);
|
||||||
|
|
||||||
this.abortController.abort();
|
this.abortController.abort();
|
||||||
this.persistVisibleLayers();
|
this.persistVisibleLayers();
|
||||||
this.stopFollowingTimeContext();
|
this.stopFollowingTimeContext();
|
||||||
@ -886,6 +908,12 @@ export default {
|
|||||||
|
|
||||||
return mostRecent[valueKey];
|
return mostRecent[valueKey];
|
||||||
},
|
},
|
||||||
|
handleZoomTransitionStart() {
|
||||||
|
this.isZooming = true;
|
||||||
|
},
|
||||||
|
handleZoomTransitionEnd() {
|
||||||
|
this.isZooming = false;
|
||||||
|
},
|
||||||
loadVisibleLayers() {
|
loadVisibleLayers() {
|
||||||
const layersMetadata = this.imageMetadataValue.layers;
|
const layersMetadata = this.imageMetadataValue.layers;
|
||||||
if (!layersMetadata) {
|
if (!layersMetadata) {
|
||||||
@ -1399,7 +1427,7 @@ export default {
|
|||||||
this.updatePanZoom(this.zoomFactor, dX, dY);
|
this.updatePanZoom(this.zoomFactor, dX, dY);
|
||||||
},
|
},
|
||||||
endPan() {
|
endPan() {
|
||||||
this.pan = undefined;
|
this.pan = null;
|
||||||
this.animateZoom = true;
|
this.animateZoom = true;
|
||||||
},
|
},
|
||||||
onMouseUp(event) {
|
onMouseUp(event) {
|
||||||
@ -1420,6 +1448,9 @@ export default {
|
|||||||
let isVisible = this.layers[index].visible === true;
|
let isVisible = this.layers[index].visible === true;
|
||||||
this.layers[index].visible = !isVisible;
|
this.layers[index].visible = !isVisible;
|
||||||
this.visibleLayers = this.layers.filter((layer) => layer.visible);
|
this.visibleLayers = this.layers.filter((layer) => layer.visible);
|
||||||
|
},
|
||||||
|
isSelected(index) {
|
||||||
|
return this.focusedImageIndex === index && this.isPaused;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -26,14 +26,21 @@
|
|||||||
<button
|
<button
|
||||||
class="c-button t-btn-zoom-out icon-minus"
|
class="c-button t-btn-zoom-out icon-minus"
|
||||||
title="Zoom out"
|
title="Zoom out"
|
||||||
|
aria-label="Zoom out"
|
||||||
@click="zoomOut"
|
@click="zoomOut"
|
||||||
></button>
|
></button>
|
||||||
|
|
||||||
<button class="c-button t-btn-zoom-in icon-plus" title="Zoom in" @click="zoomIn"></button>
|
<button
|
||||||
|
class="c-button t-btn-zoom-in icon-plus"
|
||||||
|
title="Zoom in"
|
||||||
|
aria-label="Zoom in"
|
||||||
|
@click="zoomIn"
|
||||||
|
></button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="c-button t-btn-zoom-lock"
|
class="c-button t-btn-zoom-lock"
|
||||||
title="Lock current zoom and pan across all images"
|
title="Lock current zoom and pan across all images"
|
||||||
|
aria-label="Lock current zoom and pan across all images"
|
||||||
:class="{ 'icon-unlocked': !panZoomLocked, 'icon-lock': panZoomLocked }"
|
:class="{ 'icon-unlocked': !panZoomLocked, 'icon-lock': panZoomLocked }"
|
||||||
@click="toggleZoomLock"
|
@click="toggleZoomLock"
|
||||||
></button>
|
></button>
|
||||||
@ -41,7 +48,8 @@
|
|||||||
<button
|
<button
|
||||||
class="c-button icon-reset t-btn-zoom-reset"
|
class="c-button icon-reset t-btn-zoom-reset"
|
||||||
title="Remove zoom and pan"
|
title="Remove zoom and pan"
|
||||||
@click="handleResetImage"
|
aria-label="Remove zoom and pan"
|
||||||
|
@click="resetImage"
|
||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-image-controls__zoom-factor">x{{ formattedZoomFactor }}</div>
|
<div class="c-image-controls__zoom-factor">x{{ formattedZoomFactor }}</div>
|
||||||
@ -49,13 +57,14 @@
|
|||||||
<button
|
<button
|
||||||
v-if="isMenu"
|
v-if="isMenu"
|
||||||
class="c-click-icon icon-x t-btn-close c-switcher-menu__close-button"
|
class="c-click-icon icon-x t-btn-close c-switcher-menu__close-button"
|
||||||
|
aria-label="Close menu"
|
||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['zoomIn', 'zoomOut', 'toggleZoomLock', 'resetImage'],
|
||||||
props: {
|
props: {
|
||||||
zoomFactor: {
|
zoomFactor: {
|
||||||
type: Number,
|
type: Number,
|
||||||
@ -70,10 +79,6 @@ export default {
|
|||||||
required: false
|
required: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['handle-reset-image', 'toggle-zoom-lock', 'zoom-in', 'zoom-out'],
|
|
||||||
data() {
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
formattedZoomFactor() {
|
formattedZoomFactor() {
|
||||||
return Number.parseFloat(this.zoomFactor).toPrecision(2);
|
return Number.parseFloat(this.zoomFactor).toPrecision(2);
|
||||||
@ -85,18 +90,6 @@ export default {
|
|||||||
if (!closeButton) {
|
if (!closeButton) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
handleResetImage() {
|
|
||||||
this.$emit('handle-reset-image');
|
|
||||||
},
|
|
||||||
toggleZoomLock() {
|
|
||||||
this.$emit('toggle-zoom-lock');
|
|
||||||
},
|
|
||||||
zoomIn() {
|
|
||||||
this.$emit('zoom-in');
|
|
||||||
},
|
|
||||||
zoomOut() {
|
|
||||||
this.$emit('zoom-out');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<span class="c-elements-pool__grippy c-grippy c-grippy--vertical-drag"></span>
|
<span class="c-elements-pool__grippy c-grippy c-grippy--vertical-drag"></span>
|
||||||
<object-label
|
<ObjectLabel
|
||||||
:domain-object="elementObject"
|
:domain-object="elementObject"
|
||||||
:object-path="[elementObject, domainObject]"
|
:object-path="[elementObject, domainObject]"
|
||||||
@context-click-active="setContextClickState"
|
@context-click-active="setContextClickState"
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
id="inspector-elements-tree"
|
id="inspector-elements-tree"
|
||||||
class="c-tree c-elements-pool__tree"
|
class="c-tree c-elements-pool__tree"
|
||||||
>
|
>
|
||||||
<element-item
|
<ElementItem
|
||||||
v-for="(element, index) in elements"
|
v-for="(element, index) in elements"
|
||||||
:key="element.identifier.key"
|
:key="element.identifier.key"
|
||||||
:index="index"
|
:index="index"
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
<div class="c-elements-pool__instructions">
|
<div class="c-elements-pool__instructions">
|
||||||
Select and drag an element to move it into a different axis.
|
Select and drag an element to move it into a different axis.
|
||||||
</div>
|
</div>
|
||||||
<element-item-group
|
<ElementItemGroup
|
||||||
v-for="(yAxis, index) in yAxes"
|
v-for="(yAxis, index) in yAxes"
|
||||||
:key="`element-group-yaxis-${yAxis.id}`"
|
:key="`element-group-yaxis-${yAxis.id}`"
|
||||||
:parent-object="parentObject"
|
:parent-object="parentObject"
|
||||||
@ -46,7 +46,7 @@
|
|||||||
@drop-group="moveTo($event, 0, yAxis.id)"
|
@drop-group="moveTo($event, 0, yAxis.id)"
|
||||||
>
|
>
|
||||||
<li class="js-first-place" @drop="moveTo($event, 0, yAxis.id)"></li>
|
<li class="js-first-place" @drop="moveTo($event, 0, yAxis.id)"></li>
|
||||||
<element-item
|
<ElementItem
|
||||||
v-for="(element, elemIndex) in yAxis.elements"
|
v-for="(element, elemIndex) in yAxis.elements"
|
||||||
:key="element.identifier.key"
|
:key="element.identifier.key"
|
||||||
:index="elemIndex"
|
:index="elemIndex"
|
||||||
@ -61,7 +61,7 @@
|
|||||||
class="js-last-place"
|
class="js-last-place"
|
||||||
@drop="moveTo($event, yAxis.elements.length, yAxis.id)"
|
@drop="moveTo($event, yAxis.elements.length, yAxis.id)"
|
||||||
></li>
|
></li>
|
||||||
</element-item-group>
|
</ElementItemGroup>
|
||||||
</ul>
|
</ul>
|
||||||
<div v-show="!hasElements">No contained elements</div>
|
<div v-show="!hasElements">No contained elements</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
:key="pathObject.key"
|
:key="pathObject.key"
|
||||||
class="c-location__item"
|
class="c-location__item"
|
||||||
>
|
>
|
||||||
<object-label
|
<ObjectLabel
|
||||||
:domain-object="pathObject.domainObject"
|
:domain-object="pathObject.domainObject"
|
||||||
:object-path="pathObject.objectPath"
|
:object-path="pathObject.objectPath"
|
||||||
/>
|
/>
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<div class="c-inspector__saved-styles c-inspect-styles">
|
<div class="c-inspector__saved-styles c-inspect-styles">
|
||||||
<div class="c-inspect-styles__content">
|
<div class="c-inspect-styles__content">
|
||||||
<div class="c-inspect-styles__saved-styles">
|
<div class="c-inspect-styles__saved-styles">
|
||||||
<saved-style-selector
|
<SavedStyleSelector
|
||||||
v-for="(savedStyle, index) in savedStyles"
|
v-for="(savedStyle, index) in savedStyles"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="c-inspect-styles__saved-style"
|
class="c-inspect-styles__saved-style"
|
||||||
|
@ -21,16 +21,16 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<multipane type="vertical">
|
<Multipane type="vertical">
|
||||||
<pane class="c-inspector__styles">
|
<Pane class="c-inspector__styles">
|
||||||
<div class="u-contents">
|
<div class="u-contents">
|
||||||
<StylesView />
|
<StylesView />
|
||||||
</div>
|
</div>
|
||||||
</pane>
|
</Pane>
|
||||||
<pane v-if="isEditing" class="c-inspector__saved-styles" handle="before" label="Saved Styles">
|
<Pane v-if="isEditing" class="c-inspector__saved-styles" handle="before" label="Saved Styles">
|
||||||
<SavedStylesInspectorView />
|
<SavedStylesInspectorView />
|
||||||
</pane>
|
</Pane>
|
||||||
</multipane>
|
</Multipane>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -107,7 +107,7 @@
|
|||||||
To start a new entry, click here or drag and drop any object
|
To start a new entry, click here or drag and drop any object
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<progress-bar
|
<ProgressBar
|
||||||
v-if="savingTransaction"
|
v-if="savingTransaction"
|
||||||
class="c-telemetry-table__progress-bar"
|
class="c-telemetry-table__progress-bar"
|
||||||
:model="{ progressPerc: null }"
|
:model="{ progressPerc: null }"
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="c-indicator__count">{{ notifications.length }}</span>
|
<span class="c-indicator__count">{{ notifications.length }}</span>
|
||||||
|
|
||||||
<notifications-list
|
<NotificationsList
|
||||||
v-if="showNotificationsOverlay"
|
v-if="showNotificationsOverlay"
|
||||||
:notifications="notifications"
|
:notifications="notifications"
|
||||||
@close="toggleNotificationsList"
|
@close="toggleNotificationsList"
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
<div class="c-message__title">{{ notification.model.message }}</div>
|
<div class="c-message__title">{{ notification.model.message }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="message-body">
|
<div class="message-body">
|
||||||
<progress-bar v-if="isProgressNotification" :model="progressObject" />
|
<ProgressBar v-if="isProgressNotification" :model="progressObject" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div role="list" class="w-messages c-overlay__messages">
|
<div role="list" class="w-messages c-overlay__messages">
|
||||||
<notification-message
|
<NotificationMessage
|
||||||
v-for="(notification, notificationIndex) in notifications"
|
v-for="(notification, notificationIndex) in notifications"
|
||||||
:key="notificationIndex"
|
:key="notificationIndex"
|
||||||
:close-overlay="closeOverlay"
|
:close-overlay="closeOverlay"
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<swim-lane :is-nested="isNested" :status="status">
|
<SwimLane :is-nested="isNested" :status="status">
|
||||||
<template #label>
|
<template #label>
|
||||||
{{ heading }}
|
{{ heading }}
|
||||||
</template>
|
</template>
|
||||||
@ -88,7 +88,7 @@
|
|||||||
</text>
|
</text>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
</swim-lane>
|
</SwimLane>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -23,17 +23,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="plan" class="c-plan c-timeline-holder">
|
<div ref="plan" class="c-plan c-timeline-holder">
|
||||||
<template v-if="viewBounds && !options.compact">
|
<template v-if="viewBounds && !options.compact">
|
||||||
<swim-lane>
|
<SwimLane>
|
||||||
<template #label>{{ timeSystem.name }}</template>
|
<template #label>{{ timeSystem.name }}</template>
|
||||||
<template #object>
|
<template #object>
|
||||||
<timeline-axis
|
<TimelineAxis
|
||||||
:bounds="viewBounds"
|
:bounds="viewBounds"
|
||||||
:time-system="timeSystem"
|
:time-system="timeSystem"
|
||||||
:content-height="height"
|
:content-height="height"
|
||||||
:rendering-engine="renderingEngine"
|
:rendering-engine="renderingEngine"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</swim-lane>
|
</SwimLane>
|
||||||
</template>
|
</template>
|
||||||
<div class="c-plan__contents u-contents">
|
<div class="c-plan__contents u-contents">
|
||||||
<ActivityTimeline
|
<ActivityTimeline
|
||||||
|
@ -20,19 +20,19 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<plan-activity-time-view
|
<PlanActivityTimeView
|
||||||
v-for="activity in activities"
|
v-for="activity in activities"
|
||||||
:key="activity.key"
|
:key="activity.key"
|
||||||
:activity="activity"
|
:activity="activity"
|
||||||
:heading="heading"
|
:heading="heading"
|
||||||
/>
|
/>
|
||||||
<plan-activity-properties-view
|
<PlanActivityPropertiesView
|
||||||
v-for="activity in activities"
|
v-for="activity in activities"
|
||||||
:key="activity.key"
|
:key="activity.key"
|
||||||
heading="Properties"
|
heading="Properties"
|
||||||
:activity="activity"
|
:activity="activity"
|
||||||
/>
|
/>
|
||||||
<plan-activity-status-view
|
<PlanActivityStatusView
|
||||||
v-if="canPersistState"
|
v-if="canPersistState"
|
||||||
:key="activities[0].key"
|
:key="activities[0].key"
|
||||||
:activity="activities[0]"
|
:activity="activities[0]"
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
<div v-if="properties.length" class="u-contents">
|
<div v-if="properties.length" class="u-contents">
|
||||||
<div class="c-inspect-properties__header">{{ heading }}</div>
|
<div class="c-inspect-properties__header">{{ heading }}</div>
|
||||||
<ul v-for="property in properties" :key="property.id" class="c-inspect-properties__section">
|
<ul v-for="property in properties" :key="property.id" class="c-inspect-properties__section">
|
||||||
<activity-property :label="property.label" :value="property.value" />
|
<ActivityProperty :label="property.label" :value="property.value" />
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
:key="timeProperty.id"
|
:key="timeProperty.id"
|
||||||
class="c-inspect-properties__section"
|
class="c-inspect-properties__section"
|
||||||
>
|
>
|
||||||
<activity-property :label="timeProperty.label" :value="timeProperty.value" />
|
<ActivityProperty :label="timeProperty.label" :value="timeProperty.value" />
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
<slot></slot>
|
<slot></slot>
|
||||||
<div class="plot-wrapper-axis-and-display-area flex-elem grows">
|
<div class="plot-wrapper-axis-and-display-area flex-elem grows">
|
||||||
<div v-if="seriesModels.length" class="u-contents">
|
<div v-if="seriesModels.length" class="u-contents">
|
||||||
<y-axis
|
<YAxis
|
||||||
v-for="(yAxis, index) in yAxesIds"
|
v-for="(yAxis, index) in yAxesIds"
|
||||||
:id="yAxis.id"
|
:id="yAxis.id"
|
||||||
:key="`yAxis-${yAxis.id}-${index}`"
|
:key="`yAxis-${yAxis.id}-${index}`"
|
||||||
@ -68,7 +68,7 @@
|
|||||||
class="gl-plot-chart-wrapper"
|
class="gl-plot-chart-wrapper"
|
||||||
:class="[{ 'alt-pressed': altPressed }]"
|
:class="[{ 'alt-pressed': altPressed }]"
|
||||||
>
|
>
|
||||||
<mct-chart
|
<MctChart
|
||||||
:rectangles="rectangles"
|
:rectangles="rectangles"
|
||||||
:highlights="highlights"
|
:highlights="highlights"
|
||||||
:show-limit-line-labels="limitLineLabels"
|
:show-limit-line-labels="limitLineLabels"
|
||||||
@ -159,10 +159,7 @@
|
|||||||
class="c-cursor-guide--h js-cursor-guide--h"
|
class="c-cursor-guide--h js-cursor-guide--h"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<x-axis
|
<XAxis v-if="seriesModels.length > 0 && !options.compact" :series-model="seriesModels[0]" />
|
||||||
v-if="seriesModels.length > 0 && !options.compact"
|
|
||||||
:series-model="seriesModels[0]"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -33,12 +33,12 @@
|
|||||||
's-status-timeconductor-unsynced': status === 'timeconductor-unsynced'
|
's-status-timeconductor-unsynced': status === 'timeconductor-unsynced'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<progress-bar
|
<ProgressBar
|
||||||
v-show="!!loading"
|
v-show="!!loading"
|
||||||
class="c-telemetry-table__progress-bar"
|
class="c-telemetry-table__progress-bar"
|
||||||
:model="{ progressPerc: null }"
|
:model="{ progressPerc: null }"
|
||||||
/>
|
/>
|
||||||
<mct-plot
|
<MctPlot
|
||||||
ref="mctPlot"
|
ref="mctPlot"
|
||||||
:class="[plotLegendExpandedStateClass, plotLegendPositionClass]"
|
:class="[plotLegendExpandedStateClass, plotLegendPositionClass]"
|
||||||
:init-grid-lines="gridLinesProp"
|
:init-grid-lines="gridLinesProp"
|
||||||
@ -54,7 +54,7 @@
|
|||||||
@cursor-guide="onCursorGuideChange"
|
@cursor-guide="onCursorGuideChange"
|
||||||
@grid-lines="onGridLinesChange"
|
@grid-lines="onGridLinesChange"
|
||||||
>
|
>
|
||||||
<plot-legend
|
<PlotLegend
|
||||||
v-if="configReady && hideLegend === false"
|
v-if="configReady && hideLegend === false"
|
||||||
:cursor-locked="lockHighlightPoint"
|
:cursor-locked="lockHighlightPoint"
|
||||||
:highlights="highlights"
|
:highlights="highlights"
|
||||||
@ -62,7 +62,7 @@
|
|||||||
@expanded="updateExpanded"
|
@expanded="updateExpanded"
|
||||||
@position="updatePosition"
|
@position="updatePosition"
|
||||||
/>
|
/>
|
||||||
</mct-plot>
|
</MctPlot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -37,18 +37,18 @@
|
|||||||
aria-label="Plot Canvas"
|
aria-label="Plot Canvas"
|
||||||
></canvas>
|
></canvas>
|
||||||
<div ref="limitArea" class="js-limit-area" aria-hidden="true">
|
<div ref="limitArea" class="js-limit-area" aria-hidden="true">
|
||||||
<limit-label
|
<LimitLabel
|
||||||
v-for="(limitLabel, index) in visibleLimitLabels"
|
v-for="(limitLabel, index) in visibleLimitLabels"
|
||||||
:key="`limitLabel-${limitLabel.limit.seriesKey}-${index}`"
|
:key="`limitLabel-${limitLabel.limit.seriesKey}-${index}`"
|
||||||
:point="limitLabel.point"
|
:point="limitLabel.point"
|
||||||
:limit="limitLabel.limit"
|
:limit="limitLabel.limit"
|
||||||
></limit-label>
|
></LimitLabel>
|
||||||
<limit-line
|
<LimitLine
|
||||||
v-for="(limitLine, index) in visibleLimitLines"
|
v-for="(limitLine, index) in visibleLimitLines"
|
||||||
:key="`limitLine-${limitLine.limit.seriesKey}${index}`"
|
:key="`limitLine-${limitLine.limit.seriesKey}${index}`"
|
||||||
:point="limitLine.point"
|
:point="limitLine.point"
|
||||||
:limit="limitLine.limit"
|
:limit="limitLine.limit"
|
||||||
></limit-line>
|
></LimitLine>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -22,10 +22,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="canEdit">
|
<div v-if="canEdit">
|
||||||
<plot-options-edit />
|
<PlotOptionsEdit />
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<plot-options-browse />
|
<PlotOptionsBrowse />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -24,10 +24,10 @@
|
|||||||
<ul v-if="!isStackedPlotObject" class="c-tree" role="tree">
|
<ul v-if="!isStackedPlotObject" class="c-tree" role="tree">
|
||||||
<h2 class="--first" title="Display properties for this Plot Series object">Plot Series</h2>
|
<h2 class="--first" title="Display properties for this Plot Series object">Plot Series</h2>
|
||||||
<li v-for="series in plotSeries" :key="series.keyString">
|
<li v-for="series in plotSeries" :key="series.keyString">
|
||||||
<series-form :series="series" @series-updated="updateSeriesConfigForObject" />
|
<SeriesForm :series="series" @series-updated="updateSeriesConfigForObject" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<y-axis-form
|
<YAxisForm
|
||||||
v-for="(yAxisId, index) in yAxesIds"
|
v-for="(yAxisId, index) in yAxesIds"
|
||||||
:id="yAxisId.id"
|
:id="yAxisId.id"
|
||||||
:key="`yAxis-${index}`"
|
:key="`yAxis-${index}`"
|
||||||
@ -44,7 +44,7 @@
|
|||||||
role="tree"
|
role="tree"
|
||||||
>
|
>
|
||||||
<h2 class="--first" title="Legend options">Legend</h2>
|
<h2 class="--first" title="Legend options">Legend</h2>
|
||||||
<legend-form role="treeitem" tabindex="0" class="grid-properties" :legend="config.legend" />
|
<LegendForm role="treeitem" tabindex="0" class="grid-properties" :legend="config.legend" />
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
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."
|
||||||
></div>
|
></div>
|
||||||
<plot-legend-item-collapsed
|
<PlotLegendItemCollapsed
|
||||||
v-for="(seriesObject, seriesIndex) in seriesModels"
|
v-for="(seriesObject, seriesIndex) in seriesModels"
|
||||||
:key="`${seriesObject.keyString}-${seriesIndex}-collapsed`"
|
:key="`${seriesObject.keyString}-${seriesIndex}-collapsed`"
|
||||||
:highlights="highlights"
|
:highlights="highlights"
|
||||||
@ -79,7 +79,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<plot-legend-item-expanded
|
<PlotLegendItemExpanded
|
||||||
v-for="(seriesObject, seriesIndex) in seriesModels"
|
v-for="(seriesObject, seriesIndex) in seriesModels"
|
||||||
:key="`${seriesObject.keyString}-${seriesIndex}-expanded`"
|
:key="`${seriesObject.keyString}-${seriesIndex}-expanded`"
|
||||||
:series-key-string="seriesObject.keyString"
|
:series-key-string="seriesObject.keyString"
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
:class="[plotLegendExpandedStateClass, plotLegendPositionClass]"
|
:class="[plotLegendExpandedStateClass, plotLegendPositionClass]"
|
||||||
aria-label="Stacked Plot Style Target"
|
aria-label="Stacked Plot Style Target"
|
||||||
>
|
>
|
||||||
<plot-legend
|
<PlotLegend
|
||||||
v-if="compositionObjectsConfigLoaded && showLegendsForChildren === false"
|
v-if="compositionObjectsConfigLoaded && showLegendsForChildren === false"
|
||||||
:cursor-locked="!!lockHighlightPoint"
|
:cursor-locked="!!lockHighlightPoint"
|
||||||
:highlights="highlights"
|
:highlights="highlights"
|
||||||
@ -37,7 +37,7 @@
|
|||||||
@position="updatePosition"
|
@position="updatePosition"
|
||||||
/>
|
/>
|
||||||
<div class="l-view-section">
|
<div class="l-view-section">
|
||||||
<stacked-plot-item
|
<StackedPlotItem
|
||||||
v-for="objectWrapper in compositionObjects"
|
v-for="objectWrapper in compositionObjects"
|
||||||
ref="stackedPlotItems"
|
ref="stackedPlotItems"
|
||||||
:key="objectWrapper.keyString"
|
:key="objectWrapper.keyString"
|
||||||
|
@ -76,7 +76,7 @@
|
|||||||
class="c-tabs-view__object-holder"
|
class="c-tabs-view__object-holder"
|
||||||
:class="{ 'c-tabs-view__object-holder--hidden': !isCurrent(tab) }"
|
:class="{ 'c-tabs-view__object-holder--hidden': !isCurrent(tab) }"
|
||||||
>
|
>
|
||||||
<object-view
|
<ObjectView
|
||||||
v-if="shouldLoadTab(tab)"
|
v-if="shouldLoadTab(tab)"
|
||||||
class="c-tabs-view__object"
|
class="c-tabs-view__object"
|
||||||
:default-object="tab.domainObject"
|
:default-object="tab.domainObject"
|
||||||
|
@ -100,7 +100,7 @@
|
|||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<toggle-switch
|
<ToggleSwitch
|
||||||
id="show-filtered-rows-toggle"
|
id="show-filtered-rows-toggle"
|
||||||
label="Show selected items only"
|
label="Show selected items only"
|
||||||
:checked="isShowingMarkedRowsOnly"
|
:checked="isShowingMarkedRowsOnly"
|
||||||
@ -139,7 +139,7 @@
|
|||||||
:style="dropTargetStyle"
|
:style="dropTargetStyle"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<progress-bar
|
<ProgressBar
|
||||||
v-if="loading"
|
v-if="loading"
|
||||||
class="c-telemetry-table__progress-bar"
|
class="c-telemetry-table__progress-bar"
|
||||||
:model="{ progressPerc: null }"
|
:model="{ progressPerc: null }"
|
||||||
@ -155,7 +155,7 @@
|
|||||||
<table class="c-table__headers c-telemetry-table__headers">
|
<table class="c-table__headers c-telemetry-table__headers">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="c-telemetry-table__headers__labels">
|
<tr class="c-telemetry-table__headers__labels">
|
||||||
<table-column-header
|
<TableColumnHeader
|
||||||
v-for="(title, key, headerIndex) in headers"
|
v-for="(title, key, headerIndex) in headers"
|
||||||
:key="key"
|
:key="key"
|
||||||
:header-key="key"
|
:header-key="key"
|
||||||
@ -171,10 +171,10 @@
|
|||||||
@resize-column-end="updateConfiguredColumnWidths"
|
@resize-column-end="updateConfiguredColumnWidths"
|
||||||
>
|
>
|
||||||
<span class="c-telemetry-table__headers__label">{{ title }}</span>
|
<span class="c-telemetry-table__headers__label">{{ title }}</span>
|
||||||
</table-column-header>
|
</TableColumnHeader>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="allowFiltering" class="c-telemetry-table__headers__filter">
|
<tr v-if="allowFiltering" class="c-telemetry-table__headers__filter">
|
||||||
<table-column-header
|
<TableColumnHeader
|
||||||
v-for="(title, key, headerIndex) in headers"
|
v-for="(title, key, headerIndex) in headers"
|
||||||
:key="key"
|
:key="key"
|
||||||
:header-key="key"
|
:header-key="key"
|
||||||
@ -188,7 +188,7 @@
|
|||||||
@reorder-column="reorderColumn"
|
@reorder-column="reorderColumn"
|
||||||
@resize-column-end="updateConfiguredColumnWidths"
|
@resize-column-end="updateConfiguredColumnWidths"
|
||||||
>
|
>
|
||||||
<search
|
<Search
|
||||||
:value="filters[key]"
|
:value="filters[key]"
|
||||||
class="c-table__search"
|
class="c-table__search"
|
||||||
:aria-label="`${key} filter input`"
|
:aria-label="`${key} filter input`"
|
||||||
@ -204,8 +204,8 @@
|
|||||||
>
|
>
|
||||||
/R/
|
/R/
|
||||||
</button>
|
</button>
|
||||||
</search>
|
</Search>
|
||||||
</table-column-header>
|
</TableColumnHeader>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
</table>
|
</table>
|
||||||
@ -225,7 +225,7 @@
|
|||||||
:aria-label="`${table.domainObject.name} table content`"
|
:aria-label="`${table.domainObject.name} table content`"
|
||||||
>
|
>
|
||||||
<tbody>
|
<tbody>
|
||||||
<telemetry-table-row
|
<TelemetryTableRow
|
||||||
v-for="(row, rowIndex) in visibleRows"
|
v-for="(row, rowIndex) in visibleRows"
|
||||||
:key="rowIndex"
|
:key="rowIndex"
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
@ -250,7 +250,7 @@
|
|||||||
class="c-telemetry-table__sizing js-telemetry-table__sizing"
|
class="c-telemetry-table__sizing js-telemetry-table__sizing"
|
||||||
:style="sizingTableWidth"
|
:style="sizingTableWidth"
|
||||||
>
|
>
|
||||||
<sizing-row :is-editing="isEditing" @change-height="setRowHeight" />
|
<SizingRow :is-editing="isEditing" @change-height="setRowHeight" />
|
||||||
<tr>
|
<tr>
|
||||||
<template v-for="(title, key) in headers" :key="key">
|
<template v-for="(title, key) in headers" :key="key">
|
||||||
<th
|
<th
|
||||||
@ -263,7 +263,7 @@
|
|||||||
</th>
|
</th>
|
||||||
</template>
|
</template>
|
||||||
</tr>
|
</tr>
|
||||||
<telemetry-table-row
|
<TelemetryTableRow
|
||||||
v-for="(sizingRowData, objectKeyString) in sizingRows"
|
v-for="(sizingRowData, objectKeyString) in sizingRows"
|
||||||
:key="objectKeyString"
|
:key="objectKeyString"
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
@ -273,7 +273,7 @@
|
|||||||
@row-context-click="updateViewContext"
|
@row-context-click="updateViewContext"
|
||||||
/>
|
/>
|
||||||
</table>
|
</table>
|
||||||
<table-footer-indicator
|
<TableFooterIndicator
|
||||||
class="c-telemetry-table__footer"
|
class="c-telemetry-table__footer"
|
||||||
:marked-rows="markedRows.length"
|
:marked-rows="markedRows.length"
|
||||||
:total-rows="totalNumberOfRows"
|
:total-rows="totalNumberOfRows"
|
||||||
|
@ -32,13 +32,13 @@
|
|||||||
>
|
>
|
||||||
<ConductorModeIcon class="c-conductor__mode-icon" />
|
<ConductorModeIcon class="c-conductor__mode-icon" />
|
||||||
<div class="c-compact-tc__setting-value u-fade-truncate">
|
<div class="c-compact-tc__setting-value u-fade-truncate">
|
||||||
<conductor-mode :mode="mode" :read-only="true" />
|
<ConductorMode :mode="mode" :read-only="true" />
|
||||||
<conductor-clock :read-only="true" />
|
<ConductorClock :read-only="true" />
|
||||||
<conductor-time-system :read-only="true" />
|
<ConductorTimeSystem :read-only="true" />
|
||||||
</div>
|
</div>
|
||||||
<conductor-inputs-fixed v-if="isFixed" :input-bounds="viewBounds" :read-only="true" />
|
<ConductorInputsFixed v-if="isFixed" :input-bounds="viewBounds" :read-only="true" />
|
||||||
<conductor-inputs-realtime v-else :input-bounds="viewBounds" :read-only="true" />
|
<ConductorInputsRealtime v-else :input-bounds="viewBounds" :read-only="true" />
|
||||||
<conductor-axis
|
<ConductorAxis
|
||||||
v-if="isFixed"
|
v-if="isFixed"
|
||||||
class="c-conductor__ticks"
|
class="c-conductor__ticks"
|
||||||
:view-bounds="viewBounds"
|
:view-bounds="viewBounds"
|
||||||
@ -55,7 +55,7 @@
|
|||||||
aria-label="Time Conductor Settings"
|
aria-label="Time Conductor Settings"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<conductor-pop-up
|
<ConductorPopUp
|
||||||
v-if="showConductorPopup"
|
v-if="showConductorPopup"
|
||||||
ref="conductorPopup"
|
ref="conductorPopup"
|
||||||
:bottom="false"
|
:bottom="false"
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<time-popup-fixed
|
<TimePopupFixed
|
||||||
v-if="readOnly === false"
|
v-if="readOnly === false"
|
||||||
:input-bounds="bounds"
|
:input-bounds="bounds"
|
||||||
:input-time-system="timeSystem"
|
:input-time-system="timeSystem"
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<time-popup-realtime
|
<TimePopupRealtime
|
||||||
v-if="readOnly === false"
|
v-if="readOnly === false"
|
||||||
:offsets="offsets"
|
:offsets="offsets"
|
||||||
@focus="$event.target.select()"
|
@focus="$event.target.select()"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-tc-input-popup" :class="popupClasses" :style="position">
|
<div class="c-tc-input-popup" :class="popupClasses" :style="position">
|
||||||
<div class="c-tc-input-popup__options">
|
<div class="c-tc-input-popup__options" aria-label="Time Conductor Options">
|
||||||
<IndependentMode
|
<IndependentMode
|
||||||
v-if="isIndependent"
|
v-if="isIndependent"
|
||||||
class="c-conductor__mode-select"
|
class="c-conductor__mode-select"
|
||||||
@ -44,14 +44,14 @@
|
|||||||
:button-css-class="'c-icon-button'"
|
:button-css-class="'c-icon-button'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<conductor-inputs-fixed
|
<ConductorInputsFixed
|
||||||
v-if="isFixed"
|
v-if="isFixed"
|
||||||
:input-bounds="bounds"
|
:input-bounds="bounds"
|
||||||
:object-path="objectPath"
|
:object-path="objectPath"
|
||||||
@bounds-updated="saveFixedBounds"
|
@bounds-updated="saveFixedBounds"
|
||||||
@dismiss-inputs-fixed="dismiss"
|
@dismiss-inputs-fixed="dismiss"
|
||||||
/>
|
/>
|
||||||
<conductor-inputs-realtime
|
<ConductorInputsRealtime
|
||||||
v-else
|
v-else
|
||||||
:input-bounds="bounds"
|
:input-bounds="bounds"
|
||||||
:object-path="objectPath"
|
:object-path="objectPath"
|
||||||
|
@ -17,9 +17,9 @@
|
|||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
aria-label="Start date"
|
aria-label="Start date"
|
||||||
@change="validateAllBounds('startDate')"
|
@input="validateAllBounds('startDate')"
|
||||||
/>
|
/>
|
||||||
<date-picker
|
<DatePicker
|
||||||
v-if="isUTCBased"
|
v-if="isUTCBased"
|
||||||
class="c-ctrl-wrapper--menus-right"
|
class="c-ctrl-wrapper--menus-right"
|
||||||
:default-date-time="formattedBounds.start"
|
:default-date-time="formattedBounds.start"
|
||||||
@ -37,7 +37,7 @@
|
|||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
aria-label="Start time"
|
aria-label="Start time"
|
||||||
@change="validateAllBounds('startDate')"
|
@input="validateAllBounds('startDate')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -54,9 +54,9 @@
|
|||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
aria-label="End date"
|
aria-label="End date"
|
||||||
@change="validateAllBounds('endDate')"
|
@input="validateAllBounds('endDate')"
|
||||||
/>
|
/>
|
||||||
<date-picker
|
<DatePicker
|
||||||
v-if="isUTCBased"
|
v-if="isUTCBased"
|
||||||
class="c-ctrl-wrapper--menus-left"
|
class="c-ctrl-wrapper--menus-left"
|
||||||
:default-date-time="formattedBounds.end"
|
:default-date-time="formattedBounds.end"
|
||||||
@ -74,7 +74,7 @@
|
|||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
aria-label="End time"
|
aria-label="End time"
|
||||||
@change="validateAllBounds('endDate')"
|
@input="validateAllBounds('endDate')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -83,12 +83,12 @@
|
|||||||
class="c-button c-button--major icon-check"
|
class="c-button c-button--major icon-check"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
aria-label="Submit time bounds"
|
aria-label="Submit time bounds"
|
||||||
@click.prevent="submit"
|
@click.prevent="handleFormSubmission(true)"
|
||||||
></button>
|
></button>
|
||||||
<button
|
<button
|
||||||
class="c-button icon-x"
|
class="c-button icon-x"
|
||||||
aria-label="Discard changes and close time popup"
|
aria-label="Discard changes and close time popup"
|
||||||
@click.prevent="hide"
|
@click.prevent="handleFormSubmission(true)"
|
||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -219,18 +219,21 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
submit() {
|
handleFormSubmission(shouldDismiss) {
|
||||||
|
// Validate bounds before submission
|
||||||
this.validateAllBounds('startDate');
|
this.validateAllBounds('startDate');
|
||||||
this.validateAllBounds('endDate');
|
this.validateAllBounds('endDate');
|
||||||
this.submitForm(!this.isDisabled);
|
|
||||||
},
|
// Submit the form if it's valid
|
||||||
submitForm(dismiss) {
|
if (!this.isDisabled) {
|
||||||
// Allow Vue model to catch up to user input.
|
this.setBoundsFromView(shouldDismiss);
|
||||||
// Submitting form will cause validation messages to display (but only if triggered by button click)
|
}
|
||||||
this.$nextTick(() => this.setBoundsFromView(dismiss));
|
|
||||||
},
|
},
|
||||||
validateAllBounds(ref) {
|
validateAllBounds(ref) {
|
||||||
|
this.isDisabled = false; // Reset isDisabled at the start of validation
|
||||||
|
|
||||||
if (!this.areBoundsFormatsValid()) {
|
if (!this.areBoundsFormatsValid()) {
|
||||||
|
this.isDisabled = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,7 +308,6 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
input.setCustomValidity('');
|
input.setCustomValidity('');
|
||||||
input.title = '';
|
input.title = '';
|
||||||
this.isDisabled = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$refs.fixedDeltaInput.reportValidity();
|
this.$refs.fixedDeltaInput.reportValidity();
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<swim-lane
|
<SwimLane
|
||||||
:icon-class="item.type.definition.cssClass"
|
:icon-class="item.type.definition.cssClass"
|
||||||
:status="status"
|
:status="status"
|
||||||
:min-height="item.height"
|
:min-height="item.height"
|
||||||
@ -33,7 +33,7 @@
|
|||||||
{{ item.domainObject.name }}
|
{{ item.domainObject.name }}
|
||||||
</template>
|
</template>
|
||||||
<template #object>
|
<template #object>
|
||||||
<object-view
|
<ObjectView
|
||||||
ref="objectView"
|
ref="objectView"
|
||||||
class="u-contents"
|
class="u-contents"
|
||||||
:default-object="item.domainObject"
|
:default-object="item.domainObject"
|
||||||
@ -41,7 +41,7 @@
|
|||||||
@change-action-collection="setActionCollection"
|
@change-action-collection="setActionCollection"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</swim-lane>
|
</SwimLane>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -22,22 +22,22 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="timelineHolder" class="c-timeline-holder">
|
<div ref="timelineHolder" class="c-timeline-holder">
|
||||||
<swim-lane v-for="timeSystemItem in timeSystems" :key="timeSystemItem.timeSystem.key">
|
<SwimLane v-for="timeSystemItem in timeSystems" :key="timeSystemItem.timeSystem.key">
|
||||||
<template #label>
|
<template #label>
|
||||||
{{ timeSystemItem.timeSystem.name }}
|
{{ timeSystemItem.timeSystem.name }}
|
||||||
</template>
|
</template>
|
||||||
<template #object>
|
<template #object>
|
||||||
<timeline-axis
|
<TimelineAxis
|
||||||
:bounds="timeSystemItem.bounds"
|
:bounds="timeSystemItem.bounds"
|
||||||
:time-system="timeSystemItem.timeSystem"
|
:time-system="timeSystemItem.timeSystem"
|
||||||
:content-height="height"
|
:content-height="height"
|
||||||
:rendering-engine="'svg'"
|
:rendering-engine="'svg'"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</swim-lane>
|
</SwimLane>
|
||||||
|
|
||||||
<div ref="contentHolder" class="c-timeline__objects">
|
<div ref="contentHolder" class="c-timeline__objects">
|
||||||
<timeline-object-view
|
<TimelineObjectView
|
||||||
v-for="item in items"
|
v-for="item in items"
|
||||||
:key="item.keyString"
|
:key="item.keyString"
|
||||||
class="c-timeline__content js-timeline__content"
|
class="c-timeline__content js-timeline__content"
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="timelistHolder" :class="listTypeClass">
|
<div ref="timelistHolder" :class="listTypeClass">
|
||||||
<template v-if="isExpanded">
|
<template v-if="isExpanded">
|
||||||
<expanded-view-item
|
<ExpandedViewItem
|
||||||
v-for="item in sortedItems"
|
v-for="item in sortedItems"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
:name="item.name"
|
:name="item.name"
|
||||||
@ -42,7 +42,7 @@
|
|||||||
<table class="c-table__body js-table__body">
|
<table class="c-table__body js-table__body">
|
||||||
<thead class="c-table__header">
|
<thead class="c-table__header">
|
||||||
<tr>
|
<tr>
|
||||||
<list-header
|
<ListHeader
|
||||||
v-for="headerItem in headerItems"
|
v-for="headerItem in headerItems"
|
||||||
:key="headerItem.property"
|
:key="headerItem.property"
|
||||||
:direction="getSortDirection(headerItem)"
|
:direction="getSortDirection(headerItem)"
|
||||||
@ -56,7 +56,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<list-item
|
<ListItem
|
||||||
v-for="item in sortedItems"
|
v-for="item in sortedItems"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
:class="{ '--is-in-progress': persistedActivityStates[item.id] === 'in-progress' }"
|
:class="{ '--is-in-progress': persistedActivityStates[item.id] === 'in-progress' }"
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
<span v-else>{{ sortOrderOptions[sortOrderIndex].label }}</span>
|
<span v-else>{{ sortOrderOptions[sortOrderIndex].label }}</span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<event-properties
|
<EventProperties
|
||||||
v-for="type in eventTypes"
|
v-for="type in eventTypes"
|
||||||
:key="type.prefix"
|
:key="type.prefix"
|
||||||
:label="type.label"
|
:label="type.label"
|
||||||
@ -73,7 +73,7 @@
|
|||||||
<div class="c-inspect-properties">
|
<div class="c-inspect-properties">
|
||||||
<ul class="c-inspect-properties__section">
|
<ul class="c-inspect-properties__section">
|
||||||
<div class="c-inspect-properties_header" title="'Filters'">Filtering</div>
|
<div class="c-inspect-properties_header" title="'Filters'">Filtering</div>
|
||||||
<filtering @updated="eventPropertiesUpdated" />
|
<Filtering @updated="eventPropertiesUpdated" />
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<table class="c-table__body js-table__body">
|
<table class="c-table__body js-table__body">
|
||||||
<thead class="c-table__header">
|
<thead class="c-table__header">
|
||||||
<tr>
|
<tr>
|
||||||
<list-header
|
<ListHeader
|
||||||
v-for="headerItem in headerItems"
|
v-for="headerItem in headerItems"
|
||||||
:key="headerItem.property"
|
:key="headerItem.property"
|
||||||
:direction="sortBy === headerItem.property ? ascending : headerItem.defaultDirection"
|
:direction="sortBy === headerItem.property ? ascending : headerItem.defaultDirection"
|
||||||
@ -38,7 +38,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<list-item
|
<ListItem
|
||||||
v-for="item in sortedItems"
|
v-for="item in sortedItems"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
:item="item"
|
:item="item"
|
||||||
|
@ -64,7 +64,7 @@
|
|||||||
:aria-label="`${ariaLabel} Controls`"
|
:aria-label="`${ariaLabel} Controls`"
|
||||||
>
|
>
|
||||||
<div v-if="supportsIndependentTime" class="c-conductor-holder--compact">
|
<div v-if="supportsIndependentTime" class="c-conductor-holder--compact">
|
||||||
<independent-time-conductor :domain-object="domainObject" :object-path="objectPath" />
|
<IndependentTimeConductor :domain-object="domainObject" :object-path="objectPath" />
|
||||||
</div>
|
</div>
|
||||||
<NotebookMenuSwitcher
|
<NotebookMenuSwitcher
|
||||||
v-if="notebookEnabled"
|
v-if="notebookEnabled"
|
||||||
@ -94,7 +94,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<object-view
|
<ObjectView
|
||||||
ref="objectView"
|
ref="objectView"
|
||||||
class="c-so-view__object-view js-object-view js-notebook-snapshot-item"
|
class="c-so-view__object-view js-object-view js-notebook-snapshot-item"
|
||||||
:show-edit-view="showEditView"
|
:show-edit-view="showEditView"
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
role="navigation"
|
role="navigation"
|
||||||
>
|
>
|
||||||
<li v-for="pathObject in orderedPath" :key="pathObject.key" class="c-location__item">
|
<li v-for="pathObject in orderedPath" :key="pathObject.key" class="c-location__item">
|
||||||
<object-label
|
<ObjectLabel
|
||||||
:domain-object="pathObject.domainObject"
|
:domain-object="pathObject.domainObject"
|
||||||
:object-path="pathObject.objectPath"
|
:object-path="pathObject.objectPath"
|
||||||
:read-only="readOnly"
|
:read-only="readOnly"
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="c-inspector js-inspector">
|
<div class="c-inspector js-inspector">
|
||||||
<object-name />
|
<ObjectName />
|
||||||
<InspectorTabs :is-editing="isEditing" @select-tab="selectTab" />
|
<InspectorTabs :is-editing="isEditing" @select-tab="selectTab" />
|
||||||
<InspectorViews :selected-tab="selectedTab" />
|
<InspectorViews :selected-tab="selectedTab" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
:title="`Click to ${headExpanded ? 'collapse' : 'expand'} items`"
|
:title="`Click to ${headExpanded ? 'collapse' : 'expand'} items`"
|
||||||
@click="toggleShellHead"
|
@click="toggleShellHead"
|
||||||
></button>
|
></button>
|
||||||
<notification-banner />
|
<NotificationBanner />
|
||||||
<div class="l-shell__head-section l-shell__controls">
|
<div class="l-shell__head-section l-shell__controls">
|
||||||
<button
|
<button
|
||||||
class="c-icon-button c-icon-button--major icon-new-window"
|
class="c-icon-button c-icon-button--major icon-new-window"
|
||||||
@ -67,13 +67,13 @@
|
|||||||
@click="fullScreenToggle"
|
@click="fullScreenToggle"
|
||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
<app-logo />
|
<AppLogo />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="l-shell__drawer c-drawer c-drawer--push c-drawer--align-top"></div>
|
<div class="l-shell__drawer c-drawer c-drawer--push c-drawer--align-top"></div>
|
||||||
|
|
||||||
<multipane class="l-shell__main" :class="[resizingClass]" type="horizontal">
|
<Multipane class="l-shell__main" :class="[resizingClass]" type="horizontal">
|
||||||
<pane
|
<Pane
|
||||||
class="l-shell__pane-tree"
|
class="l-shell__pane-tree"
|
||||||
handle="after"
|
handle="after"
|
||||||
label="Browse"
|
label="Browse"
|
||||||
@ -96,16 +96,16 @@
|
|||||||
@click="handleSyncTreeNavigation"
|
@click="handleSyncTreeNavigation"
|
||||||
></button>
|
></button>
|
||||||
</template>
|
</template>
|
||||||
<multipane type="vertical">
|
<Multipane type="vertical">
|
||||||
<pane>
|
<Pane>
|
||||||
<mct-tree
|
<MctTree
|
||||||
ref="mctTree"
|
ref="mctTree"
|
||||||
:sync-tree-navigation="triggerSync"
|
:sync-tree-navigation="triggerSync"
|
||||||
:reset-tree-navigation="triggerReset"
|
:reset-tree-navigation="triggerReset"
|
||||||
class="l-shell__tree"
|
class="l-shell__tree"
|
||||||
/>
|
/>
|
||||||
</pane>
|
</Pane>
|
||||||
<pane
|
<Pane
|
||||||
handle="before"
|
handle="before"
|
||||||
label="Recently Viewed"
|
label="Recently Viewed"
|
||||||
:persist-position="true"
|
:persist-position="true"
|
||||||
@ -127,18 +127,18 @@
|
|||||||
@click="handleClearRecentObjects"
|
@click="handleClearRecentObjects"
|
||||||
></button>
|
></button>
|
||||||
</template>
|
</template>
|
||||||
</pane>
|
</Pane>
|
||||||
</multipane>
|
</Multipane>
|
||||||
</pane>
|
</Pane>
|
||||||
<pane class="l-shell__pane-main" role="main">
|
<Pane class="l-shell__pane-main" role="main">
|
||||||
<browse-bar
|
<BrowseBar
|
||||||
ref="browseBar"
|
ref="browseBar"
|
||||||
class="l-shell__main-view-browse-bar"
|
class="l-shell__main-view-browse-bar"
|
||||||
:action-collection="actionCollection"
|
:action-collection="actionCollection"
|
||||||
@sync-tree-navigation="handleSyncTreeNavigation"
|
@sync-tree-navigation="handleSyncTreeNavigation"
|
||||||
/>
|
/>
|
||||||
<toolbar v-if="toolbar" class="l-shell__toolbar" />
|
<Toolbar v-if="toolbar" class="l-shell__toolbar" />
|
||||||
<object-view
|
<ObjectView
|
||||||
ref="browseObject"
|
ref="browseObject"
|
||||||
class="l-shell__main-container js-main-container js-notebook-snapshot-item"
|
class="l-shell__main-container js-main-container js-notebook-snapshot-item"
|
||||||
data-selectable
|
data-selectable
|
||||||
@ -150,8 +150,8 @@
|
|||||||
class="l-shell__time-conductor"
|
class="l-shell__time-conductor"
|
||||||
aria-label="Global Time Conductor"
|
aria-label="Global Time Conductor"
|
||||||
/>
|
/>
|
||||||
</pane>
|
</Pane>
|
||||||
<pane
|
<Pane
|
||||||
class="l-shell__pane-inspector l-pane--holds-multipane"
|
class="l-shell__pane-inspector l-pane--holds-multipane"
|
||||||
handle="before"
|
handle="before"
|
||||||
label="Inspect"
|
label="Inspect"
|
||||||
@ -161,8 +161,8 @@
|
|||||||
@end-resizing="onEndResizing"
|
@end-resizing="onEndResizing"
|
||||||
>
|
>
|
||||||
<Inspector ref="inspector" :is-editing="isEditing" />
|
<Inspector ref="inspector" :is-editing="isEditing" />
|
||||||
</pane>
|
</Pane>
|
||||||
</multipane>
|
</Multipane>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
v-if="supportsIndependentTime"
|
v-if="supportsIndependentTime"
|
||||||
class="c-conductor-holder--compact l-shell__main-independent-time-conductor"
|
class="c-conductor-holder--compact l-shell__main-independent-time-conductor"
|
||||||
>
|
>
|
||||||
<independent-time-conductor
|
<IndependentTimeConductor
|
||||||
:domain-object="domainObject"
|
:domain-object="domainObject"
|
||||||
:object-path="openmct.router.path"
|
:object-path="openmct.router.path"
|
||||||
/>
|
/>
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div ref="search" class="c-tree-and-search__search">
|
<div ref="search" class="c-tree-and-search__search">
|
||||||
<search
|
<Search
|
||||||
v-show="isSelectorTree"
|
v-show="isSelectorTree"
|
||||||
ref="shell-search"
|
ref="shell-search"
|
||||||
class="c-search"
|
class="c-search"
|
||||||
@ -81,7 +81,7 @@
|
|||||||
@scroll="updateVisibleItems()"
|
@scroll="updateVisibleItems()"
|
||||||
>
|
>
|
||||||
<div :style="childrenHeightStyles">
|
<div :style="childrenHeightStyles">
|
||||||
<tree-item
|
<TreeItem
|
||||||
v-for="(treeItem, index) in visibleItems"
|
v-for="(treeItem, index) in visibleItems"
|
||||||
:key="`${treeItem.navigationPath}-${index}-${treeItem.object.name}`"
|
:key="`${treeItem.navigationPath}-${index}-${treeItem.object.name}`"
|
||||||
:node="treeItem"
|
:node="treeItem"
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-tree-and-search l-shell__tree">
|
<div class="c-tree-and-search l-shell__tree">
|
||||||
<ul class="c-tree-and-search__tree c-tree c-tree__scrollable" aria-label="Recent Objects">
|
<ul class="c-tree-and-search__tree c-tree c-tree__scrollable" aria-label="Recent Objects">
|
||||||
<recent-objects-list-item
|
<RecentObjectsListItem
|
||||||
v-for="recentObject in recentObjects"
|
v-for="recentObject in recentObjects"
|
||||||
:key="recentObject.navigationPath"
|
:key="recentObject.navigationPath"
|
||||||
:object-path="recentObject.objectPath"
|
:object-path="recentObject.objectPath"
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
@click.capture="itemClick"
|
@click.capture="itemClick"
|
||||||
@contextmenu.capture="handleContextMenu"
|
@contextmenu.capture="handleContextMenu"
|
||||||
>
|
>
|
||||||
<view-control
|
<ViewControl
|
||||||
ref="action"
|
ref="action"
|
||||||
class="c-tree__item__view-control"
|
class="c-tree__item__view-control"
|
||||||
:domain-object="node.object"
|
:domain-object="node.object"
|
||||||
@ -49,7 +49,7 @@
|
|||||||
:enabled="!activeSearch && hasComposition"
|
:enabled="!activeSearch && hasComposition"
|
||||||
@input="itemAction()"
|
@input="itemAction()"
|
||||||
/>
|
/>
|
||||||
<object-label
|
<ObjectLabel
|
||||||
ref="objectLabel"
|
ref="objectLabel"
|
||||||
:domain-object="node.object"
|
:domain-object="node.object"
|
||||||
:object-path="node.objectPath"
|
:object-path="node.objectPath"
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="GrandSearch" aria-label="OpenMCT Search" class="c-gsearch" role="search">
|
<div ref="GrandSearch" aria-label="OpenMCT Search" class="c-gsearch" role="search">
|
||||||
<SearchResultsDropDown ref="searchResultsDropDown" />
|
<SearchResultsDropDown ref="searchResultsDropDown" />
|
||||||
<search
|
<Search
|
||||||
ref="shell-search"
|
ref="shell-search"
|
||||||
class="c-gsearch__input"
|
class="c-gsearch__input"
|
||||||
:value="searchValue"
|
:value="searchValue"
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
aria-label="Object Results"
|
aria-label="Object Results"
|
||||||
>
|
>
|
||||||
<div class="c-gsearch__results-section-title">Object Results</div>
|
<div class="c-gsearch__results-section-title">Object Results</div>
|
||||||
<object-search-result
|
<ObjectSearchResult
|
||||||
v-for="objectResult in objectResults"
|
v-for="objectResult in objectResults"
|
||||||
:key="openmct.objects.makeKeyString(objectResult.identifier)"
|
:key="openmct.objects.makeKeyString(objectResult.identifier)"
|
||||||
:result="objectResult"
|
:result="objectResult"
|
||||||
@ -56,7 +56,7 @@
|
|||||||
aria-label="Annotation Results"
|
aria-label="Annotation Results"
|
||||||
>
|
>
|
||||||
<div class="c-gsearch__results-section-title">Annotation Results</div>
|
<div class="c-gsearch__results-section-title">Annotation Results</div>
|
||||||
<annotation-search-result
|
<AnnotationSearchResult
|
||||||
v-for="annotationResult in annotationResults"
|
v-for="annotationResult in annotationResults"
|
||||||
:key="makeKeyForAnnotationResult(annotationResult)"
|
:key="makeKeyForAnnotationResult(annotationResult)"
|
||||||
:result="annotationResult"
|
:result="annotationResult"
|
||||||
@ -65,7 +65,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="searchLoading" class="c-gsearch__result-pane-msg">
|
<div v-if="searchLoading" class="c-gsearch__result-pane-msg">
|
||||||
<div class="hint">Searching...</div>
|
<div class="hint">Searching...</div>
|
||||||
<progress-bar :model="{ progressPerc: null }" />
|
<ProgressBar :model="{ progressPerc: null }" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="
|
v-if="
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
>{{ getLinkProps.text }}</span
|
>{{ getLinkProps.text }}</span
|
||||||
>
|
>
|
||||||
|
|
||||||
<progress-bar
|
<ProgressBar
|
||||||
v-if="activeModel.progressPerc"
|
v-if="activeModel.progressPerc"
|
||||||
class="c-message-banner__progress-bar"
|
class="c-message-banner__progress-bar"
|
||||||
:model="activeModel"
|
:model="activeModel"
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="l-browse-bar__end">
|
<div class="l-browse-bar__end">
|
||||||
<view-switcher :v-if="!hideViewSwitcher" :views="views" :current-view="currentView" />
|
<ViewSwitcher :v-if="!hideViewSwitcher" :views="views" :current-view="currentView" />
|
||||||
<NotebookMenuSwitcher
|
<NotebookMenuSwitcher
|
||||||
:domain-object="domainObject"
|
:domain-object="domainObject"
|
||||||
:object-path="objectPath"
|
:object-path="objectPath"
|
||||||
|
Loading…
Reference in New Issue
Block a user