mirror of
https://github.com/nasa/openmct.git
synced 2025-07-03 21:38:13 +00:00
Compare commits
11 Commits
with-vue-d
...
jamie-sour
Author | SHA1 | Date | |
---|---|---|---|
094328cd32 | |||
554f77c42f | |||
a5770817cc | |||
34b4091204 | |||
6360bc4b6c | |||
c354e1c2f1 | |||
eba6f0f505 | |||
017380bb6a | |||
810d580b18 | |||
977792fae8 | |||
a69e300f1c |
@ -5,11 +5,11 @@ orbs:
|
||||
executors:
|
||||
pw-focal-development:
|
||||
docker:
|
||||
- image: mcr.microsoft.com/playwright:v1.42.1-focal
|
||||
- image: mcr.microsoft.com/playwright:v1.44.0-focal
|
||||
environment:
|
||||
NODE_ENV: development # Needed to ensure 'dist' folder created and devDependencies installed
|
||||
PERCY_POSTINSTALL_BROWSER: 'true' # Needed to store the percy browser in cache deps
|
||||
PERCY_LOGLEVEL: 'debug' # Enable DEBUG level logging for Percy (Issue: https://github.com/nasa/openmct/issues/5742)
|
||||
PERCY_POSTINSTALL_BROWSER: "true" # Needed to store the percy browser in cache deps
|
||||
PERCY_LOGLEVEL: "debug" # Enable DEBUG level logging for Percy (Issue: https://github.com/nasa/openmct/issues/5742)
|
||||
PERCY_PARALLEL_TOTAL: 2
|
||||
ubuntu:
|
||||
machine:
|
||||
@ -17,7 +17,7 @@ executors:
|
||||
docker_layer_caching: true
|
||||
commands:
|
||||
build_and_install:
|
||||
description: 'All steps used to build and install.'
|
||||
description: "All steps used to build and install."
|
||||
parameters:
|
||||
node-version:
|
||||
type: string
|
||||
@ -27,7 +27,7 @@ commands:
|
||||
node-version: << parameters.node-version >>
|
||||
- node/install-packages
|
||||
generate_and_store_version_and_filesystem_artifacts:
|
||||
description: 'Track important packages and files'
|
||||
description: "Track important packages and files"
|
||||
steps:
|
||||
- run: |
|
||||
[[ $EUID -ne 0 ]] && (sudo mkdir -p /tmp/artifacts && sudo chmod 777 /tmp/artifacts) || (mkdir -p /tmp/artifacts && chmod 777 /tmp/artifacts)
|
||||
@ -38,7 +38,7 @@ commands:
|
||||
- store_artifacts:
|
||||
path: /tmp/artifacts/
|
||||
generate_e2e_code_cov_report:
|
||||
description: 'Generate e2e code coverage artifacts and publish to codecov.io. Needed to that we can ignore the exit code status of the npm run test'
|
||||
description: "Generate e2e code coverage artifacts and publish to codecov.io. Needed to that we can ignore the exit code status of the npm run test"
|
||||
parameters:
|
||||
suite:
|
||||
type: string
|
||||
@ -102,7 +102,7 @@ jobs:
|
||||
node-version: lts/hydrogen
|
||||
- when: #Only install chrome-beta when running the 'full' suite to save $$$
|
||||
condition:
|
||||
equal: ['full', <<parameters.suite>>]
|
||||
equal: ["full", <<parameters.suite>>]
|
||||
steps:
|
||||
- run: npx playwright install chrome-beta
|
||||
- run:
|
||||
@ -159,7 +159,7 @@ jobs:
|
||||
steps:
|
||||
- build_and_install:
|
||||
node-version: lts/hydrogen
|
||||
- run: npx playwright@1.42.1 install #Necessary for bare ubuntu machine
|
||||
- run: npx playwright@1.44.0 install #Necessary for bare ubuntu machine
|
||||
- run: |
|
||||
export $(cat src/plugins/persistence/couch/.env.ci | xargs)
|
||||
docker-compose -f src/plugins/persistence/couch/couchdb-compose.yaml up --detach
|
||||
@ -230,7 +230,7 @@ jobs:
|
||||
steps:
|
||||
- build_and_install:
|
||||
node-version: lts/iron
|
||||
- run: npm run test:e2e:visual:<<parameters.suite>>
|
||||
- run: SHARD="$((${CIRCLE_NODE_INDEX}+1))"; npm run test:e2e:visual:<<parameters.suite>> -- --shard=${SHARD}/${CIRCLE_NODE_TOTAL}
|
||||
- store_test_results:
|
||||
path: test-results/results.xml
|
||||
- store_artifacts:
|
||||
@ -282,7 +282,7 @@ workflows:
|
||||
- e2e-couchdb
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: '0 0 * * *'
|
||||
cron: "0 0 * * *"
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
|
2
.github/workflows/e2e-couchdb.yml
vendored
2
.github/workflows/e2e-couchdb.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- run: npx playwright@1.42.1 install
|
||||
- run: npx playwright@1.44.0 install
|
||||
|
||||
- name: Start CouchDB Docker Container and Init with Setup Scripts
|
||||
run: |
|
||||
|
2
.github/workflows/e2e-flakefinder.yml
vendored
2
.github/workflows/e2e-flakefinder.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- run: npx playwright@1.42.1 install
|
||||
- run: npx playwright@1.44.0 install
|
||||
- run: npm ci --no-audit --progress=false
|
||||
|
||||
- name: Run E2E Tests (Repeated 10 Times)
|
||||
|
2
.github/workflows/e2e-perf.yml
vendored
2
.github/workflows/e2e-perf.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- run: npx playwright@1.42.1 install
|
||||
- run: npx playwright@1.44.0 install
|
||||
- run: npm ci --no-audit --progress=false
|
||||
- run: npm run test:perf:localhost
|
||||
- run: npm run test:perf:contract
|
||||
|
2
.github/workflows/e2e-pr.yml
vendored
2
.github/workflows/e2e-pr.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- run: npx playwright@1.42.1 install
|
||||
- run: npx playwright@1.44.0 install
|
||||
- run: npx playwright install chrome-beta
|
||||
- run: npm ci --no-audit --progress=false
|
||||
- run: npm run test:e2e:full -- --max-failures=40
|
||||
|
@ -22,3 +22,6 @@
|
||||
!index.html
|
||||
!openmct.js
|
||||
!SECURITY.md
|
||||
|
||||
# Dont include the example html
|
||||
dist/index.html
|
@ -6,7 +6,7 @@ information to pull requests.
|
||||
|
||||
import config from './webpack.dev.mjs';
|
||||
|
||||
config.devtool = 'source-map';
|
||||
config.devtool = 'inline-source-map';
|
||||
config.devServer.hot = false;
|
||||
|
||||
config.module.rules.push({
|
||||
|
@ -15,5 +15,5 @@ export default merge(common, {
|
||||
__OPENMCT_ROOT_RELATIVE__: '""'
|
||||
})
|
||||
],
|
||||
devtool: 'source-map'
|
||||
devtool: 'eval-source-map'
|
||||
});
|
||||
|
@ -275,6 +275,17 @@ async function navigateToObjectWithFixedTimeBounds(page, url, start, end) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates directly to a given object url, in real-time mode.
|
||||
* @param {import('@playwright/test').Page} page
|
||||
* @param {string} url The url to the domainObject
|
||||
*/
|
||||
async function navigateToObjectWithRealTime(page, url, start = '1800000', end = '30000') {
|
||||
await page.goto(
|
||||
`${url}?tc.mode=local&tc.startDelta=${start}&tc.endDelta=${end}&tc.timeSystem=utc`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the given `domainObject`'s context menu from the object tree.
|
||||
* Expands the path to the object and scrolls to it if necessary.
|
||||
@ -581,9 +592,6 @@ async function waitForPlotsToRender(page) {
|
||||
* @return {Promise<PlotPixel[]>}
|
||||
*/
|
||||
async function getCanvasPixels(page, canvasSelector) {
|
||||
const getTelemValuePromise = new Promise((resolve) =>
|
||||
page.exposeFunction('getCanvasValue', resolve)
|
||||
);
|
||||
const canvasHandle = await page.evaluateHandle(
|
||||
(canvas) => document.querySelector(canvas),
|
||||
canvasSelector
|
||||
@ -594,7 +602,7 @@ async function getCanvasPixels(page, canvasSelector) {
|
||||
);
|
||||
|
||||
await waitForPlotsToRender(page);
|
||||
await page.evaluate(
|
||||
return page.evaluate(
|
||||
([canvas, ctx]) => {
|
||||
// The document canvas is where the plot points and lines are drawn.
|
||||
// The only way to access the canvas is using document (using page.evaluate)
|
||||
@ -622,12 +630,10 @@ async function getCanvasPixels(page, canvasSelector) {
|
||||
i = i + 4;
|
||||
}
|
||||
|
||||
window.getCanvasValue(plotPixels);
|
||||
return plotPixels;
|
||||
},
|
||||
[canvasHandle, canvasContextHandle]
|
||||
);
|
||||
|
||||
return getTelemValuePromise;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -656,6 +662,7 @@ export {
|
||||
getFocusedObjectUuid,
|
||||
getHashUrlToDomainObject,
|
||||
navigateToObjectWithFixedTimeBounds,
|
||||
navigateToObjectWithRealTime,
|
||||
openObjectTreeContextMenu,
|
||||
renameObjectFromContextMenu,
|
||||
setEndOffset,
|
||||
|
@ -34,7 +34,7 @@
|
||||
*/
|
||||
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import fs from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
@ -87,6 +87,27 @@ const extendedTest = test.extend({
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Writes the accessibility report to the specified path.
|
||||
*
|
||||
* @param {string} reportPath - The path to write the report to.
|
||||
* @param {Object} accessibilityScanResults - The results of the accessibility scan.
|
||||
* @returns {Promise<Object>} The accessibility scan results.
|
||||
* @throws Will throw an error if writing the report fails.
|
||||
*/
|
||||
async function writeAccessibilityReport(reportPath, accessibilityScanResults) {
|
||||
try {
|
||||
await fs.mkdir(path.dirname(reportPath), { recursive: true });
|
||||
const data = JSON.stringify(accessibilityScanResults, null, 2);
|
||||
await fs.writeFile(reportPath, data);
|
||||
console.log(`Accessibility report with violations saved successfully as ${reportPath}`);
|
||||
return accessibilityScanResults;
|
||||
} catch (err) {
|
||||
console.error(`Error writing the accessibility report to file ${reportPath}:`, err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans for accessibility violations on a page and writes a report to disk if violations are found.
|
||||
* Automatically asserts that no violations should be present.
|
||||
@ -104,25 +125,29 @@ export async function scanForA11yViolations(page, testCaseName, options = {}) {
|
||||
const accessibilityScanResults = await builder.analyze();
|
||||
|
||||
// Assert that no violations should be present
|
||||
expect(
|
||||
expect
|
||||
.soft(
|
||||
accessibilityScanResults.violations,
|
||||
`Accessibility violations found in test case: ${testCaseName}`
|
||||
).toEqual([]);
|
||||
)
|
||||
.toEqual([]);
|
||||
|
||||
// Check if there are any violations
|
||||
if (accessibilityScanResults.violations.length > 0) {
|
||||
let reportName = options.reportName || testCaseName;
|
||||
let sanitizedReportName = reportName.replace(/\//g, '_');
|
||||
const reportPath = path.join(TEST_RESULTS_DIR, `${sanitizedReportName}.json`);
|
||||
const reportName = options.reportName || testCaseName;
|
||||
const sanitizedReportName = reportName.replace(/\//g, '_');
|
||||
const reportPath = path.join(
|
||||
TEST_RESULTS_DIR,
|
||||
'a11y-json-reports',
|
||||
`${sanitizedReportName}.json`
|
||||
);
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(TEST_RESULTS_DIR)) {
|
||||
fs.mkdirSync(TEST_RESULTS_DIR);
|
||||
}
|
||||
await page.screenshot({
|
||||
path: path.join(TEST_RESULTS_DIR, 'a11y-screenshots', `${sanitizedReportName}.png`)
|
||||
});
|
||||
|
||||
fs.writeFileSync(reportPath, JSON.stringify(accessibilityScanResults, null, 2));
|
||||
console.log(`Accessibility report with violations saved successfully as ${reportPath}`);
|
||||
return accessibilityScanResults;
|
||||
return await writeAccessibilityReport(reportPath, accessibilityScanResults);
|
||||
} catch (err) {
|
||||
console.error(`Error writing the accessibility report to file ${reportPath}:`, err);
|
||||
throw err;
|
||||
|
29
e2e/helper/useDarkmatterTheme.js
Normal file
29
e2e/helper/useDarkmatterTheme.js
Normal file
@ -0,0 +1,29 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
// This should be used to install the Darkmatter theme for Open MCT.
|
||||
// e.g.
|
||||
// await page.addInitScript({ path: path.join(__dirname, 'useDarkmatterTheme.js') });
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const openmct = window.openmct;
|
||||
openmct.install(openmct.plugins.DarkmatterTheme());
|
||||
});
|
@ -18,7 +18,7 @@
|
||||
"@types/sinonjs__fake-timers": "8.1.5",
|
||||
"@percy/cli": "1.27.4",
|
||||
"@percy/playwright": "1.0.4",
|
||||
"@playwright/test": "1.42.1",
|
||||
"@playwright/test": "1.44.0",
|
||||
"@axe-core/playwright": "4.8.5",
|
||||
"sinon": "17.0.0"
|
||||
},
|
||||
|
@ -41,7 +41,7 @@ const config = {
|
||||
name: 'darkmatter-theme', //Runs the same visual tests but with darkmatter-theme
|
||||
use: {
|
||||
browserName: 'chromium',
|
||||
theme: 'darkmatter-theme'
|
||||
theme: 'darkmatter'
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -127,6 +127,11 @@ const extendedTest = test.extend({
|
||||
await page.addInitScript({
|
||||
path: fileURLToPath(new URL('./helper/useSnowTheme.js', import.meta.url))
|
||||
});
|
||||
} else if (theme === 'darkmatter') {
|
||||
//inject darkmatter theme
|
||||
await page.addInitScript({
|
||||
path: fileURLToPath(new URL('./helper/useDarkmatterTheme.js', import.meta.url))
|
||||
});
|
||||
}
|
||||
|
||||
// Attach info about the currently running test and its project.
|
||||
|
@ -371,7 +371,7 @@ test.describe('Basic Condition Set Use', () => {
|
||||
|
||||
// Validate that the condition set is evaluating and outputting
|
||||
// the correct value when the underlying telemetry subscription is active.
|
||||
let outputValue = page.locator('[aria-label="Current Output Value"]');
|
||||
let outputValue = page.getByLabel('Current Output Value');
|
||||
await expect(outputValue).toHaveText('false');
|
||||
|
||||
await page.goto(exampleTelemetry.url);
|
||||
@ -462,7 +462,7 @@ test.describe('Basic Condition Set Use', () => {
|
||||
|
||||
// Validate that the condition set is evaluating and outputting
|
||||
// the correct value when the underlying telemetry subscription is active.
|
||||
let outputValue = page.locator('[aria-label="Current Output Value"]');
|
||||
let outputValue = page.getByLabel('Current Output Value');
|
||||
await expect(outputValue).toHaveText('false');
|
||||
|
||||
await page.goto(exampleTelemetry.url);
|
||||
@ -475,3 +475,81 @@ test.describe('Basic Condition Set Use', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Condition Set Composition', () => {
|
||||
let conditionSet;
|
||||
let exampleTelemetry;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
// Create Condition Set
|
||||
conditionSet = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Condition Set'
|
||||
});
|
||||
|
||||
// Create Telemetry Object as child to Condition Set
|
||||
exampleTelemetry = await createExampleTelemetryObject(page, conditionSet.uuid);
|
||||
|
||||
// Edit Condition Set
|
||||
await page.goto(conditionSet.url);
|
||||
await page.getByRole('button', { name: 'Edit Object' }).click();
|
||||
|
||||
// Add Condition to Condition Set
|
||||
await page.getByRole('button', { name: 'Add Condition' }).click();
|
||||
|
||||
// Enter Condition Output
|
||||
await page.getByLabel('Condition Name Input').first().fill('Negative');
|
||||
await page.getByLabel('Condition Output Type').first().selectOption({ value: 'string' });
|
||||
await page.getByLabel('Condition Output String').first().fill('Negative');
|
||||
|
||||
// Condition Trigger default is okay so no change needed to form
|
||||
|
||||
// Enter Condition Criterion
|
||||
await page.getByLabel('Criterion Telemetry Selection').first().selectOption({ value: 'all' });
|
||||
await page.getByLabel('Criterion Metadata Selection').first().selectOption({ value: 'sin' });
|
||||
await page
|
||||
.locator('select[aria-label="Criterion Comparison Selection"]')
|
||||
.first()
|
||||
.selectOption({ value: 'lessThan' });
|
||||
await page.getByLabel('Criterion Input').first().fill('0');
|
||||
|
||||
// Save the Condition Set
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
});
|
||||
|
||||
test('You can remove telemetry from a condition set with existing conditions', async ({
|
||||
page
|
||||
}) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/nasa/openmct/issues/7710'
|
||||
});
|
||||
|
||||
await page.getByLabel('Expand My Items folder').click();
|
||||
await page.getByLabel(`Expand ${conditionSet.name} conditionSet`).click();
|
||||
|
||||
await page
|
||||
.getByLabel(`Navigate to ${exampleTelemetry.name}`, { exact: false })
|
||||
.click({ button: 'right' });
|
||||
|
||||
await page
|
||||
.getByLabel(`${exampleTelemetry.name} Context Menu`)
|
||||
.getByRole('menuitem', { name: 'Remove' })
|
||||
.click();
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
|
||||
await page
|
||||
.getByLabel(`Navigate to ${conditionSet.name} conditionSet Object`, { exact: true })
|
||||
.click();
|
||||
await page.getByRole('button', { name: 'Edit Object' }).click();
|
||||
await page.getByRole('tab', { name: 'Elements' }).click();
|
||||
expect(
|
||||
await page
|
||||
.getByRole('tabpanel', { name: 'Inspector Views' })
|
||||
.getByRole('listitem', { name: exampleTelemetry.name })
|
||||
.count()
|
||||
).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
@ -26,6 +26,7 @@ Testsuite for plot autoscale.
|
||||
|
||||
import { createDomainObjectWithDefaults } from '../../../../appActions.js';
|
||||
import { expect, test } from '../../../../pluginFixtures.js';
|
||||
import { setUserDefinedMinAndMax, turnOffAutoscale } from './plotActions.js';
|
||||
test.use({
|
||||
viewport: {
|
||||
width: 1280,
|
||||
@ -127,26 +128,6 @@ test.describe('Autoscale', () => {
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function turnOffAutoscale(page) {
|
||||
// uncheck autoscale
|
||||
await page.getByRole('checkbox', { name: 'Auto scale' }).uncheck();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
* @param {string} min
|
||||
* @param {string} max
|
||||
*/
|
||||
async function setUserDefinedMinAndMax(page, min, max) {
|
||||
// set minimum value
|
||||
await page.getByRole('spinbutton').first().fill(min);
|
||||
// set maximum value
|
||||
await page.getByRole('spinbutton').nth(1).fill(max);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
|
42
e2e/tests/functional/plugins/plot/plotActions.js
Normal file
42
e2e/tests/functional/plugins/plot/plotActions.js
Normal file
@ -0,0 +1,42 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function turnOffAutoscale(page) {
|
||||
// uncheck autoscale
|
||||
await page.getByRole('checkbox', { name: 'Auto scale' }).uncheck();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
* @param {string} min
|
||||
* @param {string} max
|
||||
*/
|
||||
async function setUserDefinedMinAndMax(page, min, max) {
|
||||
// set minimum value
|
||||
await page.getByRole('spinbutton').first().fill(min);
|
||||
// set maximum value
|
||||
await page.getByRole('spinbutton').nth(1).fill(max);
|
||||
}
|
||||
|
||||
export { setUserDefinedMinAndMax, turnOffAutoscale };
|
116
e2e/tests/functional/plugins/plot/plotControls.e2e.spec.js
Normal file
116
e2e/tests/functional/plugins/plot/plotControls.e2e.spec.js
Normal file
@ -0,0 +1,116 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* This test suite is dedicated to testing the rendering and interaction of plots.
|
||||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
createDomainObjectWithDefaults,
|
||||
getCanvasPixels,
|
||||
setEndOffset,
|
||||
setRealTimeMode,
|
||||
setStartOffset
|
||||
} from '../../../../appActions.js';
|
||||
import { expect, test } from '../../../../pluginFixtures.js';
|
||||
import { setUserDefinedMinAndMax, turnOffAutoscale } from './plotActions.js';
|
||||
|
||||
test.describe('Plot Controls', () => {
|
||||
let overlayPlot;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Open a browser, navigate to the main page, and wait until all networkevents to resolve
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
overlayPlot = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Overlay Plot'
|
||||
});
|
||||
|
||||
// Create an overlay plot with a sine wave generator
|
||||
await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
parent: overlayPlot.uuid
|
||||
});
|
||||
await page.goto(`${overlayPlot.url}`);
|
||||
});
|
||||
|
||||
test("Plots don't purge data when paused", async ({ page }) => {
|
||||
// Set realtime mode with 2 second window
|
||||
const startOffset = {
|
||||
startMins: '00',
|
||||
startSecs: '01'
|
||||
};
|
||||
|
||||
const endOffset = {
|
||||
endMins: '00',
|
||||
endSecs: '01'
|
||||
};
|
||||
|
||||
// Switch to real-time mode
|
||||
await setRealTimeMode(page);
|
||||
|
||||
// Set start time offset
|
||||
await setStartOffset(page, startOffset);
|
||||
|
||||
// Set end time offset
|
||||
await setEndOffset(page, endOffset);
|
||||
// Edit the overlay plot and turn off auto scale, setting the min and max to -1 and 1
|
||||
// enter edit mode
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
await page.getByRole('tab', { name: 'Config' }).click();
|
||||
await turnOffAutoscale(page);
|
||||
|
||||
await setUserDefinedMinAndMax(page, '-1', '1');
|
||||
|
||||
// save
|
||||
await page.click('button[title="Save"]');
|
||||
await Promise.all([
|
||||
page.getByRole('listitem', { name: 'Save and Finish Editing' }).click(),
|
||||
//Wait for Save Banner to appear
|
||||
page.waitForSelector('.c-message-banner__message')
|
||||
]);
|
||||
//Wait until Save Banner is gone
|
||||
await page.locator('.c-message-banner__close-button').click();
|
||||
await page.waitForSelector('.c-message-banner__message', { state: 'detached' });
|
||||
// hover over plot for plot controls
|
||||
await page.getByLabel('Plot Canvas').hover();
|
||||
// click on pause control
|
||||
await page.getByTitle('Pause incoming real-time data').click();
|
||||
// expect plot to be paused
|
||||
await expect(page.getByTitle('Resume displaying real-time data')).toBeVisible();
|
||||
// Wait for 2 seconds to stabilize plot data - future timestamp
|
||||
// eslint-disable-next-line
|
||||
await page.waitForTimeout(2000);
|
||||
// Capture the # of plot points
|
||||
const plotPixels = await getCanvasPixels(page, 'canvas');
|
||||
const plotPixelSizeAtPause = plotPixels.length;
|
||||
// Wait 2 seconds
|
||||
// eslint-disable-next-line
|
||||
await page.waitForTimeout(2000);
|
||||
// Capture the # of plot points
|
||||
const plotPixelsAfterWait = await getCanvasPixels(page, 'canvas');
|
||||
const plotPixelSizeAfterWait = plotPixelsAfterWait.length;
|
||||
// Expect before and after plot points to match
|
||||
await expect(plotPixelSizeAtPause).toEqual(plotPixelSizeAfterWait);
|
||||
});
|
||||
});
|
@ -25,7 +25,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import { createDomainObjectWithDefaults, getCanvasPixels } from '../../../../appActions.js';
|
||||
import {
|
||||
createDomainObjectWithDefaults,
|
||||
getCanvasPixels,
|
||||
setRealTimeMode
|
||||
} from '../../../../appActions.js';
|
||||
import { expect, test } from '../../../../pluginFixtures.js';
|
||||
|
||||
test.describe('Plot Rendering', () => {
|
||||
@ -50,6 +54,34 @@ test.describe('Plot Rendering', () => {
|
||||
createMineFolderRequests.push(req);
|
||||
});
|
||||
expect(createMineFolderRequests.length).toEqual(0);
|
||||
await page.getByLabel('Plot Canvas').hover();
|
||||
});
|
||||
|
||||
test('Time conductor synchronizes with plot time range when that plot control is clicked', async ({
|
||||
page
|
||||
}) => {
|
||||
// Navigate to Sine Wave Generator
|
||||
await page.goto(sineWaveGeneratorObject.url);
|
||||
// Switch to real-time mode
|
||||
await setRealTimeMode(page);
|
||||
|
||||
// hover over plot for plot controls
|
||||
await page.getByLabel('Plot Canvas').hover();
|
||||
// click on pause control
|
||||
await page.getByTitle('Pause incoming real-time data').click();
|
||||
|
||||
// expect plot to be paused
|
||||
await expect(page.getByTitle('Resume displaying real-time data')).toBeVisible();
|
||||
|
||||
// hover over plot for plot controls
|
||||
await page.getByLabel('Plot Canvas').hover();
|
||||
// click on synchronize with time conductor
|
||||
await page.getByTitle('Synchronize Time Conductor').click();
|
||||
|
||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||
|
||||
//confirm that you're now in fixed mode with the correct range
|
||||
await expect(page.getByLabel('Time Conductor Mode')).toHaveText('Fixed Timespan');
|
||||
});
|
||||
|
||||
test.fixme('Plot is rendered when infinity values exist', async ({ page }) => {
|
||||
|
@ -22,8 +22,8 @@
|
||||
|
||||
import {
|
||||
createDomainObjectWithDefaults,
|
||||
setTimeConductorBounds,
|
||||
setTimeConductorMode
|
||||
navigateToObjectWithRealTime,
|
||||
setTimeConductorBounds
|
||||
} from '../../../../appActions.js';
|
||||
import { expect, test } from '../../../../pluginFixtures.js';
|
||||
|
||||
@ -39,12 +39,52 @@ test.describe('Telemetry Table', () => {
|
||||
type: 'Sine Wave Generator',
|
||||
parent: table.uuid
|
||||
});
|
||||
await page.goto(table.url);
|
||||
await setTimeConductorMode(page, false);
|
||||
await navigateToObjectWithRealTime(page, table.url);
|
||||
const rows = page.getByLabel('table content').getByLabel('Table Row');
|
||||
await expect(rows).toHaveCount(50);
|
||||
});
|
||||
|
||||
test('on load, auto scrolls to top for descending, and to bottom for ascending', async ({
|
||||
page
|
||||
}) => {
|
||||
const sineWaveGenerator = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
parent: table.uuid
|
||||
});
|
||||
|
||||
// verify in telemetry table object view
|
||||
await navigateToObjectWithRealTime(page, table.url);
|
||||
|
||||
expect(await getScrollPosition(page)).toBe(0);
|
||||
|
||||
// verify in telemetry table view
|
||||
await page.goto(sineWaveGenerator.url);
|
||||
await page.getByLabel('Open the View Switcher Menu').click();
|
||||
await page.getByText('Telemetry Table', { exact: true }).click();
|
||||
|
||||
expect(await getScrollPosition(page)).toBe(0);
|
||||
|
||||
// navigate back to table
|
||||
await page.goto(table.url);
|
||||
|
||||
// go into edit mode
|
||||
await page.getByLabel('Edit Object').click();
|
||||
|
||||
// change sort direction
|
||||
await page.locator('thead div').filter({ hasText: 'Time' }).click();
|
||||
|
||||
// save view
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
|
||||
|
||||
// navigate away and back
|
||||
await page.goto(sineWaveGenerator.url);
|
||||
await page.goto(table.url);
|
||||
|
||||
// verify scroll position
|
||||
expect(await getScrollPosition(page, false)).toBeLessThan(1);
|
||||
});
|
||||
|
||||
test('unpauses and filters data when paused by button and user changes bounds', async ({
|
||||
page
|
||||
}) => {
|
||||
@ -183,3 +223,42 @@ test.describe('Telemetry Table', () => {
|
||||
await page.click('button[title="Pause"]');
|
||||
});
|
||||
});
|
||||
|
||||
async function getScrollPosition(page, top = true) {
|
||||
const tableBody = page.locator('.c-table__body-w');
|
||||
|
||||
// Wait for the scrollbar to appear
|
||||
await tableBody.evaluate((node) => {
|
||||
return new Promise((resolve) => {
|
||||
function checkScroll() {
|
||||
if (node.scrollHeight > node.clientHeight) {
|
||||
resolve();
|
||||
} else {
|
||||
setTimeout(checkScroll, 100);
|
||||
}
|
||||
}
|
||||
checkScroll();
|
||||
});
|
||||
});
|
||||
|
||||
// make sure there are rows
|
||||
const rows = page.getByLabel('table content').getByLabel('Table Row');
|
||||
await rows.first().waitFor();
|
||||
|
||||
// Using this to allow for rows to come and go, so we can truly test the scroll position
|
||||
// eslint-disable-next-line playwright/no-wait-for-timeout
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const { scrollTop, clientHeight, scrollHeight } = await tableBody.evaluate((node) => ({
|
||||
scrollTop: node.scrollTop,
|
||||
clientHeight: node.clientHeight,
|
||||
scrollHeight: node.scrollHeight
|
||||
}));
|
||||
|
||||
// eslint-disable-next-line playwright/no-conditional-in-test
|
||||
if (top) {
|
||||
return scrollTop;
|
||||
} else {
|
||||
return Math.abs(scrollHeight - (scrollTop + clientHeight));
|
||||
}
|
||||
}
|
||||
|
64
e2e/tests/functional/staleness.e2e.spec.js
Normal file
64
e2e/tests/functional/staleness.e2e.spec.js
Normal file
@ -0,0 +1,64 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2023, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import { createDomainObjectWithDefaults, navigateToObjectWithRealTime } from '../../appActions.js';
|
||||
import { expect, test } from '../../pluginFixtures.js';
|
||||
|
||||
test.describe('Staleness', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
});
|
||||
|
||||
test('Does not show staleness after navigating from a stale object', async ({ page }) => {
|
||||
const objectViewSelector = '.c-object-view';
|
||||
const isStaleClass = 'is-stale';
|
||||
const staleSWG = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Sine Wave Generator',
|
||||
name: 'SWG'
|
||||
});
|
||||
|
||||
// edit properties and enable staleness updates
|
||||
await page.getByLabel('More actions').click();
|
||||
await page.getByLabel('Edit properties...').click();
|
||||
await page.getByLabel('Provide Staleness Updates', { exact: true }).click();
|
||||
await page.getByLabel('Save').click();
|
||||
|
||||
const folder = await createDomainObjectWithDefaults(page, {
|
||||
type: 'Folder',
|
||||
name: 'Folder 1'
|
||||
});
|
||||
|
||||
// Navigate to the stale object
|
||||
await navigateToObjectWithRealTime(page, staleSWG.url);
|
||||
|
||||
// Assert that staleness is shown
|
||||
await expect(page.locator(`${objectViewSelector} .${isStaleClass}`)).toBeAttached({
|
||||
timeout: 30 * 1000 // Give 30 seconds for the staleness to be updated
|
||||
});
|
||||
|
||||
// Immediately navigate to the folder
|
||||
await page.goto(folder.url);
|
||||
|
||||
// Verify that staleness is not shown
|
||||
await expect(page.locator(`${objectViewSelector} .${isStaleClass}`)).not.toBeAttached();
|
||||
});
|
||||
});
|
@ -20,15 +20,14 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import { test } from '../../avpFixtures.js';
|
||||
import { scanForA11yViolations, test } from '../../avpFixtures.js';
|
||||
import { VISUAL_FIXED_URL } from '../../constants.js';
|
||||
|
||||
test.describe('a11y - Default', () => {
|
||||
test.describe('a11y - Default @a11y', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
||||
});
|
||||
test('main view', async ({ page }, testInfo) => {
|
||||
//Skipping for https://github.com/nasa/openmct/issues/7421
|
||||
//await scanForA11yViolations(page, testInfo.title);
|
||||
await scanForA11yViolations(page, testInfo.title);
|
||||
});
|
||||
});
|
||||
|
@ -27,7 +27,7 @@ Tests the branding associated with the default deployment. At least the about mo
|
||||
import percySnapshot from '@percy/playwright';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import { expect, test } from '../../../avpFixtures.js';
|
||||
import { expect, scanForA11yViolations, test } from '../../../avpFixtures.js';
|
||||
import { VISUAL_FIXED_URL } from '../../../constants.js';
|
||||
|
||||
//Declare the component scope of the visual test for Percy
|
||||
@ -69,14 +69,26 @@ test.describe('Visual - Header @a11y', () => {
|
||||
});
|
||||
|
||||
test('show snapshot button', async ({ page, theme }) => {
|
||||
test.slow(true, 'We have to wait for the snapshot indicator to stop flashing');
|
||||
await page.getByLabel('Open the Notebook Snapshot Menu').click();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Save to Notebook Snapshots' }).click();
|
||||
|
||||
await expect(page.getByLabel('Show Snapshots')).toBeVisible();
|
||||
|
||||
/**
|
||||
* We have to wait for the snapshot indicator to stop flashing. This happens
|
||||
* for a really long time (15 seconds 😳).
|
||||
* TODO: Either reduce the length of the animation, convert this to a
|
||||
* Playwright snapshot test (and disable animations), or augment the `waitForAnimations`
|
||||
* fixture to adjust the timeout.
|
||||
*/
|
||||
await expect(page.locator('.has-new-snapshot')).not.toBeAttached({
|
||||
timeout: 30 * 1000
|
||||
});
|
||||
await percySnapshot(page, `Notebook Snapshot Show button (theme: '${theme}')`, {
|
||||
scope: header
|
||||
});
|
||||
await expect(page.getByLabel('Show Snapshots')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@ -99,7 +111,6 @@ test.describe('Mission Header @a11y', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
// Skipping for https://github.com/nasa/openmct/issues/7421
|
||||
// test.afterEach(async ({ page }, testInfo) => {
|
||||
// await scanForA11yViolations(page, testInfo.title);
|
||||
// });
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await scanForA11yViolations(page, testInfo.title);
|
||||
});
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
import percySnapshot from '@percy/playwright';
|
||||
|
||||
import { test } from '../../../avpFixtures.js';
|
||||
import { scanForA11yViolations, test } from '../../../avpFixtures.js';
|
||||
import { MISSION_TIME, VISUAL_FIXED_URL } from '../../../constants.js';
|
||||
|
||||
//Declare the scope of the visual test
|
||||
@ -55,7 +55,6 @@ test.describe('Visual - Inspector @ally @clock', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
// Skipping for https://github.com/nasa/openmct/issues/7421
|
||||
// test.afterEach(async ({ page }, testInfo) => {
|
||||
// await scanForA11yViolations(page, testInfo.title);
|
||||
// });
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await scanForA11yViolations(page, testInfo.title);
|
||||
});
|
||||
|
@ -23,7 +23,7 @@
|
||||
import percySnapshot from '@percy/playwright';
|
||||
|
||||
import { createDomainObjectWithDefaults, expandTreePaneItemByName } from '../../appActions.js';
|
||||
import { expect, test } from '../../avpFixtures.js';
|
||||
import { expect, scanForA11yViolations, test } from '../../avpFixtures.js';
|
||||
import { VISUAL_FIXED_URL } from '../../constants.js';
|
||||
import { enterTextEntry, startAndAddRestrictedNotebookObject } from '../../helper/notebookUtils.js';
|
||||
|
||||
@ -163,8 +163,7 @@ test.describe('Visual - Notebook @a11y', () => {
|
||||
// Take a snapshot
|
||||
await percySnapshot(page, `Notebook Selected Entry Text Area Active (theme: '${theme}')`);
|
||||
});
|
||||
// Skipping for https://github.com/nasa/openmct/issues/7421
|
||||
// test.afterEach(async ({ page }, testInfo) => {
|
||||
// await scanForA11yViolations(page, testInfo.title);
|
||||
// });
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await scanForA11yViolations(page, testInfo.title);
|
||||
});
|
||||
});
|
||||
|
@ -24,91 +24,15 @@ import percySnapshot from '@percy/playwright';
|
||||
import fs from 'fs';
|
||||
|
||||
import { createDomainObjectWithDefaults, createPlanFromJSON } from '../../appActions.js';
|
||||
import { test } from '../../avpFixtures.js';
|
||||
import { scanForA11yViolations, test } from '../../avpFixtures.js';
|
||||
import { VISUAL_FIXED_URL } from '../../constants.js';
|
||||
import {
|
||||
createTimelistWithPlanAndSetActivityInProgress,
|
||||
getFirstActivity,
|
||||
setBoundsToSpanAllActivities,
|
||||
setDraftStatusForPlan
|
||||
} from '../../helper/planningUtils.js';
|
||||
|
||||
const examplePlanSmall1 = JSON.parse(
|
||||
fs.readFileSync(new URL('../../test-data/examplePlans/ExamplePlan_Small1.json', import.meta.url))
|
||||
);
|
||||
import { setBoundsToSpanAllActivities, setDraftStatusForPlan } from '../../helper/planningUtils.js';
|
||||
|
||||
const examplePlanSmall2 = JSON.parse(
|
||||
fs.readFileSync(new URL('../../test-data/examplePlans/ExamplePlan_Small2.json', import.meta.url))
|
||||
);
|
||||
|
||||
test.describe('Visual - Timelist progress bar @clock', () => {
|
||||
const firstActivity = getFirstActivity(examplePlanSmall1);
|
||||
|
||||
test.use({
|
||||
clockOptions: {
|
||||
now: firstActivity.end + 10000,
|
||||
shouldAdvanceTime: true
|
||||
}
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createTimelistWithPlanAndSetActivityInProgress(page, examplePlanSmall1);
|
||||
await page.getByLabel('Click to collapse items').click();
|
||||
});
|
||||
|
||||
test('progress pie is full', async ({ page, theme }) => {
|
||||
// Progress pie is completely full and doesn't update if now is greater than the end time
|
||||
await percySnapshot(page, `Time List with Activity in Progress (theme: ${theme})`);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Visual - Planning', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
||||
});
|
||||
|
||||
test('Plan View', async ({ page, theme }) => {
|
||||
const plan = await createPlanFromJSON(page, {
|
||||
name: 'Plan Visual Test',
|
||||
json: examplePlanSmall2
|
||||
});
|
||||
await setBoundsToSpanAllActivities(page, examplePlanSmall2, plan.url);
|
||||
await percySnapshot(page, `Plan View (theme: ${theme})`);
|
||||
});
|
||||
|
||||
test('Resize Plan View @2p', async ({ browser, theme }) => {
|
||||
// need to set viewport to null to allow for resizing
|
||||
const newContext = await browser.newContext({
|
||||
viewport: null
|
||||
});
|
||||
const newPage = await newContext.newPage();
|
||||
|
||||
await newPage.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
||||
const plan = await createPlanFromJSON(newPage, {
|
||||
name: 'Plan Visual Test',
|
||||
json: examplePlanSmall2
|
||||
});
|
||||
|
||||
await setBoundsToSpanAllActivities(newPage, examplePlanSmall2, plan.url);
|
||||
// resize the window
|
||||
await newPage.setViewportSize({ width: 800, height: 600 });
|
||||
await percySnapshot(newPage, `Plan View resized (theme: ${theme})`);
|
||||
});
|
||||
|
||||
test('Plan View w/ draft status', async ({ page, theme }) => {
|
||||
const plan = await createPlanFromJSON(page, {
|
||||
name: 'Plan Visual Test (Draft)',
|
||||
json: examplePlanSmall2
|
||||
});
|
||||
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
||||
await setDraftStatusForPlan(page, plan);
|
||||
|
||||
await setBoundsToSpanAllActivities(page, examplePlanSmall2, plan.url);
|
||||
await percySnapshot(page, `Plan View w/ draft status (theme: ${theme})`);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Visual - Gantt Chart', () => {
|
||||
test.describe('Visual - Gantt Chart @a11y', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
||||
});
|
||||
@ -179,7 +103,6 @@ test.describe('Visual - Gantt Chart', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// Skipping for https://github.com/nasa/openmct/issues/7421
|
||||
// test.afterEach(async ({ page }, testInfo) => {
|
||||
// await scanForA11yViolations(page, testInfo.title);
|
||||
// });
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await scanForA11yViolations(page, testInfo.title);
|
||||
});
|
59
e2e/tests/visual-a11y/planning-timelist.visual.spec.js
Normal file
59
e2e/tests/visual-a11y/planning-timelist.visual.spec.js
Normal file
@ -0,0 +1,59 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import percySnapshot from '@percy/playwright';
|
||||
import fs from 'fs';
|
||||
|
||||
import { scanForA11yViolations, test } from '../../avpFixtures.js';
|
||||
import {
|
||||
createTimelistWithPlanAndSetActivityInProgress,
|
||||
getFirstActivity
|
||||
} from '../../helper/planningUtils.js';
|
||||
|
||||
const examplePlanSmall1 = JSON.parse(
|
||||
fs.readFileSync(new URL('../../test-data/examplePlans/ExamplePlan_Small1.json', import.meta.url))
|
||||
);
|
||||
|
||||
test.describe('Visual - Timelist progress bar @clock @a11y', () => {
|
||||
const firstActivity = getFirstActivity(examplePlanSmall1);
|
||||
|
||||
test.use({
|
||||
clockOptions: {
|
||||
now: firstActivity.end + 10000,
|
||||
shouldAdvanceTime: true
|
||||
}
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createTimelistWithPlanAndSetActivityInProgress(page, examplePlanSmall1);
|
||||
await page.getByLabel('Click to collapse items').click();
|
||||
});
|
||||
|
||||
test('progress pie is full', async ({ page, theme }) => {
|
||||
// Progress pie is completely full and doesn't update if now is greater than the end time
|
||||
await percySnapshot(page, `Time List with Activity in Progress (theme: ${theme})`);
|
||||
});
|
||||
});
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await scanForA11yViolations(page, testInfo.title);
|
||||
});
|
113
e2e/tests/visual-a11y/planning-view.visual.spec.js
Normal file
113
e2e/tests/visual-a11y/planning-view.visual.spec.js
Normal file
@ -0,0 +1,113 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2024, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import percySnapshot from '@percy/playwright';
|
||||
import fs from 'fs';
|
||||
|
||||
import { createPlanFromJSON } from '../../appActions.js';
|
||||
import { scanForA11yViolations, test } from '../../avpFixtures.js';
|
||||
import { VISUAL_FIXED_URL } from '../../constants.js';
|
||||
import {
|
||||
createTimelistWithPlanAndSetActivityInProgress,
|
||||
getFirstActivity,
|
||||
setBoundsToSpanAllActivities,
|
||||
setDraftStatusForPlan
|
||||
} from '../../helper/planningUtils.js';
|
||||
|
||||
const examplePlanSmall1 = JSON.parse(
|
||||
fs.readFileSync(new URL('../../test-data/examplePlans/ExamplePlan_Small1.json', import.meta.url))
|
||||
);
|
||||
|
||||
const examplePlanSmall2 = JSON.parse(
|
||||
fs.readFileSync(new URL('../../test-data/examplePlans/ExamplePlan_Small2.json', import.meta.url))
|
||||
);
|
||||
|
||||
test.describe('Visual - Timelist progress bar @clock @a11y', () => {
|
||||
const firstActivity = getFirstActivity(examplePlanSmall1);
|
||||
|
||||
test.use({
|
||||
clockOptions: {
|
||||
now: firstActivity.end + 10000,
|
||||
shouldAdvanceTime: true
|
||||
}
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createTimelistWithPlanAndSetActivityInProgress(page, examplePlanSmall1);
|
||||
await page.getByLabel('Click to collapse items').click();
|
||||
});
|
||||
|
||||
test('progress pie is full', async ({ page, theme }) => {
|
||||
// Progress pie is completely full and doesn't update if now is greater than the end time
|
||||
await percySnapshot(page, `Time List with Activity in Progress (theme: ${theme})`);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Visual - Plan View @a11y', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
||||
});
|
||||
|
||||
test('Plan View', async ({ page, theme }) => {
|
||||
const plan = await createPlanFromJSON(page, {
|
||||
name: 'Plan Visual Test',
|
||||
json: examplePlanSmall2
|
||||
});
|
||||
await setBoundsToSpanAllActivities(page, examplePlanSmall2, plan.url);
|
||||
await percySnapshot(page, `Plan View (theme: ${theme})`);
|
||||
});
|
||||
|
||||
test('Resize Plan View @2p', async ({ browser, theme }) => {
|
||||
// need to set viewport to null to allow for resizing
|
||||
const newContext = await browser.newContext({
|
||||
viewport: null
|
||||
});
|
||||
const newPage = await newContext.newPage();
|
||||
|
||||
await newPage.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
||||
const plan = await createPlanFromJSON(newPage, {
|
||||
name: 'Plan Visual Test',
|
||||
json: examplePlanSmall2
|
||||
});
|
||||
|
||||
await setBoundsToSpanAllActivities(newPage, examplePlanSmall2, plan.url);
|
||||
// resize the window
|
||||
await newPage.setViewportSize({ width: 800, height: 600 });
|
||||
await percySnapshot(newPage, `Plan View resized (theme: ${theme})`);
|
||||
});
|
||||
|
||||
test('Plan View w/ draft status', async ({ page, theme }) => {
|
||||
const plan = await createPlanFromJSON(page, {
|
||||
name: 'Plan Visual Test (Draft)',
|
||||
json: examplePlanSmall2
|
||||
});
|
||||
await page.goto(VISUAL_FIXED_URL, { waitUntil: 'domcontentloaded' });
|
||||
await setDraftStatusForPlan(page, plan);
|
||||
|
||||
await setBoundsToSpanAllActivities(page, examplePlanSmall2, plan.url);
|
||||
await percySnapshot(page, `Plan View w/ draft status (theme: ${theme})`);
|
||||
});
|
||||
});
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await scanForA11yViolations(page, testInfo.title);
|
||||
});
|
@ -26,7 +26,6 @@ export default class SinewaveLimitProvider extends EventEmitter {
|
||||
#openmct;
|
||||
#observingStaleness;
|
||||
#watchingTheClock;
|
||||
#isRealTime;
|
||||
|
||||
constructor(openmct) {
|
||||
super();
|
||||
@ -34,7 +33,6 @@ export default class SinewaveLimitProvider extends EventEmitter {
|
||||
this.#openmct = openmct;
|
||||
this.#observingStaleness = {};
|
||||
this.#watchingTheClock = false;
|
||||
this.#isRealTime = undefined;
|
||||
}
|
||||
|
||||
supportsStaleness(domainObject) {
|
||||
@ -61,10 +59,7 @@ export default class SinewaveLimitProvider extends EventEmitter {
|
||||
subscribeToStaleness(domainObject, callback) {
|
||||
const id = this.#getObjectKeyString(domainObject);
|
||||
|
||||
if (this.#isRealTime === undefined) {
|
||||
this.#updateRealTime(this.#openmct.time.getMode());
|
||||
}
|
||||
|
||||
this.#realTimeCheck();
|
||||
this.#handleClockUpdate();
|
||||
|
||||
if (this.#observerExists(id)) {
|
||||
@ -92,17 +87,15 @@ export default class SinewaveLimitProvider extends EventEmitter {
|
||||
|
||||
if (observers && !this.#watchingTheClock) {
|
||||
this.#watchingTheClock = true;
|
||||
this.#openmct.time.on('modeChanged', this.#updateRealTime, this);
|
||||
this.#openmct.time.on('modeChanged', this.#realTimeCheck, this);
|
||||
} else if (!observers && this.#watchingTheClock) {
|
||||
this.#watchingTheClock = false;
|
||||
this.#openmct.time.off('modeChanged', this.#updateRealTime, this);
|
||||
this.#openmct.time.off('modeChanged', this.#realTimeCheck, this);
|
||||
}
|
||||
}
|
||||
|
||||
#updateRealTime(mode) {
|
||||
this.#isRealTime = mode !== 'fixed';
|
||||
|
||||
if (!this.#isRealTime) {
|
||||
#realTimeCheck() {
|
||||
if (!this.#openmct.time.isRealTime()) {
|
||||
Object.keys(this.#observingStaleness).forEach((id) => {
|
||||
this.#updateStaleness(id, false);
|
||||
});
|
||||
@ -140,7 +133,7 @@ export default class SinewaveLimitProvider extends EventEmitter {
|
||||
}
|
||||
|
||||
#providingStaleness(domainObject) {
|
||||
return domainObject.telemetry?.staleness === true && this.#isRealTime;
|
||||
return domainObject.telemetry?.staleness === true && this.#openmct.time.isRealTime();
|
||||
}
|
||||
|
||||
#getObjectKeyString(object) {
|
||||
|
32
package-lock.json
generated
32
package-lock.json
generated
@ -43,7 +43,7 @@
|
||||
"eslint-plugin-unicorn": "49.0.0",
|
||||
"eslint-plugin-vue": "9.22.0",
|
||||
"eslint-plugin-you-dont-need-lodash-underscore": "6.13.0",
|
||||
"eventemitter3": "1.2.0",
|
||||
"eventemitter3": "5.0.1",
|
||||
"file-saver": "2.0.5",
|
||||
"flatbush": "4.2.0",
|
||||
"git-rev-sync": "3.0.2",
|
||||
@ -104,7 +104,7 @@
|
||||
"@axe-core/playwright": "4.8.5",
|
||||
"@percy/cli": "1.27.4",
|
||||
"@percy/playwright": "1.0.4",
|
||||
"@playwright/test": "1.42.1",
|
||||
"@playwright/test": "1.44.0",
|
||||
"@types/sinonjs__fake-timers": "8.1.5",
|
||||
"sinon": "17.0.0"
|
||||
}
|
||||
@ -1559,12 +1559,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.42.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz",
|
||||
"integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==",
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.0.tgz",
|
||||
"integrity": "sha512-rNX5lbNidamSUorBhB4XZ9SQTjAqfe5M+p37Z8ic0jPFBMo5iCtQz1kRWkEMg+rYOKSlVycpQmpqjSFq7LXOfg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright": "1.42.1"
|
||||
"playwright": "1.44.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@ -5391,9 +5391,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz",
|
||||
"integrity": "sha512-DOFqA1MF46fmZl2xtzXR3MPCRsXqgoFqdXcrCVYM3JNnfUeHTm/fh/v/iU7gBFpwkuBmoJPAm5GuhdDfSEJMJA==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/events": {
|
||||
@ -8833,12 +8833,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.42.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz",
|
||||
"integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==",
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.0.tgz",
|
||||
"integrity": "sha512-F9b3GUCLQ3Nffrfb6dunPOkE5Mh68tR7zN32L4jCk4FjQamgesGay7/dAAe1WaMEGV04DkdJfcJzjoCKygUaRQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright-core": "1.42.1"
|
||||
"playwright-core": "1.44.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@ -8851,9 +8851,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.42.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz",
|
||||
"integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==",
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.0.tgz",
|
||||
"integrity": "sha512-ZTbkNpFfYcGWohvTTl+xewITm7EOuqIqex0c7dNZ+aXsbrLj0qI8XlGKfPpipjm0Wny/4Lt4CJsWJk1stVS5qQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
|
@ -46,7 +46,7 @@
|
||||
"eslint-plugin-unicorn": "49.0.0",
|
||||
"eslint-plugin-vue": "9.22.0",
|
||||
"eslint-plugin-you-dont-need-lodash-underscore": "6.13.0",
|
||||
"eventemitter3": "1.2.0",
|
||||
"eventemitter3": "5.0.1",
|
||||
"file-saver": "2.0.5",
|
||||
"flatbush": "4.2.0",
|
||||
"git-rev-sync": "3.0.2",
|
||||
@ -96,7 +96,7 @@
|
||||
"webpack-merge": "5.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf ./dist ./node_modules ./coverage ./html-test-results ./test-results ./.nyc_output",
|
||||
"clean": "rm -rf ./dist ./node_modules ./coverage ./html-test-results ./e2e/test-results ./.nyc_output ./e2e/.nyc_output",
|
||||
"start": "npx webpack serve --config ./.webpack/webpack.dev.mjs",
|
||||
"start:prod": "npx webpack serve --config ./.webpack/webpack.prod.mjs",
|
||||
"start:coverage": "npx webpack serve --config ./.webpack/webpack.coverage.mjs",
|
||||
|
@ -79,7 +79,6 @@ import Browse from './ui/router/Browse.js';
|
||||
export class MCT extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.buildInfo = {
|
||||
version: __OPENMCT_VERSION__,
|
||||
@ -371,6 +370,5 @@ export class MCT extends EventEmitter {
|
||||
destroy() {
|
||||
window.removeEventListener('beforeunload', this.destroy);
|
||||
this.emit('destroy');
|
||||
this.router.destroy();
|
||||
}
|
||||
}
|
||||
|
@ -57,14 +57,21 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const CONTEXT_MENU_ACTIONS = ['viewDatumAction', 'viewHistoricalData', 'remove'];
|
||||
const BLANK_VALUE = '---';
|
||||
|
||||
import { objectPathToUrl } from '/src/tools/url.js';
|
||||
import PreviewAction from '@/ui/preview/PreviewAction.js';
|
||||
import { REMOVE_ACTION_KEY } from '@/plugins/remove/RemoveAction.js';
|
||||
import { VIEW_DATUM_ACTION_KEY } from '@/plugins/viewDatumAction/ViewDatumAction.js';
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
import { VIEW_HISTORICAL_DATA_ACTION_KEY } from '@/ui/preview/ViewHistoricalDataAction.js';
|
||||
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins.js';
|
||||
|
||||
const BLANK_VALUE = '---';
|
||||
const CONTEXT_MENU_ACTIONS = [
|
||||
VIEW_DATUM_ACTION_KEY,
|
||||
VIEW_HISTORICAL_DATA_ACTION_KEY,
|
||||
REMOVE_ACTION_KEY
|
||||
];
|
||||
|
||||
export default {
|
||||
mixins: [tooltipHelpers],
|
||||
inject: ['openmct', 'currentView', 'renderWhenVisible'],
|
||||
@ -236,14 +243,12 @@ export default {
|
||||
this.setUnit();
|
||||
}
|
||||
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction.on('isVisible', this.togglePreviewState);
|
||||
this.previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
},
|
||||
unmounted() {
|
||||
this.openmct.time.off('timeSystem', this.updateTimeSystem);
|
||||
this.telemetryCollection.off('add', this.setLatestValues);
|
||||
this.telemetryCollection.off('clear', this.resetValues);
|
||||
this.previewAction.off('isVisible', this.togglePreviewState);
|
||||
|
||||
this.telemetryCollection.destroy();
|
||||
},
|
||||
|
@ -32,16 +32,18 @@ function inSelectionPath(openmct, domainObject) {
|
||||
});
|
||||
}
|
||||
|
||||
export default class ClearDataAction {
|
||||
const CLEAR_DATA_ACTION_KEY = 'clear-data-action';
|
||||
class ClearDataAction {
|
||||
constructor(openmct, appliesToObjects) {
|
||||
this.name = 'Clear Data for Object';
|
||||
this.key = 'clear-data-action';
|
||||
this.key = CLEAR_DATA_ACTION_KEY;
|
||||
this.description = 'Clears current data for object, unsubscribes and resubscribes to data';
|
||||
this.cssClass = 'icon-clear-data';
|
||||
|
||||
this._openmct = openmct;
|
||||
this._appliesToObjects = appliesToObjects;
|
||||
}
|
||||
|
||||
invoke(objectPath) {
|
||||
let domainObject = null;
|
||||
if (objectPath) {
|
||||
@ -76,3 +78,7 @@ export default class ClearDataAction {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { CLEAR_DATA_ACTION_KEY };
|
||||
|
||||
export default ClearDataAction;
|
||||
|
@ -46,14 +46,6 @@ export default class ConditionManager extends EventEmitter {
|
||||
applied: false
|
||||
};
|
||||
this.initialize();
|
||||
|
||||
this.stopObservingForChanges = this.openmct.objects.observe(
|
||||
this.conditionSetDomainObject,
|
||||
'*',
|
||||
(newDomainObject) => {
|
||||
this.conditionSetDomainObject = newDomainObject;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async requestLatestValue(endpoint) {
|
||||
@ -518,10 +510,6 @@ export default class ConditionManager extends EventEmitter {
|
||||
Object.values(this.subscriptions).forEach((unsubscribe) => unsubscribe());
|
||||
delete this.subscriptions;
|
||||
|
||||
if (this.stopObservingForChanges) {
|
||||
this.stopObservingForChanges();
|
||||
}
|
||||
|
||||
this.conditions.forEach((condition) => {
|
||||
condition.destroy();
|
||||
});
|
||||
|
@ -21,7 +21,11 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<section id="conditionCollection" :class="{ 'is-expanded': expanded }">
|
||||
<section
|
||||
id="conditionCollection"
|
||||
:class="{ 'is-expanded': expanded }"
|
||||
aria-label="Condition Set Condition Collection"
|
||||
>
|
||||
<div class="c-cs__header c-section__header">
|
||||
<span
|
||||
class="c-disclosure-triangle c-tree__item__view-control is-enabled"
|
||||
|
@ -24,6 +24,7 @@
|
||||
<div
|
||||
class="c-condition-h"
|
||||
:class="{ 'is-drag-target': draggingOver }"
|
||||
aria-label="Condition Set Condition"
|
||||
@dragover.prevent
|
||||
@drop.prevent="dropCondition($event, conditionIndex)"
|
||||
@dragenter="dragEnter($event, conditionIndex)"
|
||||
@ -83,6 +84,7 @@
|
||||
<input
|
||||
v-model="condition.configuration.name"
|
||||
class="t-condition-input__name"
|
||||
aria-label="Condition Name Input"
|
||||
type="text"
|
||||
@change="persist"
|
||||
/>
|
||||
@ -91,7 +93,11 @@
|
||||
<span class="c-cdef__label">Output</span>
|
||||
<span class="c-cdef__controls">
|
||||
<span class="c-cdef__control">
|
||||
<select v-model="selectedOutputSelection" @change="setOutputValue">
|
||||
<select
|
||||
v-model="selectedOutputSelection"
|
||||
aria-label="Condition Output Type"
|
||||
@change="setOutputValue"
|
||||
>
|
||||
<option v-for="option in outputOptions" :key="option" :value="option">
|
||||
{{ initCap(option) }}
|
||||
</option>
|
||||
@ -101,6 +107,7 @@
|
||||
<input
|
||||
v-if="selectedOutputSelection === outputOptions[2]"
|
||||
v-model="condition.configuration.output"
|
||||
aria-label="Condition Output String"
|
||||
class="t-condition-name-input"
|
||||
type="text"
|
||||
@change="persist"
|
||||
|
@ -21,7 +21,7 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="c-cs" :class="{ 'is-stale': isStale }">
|
||||
<div class="c-cs" :class="{ 'is-stale': isStale }" aria-label="Condition Set">
|
||||
<section class="c-cs__current-output c-section">
|
||||
<div class="c-cs__content c-cs__current-output-value">
|
||||
<span class="c-cs__current-output-value__label">Current Output</span>
|
||||
|
@ -21,7 +21,12 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<section v-show="isEditing" id="test-data" :class="{ 'is-expanded': expanded }">
|
||||
<section
|
||||
v-show="isEditing"
|
||||
id="test-data"
|
||||
:class="{ 'is-expanded': expanded }"
|
||||
aria-label="Condition Set Test Data"
|
||||
>
|
||||
<div class="c-cs__header c-section__header">
|
||||
<span
|
||||
class="c-disclosure-triangle c-tree__item__view-control is-enabled"
|
||||
|
@ -142,7 +142,7 @@ import {
|
||||
getConditionSetIdentifierForItem,
|
||||
getConsolidatedStyleValues
|
||||
} from '@/plugins/condition/utils/styleUtils';
|
||||
import PreviewAction from '@/ui/preview/PreviewAction.js';
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import FontStyleEditor from '../../../inspectorViews/styles/FontStyleEditor.vue';
|
||||
import StyleEditor from './StyleEditor.vue';
|
||||
@ -237,7 +237,7 @@ export default {
|
||||
this.stylesManager.off('styleSelected', this.applyStyleToSelection);
|
||||
},
|
||||
mounted() {
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
this.isMultipleSelection = this.selection.length > 1;
|
||||
this.getObjectsAndItemsFromSelection();
|
||||
this.useConditionSetOutputAsLabel = this.getConfigurationForLabel();
|
||||
|
@ -1,13 +1,15 @@
|
||||
import clipboard from '@/utils/clipboard';
|
||||
|
||||
export default class CopyToClipboardAction {
|
||||
const COPY_TO_CLIPBOARD_ACTION_KEY = 'copyToClipboard';
|
||||
|
||||
class CopyToClipboardAction {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
|
||||
this.cssClass = 'icon-duplicate';
|
||||
this.description = 'Copy value to clipboard';
|
||||
this.group = 'action';
|
||||
this.key = 'copyToClipboard';
|
||||
this.key = COPY_TO_CLIPBOARD_ACTION_KEY;
|
||||
this.name = 'Copy to Clipboard';
|
||||
this.priority = 1;
|
||||
}
|
||||
@ -36,3 +38,7 @@ export default class CopyToClipboardAction {
|
||||
return row.formattedValueForCopy && typeof row.formattedValueForCopy === 'function';
|
||||
}
|
||||
}
|
||||
|
||||
export { COPY_TO_CLIPBOARD_ACTION_KEY };
|
||||
|
||||
export default CopyToClipboardAction;
|
||||
|
@ -72,11 +72,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { COPY_TO_CLIPBOARD_ACTION_KEY } from '@/plugins/displayLayout/actions/CopyToClipboardAction.js';
|
||||
import { COPY_TO_NOTEBOOK_ACTION_KEY } from '@/plugins/notebook/actions/CopyToNotebookAction.js';
|
||||
import {
|
||||
getDefaultNotebook,
|
||||
getNotebookSectionAndPage
|
||||
} from '@/plugins/notebook/utils/notebook-storage.js';
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
import { VIEW_HISTORICAL_DATA_ACTION_KEY } from '@/ui/preview/ViewHistoricalDataAction.js';
|
||||
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins.js';
|
||||
import conditionalStylesMixin from '../mixins/objectStyles-mixin.js';
|
||||
@ -84,7 +87,11 @@ import LayoutFrame from './LayoutFrame.vue';
|
||||
|
||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5];
|
||||
const DEFAULT_POSITION = [1, 1];
|
||||
const CONTEXT_MENU_ACTIONS = ['copyToClipboard', 'copyToNotebook', 'viewHistoricalData'];
|
||||
const CONTEXT_MENU_ACTIONS = [
|
||||
COPY_TO_CLIPBOARD_ACTION_KEY,
|
||||
COPY_TO_NOTEBOOK_ACTION_KEY,
|
||||
VIEW_HISTORICAL_DATA_ACTION_KEY
|
||||
];
|
||||
|
||||
export default {
|
||||
makeDefinition(openmct, gridSize, domainObject, position) {
|
||||
@ -381,7 +388,7 @@ export default {
|
||||
|
||||
return CONTEXT_MENU_ACTIONS.map((actionKey) => {
|
||||
const action = this.openmct.actions.getAction(actionKey);
|
||||
if (action.key === 'copyToNotebook') {
|
||||
if (action.key === COPY_TO_NOTEBOOK_ACTION_KEY) {
|
||||
action.name = defaultNotebookName;
|
||||
}
|
||||
|
||||
|
@ -21,10 +21,12 @@
|
||||
*****************************************************************************/
|
||||
import DuplicateTask from './DuplicateTask.js';
|
||||
|
||||
export default class DuplicateAction {
|
||||
const DUPLICATE_ACTION_KEY = 'duplicate';
|
||||
|
||||
class DuplicateAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Duplicate';
|
||||
this.key = 'duplicate';
|
||||
this.key = DUPLICATE_ACTION_KEY;
|
||||
this.description = 'Duplicate this object.';
|
||||
this.cssClass = 'icon-duplicate';
|
||||
this.group = 'action';
|
||||
@ -169,3 +171,7 @@ export default class DuplicateAction {
|
||||
this.transaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
export { DUPLICATE_ACTION_KEY };
|
||||
|
||||
export default DuplicateAction;
|
||||
|
@ -22,8 +22,9 @@
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import JSONExporter from '/src/exporters/JSONExporter.js';
|
||||
const EXPORT_AS_JSON_ACTION_KEY = 'export.JSON';
|
||||
|
||||
export default class ExportAsJSONAction {
|
||||
class ExportAsJSONAction {
|
||||
#openmct;
|
||||
|
||||
/**
|
||||
@ -39,7 +40,7 @@ export default class ExportAsJSONAction {
|
||||
this.saveAs = this.saveAs.bind(this);
|
||||
|
||||
this.name = 'Export as JSON';
|
||||
this.key = 'export.JSON';
|
||||
this.key = EXPORT_AS_JSON_ACTION_KEY;
|
||||
this.description = '';
|
||||
this.cssClass = 'icon-export';
|
||||
this.group = 'export';
|
||||
@ -410,3 +411,7 @@ export default class ExportAsJSONAction {
|
||||
return JSON.parse(JSON.stringify(object));
|
||||
}
|
||||
}
|
||||
|
||||
export { EXPORT_AS_JSON_ACTION_KEY };
|
||||
|
||||
export default ExportAsJSONAction;
|
||||
|
@ -26,19 +26,22 @@ import { v4 as uuid } from 'uuid';
|
||||
import CreateWizard from './CreateWizard.js';
|
||||
import PropertiesAction from './PropertiesAction.js';
|
||||
|
||||
export default class CreateAction extends PropertiesAction {
|
||||
const CREATE_ACTION_KEY = 'create';
|
||||
|
||||
class CreateAction extends PropertiesAction {
|
||||
#transaction;
|
||||
|
||||
constructor(openmct, type, parentDomainObject) {
|
||||
constructor(openmct) {
|
||||
super(openmct);
|
||||
|
||||
this.type = type;
|
||||
this.parentDomainObject = parentDomainObject;
|
||||
this.#transaction = null;
|
||||
this.key = CREATE_ACTION_KEY;
|
||||
// Hide the create action from context menus by default
|
||||
this.isHidden = true;
|
||||
}
|
||||
|
||||
invoke() {
|
||||
this._showCreateForm(this.type);
|
||||
get invoke() {
|
||||
return (type, parentDomainObject) => this._showCreateForm(type, parentDomainObject);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,7 +145,7 @@ export default class CreateAction extends PropertiesAction {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_showCreateForm(type) {
|
||||
_showCreateForm(type, parentDomainObject) {
|
||||
const typeDefinition = this.openmct.types.get(type);
|
||||
const definition = typeDefinition.definition;
|
||||
const domainObject = {
|
||||
@ -150,7 +153,7 @@ export default class CreateAction extends PropertiesAction {
|
||||
type,
|
||||
identifier: {
|
||||
key: uuid(),
|
||||
namespace: this.parentDomainObject.identifier.namespace
|
||||
namespace: parentDomainObject.identifier.namespace
|
||||
}
|
||||
};
|
||||
|
||||
@ -160,7 +163,7 @@ export default class CreateAction extends PropertiesAction {
|
||||
definition.initialize(this.domainObject);
|
||||
}
|
||||
|
||||
const createWizard = new CreateWizard(this.openmct, this.domainObject, this.parentDomainObject);
|
||||
const createWizard = new CreateWizard(this.openmct, this.domainObject, parentDomainObject);
|
||||
const formStructure = createWizard.getFormStructure(true);
|
||||
formStructure.title = 'Create a New ' + definition.name;
|
||||
|
||||
@ -191,3 +194,7 @@ export default class CreateAction extends PropertiesAction {
|
||||
this.#transaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
export { CREATE_ACTION_KEY };
|
||||
|
||||
export default CreateAction;
|
||||
|
@ -22,7 +22,7 @@
|
||||
import { debounce } from 'lodash';
|
||||
import { createOpenMct, resetApplicationState } from 'utils/testing';
|
||||
|
||||
import CreateAction from './CreateAction.js';
|
||||
import { CREATE_ACTION_KEY } from './CreateAction.js';
|
||||
|
||||
let parentObject;
|
||||
let parentObjectPath;
|
||||
@ -115,8 +115,8 @@ describe('The create action plugin', () => {
|
||||
const deBouncedCallback = debounce(callback, 300);
|
||||
unObserve = openmct.objects.observe(parentObject, '*', deBouncedCallback);
|
||||
|
||||
const createAction = new CreateAction(openmct, type, parentObject);
|
||||
createAction.invoke();
|
||||
const createAction = openmct.actions.getAction(CREATE_ACTION_KEY);
|
||||
createAction.invoke(type, parentObject);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -24,13 +24,14 @@ import _ from 'lodash';
|
||||
|
||||
import CreateWizard from './CreateWizard.js';
|
||||
import PropertiesAction from './PropertiesAction.js';
|
||||
const EDIT_PROPERTIES_ACTION_KEY = 'properties';
|
||||
|
||||
export default class EditPropertiesAction extends PropertiesAction {
|
||||
class EditPropertiesAction extends PropertiesAction {
|
||||
constructor(openmct) {
|
||||
super(openmct);
|
||||
|
||||
this.name = 'Edit Properties...';
|
||||
this.key = 'properties';
|
||||
this.key = EDIT_PROPERTIES_ACTION_KEY;
|
||||
this.description = 'Edit properties of this object.';
|
||||
this.cssClass = 'major icon-pencil';
|
||||
this.hideInDefaultMenu = true;
|
||||
@ -100,3 +101,7 @@ export default class EditPropertiesAction extends PropertiesAction {
|
||||
.catch(this._onCancel.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
export { EDIT_PROPERTIES_ACTION_KEY };
|
||||
|
||||
export default EditPropertiesAction;
|
||||
|
@ -20,10 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import CreateAction from './CreateAction.js';
|
||||
import EditPropertiesAction from './EditPropertiesAction.js';
|
||||
|
||||
export default function () {
|
||||
return function (openmct) {
|
||||
openmct.actions.register(new EditPropertiesAction(openmct));
|
||||
openmct.actions.register(new CreateAction(openmct));
|
||||
};
|
||||
}
|
||||
|
@ -20,10 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class GoToOriginalAction {
|
||||
const GO_TO_ORIGINAL_ACTION_KEY = 'goToOriginal';
|
||||
|
||||
class GoToOriginalAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Go To Original';
|
||||
this.key = 'goToOriginal';
|
||||
this.key = GO_TO_ORIGINAL_ACTION_KEY;
|
||||
this.description = 'Go to the original unlinked instance of this object';
|
||||
this.group = 'action';
|
||||
this.priority = 4;
|
||||
@ -62,3 +64,7 @@ export default class GoToOriginalAction {
|
||||
return parentKeystring !== objectPath[0].location;
|
||||
}
|
||||
}
|
||||
|
||||
export { GO_TO_ORIGINAL_ACTION_KEY };
|
||||
|
||||
export default GoToOriginalAction;
|
||||
|
@ -20,14 +20,15 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class OpenImageInNewTabAction {
|
||||
const OPEN_IMAGE_IN_NEW_TAB_ACTION_KEY = 'openImageInNewTab';
|
||||
class OpenImageInNewTabAction {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
|
||||
this.cssClass = 'icon-new-window';
|
||||
this.description = 'Open the image in a new tab';
|
||||
this.group = 'action';
|
||||
this.key = 'openImageInNewTab';
|
||||
this.key = OPEN_IMAGE_IN_NEW_TAB_ACTION_KEY;
|
||||
this.name = 'Open Image in New Tab';
|
||||
this.priority = 1;
|
||||
}
|
||||
@ -44,3 +45,7 @@ export default class OpenImageInNewTabAction {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { OPEN_IMAGE_IN_NEW_TAB_ACTION_KEY };
|
||||
|
||||
export default OpenImageInNewTabAction;
|
||||
|
@ -20,14 +20,15 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class SaveImageAction {
|
||||
const SAVE_IMAGE_ACTION_KEY = 'saveImageAs';
|
||||
class SaveImageAction {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
|
||||
this.cssClass = 'icon-save-as';
|
||||
this.description = 'Save image to file';
|
||||
this.group = 'action';
|
||||
this.key = 'saveImageAs';
|
||||
this.key = SAVE_IMAGE_ACTION_KEY;
|
||||
this.name = 'Save Image As';
|
||||
this.priority = 1;
|
||||
}
|
||||
@ -65,3 +66,7 @@ export default class SaveImageAction {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { SAVE_IMAGE_ACTION_KEY };
|
||||
|
||||
export default SaveImageAction;
|
||||
|
@ -38,13 +38,15 @@ import isEqual from 'lodash/isEqual';
|
||||
import { toRaw } from 'vue';
|
||||
|
||||
import TagEditorClassNames from '../../inspectorViews/annotations/tags/TagEditorClassNames.js';
|
||||
import { OPEN_IMAGE_IN_NEW_TAB_ACTION_KEY } from '../actions/OpenImageInNewTabAction.js';
|
||||
import { SAVE_IMAGE_ACTION_KEY } from '../actions/SaveImageAsAction.js';
|
||||
|
||||
const EXISTING_ANNOTATION_STROKE_STYLE = '#D79078';
|
||||
const EXISTING_ANNOTATION_FILL_STYLE = 'rgba(202, 202, 142, 0.2)';
|
||||
const SELECTED_ANNOTATION_STROKE_COLOR = '#BD8ECC';
|
||||
const SELECTED_ANNOTATION_FILL_STYLE = 'rgba(199, 87, 231, 0.2)';
|
||||
|
||||
const CONTEXT_MENU_ACTIONS = ['openImageInNewTab', 'saveImageAs'];
|
||||
const CONTEXT_MENU_ACTIONS = [OPEN_IMAGE_IN_NEW_TAB_ACTION_KEY, SAVE_IMAGE_ACTION_KEY];
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject', 'objectPath', 'currentView'],
|
||||
|
@ -32,7 +32,7 @@ import _ from 'lodash';
|
||||
import mount from 'utils/mount';
|
||||
|
||||
import SwimLane from '@/ui/components/swim-lane/SwimLane.vue';
|
||||
import PreviewAction from '@/ui/preview/PreviewAction';
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import imageryData from '../../imagery/mixins/imageryData.js';
|
||||
|
||||
@ -71,7 +71,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
|
||||
this.canvas = this.$refs.imagery.appendChild(document.createElement('canvas'));
|
||||
this.canvas.height = 0;
|
||||
|
@ -213,8 +213,10 @@ import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
import { TIME_CONTEXT_EVENTS } from '../../../api/time/constants.js';
|
||||
import imageryData from '../../imagery/mixins/imageryData.js';
|
||||
import { TIME_CONTEXT_EVENTS } from '@/api/time/constants.js';
|
||||
import imageryData from '@/plugins/imagery/mixins/imageryData.js';
|
||||
import { VIEW_LARGE_ACTION_KEY } from '@/plugins/viewLargeAction/viewLargeAction.js';
|
||||
|
||||
import eventHelpers from '../lib/eventHelpers.js';
|
||||
import AnnotationsCanvas from './AnnotationsCanvas.vue';
|
||||
import Compass from './Compass/CompassComponent.vue';
|
||||
@ -827,7 +829,9 @@ export default {
|
||||
this.currentView
|
||||
);
|
||||
const visibleActions = actionCollection.getVisibleActions();
|
||||
const viewLargeAction = visibleActions?.find((action) => action.key === 'large.view');
|
||||
const viewLargeAction = visibleActions?.find(
|
||||
(action) => action.key === VIEW_LARGE_ACTION_KEY
|
||||
);
|
||||
|
||||
if (viewLargeAction?.appliesTo(this.objectPath, this.currentView)) {
|
||||
viewLargeAction.invoke(this.objectPath, this.currentView);
|
||||
|
@ -41,7 +41,7 @@ const helperFunctions = {
|
||||
} else if (object.addEventListener) {
|
||||
object.addEventListener(event, listener._cb);
|
||||
} else {
|
||||
object.on(event, listener._cb);
|
||||
object.on(event, listener._cb, listener.context);
|
||||
}
|
||||
|
||||
this._listeningTo.push(listener);
|
||||
@ -78,7 +78,7 @@ const helperFunctions = {
|
||||
} else if (listener.object.removeEventListener) {
|
||||
listener.object.removeEventListener(listener.event, listener._cb);
|
||||
} else {
|
||||
listener.object.off(listener.event, listener._cb);
|
||||
listener.object.off(listener.event, listener._cb, listener.context);
|
||||
}
|
||||
|
||||
return listener;
|
||||
|
@ -28,6 +28,8 @@ import {
|
||||
} from 'utils/testing';
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
import { PREVIEW_ACTION_KEY } from '../../ui/preview/PreviewAction.js';
|
||||
|
||||
const ONE_MINUTE = 1000 * 60;
|
||||
const TEN_MINUTES = ONE_MINUTE * 10;
|
||||
const MAIN_IMAGE_CLASS = '.js-imageryView-image';
|
||||
@ -81,11 +83,10 @@ describe('The Imagery View Layouts', () => {
|
||||
const START = Date.now();
|
||||
const COUNT = 10;
|
||||
|
||||
// let resolveFunction;
|
||||
let originalRouterPath;
|
||||
let telemetryPromise;
|
||||
let telemetryPromiseResolve;
|
||||
let cleanupFirst;
|
||||
let previewAction;
|
||||
|
||||
let openmct;
|
||||
let parent;
|
||||
@ -172,8 +173,6 @@ describe('The Imagery View Layouts', () => {
|
||||
|
||||
// this setups up the app
|
||||
beforeEach((done) => {
|
||||
cleanupFirst = [];
|
||||
|
||||
openmct = createOpenMct();
|
||||
|
||||
telemetryPromise = new Promise((resolve) => {
|
||||
@ -193,6 +192,8 @@ describe('The Imagery View Layouts', () => {
|
||||
return telemetryPromise;
|
||||
});
|
||||
|
||||
previewAction = openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
|
||||
parent = document.createElement('div');
|
||||
parent.style.width = '640px';
|
||||
parent.style.height = '480px';
|
||||
@ -222,20 +223,11 @@ describe('The Imagery View Layouts', () => {
|
||||
openmct.startHeadless();
|
||||
});
|
||||
|
||||
afterEach((done) => {
|
||||
afterEach(async () => {
|
||||
openmct.router.path = originalRouterPath;
|
||||
|
||||
// Needs to be in a timeout because plots use a bunch of setTimeouts, some of which can resolve during or after
|
||||
// teardown, which causes problems
|
||||
// This is hacky, we should find a better approach here.
|
||||
setTimeout(() => {
|
||||
//Cleanup code that needs to happen before dom elements start being destroyed
|
||||
cleanupFirst.forEach((cleanup) => cleanup());
|
||||
cleanupFirst = [];
|
||||
document.body.removeChild(parent);
|
||||
|
||||
resetApplicationState(openmct).then(done).catch(done);
|
||||
});
|
||||
await resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it('should provide an imagery time strip view when in a time strip', () => {
|
||||
@ -609,7 +601,7 @@ describe('The Imagery View Layouts', () => {
|
||||
imageryTimeView.show(child);
|
||||
|
||||
componentView = imageryTimeView.getComponent().$refs.root;
|
||||
spyOn(componentView.previewAction, 'invoke').and.callThrough();
|
||||
spyOn(previewAction, 'invoke').and.callThrough();
|
||||
|
||||
return nextTick();
|
||||
});
|
||||
@ -633,13 +625,12 @@ describe('The Imagery View Layouts', () => {
|
||||
imageWrapper[2].dispatchEvent(mouseDownEvent);
|
||||
await nextTick();
|
||||
const timestamp = imageWrapper[2].id.replace('wrapper-', '');
|
||||
const mockInvoke = componentView.previewAction.invoke;
|
||||
// Make sure the function was called
|
||||
expect(mockInvoke).toHaveBeenCalled();
|
||||
expect(previewAction.invoke).toHaveBeenCalled();
|
||||
|
||||
// Get the arguments of the first call
|
||||
const firstArg = mockInvoke.calls.mostRecent().args[0];
|
||||
const secondArg = mockInvoke.calls.mostRecent().args[1];
|
||||
const firstArg = previewAction.invoke.calls.mostRecent().args[0];
|
||||
const secondArg = previewAction.invoke.calls.mostRecent().args[1];
|
||||
|
||||
// Compare the first argument
|
||||
expect(firstArg).toEqual([componentView.objectPath[0]]);
|
||||
|
@ -24,10 +24,12 @@ import { parseKeyString } from 'objectUtils';
|
||||
import { filter__proto__ } from 'utils/sanitization';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export default class ImportAsJSONAction {
|
||||
const IMPORT_FROM_JSON_ACTION_KEY = 'import.JSON';
|
||||
|
||||
class ImportFromJSONAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Import from JSON';
|
||||
this.key = 'import.JSON';
|
||||
this.key = IMPORT_FROM_JSON_ACTION_KEY;
|
||||
this.description = '';
|
||||
this.cssClass = 'icon-import';
|
||||
this.group = 'import';
|
||||
@ -405,3 +407,7 @@ export default class ImportAsJSONAction {
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
export { IMPORT_FROM_JSON_ACTION_KEY };
|
||||
|
||||
export default ImportFromJSONAction;
|
||||
|
@ -66,6 +66,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { NEW_TAB_ACTION_KEY } from '@/plugins/openInNewTabAction/openInNewTabAction.js';
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
provide() {
|
||||
@ -119,7 +122,7 @@ export default {
|
||||
'tc.endBound': timeBounds?.end,
|
||||
'tc.mode': 'fixed'
|
||||
};
|
||||
const newTabAction = this.openmct.actions.getAction('newTab');
|
||||
const newTabAction = this.openmct.actions.getAction(NEW_TAB_ACTION_KEY);
|
||||
// No view context needed, so pass undefined.
|
||||
// The urlParams arg will override the global time bounds with the data visualization
|
||||
// plot bounds.
|
||||
@ -127,7 +130,7 @@ export default {
|
||||
this.showMenu = false;
|
||||
},
|
||||
previewTelemetry() {
|
||||
const previewAction = this.openmct.actions.getAction('preview');
|
||||
const previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
previewAction.invoke([this.telemetryObject]);
|
||||
this.showMenu = false;
|
||||
}
|
||||
|
@ -181,3 +181,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-tag-applier__add-btn.c-icon-button.c-icon-button--major.icon-plus {
|
||||
color: $colorKey !important;
|
||||
}
|
||||
|
@ -20,10 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class LinkAction {
|
||||
const LINK_ACTION_KEY = 'link';
|
||||
|
||||
class LinkAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Create Link';
|
||||
this.key = 'link';
|
||||
this.key = LINK_ACTION_KEY;
|
||||
this.description = 'Create Link to object in another location.';
|
||||
this.cssClass = 'icon-link';
|
||||
this.group = 'action';
|
||||
@ -154,3 +156,7 @@ export default class LinkAction {
|
||||
this.transaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
export { LINK_ACTION_KEY };
|
||||
|
||||
export default LinkAction;
|
||||
|
@ -21,7 +21,7 @@
|
||||
*****************************************************************************/
|
||||
import { createOpenMct, getMockObjects, resetApplicationState } from 'utils/testing';
|
||||
|
||||
import LinkAction from './LinkAction.js';
|
||||
import { LINK_ACTION_KEY } from './LinkAction.js';
|
||||
import LinkActionPlugin from './plugin.js';
|
||||
|
||||
describe('The Link Action plugin', () => {
|
||||
@ -31,8 +31,7 @@ describe('The Link Action plugin', () => {
|
||||
let parentObject;
|
||||
let anotherParentObject;
|
||||
const ORIGINAL_PARENT_ID = 'original-parent-object';
|
||||
const LINK_ACITON_KEY = 'link';
|
||||
const LINK_ACITON_NAME = 'Create Link';
|
||||
const LINK_ACTION_NAME = 'Create Link';
|
||||
|
||||
beforeEach((done) => {
|
||||
const appHolder = document.createElement('div');
|
||||
@ -97,14 +96,14 @@ describe('The Link Action plugin', () => {
|
||||
it('should make the link action available for an appropriate domainObject', () => {
|
||||
const actionCollection = openmct.actions.getActionsCollection([childObject]);
|
||||
const visibleActions = actionCollection.getVisibleActions();
|
||||
linkAction = visibleActions.find((a) => a.key === LINK_ACITON_KEY);
|
||||
linkAction = visibleActions.find((a) => a.key === LINK_ACTION_KEY);
|
||||
|
||||
expect(linkAction.name).toEqual(LINK_ACITON_NAME);
|
||||
expect(linkAction.name).toEqual(LINK_ACTION_NAME);
|
||||
});
|
||||
|
||||
describe('when linking an object in a new parent', () => {
|
||||
beforeEach(() => {
|
||||
linkAction = new LinkAction(openmct);
|
||||
linkAction = openmct.actions.getAction(LINK_ACTION_KEY);
|
||||
linkAction.linkInNewParent(childObject, anotherParentObject);
|
||||
});
|
||||
|
||||
|
@ -19,10 +19,13 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
export default class MoveAction {
|
||||
|
||||
const MOVE_ACTION_KEY = 'move';
|
||||
|
||||
class MoveAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Move';
|
||||
this.key = 'move';
|
||||
this.key = MOVE_ACTION_KEY;
|
||||
this.description = 'Move this object from its containing object to another object.';
|
||||
this.cssClass = 'icon-move';
|
||||
this.group = 'action';
|
||||
@ -216,3 +219,7 @@ export default class MoveAction {
|
||||
this.transaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
export { MOVE_ACTION_KEY };
|
||||
|
||||
export default MoveAction;
|
||||
|
@ -19,13 +19,14 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import CreateAction from '@/plugins/formActions/CreateAction';
|
||||
|
||||
export default class NewFolderAction {
|
||||
const NEW_FOLDER_ACTION_KEY = 'newFolder';
|
||||
|
||||
class NewFolderAction {
|
||||
constructor(openmct) {
|
||||
this.type = 'folder';
|
||||
this.name = 'Add New Folder';
|
||||
this.key = 'newFolder';
|
||||
this.key = NEW_FOLDER_ACTION_KEY;
|
||||
this.description = 'Create a new folder';
|
||||
this.cssClass = 'icon-folder-new';
|
||||
this.group = 'action';
|
||||
@ -36,8 +37,8 @@ export default class NewFolderAction {
|
||||
|
||||
invoke(objectPath) {
|
||||
const parentDomainObject = objectPath[0];
|
||||
const createAction = new CreateAction(this._openmct, this.type, parentDomainObject);
|
||||
createAction.invoke();
|
||||
const createAction = this._openmct.actions.getAction('create');
|
||||
createAction.invoke(this.type, parentDomainObject);
|
||||
}
|
||||
|
||||
appliesTo(objectPath) {
|
||||
@ -47,3 +48,7 @@ export default class NewFolderAction {
|
||||
return domainObject.type === this.type && isPersistable;
|
||||
}
|
||||
}
|
||||
|
||||
export { NEW_FOLDER_ACTION_KEY };
|
||||
|
||||
export default NewFolderAction;
|
||||
|
@ -1,14 +1,15 @@
|
||||
import { addNotebookEntry } from '../utils/notebook-entries.js';
|
||||
import { getDefaultNotebook, getNotebookSectionAndPage } from '../utils/notebook-storage.js';
|
||||
|
||||
export default class CopyToNotebookAction {
|
||||
const COPY_TO_NOTEBOOK_ACTION_KEY = 'copyToNotebook';
|
||||
class CopyToNotebookAction {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
|
||||
this.cssClass = 'icon-duplicate';
|
||||
this.description = 'Copy value to notebook as an entry';
|
||||
this.group = 'action';
|
||||
this.key = 'copyToNotebook';
|
||||
this.key = COPY_TO_NOTEBOOK_ACTION_KEY;
|
||||
this.name = 'Copy to Notebook';
|
||||
this.priority = 1;
|
||||
}
|
||||
@ -49,3 +50,7 @@ export default class CopyToNotebookAction {
|
||||
return row.formattedValueForCopy && typeof row.formattedValueForCopy === 'function';
|
||||
}
|
||||
}
|
||||
|
||||
export { COPY_TO_NOTEBOOK_ACTION_KEY };
|
||||
|
||||
export default CopyToNotebookAction;
|
||||
|
@ -5,15 +5,16 @@ import { NOTEBOOK_TYPE, RESTRICTED_NOTEBOOK_TYPE } from '../notebook-constants.j
|
||||
const UNKNOWN_USER = 'Unknown';
|
||||
const UNKNOWN_TIME = 'Unknown';
|
||||
const ALLOWED_TYPES = [NOTEBOOK_TYPE, RESTRICTED_NOTEBOOK_TYPE];
|
||||
const EXPORT_NOTEBOOK_AS_TEXT_ACTION_KEY = 'exportNotebookAsText';
|
||||
|
||||
export default class ExportNotebookAsTextAction {
|
||||
class ExportNotebookAsTextAction {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
|
||||
this.cssClass = 'icon-export';
|
||||
this.description = 'Exports notebook contents as a text file';
|
||||
this.group = 'export';
|
||||
this.key = 'exportNotebookAsText';
|
||||
this.key = EXPORT_NOTEBOOK_AS_TEXT_ACTION_KEY;
|
||||
this.name = 'Export Notebook as Text';
|
||||
}
|
||||
|
||||
@ -179,3 +180,7 @@ export default class ExportNotebookAsTextAction {
|
||||
return this.onSave(changes, objectPath);
|
||||
}
|
||||
}
|
||||
|
||||
export { EXPORT_NOTEBOOK_AS_TEXT_ACTION_KEY };
|
||||
|
||||
export default ExportNotebookAsTextAction;
|
||||
|
@ -54,10 +54,10 @@ import Moment from 'moment';
|
||||
import mount from 'utils/mount';
|
||||
|
||||
import { objectPathToUrl } from '@/tools/url';
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins.js';
|
||||
import ImageExporter from '../../../exporters/ImageExporter.js';
|
||||
import PreviewAction from '../../../ui/preview/PreviewAction.js';
|
||||
import { updateNotebookImageDomainObject } from '../utils/notebook-image.js';
|
||||
import PainterroInstance from '../utils/painterroInstance.js';
|
||||
import RemoveDialog from '../utils/removeDialog.js';
|
||||
@ -393,10 +393,9 @@ export default {
|
||||
}
|
||||
},
|
||||
previewEmbed() {
|
||||
const self = this;
|
||||
const previewAction = new PreviewAction(self.openmct);
|
||||
const previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
this.openmct.objects
|
||||
.get(self.embed.domainObject.identifier)
|
||||
.get(this.embed.domainObject.identifier)
|
||||
.then((domainObject) => previewAction.invoke([domainObject]));
|
||||
},
|
||||
removeEmbed(success) {
|
||||
|
@ -20,10 +20,13 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import { objectPathToUrl } from '/src/tools/url.js';
|
||||
export default class OpenInNewTab {
|
||||
|
||||
const NEW_TAB_ACTION_KEY = 'newTab';
|
||||
|
||||
class OpenInNewTab {
|
||||
constructor(openmct) {
|
||||
this.name = 'Open In New Tab';
|
||||
this.key = 'newTab';
|
||||
this.key = NEW_TAB_ACTION_KEY;
|
||||
this.description = 'Open in a new browser tab';
|
||||
this.group = 'windowing';
|
||||
this.priority = 10;
|
||||
@ -54,3 +57,7 @@ export default class OpenInNewTab {
|
||||
window.open(url, undefined, 'noopener');
|
||||
}
|
||||
}
|
||||
|
||||
export { NEW_TAB_ACTION_KEY };
|
||||
|
||||
export default OpenInNewTab;
|
||||
|
@ -180,6 +180,7 @@ import _ from 'lodash';
|
||||
import { useEventBus } from 'utils/useEventBus';
|
||||
import { toRaw } from 'vue';
|
||||
|
||||
import { MODES } from '../../api/time/constants';
|
||||
import TagEditorClassNames from '../inspectorViews/annotations/tags/TagEditorClassNames.js';
|
||||
import XAxis from './axis/XAxis.vue';
|
||||
import YAxis from './axis/YAxis.vue';
|
||||
@ -803,12 +804,12 @@ export default {
|
||||
this.synchronizeIfBoundsMatch();
|
||||
this.loadMoreData(newRange, true);
|
||||
} else {
|
||||
// If we're not panning or zooming (time conductor and plot x-axis times are not out of sync)
|
||||
// If we're not paused, panning or zooming (time conductor and plot x-axis times are not out of sync)
|
||||
// Drop any data that is more than 1x (max-min) before min.
|
||||
// Limit these purges to once a second.
|
||||
const isPanningOrZooming = this.isTimeOutOfSync;
|
||||
const purgeRecords =
|
||||
!isPanningOrZooming && (!this.nextPurge || this.nextPurge < Date.now());
|
||||
!this.isFrozen && !isPanningOrZooming && (!this.nextPurge || this.nextPurge < Date.now());
|
||||
if (purgeRecords) {
|
||||
const keepRange = {
|
||||
min: newRange.min - (newRange.max - newRange.min),
|
||||
@ -1896,7 +1897,7 @@ export default {
|
||||
|
||||
synchronizeTimeConductor() {
|
||||
const range = this.config.xAxis.get('displayRange');
|
||||
this.timeContext.bounds({
|
||||
this.timeContext.setMode(MODES.fixed, {
|
||||
start: range.min,
|
||||
end: range.max
|
||||
});
|
||||
|
@ -550,7 +550,7 @@ export default {
|
||||
this.canvas = mainCanvas;
|
||||
this.overlay = overlayCanvas;
|
||||
this.drawAPI = DrawLoader.getDrawAPI(mainCanvas, overlayCanvas);
|
||||
if (this.drawAPI) {
|
||||
if (this.drawAPI?.on) {
|
||||
this.listenTo(this.drawAPI, 'error', this.fallbackToCanvas, this);
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,10 @@ import { MARKER_SHAPES } from './MarkerShapes.js';
|
||||
* @param {CanvasElement} canvas the canvas object to render upon
|
||||
* @throws {Error} an error is thrown if Canvas's 2D API is unavailable
|
||||
*/
|
||||
function Draw2D(canvas) {
|
||||
class Draw2D extends EventEmitter {
|
||||
constructor(canvas) {
|
||||
super();
|
||||
eventHelpers.extend(this);
|
||||
this.canvas = canvas;
|
||||
this.c2d = canvas.getContext('2d');
|
||||
this.width = canvas.width;
|
||||
@ -50,23 +53,17 @@ function Draw2D(canvas) {
|
||||
if (!this.c2d) {
|
||||
throw new Error('Canvas 2d API unavailable.');
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(Draw2D.prototype, EventEmitter.prototype);
|
||||
eventHelpers.extend(Draw2D.prototype);
|
||||
|
||||
// Convert from logical to physical x coordinates
|
||||
Draw2D.prototype.x = function (v) {
|
||||
}
|
||||
// Convert from logical to physical x coordinates
|
||||
x(v) {
|
||||
return ((v - this.origin[0]) / this.dimensions[0]) * this.width;
|
||||
};
|
||||
|
||||
// Convert from logical to physical y coordinates
|
||||
Draw2D.prototype.y = function (v) {
|
||||
}
|
||||
// Convert from logical to physical y coordinates
|
||||
y(v) {
|
||||
return this.height - ((v - this.origin[1]) / this.dimensions[1]) * this.height;
|
||||
};
|
||||
|
||||
// Set the color to be used for drawing operations
|
||||
Draw2D.prototype.setColor = function (color) {
|
||||
}
|
||||
// Set the color to be used for drawing operations
|
||||
setColor(color) {
|
||||
const mappedColor = color
|
||||
.map(function (c, i) {
|
||||
return i < 3 ? Math.floor(c * 255) : c;
|
||||
@ -74,20 +71,17 @@ Draw2D.prototype.setColor = function (color) {
|
||||
.join(',');
|
||||
this.c2d.strokeStyle = 'rgba(' + mappedColor + ')';
|
||||
this.c2d.fillStyle = 'rgba(' + mappedColor + ')';
|
||||
};
|
||||
|
||||
Draw2D.prototype.clear = function () {
|
||||
}
|
||||
clear() {
|
||||
this.width = this.canvas.width = this.canvas.offsetWidth;
|
||||
this.height = this.canvas.height = this.canvas.offsetHeight;
|
||||
this.c2d.clearRect(0, 0, this.width, this.height);
|
||||
};
|
||||
|
||||
Draw2D.prototype.setDimensions = function (newDimensions, newOrigin) {
|
||||
}
|
||||
setDimensions(newDimensions, newOrigin) {
|
||||
this.dimensions = newDimensions;
|
||||
this.origin = newOrigin;
|
||||
};
|
||||
|
||||
Draw2D.prototype.drawLine = function (buf, color, points) {
|
||||
}
|
||||
drawLine(buf, color, points) {
|
||||
let i;
|
||||
|
||||
this.setColor(color);
|
||||
@ -108,9 +102,8 @@ Draw2D.prototype.drawLine = function (buf, color, points) {
|
||||
|
||||
// ...before finally drawing it.
|
||||
this.c2d.stroke();
|
||||
};
|
||||
|
||||
Draw2D.prototype.drawSquare = function (min, max, color) {
|
||||
}
|
||||
drawSquare(min, max, color) {
|
||||
const x1 = this.x(min[0]);
|
||||
const y1 = this.y(min[1]);
|
||||
const w = this.x(max[0]) - x1;
|
||||
@ -118,9 +111,8 @@ Draw2D.prototype.drawSquare = function (min, max, color) {
|
||||
|
||||
this.setColor(color);
|
||||
this.c2d.fillRect(x1, y1, w, h);
|
||||
};
|
||||
|
||||
Draw2D.prototype.drawPoints = function (buf, color, points, pointSize, shape) {
|
||||
}
|
||||
drawPoints(buf, color, points, pointSize, shape) {
|
||||
const drawC2DShape = MARKER_SHAPES[shape].drawC2D.bind(this);
|
||||
|
||||
this.setColor(color);
|
||||
@ -128,16 +120,14 @@ Draw2D.prototype.drawPoints = function (buf, color, points, pointSize, shape) {
|
||||
for (let i = 0; i < points; i++) {
|
||||
drawC2DShape(this.x(buf[i * 2]), this.y(buf[i * 2 + 1]), pointSize);
|
||||
}
|
||||
};
|
||||
|
||||
Draw2D.prototype.drawLimitPoint = function (x, y, size) {
|
||||
}
|
||||
drawLimitPoint(x, y, size) {
|
||||
this.c2d.fillRect(x + size, y, size, size);
|
||||
this.c2d.fillRect(x, y + size, size, size);
|
||||
this.c2d.fillRect(x - size, y, size, size);
|
||||
this.c2d.fillRect(x, y - size, size, size);
|
||||
};
|
||||
|
||||
Draw2D.prototype.drawLimitPoints = function (points, color, pointSize) {
|
||||
}
|
||||
drawLimitPoints(points, color, pointSize) {
|
||||
const limitSize = pointSize * 2;
|
||||
const offset = limitSize / 2;
|
||||
|
||||
@ -146,6 +136,7 @@ Draw2D.prototype.drawLimitPoints = function (points, color, pointSize) {
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
this.drawLimitPoint(this.x(points[i].x) - offset, this.y(points[i].y) - offset, limitSize);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default Draw2D;
|
||||
|
@ -83,7 +83,10 @@ const VERTEX_SHADER = `
|
||||
* @param {CanvasElement} canvas the canvas object to render upon
|
||||
* @throws {Error} an error is thrown if WebGL is unavailable.
|
||||
*/
|
||||
function DrawWebGL(canvas, overlay) {
|
||||
class DrawWebGL extends EventEmitter {
|
||||
constructor(canvas, overlay) {
|
||||
super();
|
||||
eventHelpers.extend(this);
|
||||
this.canvas = canvas;
|
||||
this.gl =
|
||||
this.canvas.getContext('webgl', { preserveDrawingBuffer: true }) ||
|
||||
@ -103,19 +106,14 @@ function DrawWebGL(canvas, overlay) {
|
||||
this.initContext();
|
||||
|
||||
this.listenTo(this.canvas, 'webglcontextlost', this.onContextLost, this);
|
||||
}
|
||||
|
||||
Object.assign(DrawWebGL.prototype, EventEmitter.prototype);
|
||||
eventHelpers.extend(DrawWebGL.prototype);
|
||||
|
||||
DrawWebGL.prototype.onContextLost = function (event) {
|
||||
}
|
||||
onContextLost(event) {
|
||||
this.emit('error');
|
||||
this.isContextLost = true;
|
||||
this.destroy();
|
||||
// TODO re-initialize and re-draw on context restored
|
||||
};
|
||||
|
||||
DrawWebGL.prototype.initContext = function () {
|
||||
}
|
||||
initContext() {
|
||||
// Initialize shaders
|
||||
this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
|
||||
this.gl.shaderSource(this.vertexShader, VERTEX_SHADER);
|
||||
@ -149,9 +147,8 @@ DrawWebGL.prototype.initContext = function () {
|
||||
// Enable blending, for smoothness
|
||||
this.gl.enable(this.gl.BLEND);
|
||||
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
|
||||
};
|
||||
|
||||
DrawWebGL.prototype.destroy = function () {
|
||||
}
|
||||
destroy() {
|
||||
// Lose the context and delete all associated resources
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#lose_contexts_eagerly
|
||||
this.gl?.getExtension('WEBGL_lose_context')?.loseContext();
|
||||
@ -168,19 +165,16 @@ DrawWebGL.prototype.destroy = function () {
|
||||
this.stopListening();
|
||||
this.canvas = undefined;
|
||||
this.overlay = undefined;
|
||||
};
|
||||
|
||||
// Convert from logical to physical x coordinates
|
||||
DrawWebGL.prototype.x = function (v) {
|
||||
}
|
||||
// Convert from logical to physical x coordinates
|
||||
x(v) {
|
||||
return ((v - this.origin[0]) / this.dimensions[0]) * this.width;
|
||||
};
|
||||
|
||||
// Convert from logical to physical y coordinates
|
||||
DrawWebGL.prototype.y = function (v) {
|
||||
}
|
||||
// Convert from logical to physical y coordinates
|
||||
y(v) {
|
||||
return this.height - ((v - this.origin[1]) / this.dimensions[1]) * this.height;
|
||||
};
|
||||
|
||||
DrawWebGL.prototype.doDraw = function (drawType, buf, color, points, shape) {
|
||||
}
|
||||
doDraw(drawType, buf, color, points, shape) {
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
@ -195,9 +189,8 @@ DrawWebGL.prototype.doDraw = function (drawType, buf, color, points, shape) {
|
||||
if (points !== 0) {
|
||||
this.gl.drawArrays(drawType, 0, points);
|
||||
}
|
||||
};
|
||||
|
||||
DrawWebGL.prototype.clear = function () {
|
||||
}
|
||||
clear() {
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
@ -211,16 +204,15 @@ DrawWebGL.prototype.clear = function () {
|
||||
// resolution than the canvas we requested.
|
||||
this.gl.viewport(0, 0, this.gl.drawingBufferWidth, this.gl.drawingBufferHeight);
|
||||
this.gl.clear(this.gl.COLOR_BUFFER_BIT + this.gl.DEPTH_BUFFER_BIT);
|
||||
};
|
||||
|
||||
/**
|
||||
}
|
||||
/**
|
||||
* Set the logical boundaries of the chart.
|
||||
* @param {number[]} dimensions the horizontal and
|
||||
* vertical dimensions of the chart
|
||||
* @param {number[]} origin the horizontal/vertical
|
||||
* origin of the chart
|
||||
*/
|
||||
DrawWebGL.prototype.setDimensions = function (dimensions, origin) {
|
||||
setDimensions(dimensions, origin) {
|
||||
this.dimensions = dimensions;
|
||||
this.origin = origin;
|
||||
if (this.isContextLost) {
|
||||
@ -231,9 +223,8 @@ DrawWebGL.prototype.setDimensions = function (dimensions, origin) {
|
||||
this.gl?.uniform2fv(this.uDimensions, dimensions);
|
||||
this.gl?.uniform2fv(this.uOrigin, origin);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
}
|
||||
/**
|
||||
* Draw the supplied buffer as a line strip (a sequence
|
||||
* of line segments), in the chosen color.
|
||||
* @param {Float32Array} buf the line strip to draw,
|
||||
@ -243,28 +234,26 @@ DrawWebGL.prototype.setDimensions = function (dimensions, origin) {
|
||||
* is in the range of 0.0-1.0
|
||||
* @param {number} points the number of points to draw
|
||||
*/
|
||||
DrawWebGL.prototype.drawLine = function (buf, color, points) {
|
||||
drawLine(buf, color, points) {
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.doDraw(this.gl.LINE_STRIP, buf, color, points);
|
||||
};
|
||||
|
||||
/**
|
||||
}
|
||||
/**
|
||||
* Draw the buffer as points.
|
||||
*
|
||||
*/
|
||||
DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize, shape) {
|
||||
drawPoints(buf, color, points, pointSize, shape) {
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.gl.uniform1f(this.uPointSize, pointSize);
|
||||
this.doDraw(this.gl.POINTS, buf, color, points, shape);
|
||||
};
|
||||
|
||||
/**
|
||||
}
|
||||
/**
|
||||
* Draw a rectangle extending from one corner to another,
|
||||
* in the chosen color.
|
||||
* @param {number[]} min the first corner of the rectangle
|
||||
@ -273,7 +262,7 @@ DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize, shape)
|
||||
* the rectangle, as an RGBA color where each element
|
||||
* is in the range of 0.0-1.0
|
||||
*/
|
||||
DrawWebGL.prototype.drawSquare = function (min, max, color) {
|
||||
drawSquare(min, max, color) {
|
||||
if (this.isContextLost) {
|
||||
return;
|
||||
}
|
||||
@ -284,16 +273,14 @@ DrawWebGL.prototype.drawSquare = function (min, max, color) {
|
||||
color,
|
||||
4
|
||||
);
|
||||
};
|
||||
|
||||
DrawWebGL.prototype.drawLimitPoint = function (x, y, size) {
|
||||
}
|
||||
drawLimitPoint(x, y, size) {
|
||||
this.c2d.fillRect(x + size, y, size, size);
|
||||
this.c2d.fillRect(x, y + size, size, size);
|
||||
this.c2d.fillRect(x - size, y, size, size);
|
||||
this.c2d.fillRect(x, y - size, size, size);
|
||||
};
|
||||
|
||||
DrawWebGL.prototype.drawLimitPoints = function (points, color, pointSize) {
|
||||
}
|
||||
drawLimitPoints(points, color, pointSize) {
|
||||
const limitSize = pointSize * 2;
|
||||
const offset = limitSize / 2;
|
||||
|
||||
@ -308,6 +295,7 @@ DrawWebGL.prototype.drawLimitPoints = function (points, color, pointSize) {
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
this.drawLimitPoint(this.x(points[i].x) - offset, this.y(points[i].y) - offset, limitSize);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default DrawWebGL;
|
||||
|
@ -37,7 +37,7 @@ const helperFunctions = {
|
||||
if (object.addEventListener) {
|
||||
object.addEventListener(event, listener._cb);
|
||||
} else {
|
||||
object.on(event, listener._cb);
|
||||
object.on(event, listener._cb, listener.context);
|
||||
}
|
||||
|
||||
this._listeningTo.push(listener);
|
||||
@ -74,7 +74,7 @@ const helperFunctions = {
|
||||
} else if (listener.object.removeEventListener) {
|
||||
listener.object.removeEventListener(listener.event, listener._cb);
|
||||
} else {
|
||||
listener.object.off(listener.event, listener._cb);
|
||||
listener.object.off(listener.event, listener._cb, listener.context);
|
||||
}
|
||||
|
||||
return listener;
|
||||
|
@ -19,10 +19,13 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
export default class ReloadAction {
|
||||
|
||||
const RELOAD_ACTION_KEY = 'reload';
|
||||
|
||||
class ReloadAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Reload';
|
||||
this.key = 'reload';
|
||||
this.key = RELOAD_ACTION_KEY;
|
||||
this.description = 'Reload this object and its children';
|
||||
this.group = 'action';
|
||||
this.priority = 10;
|
||||
@ -35,3 +38,7 @@ export default class ReloadAction {
|
||||
this.openmct.objectViews.emit('reload', domainObject);
|
||||
}
|
||||
}
|
||||
|
||||
export { RELOAD_ACTION_KEY };
|
||||
|
||||
export default ReloadAction;
|
||||
|
@ -21,13 +21,14 @@
|
||||
*****************************************************************************/
|
||||
|
||||
const SPECIAL_MESSAGE_TYPES = ['layout', 'flexible-layout'];
|
||||
const REMOVE_ACTION_KEY = 'remove';
|
||||
|
||||
export default class RemoveAction {
|
||||
class RemoveAction {
|
||||
#transaction;
|
||||
|
||||
constructor(openmct) {
|
||||
this.name = 'Remove';
|
||||
this.key = 'remove';
|
||||
this.key = REMOVE_ACTION_KEY;
|
||||
this.description = 'Remove this object from its containing object.';
|
||||
this.cssClass = 'icon-trash';
|
||||
this.group = 'action';
|
||||
@ -162,3 +163,7 @@ export default class RemoveAction {
|
||||
this.#transaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
export { REMOVE_ACTION_KEY };
|
||||
|
||||
export default RemoveAction;
|
||||
|
@ -41,7 +41,7 @@ const helperFunctions = {
|
||||
} else if (object.addEventListener) {
|
||||
object.addEventListener(event, listener._cb);
|
||||
} else {
|
||||
object.on(event, listener._cb);
|
||||
object.on(event, listener._cb, listener.context);
|
||||
}
|
||||
|
||||
this._listeningTo.push(listener);
|
||||
@ -78,7 +78,7 @@ const helperFunctions = {
|
||||
} else if (listener.object.removeEventListener) {
|
||||
listener.object.removeEventListener(listener.event, listener._cb);
|
||||
} else {
|
||||
listener.object.off(listener.event, listener._cb);
|
||||
listener.object.off(listener.event, listener._cb, listener.context);
|
||||
}
|
||||
|
||||
return listener;
|
||||
|
@ -91,7 +91,6 @@ import _ from 'lodash';
|
||||
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins.js';
|
||||
import ObjectView from '../../../ui/components/ObjectView.vue';
|
||||
import RemoveAction from '../../remove/RemoveAction.js';
|
||||
|
||||
const unknownObjectType = {
|
||||
definition: {
|
||||
@ -171,7 +170,6 @@ export default {
|
||||
this.updateCurrentTab = this.updateCurrentTab.bind(this);
|
||||
this.openmct.router.on('change:params', this.updateCurrentTab);
|
||||
|
||||
this.RemoveAction = new RemoveAction(this.openmct);
|
||||
document.addEventListener('dragstart', this.dragstart);
|
||||
document.addEventListener('dragend', this.dragend);
|
||||
},
|
||||
|
@ -19,6 +19,8 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import { VIEW_DATUM_ACTION_KEY } from '@/plugins/viewDatumAction/ViewDatumAction.js';
|
||||
import { VIEW_HISTORICAL_DATA_ACTION_KEY } from '@/ui/preview/ViewHistoricalDataAction.js';
|
||||
|
||||
export default class TelemetryTableRow {
|
||||
constructor(datum, columns, objectKeyString, limitEvaluator, inPlaceUpdateKey) {
|
||||
@ -86,7 +88,7 @@ export default class TelemetryTableRow {
|
||||
}
|
||||
|
||||
getContextMenuActions() {
|
||||
return ['viewDatumAction', 'viewHistoricalData'];
|
||||
return [VIEW_DATUM_ACTION_KEY, VIEW_HISTORICAL_DATA_ACTION_KEY];
|
||||
}
|
||||
|
||||
updateWithDatum(updatesToDatum) {
|
||||
|
@ -561,6 +561,9 @@ export default {
|
||||
|
||||
this.table.initialize();
|
||||
this.rescaleToContainer();
|
||||
|
||||
// Scroll to the top of the table after loading
|
||||
this.addToAfterLoadActions(this.scroll);
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.table.off('object-added', this.addObject);
|
||||
|
@ -96,6 +96,7 @@
|
||||
max="59"
|
||||
title="Enter 0 - 59"
|
||||
step="1"
|
||||
aria-label="End offset minutes"
|
||||
@change="validate()"
|
||||
@keyup="validate()"
|
||||
@focusin="selectAll($event)"
|
||||
|
@ -264,7 +264,6 @@
|
||||
- 'Completed', 'Aborted', 'Skipped' : itemState.<that state>
|
||||
*/
|
||||
&.--is-not-started {
|
||||
|
||||
}
|
||||
|
||||
&.--is-in-progress {
|
||||
@ -282,7 +281,6 @@
|
||||
&.--is-overdue,
|
||||
&.--is-incomplete {
|
||||
@include showTliGraphic('alert-triangle');
|
||||
|
||||
}
|
||||
|
||||
&.--is-completed {
|
||||
|
@ -20,10 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class PauseTimerAction {
|
||||
const PAUSE_TIMER_ACTION_KEY = 'timer.pause';
|
||||
|
||||
class PauseTimerAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Pause';
|
||||
this.key = 'timer.pause';
|
||||
this.key = PAUSE_TIMER_ACTION_KEY;
|
||||
this.description = 'Pause the currently displayed timer';
|
||||
this.group = 'view';
|
||||
this.cssClass = 'icon-pause';
|
||||
@ -59,3 +61,7 @@ export default class PauseTimerAction {
|
||||
: domainObject.type === 'timer' && timerState === 'started';
|
||||
}
|
||||
}
|
||||
|
||||
export { PAUSE_TIMER_ACTION_KEY };
|
||||
|
||||
export default PauseTimerAction;
|
||||
|
@ -20,10 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class RestartTimerAction {
|
||||
const RESTART_TIMER_ACTION_KEY = 'timer.restart';
|
||||
|
||||
class RestartTimerAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Restart at 0';
|
||||
this.key = 'timer.restart';
|
||||
this.key = RESTART_TIMER_ACTION_KEY;
|
||||
this.description = 'Restart the currently displayed timer';
|
||||
this.group = 'view';
|
||||
this.cssClass = 'icon-refresh';
|
||||
@ -60,3 +62,7 @@ export default class RestartTimerAction {
|
||||
: domainObject.type === 'timer' && timerState !== 'stopped';
|
||||
}
|
||||
}
|
||||
|
||||
export { RESTART_TIMER_ACTION_KEY };
|
||||
|
||||
export default RestartTimerAction;
|
||||
|
@ -20,10 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class StartTimerAction {
|
||||
const START_TIMER_ACTION_KEY = 'timer.start';
|
||||
|
||||
class StartTimerAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Start';
|
||||
this.key = 'timer.start';
|
||||
this.key = START_TIMER_ACTION_KEY;
|
||||
this.description = 'Start the currently displayed timer';
|
||||
this.group = 'view';
|
||||
this.cssClass = 'icon-play';
|
||||
@ -72,3 +74,7 @@ export default class StartTimerAction {
|
||||
: domainObject.type === 'timer' && timerState !== 'started';
|
||||
}
|
||||
}
|
||||
|
||||
export { START_TIMER_ACTION_KEY };
|
||||
|
||||
export default StartTimerAction;
|
||||
|
@ -20,10 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class StopTimerAction {
|
||||
const STOP_TIMER_ACTION_KEY = 'timer.stop';
|
||||
|
||||
class StopTimerAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Stop';
|
||||
this.key = 'timer.stop';
|
||||
this.key = STOP_TIMER_ACTION_KEY;
|
||||
this.description = 'Stop the currently displayed timer';
|
||||
this.group = 'view';
|
||||
this.cssClass = 'icon-box-round-corners';
|
||||
@ -60,3 +62,7 @@ export default class StopTimerAction {
|
||||
: domainObject.type === 'timer' && timerState !== 'stopped';
|
||||
}
|
||||
}
|
||||
|
||||
export { STOP_TIMER_ACTION_KEY };
|
||||
|
||||
export default StopTimerAction;
|
||||
|
@ -24,10 +24,12 @@ import mount from 'utils/mount';
|
||||
|
||||
import MetadataListView from './components/MetadataList.vue';
|
||||
|
||||
export default class ViewDatumAction {
|
||||
const VIEW_DATUM_ACTION_KEY = 'viewDatumAction';
|
||||
|
||||
class ViewDatumAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'View Full Datum';
|
||||
this.key = 'viewDatumAction';
|
||||
this.key = VIEW_DATUM_ACTION_KEY;
|
||||
this.description = 'View full value of datum received';
|
||||
this.cssClass = 'icon-object';
|
||||
|
||||
@ -76,3 +78,7 @@ export default class ViewDatumAction {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export { VIEW_DATUM_ACTION_KEY };
|
||||
|
||||
export default ViewDatumAction;
|
||||
|
@ -24,14 +24,16 @@ import mount from 'utils/mount';
|
||||
|
||||
import PreviewContainer from '@/ui/preview/PreviewContainer.vue';
|
||||
|
||||
export default class ViewLargeAction {
|
||||
const VIEW_LARGE_ACTION_KEY = 'large.view';
|
||||
|
||||
class ViewLargeAction {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
|
||||
this.cssClass = 'icon-items-expand';
|
||||
this.description = 'View Large';
|
||||
this.group = 'windowing';
|
||||
this.key = 'large.view';
|
||||
this.key = VIEW_LARGE_ACTION_KEY;
|
||||
this.name = 'Large View';
|
||||
this.priority = 1;
|
||||
this.showInStatusBar = true;
|
||||
@ -107,3 +109,7 @@ export default class ViewLargeAction {
|
||||
return this.preview.$el;
|
||||
}
|
||||
}
|
||||
|
||||
export { VIEW_LARGE_ACTION_KEY };
|
||||
|
||||
export default ViewLargeAction;
|
||||
|
@ -24,7 +24,7 @@
|
||||
// Fonts
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto+Condensed&display=swap'); // Header font, Roboto Condensed. This is an alternative to the DIN Alt font, which is not available on Google Fonts.
|
||||
@import url('https://fonts.googleapis.com/css2?family=Exo:ital,wght@0,100..900;1,100..900&display=swap'); // Body Font, Exo
|
||||
@import url('https://fonts.googleapis.com/css2?family=Chakra+Petch&family=Roboto+Condensed&display=swap');// Temporary numeric font, Chakra Petch (need to import local font instead).
|
||||
@import url('https://fonts.googleapis.com/css2?family=Chakra+Petch&family=Roboto+Condensed&display=swap'); // Temporary numeric font, Chakra Petch (need to import local font instead).
|
||||
$heroFont: 'Teko', sans-serif;
|
||||
$headerFont: 'Cabin Condensed', sans-serif;
|
||||
$bodyFont: 'Exo', sans-serif;
|
||||
@ -45,7 +45,7 @@ $numericFont: 'Chakra Petch', sans-serif;
|
||||
font-size: $size;
|
||||
}
|
||||
|
||||
@mixin numericFont($size: 1em){
|
||||
@mixin numericFont($size: 1em) {
|
||||
font-family: $numericFont;
|
||||
font-size: $size;
|
||||
}
|
||||
@ -70,16 +70,17 @@ $numericFont: 'Chakra Petch', sans-serif;
|
||||
}
|
||||
|
||||
@mixin themedButton($c: $colorBtnBg) {
|
||||
background: radial-gradient(rgba($c, 1) , rgba($c, .7));
|
||||
background: radial-gradient(rgba($c, 1), rgba($c, 0.7));
|
||||
box-shadow: rgba(black, 0.5) 0 0.5px 2px;
|
||||
}
|
||||
|
||||
@mixin telemetryView(){
|
||||
@mixin telemetryView() {
|
||||
border: 1px solid $colorBodyFg;
|
||||
border-radius: $controlCr;
|
||||
}
|
||||
|
||||
@mixin browseFrameBorder() { // Used on main object container to add highlighted corners to non-hidden frames.
|
||||
@mixin browseFrameBorder() {
|
||||
// Used on main object container to add highlighted corners to non-hidden frames.
|
||||
border-image: radial-gradient(circle, #575757, #6c6c6c, #818181, #979797, #aeaeae);
|
||||
border-style: solid;
|
||||
padding: 10px;
|
||||
@ -93,7 +94,7 @@ $numericFont: 'Chakra Petch', sans-serif;
|
||||
linear-gradient(to bottom, $browseFrameCornerColor, transparent $browseFrameCornerWidth) 100% 0,
|
||||
linear-gradient(to top, $browseFrameCornerColor, transparent $browseFrameCornerWidth) 0 100%,
|
||||
linear-gradient(to top, $browseFrameCornerColor, transparent $browseFrameCornerWidth) 100% 100%,
|
||||
rgb(0, 0, 0, .4);
|
||||
rgb(0, 0, 0, 0.4);
|
||||
|
||||
background-repeat: no-repeat;
|
||||
background-size: 35px 35px;
|
||||
@ -101,10 +102,9 @@ $numericFont: 'Chakra Petch', sans-serif;
|
||||
box-shadow: 0px 0px 20px 2px rgb(140 140 140 / 20%);
|
||||
}
|
||||
|
||||
|
||||
// Functions
|
||||
@function buttonBg($c: $colorBtnBg) {
|
||||
@return radial-gradient(rgba($colorBodyBg, 1) , rgba($colorBodyBg, .6));
|
||||
@return radial-gradient(rgba($colorBodyBg, 1), rgba($colorBodyBg, 0.6));
|
||||
}
|
||||
|
||||
@function pullForward($val, $amt) {
|
||||
@ -124,26 +124,28 @@ $shdwBtns: rgba(black, 0.2) 0 1px 2px;
|
||||
$shdwBtnsOverlay: rgba(black, 0.5) 0 1px 5px;
|
||||
|
||||
// Base colors
|
||||
$colorBodyBg: #17171B;
|
||||
$colorBodyBg: #17171b;
|
||||
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
|
||||
$colorBodyBgSubtleHov: pullForward($colorBodyBg, 10%);
|
||||
$colorBodyFg: #aaaaaa;
|
||||
$colorBodyFgSubtle: #9c9c9c;
|
||||
$colorBodyFgEm: #fff;
|
||||
$colorGenBg: #222;
|
||||
$colorHeadBg: rgba($colorBodyBg, .5);
|
||||
$colorHeadBg: rgba($colorBodyBg, 0.5);
|
||||
$colorHeadFg: $colorBodyFg;
|
||||
$colorKey: #1C67E3;
|
||||
$colorKey: #1c67e3;
|
||||
$colorKeyBg: #015fca;
|
||||
$colorKeyFg: #fff; // Darker version of colorKey for use in major buttons
|
||||
$colorKeyHov: lighten($colorKey, 10%);
|
||||
$colorKeyBgHov: lighten($colorKeyBg, 10%);
|
||||
$colorKeyFilter: invert(36%) sepia(10%) saturate(2512%) hue-rotate(170deg) brightness(100%) contrast(200%);
|
||||
$colorKeyFilter: invert(36%) sepia(10%) saturate(2512%) hue-rotate(170deg) brightness(100%)
|
||||
contrast(200%);
|
||||
$colorKeyFilterHov: invert(63%) sepia(88%) saturate(3029%) hue-rotate(154deg) brightness(101%)
|
||||
contrast(100%);
|
||||
$colorKeySelectedBg: $colorKey;
|
||||
$uiColor: #0093ff; // Resize bars, splitter bars, etc.
|
||||
$colorInteriorBorder: rgba($colorBodyFg, 0.2);
|
||||
$colorInteriorBorderNotebook: rgba($colorBodyFg, 0.5);
|
||||
$colorA: #ccc;
|
||||
$colorAHov: #fff;
|
||||
$filterHov: brightness(1.3) contrast(1.5); // Tree, location items
|
||||
@ -173,11 +175,14 @@ $sideBarHeaderFg: rgba($colorBodyFg, 0.7);
|
||||
$colorStatusFg: #888;
|
||||
$colorStatusDefault: #ccc;
|
||||
$colorStatusInfo: #60ba7b;
|
||||
$colorStatusInfoFilter: invert(58%) sepia(44%) saturate(405%) hue-rotate(85deg) brightness(102%) contrast(92%);
|
||||
$colorStatusInfoFilter: invert(58%) sepia(44%) saturate(405%) hue-rotate(85deg) brightness(102%)
|
||||
contrast(92%);
|
||||
$colorStatusAlert: #ffb66c;
|
||||
$colorStatusAlertFilter: invert(78%) sepia(26%) saturate(1160%) hue-rotate(324deg) brightness(107%) contrast(101%);
|
||||
$colorStatusAlertFilter: invert(78%) sepia(26%) saturate(1160%) hue-rotate(324deg) brightness(107%)
|
||||
contrast(101%);
|
||||
$colorStatusError: #da0004;
|
||||
$colorStatusErrorFilter: invert(10%) sepia(96%) saturate(4360%) hue-rotate(351deg) brightness(111%) contrast(115%);
|
||||
$colorStatusErrorFilter: invert(10%) sepia(96%) saturate(4360%) hue-rotate(351deg) brightness(111%)
|
||||
contrast(115%);
|
||||
$colorStatusBtnBg: #666; // Where is this used?
|
||||
$colorStatusPartialBg: #3f5e8b;
|
||||
$colorStatusCompleteBg: #457638;
|
||||
@ -235,7 +240,7 @@ $timeConductorActivePanBg: #226074;
|
||||
|
||||
// Browse
|
||||
$browseFrameColor: pullForward($colorBodyBg, 10%);
|
||||
$browseFrameBorder: 1px solid rgb(89, 89, 89, .4); // Frames in Disp and Flex Layouts when frame is showing
|
||||
$browseFrameBorder: 1px solid rgb(89, 89, 89, 0.4); // Frames in Disp and Flex Layouts when frame is showing
|
||||
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
|
||||
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
|
||||
$filterItemHoverFg: brightness(1.2) contrast(1.1);
|
||||
@ -252,7 +257,10 @@ $browseFrameCornerColor: $colorKey 4px; //Color used for the corners of frames
|
||||
$editUIColor: $uiColor; // Base color
|
||||
$editUIColorBg: $editUIColor;
|
||||
$editUIColorFg: #fff;
|
||||
$editUIColorHov: pullForward(saturate($uiColor, 10%), 10%); // Hover color when $editUIColor is applied as a base color
|
||||
$editUIColorHov: pullForward(
|
||||
saturate($uiColor, 10%),
|
||||
10%
|
||||
); // Hover color when $editUIColor is applied as a base color
|
||||
$editUIBaseColor: #344b8d; // Base color, toolbar bg
|
||||
$editUIBaseColorHov: pullForward($editUIBaseColor, 20%);
|
||||
$editUIBaseColorFg: #ffffff; // Toolbar button icon colors, etc.
|
||||
@ -271,7 +279,10 @@ $editFrameColorHandleBg: $colorBodyBg; // Resize handle 'offset' color to make h
|
||||
$editFrameColorHandleFg: $editFrameColorSelected; // Resize handle main color
|
||||
$editFrameSelectedShdw: rgba(black, 0.4) 0 1px 5px 1px;
|
||||
$editFrameMovebarColorBg: $editFrameColor; // Movebar bg color
|
||||
$editFrameMovebarColorFg: pullForward($editFrameMovebarColorBg, 20%); // Grippy lines, container size text
|
||||
$editFrameMovebarColorFg: pullForward(
|
||||
$editFrameMovebarColorBg,
|
||||
20%
|
||||
); // Grippy lines, container size text
|
||||
$editFrameHovMovebarColorBg: pullForward($editFrameMovebarColorBg, 10%); // Hover style
|
||||
$editFrameHovMovebarColorFg: pullForward($editFrameMovebarColorFg, 10%);
|
||||
$editFrameSelectedMovebarColorBg: pullForward($editFrameMovebarColorBg, 15%); // Selected style
|
||||
@ -376,13 +387,13 @@ $colorInspectorBg: pullForward($colorBodyBg, 5%);
|
||||
$colorInspectorFg: $colorBodyFg;
|
||||
$colorInspectorPropName: $colorBodyFg;
|
||||
$colorInspectorPropVal: pullForward($colorInspectorFg, 15%);
|
||||
$colorInspectorSectionHeaderBg: rgba($colorBodyBg, .75);
|
||||
$colorInspectorSectionHeaderBg: rgba($colorBodyBg, 0.75);
|
||||
$colorInspectorSectionHeaderFg: pullForward($colorInspectorBg, 40%);
|
||||
|
||||
// Tabs
|
||||
$colorTabBg: $colorBodyBg;
|
||||
$colorTabFg: $colorBodyFgEm;
|
||||
$colorTabCurrentBg: rgba($colorKey, .71);
|
||||
$colorTabCurrentBg: rgba($colorKey, 0.71);
|
||||
$colorTabCurrentFg: $colorBodyFgEm;
|
||||
$colorTabsBaseline: $colorBodyBg;
|
||||
|
||||
@ -413,7 +424,7 @@ $colorLimitYellowIc: #fdc707;
|
||||
$colorLimitOrangeBg: #b36b00;
|
||||
$colorLimitOrangeFg: #ffe0b2;
|
||||
$colorLimitOrangeIc: #ff9900;
|
||||
$colorLimitRedBg: #B60109;
|
||||
$colorLimitRedBg: #b60109;
|
||||
$colorLimitRedFg: #ffa489;
|
||||
$colorLimitRedIc: #ff4222;
|
||||
$colorLimitPurpleBg: #891bb3;
|
||||
@ -424,7 +435,7 @@ $colorLimitCyanFg: #d3faff;
|
||||
$colorLimitCyanIc: #6bedff;
|
||||
|
||||
// Events
|
||||
$colorEventPurpleFg: #aB8fff;
|
||||
$colorEventPurpleFg: #ab8fff;
|
||||
$colorEventRedFg: #ff9999;
|
||||
$colorEventOrangeFg: #ff8800;
|
||||
$colorEventYellowFg: #ffdb63;
|
||||
@ -475,17 +486,17 @@ $colorPlotLimitLineBg: rgba($colorBodyBg, 0.2);
|
||||
|
||||
// Gauges
|
||||
$colorGaugeBase: $colorKeyBg;
|
||||
$colorGaugeBg: rgba($colorGaugeBase, .35); // Gauge radial area background, meter background
|
||||
$colorGaugeBg: rgba($colorGaugeBase, 0.35); // Gauge radial area background, meter background
|
||||
$colorGaugeValue: $colorKeyBg; // Gauge value graphic (radial sweep, bar) color
|
||||
$colorGaugeTextValue: #fff; // Radial gauge text value
|
||||
$colorGaugeMeterTextValue: #fff; // Meter text value, overlaid on value bar
|
||||
$colorGaugeRange: $colorBodyFg; // Range text color
|
||||
$colorGaugeLimitHigh: rgba($colorLimitRedBg, .5);
|
||||
$colorGaugeLimitHigh: rgba($colorLimitRedBg, 0.5);
|
||||
$colorGaugeLimitLow: $colorGaugeLimitHigh;
|
||||
$colorGaugeNeedle: $colorGaugeBase; // Color of needle in a needle gauge.
|
||||
$transitionTimeGauge: 150ms; // CSS transition time used to smooth needle gauge and meter value transitions
|
||||
$marginGaugeMeterValue: 10%; // Margin between meter value bar and bounds edges
|
||||
$gaugeMeterValueShadow: rgba(255, 255, 255, .5);
|
||||
$gaugeMeterValueShadow: rgba(255, 255, 255, 0.5);
|
||||
// TODO: This is some code regarding how we can make Gauges include a border or glow. We may need to revisit this.
|
||||
// padding: 5%;
|
||||
// background: radial-gradient(circle, transparent 0%, transparent 65%, rgba(255, 255, 255,0.4) 64%, rgba(255,255,255,0) 70%)
|
||||
|
@ -66,8 +66,10 @@ $numericFont: $heroFont;
|
||||
box-shadow: rgba(black, 0.5) 0 0.5px 2px;
|
||||
}
|
||||
|
||||
@mixin telemetryView(){}
|
||||
@mixin browseFrameBorder() {}
|
||||
@mixin telemetryView() {
|
||||
}
|
||||
@mixin browseFrameBorder() {
|
||||
}
|
||||
|
||||
// Functions
|
||||
@function buttonBg($c: $colorBtnBg) {
|
||||
@ -100,17 +102,20 @@ $colorBodyFgEm: #fff;
|
||||
$colorGenBg: #222;
|
||||
$colorHeadBg: #000;
|
||||
$colorHeadFg: $colorBodyFg;
|
||||
$colorKey: #009fd4;
|
||||
$colorKey: #03ace4;
|
||||
$colorKeyBg: #007fad; // Darker version of colorKey for use in major buttons
|
||||
$colorKeyFg: #fff;
|
||||
$colorKeyHov: lighten($colorKey, 10%);
|
||||
$colorKeyBgHov: lighten($colorKeyBg, 10%);
|
||||
$colorKeyFilter: invert(36%) sepia(76%) saturate(2514%) hue-rotate(170deg) brightness(99%) contrast(101%);
|
||||
$colorKeyFilterHov: invert(63%) sepia(88%) saturate(3029%) hue-rotate(154deg) brightness(101%) contrast(100%);
|
||||
$colorKeyFilter: invert(36%) sepia(76%) saturate(2514%) hue-rotate(170deg) brightness(99%)
|
||||
contrast(101%);
|
||||
$colorKeyFilterHov: invert(63%) sepia(88%) saturate(3029%) hue-rotate(154deg) brightness(101%)
|
||||
contrast(100%);
|
||||
$colorKeySelectedBg: $colorKeyBg;
|
||||
$colorKeySubtle: pushBack($colorKey, 10%);
|
||||
$uiColor: #0093ff; // Resize bars, splitter bars, etc.
|
||||
$colorInteriorBorder: rgba($colorBodyFg, 0.2);
|
||||
$colorInteriorBorderNotebook: rgba($colorBodyFg, 0.2);
|
||||
$colorA: #ccc;
|
||||
$colorAHov: #fff;
|
||||
$filterHov: brightness(1.3) contrast(1.5); // Tree, location items
|
||||
@ -125,7 +130,7 @@ $bodySize: 100%;
|
||||
|
||||
// Object labels
|
||||
$objectLabelTypeIconOpacity: 0.8; //JOHN
|
||||
$objectLabelNameColorFg: lighten($colorBodyFg, 10%);
|
||||
$objectLabelNameColorFg: lighten($colorBodyFg, 20%);
|
||||
|
||||
// Layout
|
||||
$shellMainPad: 4px 0;
|
||||
@ -140,11 +145,14 @@ $sideBarHeaderFg: rgba($colorBodyFg, 0.7);
|
||||
$colorStatusFg: #888;
|
||||
$colorStatusDefault: #ccc;
|
||||
$colorStatusInfo: #60ba7b;
|
||||
$colorStatusInfoFilter: invert(58%) sepia(44%) saturate(405%) hue-rotate(85deg) brightness(102%) contrast(92%);
|
||||
$colorStatusInfoFilter: invert(58%) sepia(44%) saturate(405%) hue-rotate(85deg) brightness(102%)
|
||||
contrast(92%);
|
||||
$colorStatusAlert: #ffb66c;
|
||||
$colorStatusAlertFilter: invert(78%) sepia(26%) saturate(1160%) hue-rotate(324deg) brightness(107%) contrast(101%);
|
||||
$colorStatusAlertFilter: invert(78%) sepia(26%) saturate(1160%) hue-rotate(324deg) brightness(107%)
|
||||
contrast(101%);
|
||||
$colorStatusError: #da0004;
|
||||
$colorStatusErrorFilter: invert(10%) sepia(96%) saturate(4360%) hue-rotate(351deg) brightness(111%) contrast(115%);
|
||||
$colorStatusErrorFilter: invert(10%) sepia(96%) saturate(4360%) hue-rotate(351deg) brightness(111%)
|
||||
contrast(115%);
|
||||
$colorStatusBtnBg: #666; // Where is this used?
|
||||
$colorStatusPartialBg: #3f5e8b;
|
||||
$colorStatusCompleteBg: #457638;
|
||||
@ -160,7 +168,7 @@ $colorDiagnostic: #a4b442;
|
||||
$colorDiagnosticFg: #39461a;
|
||||
$colorCommand: #3693bd;
|
||||
$colorCommandFg: #fff;
|
||||
$colorInfo: #2294a2;
|
||||
$colorInfo: #198290;
|
||||
$colorInfoFg: #fff;
|
||||
$colorOk: #33cc33;
|
||||
$colorOkFg: #fff;
|
||||
@ -218,7 +226,10 @@ $borderMissing: 1px dashed $colorAlert !important;
|
||||
$editUIColor: $uiColor; // Base color
|
||||
$editUIColorBg: $editUIColor;
|
||||
$editUIColorFg: #fff;
|
||||
$editUIColorHov: pullForward(saturate($uiColor, 10%), 10%); // Hover color when $editUIColor is applied as a base color
|
||||
$editUIColorHov: pullForward(
|
||||
saturate($uiColor, 10%),
|
||||
10%
|
||||
); // Hover color when $editUIColor is applied as a base color
|
||||
$editUIBaseColor: #344b8d; // Base color, toolbar bg
|
||||
$editUIBaseColorHov: pullForward($editUIBaseColor, 20%);
|
||||
$editUIBaseColorFg: #ffffff; // Toolbar button icon colors, etc.
|
||||
@ -237,7 +248,10 @@ $editFrameColorHandleBg: $colorBodyBg; // Resize handle 'offset' color to make h
|
||||
$editFrameColorHandleFg: $editFrameColorSelected; // Resize handle main color
|
||||
$editFrameSelectedShdw: rgba(black, 0.4) 0 1px 5px 1px;
|
||||
$editFrameMovebarColorBg: $editFrameColor; // Movebar bg color
|
||||
$editFrameMovebarColorFg: pullForward($editFrameMovebarColorBg, 20%); // Grippy lines, container size text
|
||||
$editFrameMovebarColorFg: pullForward(
|
||||
$editFrameMovebarColorBg,
|
||||
20%
|
||||
); // Grippy lines, container size text
|
||||
$editFrameHovMovebarColorBg: pullForward($editFrameMovebarColorBg, 10%); // Hover style
|
||||
$editFrameHovMovebarColorFg: pullForward($editFrameMovebarColorFg, 10%);
|
||||
$editFrameSelectedMovebarColorBg: pullForward($editFrameMovebarColorBg, 15%); // Selected style
|
||||
@ -390,7 +404,7 @@ $colorLimitCyanFg: #d3faff;
|
||||
$colorLimitCyanIc: #6bedff;
|
||||
|
||||
// Events
|
||||
$colorEventPurpleFg: #aB8fff;
|
||||
$colorEventPurpleFg: #ab8fff;
|
||||
$colorEventRedFg: #ff9999;
|
||||
$colorEventOrangeFg: #ff8800;
|
||||
$colorEventYellowFg: #ffdb63;
|
||||
@ -457,7 +471,7 @@ $colorPastBg: #444;
|
||||
$colorPastFg: pushBack($colorBodyFg, 10%);
|
||||
$colorPastFgEm: $colorBodyFg;
|
||||
$colorCurrentBg: #666;
|
||||
$colorCurrentFg: $colorBodyFg;
|
||||
$colorCurrentFg: pullForward($colorBodyFg, 10%);
|
||||
$colorCurrentFgEm: $colorBodyFgEm;
|
||||
$colorCurrentBorder: $colorBodyBg;
|
||||
$colorFutureBg: $colorPastBg;
|
||||
|
@ -66,8 +66,10 @@ $numericFont: $heroFont;
|
||||
box-shadow: rgba(black, 0.5) 0 0.5px 2px;
|
||||
}
|
||||
|
||||
@mixin telemetryView(){}
|
||||
@mixin browseFrameBorder() {}
|
||||
@mixin telemetryView() {
|
||||
}
|
||||
@mixin browseFrameBorder() {
|
||||
}
|
||||
|
||||
/**************************************************** OVERRIDES */
|
||||
.c-frame {
|
||||
@ -121,12 +123,15 @@ $colorKeyBg: #007fad; // Darker version of colorKey for use in major buttons
|
||||
$colorKeyFg: #fff;
|
||||
$colorKeyHov: #26d8ff;
|
||||
$colorKeyBgHov: lighten($colorKeyBg, 10%);
|
||||
$colorKeyFilter: invert(36%) sepia(76%) saturate(2514%) hue-rotate(170deg) brightness(99%) contrast(101%);
|
||||
$colorKeyFilterHov: invert(63%) sepia(88%) saturate(3029%) hue-rotate(154deg) brightness(101%) contrast(100%);
|
||||
$colorKeyFilter: invert(36%) sepia(76%) saturate(2514%) hue-rotate(170deg) brightness(99%)
|
||||
contrast(101%);
|
||||
$colorKeyFilterHov: invert(63%) sepia(88%) saturate(3029%) hue-rotate(154deg) brightness(101%)
|
||||
contrast(100%);
|
||||
$colorKeySelectedBg: $colorKey;
|
||||
$colorKeySubtle: pushBack($colorKey, 10%);
|
||||
$uiColor: #0093ff; // Resize bars, splitter bars, etc.
|
||||
$colorInteriorBorder: rgba($colorBodyFg, 0.2);
|
||||
$colorInteriorBorderNotebook: rgba($colorBodyFg, 0.5);
|
||||
$colorA: #ccc;
|
||||
$colorAHov: #fff;
|
||||
$filterHov: brightness(1.3) contrast(1.5); // Tree, location items
|
||||
@ -156,11 +161,14 @@ $sideBarHeaderFg: rgba($colorBodyFg, 0.7);
|
||||
$colorStatusFg: #999;
|
||||
$colorStatusDefault: #ccc;
|
||||
$colorStatusInfo: #60ba7b;
|
||||
$colorStatusInfoFilter: invert(58%) sepia(44%) saturate(405%) hue-rotate(85deg) brightness(102%) contrast(92%);
|
||||
$colorStatusInfoFilter: invert(58%) sepia(44%) saturate(405%) hue-rotate(85deg) brightness(102%)
|
||||
contrast(92%);
|
||||
$colorStatusAlert: #ffb66c;
|
||||
$colorStatusAlertFilter: invert(78%) sepia(26%) saturate(1160%) hue-rotate(324deg) brightness(107%) contrast(101%);
|
||||
$colorStatusAlertFilter: invert(78%) sepia(26%) saturate(1160%) hue-rotate(324deg) brightness(107%)
|
||||
contrast(101%);
|
||||
$colorStatusError: #da0004;
|
||||
$colorStatusErrorFilter: invert(10%) sepia(96%) saturate(4360%) hue-rotate(351deg) brightness(111%) contrast(115%);
|
||||
$colorStatusErrorFilter: invert(10%) sepia(96%) saturate(4360%) hue-rotate(351deg) brightness(111%)
|
||||
contrast(115%);
|
||||
$colorStatusBtnBg: #666; // Where is this used?
|
||||
$colorStatusPartialBg: #3f5e8b;
|
||||
$colorStatusCompleteBg: #457638;
|
||||
@ -234,7 +242,10 @@ $borderMissing: 1px dashed $colorAlert !important;
|
||||
$editUIColor: $uiColor; // Base color
|
||||
$editUIColorBg: $editUIColor;
|
||||
$editUIColorFg: #fff;
|
||||
$editUIColorHov: pullForward(saturate($uiColor, 10%), 10%); // Hover color when $editUIColor is applied as a base color
|
||||
$editUIColorHov: pullForward(
|
||||
saturate($uiColor, 10%),
|
||||
10%
|
||||
); // Hover color when $editUIColor is applied as a base color
|
||||
$editUIBaseColor: #344b8d; // Base color, toolbar bg
|
||||
$editUIBaseColorHov: pullForward($editUIBaseColor, 20%);
|
||||
$editUIBaseColorFg: #ffffff; // Toolbar button icon colors, etc.
|
||||
@ -253,7 +264,10 @@ $editFrameColorHandleBg: $colorBodyBg; // Resize handle 'offset' color to make h
|
||||
$editFrameColorHandleFg: $editFrameColorSelected; // Resize handle main color
|
||||
$editFrameSelectedShdw: rgba(black, 0.4) 0 1px 5px 1px;
|
||||
$editFrameMovebarColorBg: $editFrameColor; // Movebar bg color
|
||||
$editFrameMovebarColorFg: pullForward($editFrameMovebarColorBg, 20%); // Grippy lines, container size text
|
||||
$editFrameMovebarColorFg: pullForward(
|
||||
$editFrameMovebarColorBg,
|
||||
20%
|
||||
); // Grippy lines, container size text
|
||||
$editFrameHovMovebarColorBg: pullForward($editFrameMovebarColorBg, 10%); // Hover style
|
||||
$editFrameHovMovebarColorFg: pullForward($editFrameMovebarColorFg, 10%);
|
||||
$editFrameSelectedMovebarColorBg: pullForward($editFrameMovebarColorBg, 15%); // Selected style
|
||||
@ -406,7 +420,7 @@ $colorLimitCyanFg: #d3faff;
|
||||
$colorLimitCyanIc: #6bedff;
|
||||
|
||||
// Events
|
||||
$colorEventPurpleFg: #aB8fff;
|
||||
$colorEventPurpleFg: #ab8fff;
|
||||
$colorEventRedFg: #ff9999;
|
||||
$colorEventOrangeFg: #ff8800;
|
||||
$colorEventYellowFg: #ffdb63;
|
||||
|
@ -65,8 +65,10 @@ $numericFont: $heroFont;
|
||||
background: $c;
|
||||
}
|
||||
|
||||
@mixin telemetryView(){}
|
||||
@mixin browseFrameBorder() {}
|
||||
@mixin telemetryView() {
|
||||
}
|
||||
@mixin browseFrameBorder() {
|
||||
}
|
||||
|
||||
// Functions
|
||||
@function buttonBg($c: $colorBtnBg) {
|
||||
@ -104,12 +106,15 @@ $colorKeyBg: #007fad; // Darker version of colorKey for use in major buttons
|
||||
$colorKeyFg: #fff;
|
||||
$colorKeyHov: lighten($colorKey, 10%); //#00c0f6;
|
||||
$colorKeyBgHov: lighten($colorKeyBg, 10%);
|
||||
$colorKeyFilter: invert(37%) sepia(100%) saturate(686%) hue-rotate(157deg) brightness(102%) contrast(102%);
|
||||
$colorKeyFilterHov: invert(69%) sepia(87%) saturate(3243%) hue-rotate(151deg) brightness(97%) contrast(102%);
|
||||
$colorKeyFilter: invert(37%) sepia(100%) saturate(686%) hue-rotate(157deg) brightness(102%)
|
||||
contrast(102%);
|
||||
$colorKeyFilterHov: invert(69%) sepia(87%) saturate(3243%) hue-rotate(151deg) brightness(97%)
|
||||
contrast(102%);
|
||||
$colorKeySelectedBg: $colorKey;
|
||||
$colorKeySubtle: pushBack($colorKey, 20%);
|
||||
$uiColor: #289fec; // Resize bars, splitter bars, etc.
|
||||
$colorInteriorBorder: rgba($colorBodyFg, 0.2);
|
||||
$colorInteriorBorderNotebook: rgba($colorBodyFg, 0.5);
|
||||
$colorA: $colorBodyFg;
|
||||
$colorAHov: $colorKey;
|
||||
$filterHov: hue-rotate(-10deg) brightness(0.8) contrast(2); // Tree, location items
|
||||
@ -139,11 +144,14 @@ $sideBarHeaderFg: rgba($colorBodyFg, 0.7);
|
||||
$colorStatusFg: #999;
|
||||
$colorStatusDefault: #ccc;
|
||||
$colorStatusInfo: #60ba7b;
|
||||
$colorStatusInfoFilter: invert(64%) sepia(42%) saturate(416%) hue-rotate(85deg) brightness(93%) contrast(93%);
|
||||
$colorStatusInfoFilter: invert(64%) sepia(42%) saturate(416%) hue-rotate(85deg) brightness(93%)
|
||||
contrast(93%);
|
||||
$colorStatusAlert: #ff8a0d;
|
||||
$colorStatusAlertFilter: invert(89%) sepia(26%) saturate(5035%) hue-rotate(316deg) brightness(114%) contrast(107%);
|
||||
$colorStatusAlertFilter: invert(89%) sepia(26%) saturate(5035%) hue-rotate(316deg) brightness(114%)
|
||||
contrast(107%);
|
||||
$colorStatusError: #da0004;
|
||||
$colorStatusErrorFilter: invert(8%) sepia(96%) saturate(4511%) hue-rotate(352deg) brightness(136%) contrast(114%);
|
||||
$colorStatusErrorFilter: invert(8%) sepia(96%) saturate(4511%) hue-rotate(352deg) brightness(136%)
|
||||
contrast(114%);
|
||||
$colorStatusBtnBg: #666; // Where is this used?
|
||||
$colorStatusPartialBg: #c9d6ff;
|
||||
$colorStatusCompleteBg: #a4e4b4;
|
||||
@ -217,7 +225,10 @@ $borderMissing: 1px dashed $colorAlert !important;
|
||||
$editUIColor: $uiColor; // Base color
|
||||
$editUIColorBg: $editUIColor;
|
||||
$editUIColorFg: #fff;
|
||||
$editUIColorHov: pullForward(saturate($uiColor, 10%),20%); // Hover color when $editUIColor is applied as a base color
|
||||
$editUIColorHov: pullForward(
|
||||
saturate($uiColor, 10%),
|
||||
20%
|
||||
); // Hover color when $editUIColor is applied as a base color
|
||||
$editUIBaseColor: #cae1ff; // Base color, toolbar bg
|
||||
$editUIBaseColorHov: pushBack($editUIBaseColor, 20%);
|
||||
$editUIBaseColorFg: #4c4c4c; // Toolbar button icon colors, etc.
|
||||
@ -236,7 +247,10 @@ $editFrameColorHandleBg: $colorBodyBg; // Resize handle 'offset' color to make h
|
||||
$editFrameColorHandleFg: $editFrameColorSelected; // Resize handle main color
|
||||
$editFrameSelectedShdw: rgba(black, 0.5) 0 1px 5px 2px;
|
||||
$editFrameMovebarColorBg: $editFrameColor; // Movebar bg color
|
||||
$editFrameMovebarColorFg: pullForward($editFrameMovebarColorBg, 20%); // Grippy lines, container size text
|
||||
$editFrameMovebarColorFg: pullForward(
|
||||
$editFrameMovebarColorBg,
|
||||
20%
|
||||
); // Grippy lines, container size text
|
||||
$editFrameHovMovebarColorBg: pullForward($editFrameMovebarColorBg, 10%); // Hover style
|
||||
$editFrameHovMovebarColorFg: pullForward($editFrameMovebarColorFg, 10%);
|
||||
$editFrameSelectedMovebarColorBg: pullForward($editFrameMovebarColorBg, 15%); // Selected style
|
||||
|
@ -312,12 +312,17 @@
|
||||
$impStr: !important;
|
||||
}
|
||||
|
||||
background-image: linear-gradient(45deg, $color 25%, transparent 25%),
|
||||
background-image:
|
||||
linear-gradient(45deg, $color 25%, transparent 25%),
|
||||
linear-gradient(45deg, transparent 75%, $color 75%),
|
||||
linear-gradient(45deg, transparent 75%, $color 75%),
|
||||
linear-gradient(45deg, $color 25%, transparent 25%) $impStr;
|
||||
background-size: $size $size;
|
||||
background-position: 0 0, 0 0, -1 * $bgPos -1 * $bgPos, $bgPos $bgPos;
|
||||
background-position:
|
||||
0 0,
|
||||
0 0,
|
||||
-1 * $bgPos -1 * $bgPos,
|
||||
$bgPos $bgPos;
|
||||
}
|
||||
|
||||
@mixin disabled() {
|
||||
@ -350,7 +355,9 @@
|
||||
@mixin dropDownArrowBg() {
|
||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10'%3e%3cpath fill='%23#{svgColorFromHex($colorSelectArw)}' d='M5 5l5-5H0z'/%3e%3c/svg%3e");
|
||||
background-repeat: no-repeat, no-repeat;
|
||||
background-position: right 0.4em top 80%, 0 0;
|
||||
background-position:
|
||||
right 0.4em top 80%,
|
||||
0 0;
|
||||
}
|
||||
|
||||
@mixin noColor() {
|
||||
@ -621,8 +628,8 @@
|
||||
color: $colorBtnFg;
|
||||
cursor: pointer;
|
||||
|
||||
&[class*="--major"],
|
||||
&[class*='is-active']{
|
||||
&[class*='--major'],
|
||||
&[class*='is-active'] {
|
||||
background: $colorBtnMajorBg !important;
|
||||
color: $colorBtnMajorFg !important;
|
||||
}
|
||||
|
@ -124,7 +124,7 @@
|
||||
}
|
||||
|
||||
&__drag-area {
|
||||
background: rgba($colorKey, 0.1);
|
||||
background: rgba($colorKey, 0.02);
|
||||
border: 1px dashed rgba($colorKey, 0.7);
|
||||
color: $colorKey;
|
||||
cursor: pointer;
|
||||
@ -136,7 +136,7 @@
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba($colorBodyFg, 0.1);
|
||||
background: rgba($colorBodyFg, 0.2);
|
||||
}
|
||||
|
||||
&.drag-active,
|
||||
@ -288,7 +288,7 @@
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
background: rgba($colorKey, 0.2);
|
||||
background: rgba($colorKey, 0.1);
|
||||
|
||||
.c-ne__text {
|
||||
.c-notebook--page-unlocked & {
|
||||
@ -311,7 +311,7 @@
|
||||
|
||||
&__creator,
|
||||
&__embed__time {
|
||||
opacity: 0.7;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
&__time-and-creator-and-delete,
|
||||
@ -330,6 +330,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__time-and-creator {
|
||||
color: $colorA;
|
||||
}
|
||||
|
||||
&__creator [class*='icon'] {
|
||||
font-size: 0.95em;
|
||||
}
|
||||
@ -375,7 +379,11 @@
|
||||
}
|
||||
|
||||
// Resets and styling for markdown
|
||||
h1, h2, h3, h4, h5 {
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5 {
|
||||
margin: unset !important;
|
||||
padding: unset !important;
|
||||
}
|
||||
@ -394,13 +402,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
p, blockquote, pre {
|
||||
p,
|
||||
blockquote,
|
||||
pre {
|
||||
margin: 0;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: $interiorMarginLg;
|
||||
}
|
||||
;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
@ -409,7 +418,8 @@
|
||||
margin-right: $m;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
ul,
|
||||
ol {
|
||||
padding: 0 0 0 16px;
|
||||
}
|
||||
|
||||
@ -429,7 +439,8 @@
|
||||
width: auto;
|
||||
}
|
||||
|
||||
th, td {
|
||||
th,
|
||||
td {
|
||||
border: 1px solid rgba($colorBodyFg, 0.7);
|
||||
}
|
||||
|
||||
@ -524,7 +535,7 @@
|
||||
display: inline-flex;
|
||||
flex: 0 0 auto;
|
||||
padding: $interiorMargin;
|
||||
border: 1px solid $colorInteriorBorder;
|
||||
border: 1px solid $colorInteriorBorderNotebook;
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
|
@ -50,11 +50,12 @@
|
||||
<script>
|
||||
import { inject } from 'vue';
|
||||
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import tooltipHelpers from '../../api/tooltips/tooltipMixins.js';
|
||||
import { useIsEditing } from '../../ui/composables/edit.js';
|
||||
import ContextMenuGesture from '../mixins/context-menu-gesture.js';
|
||||
import ObjectLink from '../mixins/object-link.js';
|
||||
import PreviewAction from '../preview/PreviewAction.js';
|
||||
|
||||
export default {
|
||||
mixins: [ObjectLink, ContextMenuGesture, tooltipHelpers],
|
||||
@ -116,7 +117,7 @@ export default {
|
||||
this.setStatus
|
||||
);
|
||||
this.status = this.openmct.status.get(this.domainObject.identifier);
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
},
|
||||
unmounted() {
|
||||
this.removeStatusListener();
|
||||
|
@ -352,6 +352,10 @@ export default {
|
||||
show(object, viewKey, immediatelySelect, currentObjectPath) {
|
||||
this.updateStyle();
|
||||
|
||||
if (this.domainObject) {
|
||||
this.triggerUnsubscribeFromStaleness(this.domainObject);
|
||||
}
|
||||
|
||||
if (this.removeSelectable) {
|
||||
this.removeSelectable();
|
||||
delete this.removeSelectable;
|
||||
|
@ -31,9 +31,8 @@
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CreateAction from '@/plugins/formActions/CreateAction';
|
||||
import { CREATE_ACTION_KEY } from '@/plugins/formActions/CreateAction';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
@ -102,9 +101,8 @@ export default {
|
||||
this.isEditing = isEditing;
|
||||
},
|
||||
create(key) {
|
||||
const createAction = new CreateAction(this.openmct, key, this.openmct.router.path[0]);
|
||||
|
||||
createAction.invoke();
|
||||
const createAction = this.openmct.actions.getAction(CREATE_ACTION_KEY);
|
||||
createAction.invoke(key, this.openmct.router.path[0]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -62,9 +62,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import tooltipHelpers from '../../api/tooltips/tooltipMixins.js';
|
||||
import ObjectPath from '../components/ObjectPath.vue';
|
||||
import PreviewAction from '../preview/PreviewAction.js';
|
||||
|
||||
export default {
|
||||
name: 'RecentObjectsListItem',
|
||||
@ -99,7 +100,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
this.previewAction.on('isVisible', this.togglePreviewState);
|
||||
},
|
||||
unmounted() {
|
||||
|
@ -56,9 +56,10 @@
|
||||
import { Marked } from 'marked';
|
||||
import sanitizeHtml from 'sanitize-html';
|
||||
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import { identifierToString } from '../../../../src/tools/url.js';
|
||||
import ObjectPath from '../../components/ObjectPath.vue';
|
||||
import PreviewAction from '../../preview/PreviewAction.js';
|
||||
|
||||
export default {
|
||||
name: 'AnnotationSearchResult',
|
||||
@ -109,12 +110,10 @@ export default {
|
||||
this.marked = new Marked();
|
||||
},
|
||||
mounted() {
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction.on('isVisible', this.togglePreviewState);
|
||||
this.previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
this.fireAnnotationSelection = this.fireAnnotationSelection.bind(this);
|
||||
},
|
||||
unmounted() {
|
||||
this.previewAction.off('isVisible', this.togglePreviewState);
|
||||
this.openmct.selection.off('change', this.fireAnnotationSelection);
|
||||
},
|
||||
methods: {
|
||||
|
@ -54,10 +54,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { PREVIEW_ACTION_KEY } from '@/ui/preview/PreviewAction.js';
|
||||
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins.js';
|
||||
import { objectPathToUrl } from '../../../tools/url.js';
|
||||
import ObjectPath from '../../components/ObjectPath.vue';
|
||||
import PreviewAction from '../../preview/PreviewAction.js';
|
||||
|
||||
export default {
|
||||
name: 'ObjectSearchResult',
|
||||
@ -88,7 +89,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction = this.openmct.actions.getAction(PREVIEW_ACTION_KEY);
|
||||
this.previewAction.on('isVisible', this.togglePreviewState);
|
||||
},
|
||||
unmounted() {
|
||||
|
@ -47,7 +47,7 @@
|
||||
// Make <a> in label look like buttons
|
||||
@include transition(background-color);
|
||||
background-color: transparent;
|
||||
border: 1px solid rgba($colorIndicatorMenuFg, 0.5);
|
||||
border: 1px solid rgba($colorIndicatorMenuFg, 0.8);
|
||||
border-radius: $controlCr;
|
||||
box-sizing: border-box;
|
||||
color: inherit;
|
||||
|
@ -24,14 +24,16 @@ import mount from 'utils/mount';
|
||||
|
||||
import PreviewContainer from './PreviewContainer.vue';
|
||||
|
||||
export default class PreviewAction extends EventEmitter {
|
||||
const PREVIEW_ACTION_KEY = 'preview';
|
||||
|
||||
class PreviewAction extends EventEmitter {
|
||||
constructor(openmct) {
|
||||
super();
|
||||
/**
|
||||
* Metadata
|
||||
*/
|
||||
this.name = 'View';
|
||||
this.key = 'preview';
|
||||
this.key = PREVIEW_ACTION_KEY;
|
||||
this.description = 'View in large dialog';
|
||||
this.cssClass = 'icon-items-expand';
|
||||
this.group = 'windowing';
|
||||
@ -110,3 +112,7 @@ export default class PreviewAction extends EventEmitter {
|
||||
return noPreviewTypes.includes(objectPath[0].type);
|
||||
}
|
||||
}
|
||||
|
||||
export { PREVIEW_ACTION_KEY };
|
||||
|
||||
export default PreviewAction;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user