mirror of
https://github.com/nasa/openmct.git
synced 2024-12-20 13:43:09 +00:00
Merge branch 'master' of https://github.com/nasa/openmct into activity-states-and-compact-view
This commit is contained in:
commit
50de0f6f27
@ -6,15 +6,15 @@ This is the OpenMCT common webpack file. It is imported by the other three webpa
|
|||||||
There are separate npm scripts to use these configurations, though simply running `npm install`
|
There are separate npm scripts to use these configurations, though simply running `npm install`
|
||||||
will use the default production configuration.
|
will use the default production configuration.
|
||||||
*/
|
*/
|
||||||
import path from 'node:path';
|
|
||||||
import CopyWebpackPlugin from 'copy-webpack-plugin';
|
|
||||||
import webpack from 'webpack';
|
|
||||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
|
||||||
import fs from 'node:fs';
|
|
||||||
import { execSync } from 'node:child_process';
|
import { execSync } from 'node:child_process';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
import CopyWebpackPlugin from 'copy-webpack-plugin';
|
||||||
|
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||||
import { VueLoaderPlugin } from 'vue-loader';
|
import { VueLoaderPlugin } from 'vue-loader';
|
||||||
|
import webpack from 'webpack';
|
||||||
let gitRevision = 'error-retrieving-revision';
|
let gitRevision = 'error-retrieving-revision';
|
||||||
let gitBranch = 'error-retrieving-branch';
|
let gitBranch = 'error-retrieving-branch';
|
||||||
|
|
||||||
@ -22,9 +22,7 @@ const packageDefinition = JSON.parse(fs.readFileSync(new URL('../package.json',
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
gitRevision = execSync('git rev-parse HEAD').toString().trim();
|
gitRevision = execSync('git rev-parse HEAD').toString().trim();
|
||||||
gitBranch = execSync('git rev-parse --abbrev-ref HEAD')
|
gitBranch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
|
||||||
.toString()
|
|
||||||
.trim();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(err);
|
console.warn(err);
|
||||||
}
|
}
|
||||||
@ -67,7 +65,6 @@ const config = {
|
|||||||
alias: {
|
alias: {
|
||||||
'@': path.join(projectRootDir, 'src'),
|
'@': path.join(projectRootDir, 'src'),
|
||||||
legacyRegistry: path.join(projectRootDir, 'src/legacyRegistry'),
|
legacyRegistry: path.join(projectRootDir, 'src/legacyRegistry'),
|
||||||
saveAs: 'file-saver/src/FileSaver.js',
|
|
||||||
csv: 'comma-separated-values',
|
csv: 'comma-separated-values',
|
||||||
EventEmitter: 'eventemitter3',
|
EventEmitter: 'eventemitter3',
|
||||||
bourbon: 'bourbon.scss',
|
bourbon: 'bourbon.scss',
|
||||||
|
@ -43,7 +43,7 @@ test.describe('Clear Data Action', () => {
|
|||||||
await expect(page.locator(backgroundImageSelector)).toBeVisible();
|
await expect(page.locator(backgroundImageSelector)).toBeVisible();
|
||||||
});
|
});
|
||||||
test('works as expected with Example Imagery', async ({ page }) => {
|
test('works as expected with Example Imagery', async ({ page }) => {
|
||||||
await expect(await page.locator('.c-thumb__image').count()).toBeGreaterThan(0);
|
expect(await page.locator('.c-thumb__image').count()).toBeGreaterThan(0);
|
||||||
// Click the "Clear Data" menu action
|
// Click the "Clear Data" menu action
|
||||||
await page.getByTitle('More actions').click();
|
await page.getByTitle('More actions').click();
|
||||||
await expect(
|
await expect(
|
||||||
@ -59,6 +59,6 @@ test.describe('Clear Data Action', () => {
|
|||||||
|
|
||||||
// Verify that the background image is no longer visible
|
// Verify that the background image is no longer visible
|
||||||
await expect(page.locator(backgroundImageSelector)).toBeHidden();
|
await expect(page.locator(backgroundImageSelector)).toBeHidden();
|
||||||
await expect(await page.locator('.c-thumb__image').count()).toBe(0);
|
expect(await page.locator('.c-thumb__image').count()).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -95,18 +95,15 @@ async function makeOverlayPlot(page, myItemsFolderName) {
|
|||||||
|
|
||||||
await page.locator('button.c-create-button').click();
|
await page.locator('button.c-create-button').click();
|
||||||
await page.locator('li[role="menuitem"]:has-text("Overlay Plot")').click();
|
await page.locator('li[role="menuitem"]:has-text("Overlay Plot")').click();
|
||||||
|
// Click OK button and wait for Navigate event
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.waitForNavigation({ waitUntil: 'networkidle' }),
|
page.waitForLoadState(),
|
||||||
page.locator('button:has-text("OK")').click(),
|
await page.getByRole('button', { name: 'Save' }).click(),
|
||||||
// Wait for Save Banner to appear
|
// Wait for Save Banner to appear
|
||||||
page.waitForSelector('.c-message-banner__message')
|
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' });
|
|
||||||
|
|
||||||
// save the overlay plot
|
// save the overlay plot
|
||||||
|
|
||||||
await saveOverlayPlot(page);
|
await saveOverlayPlot(page);
|
||||||
|
|
||||||
// create a sinewave generator
|
// create a sinewave generator
|
||||||
@ -114,34 +111,24 @@ async function makeOverlayPlot(page, myItemsFolderName) {
|
|||||||
await page.locator('button.c-create-button').click();
|
await page.locator('button.c-create-button').click();
|
||||||
await page.locator('li[role="menuitem"]:has-text("Sine Wave Generator")').click();
|
await page.locator('li[role="menuitem"]:has-text("Sine Wave Generator")').click();
|
||||||
|
|
||||||
// set amplitude to 6, offset 4, period 2
|
// set amplitude to 6, offset 4, data rate 2 hz
|
||||||
|
|
||||||
await page.locator('div:nth-child(5) .c-form-row__controls .form-control .field input').click();
|
await page.getByLabel('Amplitude').fill('6');
|
||||||
await page.locator('div:nth-child(5) .c-form-row__controls .form-control .field input').fill('6');
|
await page.getByLabel('Offset').fill('4');
|
||||||
|
await page.getByLabel('Data Rate (hz)').fill('2');
|
||||||
await page.locator('div:nth-child(6) .c-form-row__controls .form-control .field input').click();
|
|
||||||
await page.locator('div:nth-child(6) .c-form-row__controls .form-control .field input').fill('4');
|
|
||||||
|
|
||||||
await page.locator('div:nth-child(7) .c-form-row__controls .form-control .field input').click();
|
|
||||||
await page.locator('div:nth-child(7) .c-form-row__controls .form-control .field input').fill('2');
|
|
||||||
|
|
||||||
// Click OK to make generator
|
|
||||||
|
|
||||||
|
// Click OK button and wait for Navigate event
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.waitForNavigation({ waitUntil: 'networkidle' }),
|
page.waitForLoadState(),
|
||||||
page.locator('button:has-text("OK")').click(),
|
await page.getByRole('button', { name: 'Save' }).click(),
|
||||||
// Wait for Save Banner to appear
|
// Wait for Save Banner to appear
|
||||||
page.waitForSelector('.c-message-banner__message')
|
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' });
|
|
||||||
|
|
||||||
// click on overlay plot
|
// click on overlay plot
|
||||||
|
|
||||||
await page.locator(`text=Open MCT ${myItemsFolderName} >> span`).nth(3).click();
|
await page.locator(`text=Open MCT ${myItemsFolderName} >> span`).nth(3).click();
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.waitForNavigation(),
|
page.waitForLoadState(),
|
||||||
page.locator('text=Unnamed Overlay Plot').first().click()
|
page.locator('text=Unnamed Overlay Plot').first().click()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -21,24 +21,24 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
const DEFAULT_IMAGE_SAMPLES = [
|
const DEFAULT_IMAGE_SAMPLES = [
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18731.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18731.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18732.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18732.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18733.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18733.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18734.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18734.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18735.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18735.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18736.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18736.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18737.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18737.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18738.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18738.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18739.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18739.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18740.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18740.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18741.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18741.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18742.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18742.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18743.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18743.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18744.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18744.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18745.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18745.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18746.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18746.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18747.jpg',
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18747.jpg',
|
||||||
'https://www.hq.nasa.gov/alsj/a16/AS16-117-18748.jpg'
|
'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18748.jpg'
|
||||||
];
|
];
|
||||||
const DEFAULT_IMAGE_LOAD_DELAY_IN_MILLISECONDS = 20000;
|
const DEFAULT_IMAGE_LOAD_DELAY_IN_MILLISECONDS = 20000;
|
||||||
const MIN_IMAGE_LOAD_DELAY_IN_MILLISECONDS = 5000;
|
const MIN_IMAGE_LOAD_DELAY_IN_MILLISECONDS = 5000;
|
||||||
|
@ -78,9 +78,7 @@ class MenuAPI {
|
|||||||
if (isActionGroup) {
|
if (isActionGroup) {
|
||||||
action = this.actionsToMenuItems(action, objectPath, view);
|
action = this.actionsToMenuItems(action, objectPath, view);
|
||||||
} else {
|
} else {
|
||||||
action.onItemClicked = () => {
|
action.onItemClicked = () => action.invoke(objectPath, view);
|
||||||
action.invoke(objectPath, view);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return action;
|
return action;
|
||||||
|
@ -32,7 +32,7 @@ class Overlay extends EventEmitter {
|
|||||||
const { destroy } = mount(
|
const { destroy } = mount(
|
||||||
{
|
{
|
||||||
components: {
|
components: {
|
||||||
OverlayComponent: OverlayComponent
|
OverlayComponent
|
||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
dismiss: this.notifyAndDismiss.bind(this),
|
dismiss: this.notifyAndDismiss.bind(this),
|
||||||
@ -60,7 +60,6 @@ class Overlay extends EventEmitter {
|
|||||||
|
|
||||||
dismiss() {
|
dismiss() {
|
||||||
this.emit('destroy');
|
this.emit('destroy');
|
||||||
document.body.removeChild(this.container);
|
|
||||||
this.destroy();
|
this.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,14 +82,17 @@ class OverlayAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A description of option properties that can be passed into the overlay
|
* Creates and displays an overlay with the specified options.
|
||||||
* @typedef options
|
*
|
||||||
* @property {object} element DOMElement that is to be inserted/shown on the overlay
|
* @typedef {Object} OverlayOptions
|
||||||
* @property {string} size preferred size of the overlay (large, small, fit)
|
* @property {HTMLElement} element The DOM Element to be inserted or shown in the overlay.
|
||||||
* @property {array} buttons optional button objects with label and callback properties
|
* @property {'large'|'small'|'fit'} size The preferred size of the overlay.
|
||||||
* @property {function} onDestroy callback to be called when overlay is destroyed
|
* @property {Array<{label: string, callback: Function}>} [buttons] Optional array of button objects, each with 'label' and 'callback' properties.
|
||||||
* @property {boolean} dismissable allow user to dismiss overlay by using esc, and clicking away
|
* @property {Function} onDestroy Callback to be called when the overlay is destroyed.
|
||||||
* from overlay. Unless set to false, all overlays will be dismissable by default.
|
* @property {boolean} [dismissable=true] Whether the overlay can be dismissed by pressing 'esc' or clicking outside of it. Defaults to true.
|
||||||
|
*
|
||||||
|
* @param {OverlayOptions} options - The configuration options for the overlay.
|
||||||
|
* @returns {Overlay} An instance of the Overlay class.
|
||||||
*/
|
*/
|
||||||
overlay(options) {
|
overlay(options) {
|
||||||
let overlay = new Overlay(options);
|
let overlay = new Overlay(options);
|
||||||
|
@ -17,7 +17,7 @@ class ProgressDialog extends Overlay {
|
|||||||
}) {
|
}) {
|
||||||
const { vNode, destroy } = mount({
|
const { vNode, destroy } = mount({
|
||||||
components: {
|
components: {
|
||||||
ProgressDialogComponent: ProgressDialogComponent
|
ProgressDialogComponent
|
||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
iconClass,
|
iconClass,
|
||||||
@ -28,16 +28,15 @@ class ProgressDialog extends Overlay {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
model: {
|
progressPerc,
|
||||||
progressPerc: progressPerc || 0,
|
|
||||||
progressText
|
progressText
|
||||||
}
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
template: '<progress-dialog-component :model="model"></progress-dialog-component>'
|
template:
|
||||||
|
'<progress-dialog-component :progress-perc="progressPerc" :progress-text="progressText"></progress-dialog-component>'
|
||||||
});
|
});
|
||||||
component = vNode.componentInstance;
|
|
||||||
|
|
||||||
|
component = vNode.componentInstance;
|
||||||
super({
|
super({
|
||||||
element: vNode.el,
|
element: vNode.el,
|
||||||
size: 'fit',
|
size: 'fit',
|
||||||
@ -51,8 +50,8 @@ class ProgressDialog extends Overlay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateProgress(progressPerc, progressText) {
|
updateProgress(progressPerc, progressText) {
|
||||||
component.model.progressPerc = progressPerc;
|
component.$data.progressPerc = progressPerc;
|
||||||
component.model.progressText = progressText;
|
component.$data.progressText = progressText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,9 +20,9 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<dialog-component>
|
<DialogComponent>
|
||||||
<progress-component :model="model" />
|
<ProgressComponent :progress-perc="progressPerc" :progress-text="progressText" />
|
||||||
</dialog-component>
|
</DialogComponent>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -31,14 +31,18 @@ import DialogComponent from './DialogComponent.vue';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
DialogComponent: DialogComponent,
|
DialogComponent,
|
||||||
ProgressComponent: ProgressComponent
|
ProgressComponent
|
||||||
},
|
},
|
||||||
inject: ['iconClass', 'title', 'hint', 'timestamp', 'message'],
|
inject: ['iconClass', 'title', 'hint', 'timestamp', 'message'],
|
||||||
props: {
|
props: {
|
||||||
model: {
|
progressPerc: {
|
||||||
type: Object,
|
type: Number,
|
||||||
required: true
|
default: 0
|
||||||
|
},
|
||||||
|
progressText: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import CSV from 'comma-separated-values';
|
import CSV from 'comma-separated-values';
|
||||||
import { saveAs } from 'saveAs';
|
import { saveAs } from 'file-saver';
|
||||||
|
|
||||||
class CSVExporter {
|
class CSVExporter {
|
||||||
export(rows, options) {
|
export(rows, options) {
|
||||||
|
@ -31,8 +31,8 @@ function replaceDotsWithUnderscores(filename) {
|
|||||||
return filename.replace(regex, '_');
|
return filename.replace(regex, '_');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
import html2canvas from 'html2canvas';
|
import html2canvas from 'html2canvas';
|
||||||
import { saveAs } from 'saveAs';
|
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
class ImageExporter {
|
class ImageExporter {
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import { saveAs } from 'saveAs';
|
import { saveAs } from 'file-saver';
|
||||||
|
|
||||||
class JSONExporter {
|
class JSONExporter {
|
||||||
export(obj, options) {
|
export(obj, options) {
|
||||||
|
@ -24,8 +24,19 @@ import { v4 as uuid } from 'uuid';
|
|||||||
import JSONExporter from '/src/exporters/JSONExporter.js';
|
import JSONExporter from '/src/exporters/JSONExporter.js';
|
||||||
|
|
||||||
export default class ExportAsJSONAction {
|
export default class ExportAsJSONAction {
|
||||||
|
#openmct;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('../../../openmct').OpenMCT} openmct The Open MCT API
|
||||||
|
*/
|
||||||
constructor(openmct) {
|
constructor(openmct) {
|
||||||
this.openmct = openmct;
|
this.#openmct = openmct;
|
||||||
|
|
||||||
|
// Bind public methods
|
||||||
|
this.invoke = this.invoke.bind(this);
|
||||||
|
this.appliesTo = this.appliesTo.bind(this);
|
||||||
|
// FIXME: This should be private but is used in tests
|
||||||
|
this.saveAs = this.saveAs.bind(this);
|
||||||
|
|
||||||
this.name = 'Export as JSON';
|
this.name = 'Export as JSON';
|
||||||
this.key = 'export.JSON';
|
this.key = 'export.JSON';
|
||||||
@ -37,6 +48,10 @@ export default class ExportAsJSONAction {
|
|||||||
this.tree = null;
|
this.tree = null;
|
||||||
this.calls = null;
|
this.calls = null;
|
||||||
this.idMap = null;
|
this.idMap = null;
|
||||||
|
this.dialog = null;
|
||||||
|
this.progressPerc = 0;
|
||||||
|
this.exportedCount = 0;
|
||||||
|
this.totalToExport = 0;
|
||||||
|
|
||||||
this.JSONExportService = new JSONExporter();
|
this.JSONExportService = new JSONExporter();
|
||||||
}
|
}
|
||||||
@ -50,87 +65,127 @@ export default class ExportAsJSONAction {
|
|||||||
appliesTo(objectPath) {
|
appliesTo(objectPath) {
|
||||||
let domainObject = objectPath[0];
|
let domainObject = objectPath[0];
|
||||||
|
|
||||||
return this._isCreatableAndPersistable(domainObject);
|
return this.#isCreatableAndPersistable(domainObject);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {object} objectpath
|
* @param {import('../../api/objects/ObjectAPI').DomainObject[]} objectPath
|
||||||
*/
|
*/
|
||||||
invoke(objectpath) {
|
invoke(objectPath) {
|
||||||
this.tree = {};
|
this.tree = {};
|
||||||
this.calls = 0;
|
this.calls = 0;
|
||||||
this.idMap = {};
|
this.idMap = {};
|
||||||
|
|
||||||
const root = objectpath[0];
|
const root = objectPath[0];
|
||||||
this.root = this._copy(root);
|
this.root = this.#copy(root);
|
||||||
|
|
||||||
const rootId = this._getKeystring(this.root);
|
const rootId = this.#getKeystring(this.root);
|
||||||
this.tree[rootId] = this.root;
|
this.tree[rootId] = this.root;
|
||||||
|
|
||||||
this._write(this.root);
|
this.dialog = this.#openmct.overlays.progressDialog({
|
||||||
|
message:
|
||||||
|
'Do not navigate away from this page or close this browser tab while this message is displayed.',
|
||||||
|
iconClass: 'info',
|
||||||
|
title: 'Exporting'
|
||||||
|
});
|
||||||
|
this.dialog.show();
|
||||||
|
this.#write(this.root)
|
||||||
|
.then(() => {
|
||||||
|
this.exportedCount++;
|
||||||
|
this.#updateProgress();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.dialog.dismiss();
|
||||||
|
this.dialog = null;
|
||||||
|
this.#resetCounts();
|
||||||
|
this.#openmct.notifications.error({
|
||||||
|
title: 'Export as JSON failed',
|
||||||
|
message: error.message
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {object} parent
|
* @param {import('../../api/objects/ObjectAPI').DomainObject} parent
|
||||||
*/
|
*/
|
||||||
async _write(parent) {
|
async #write(parent) {
|
||||||
|
this.totalToExport++;
|
||||||
this.calls++;
|
this.calls++;
|
||||||
|
|
||||||
//conditional object styles are not saved on the composition, so we need to check for them
|
//conditional object styles are not saved on the composition, so we need to check for them
|
||||||
const conditionSetIdentifier = this._getConditionSetIdentifier(parent);
|
const conditionSetIdentifier = this.#getConditionSetIdentifier(parent);
|
||||||
const hasItemConditionSetIdentifiers = this._hasItemConditionSetIdentifiers(parent);
|
const hasItemConditionSetIdentifiers = this.#hasItemConditionSetIdentifiers(parent);
|
||||||
const composition = this.openmct.composition.get(parent);
|
const composition = this.#openmct.composition.get(parent);
|
||||||
|
|
||||||
if (composition) {
|
if (composition) {
|
||||||
const children = await composition.load();
|
const children = await composition.load();
|
||||||
|
const exportPromises = children.map((child) => this.#exportObject(child, parent));
|
||||||
|
|
||||||
children.forEach((child) => {
|
await Promise.all(exportPromises);
|
||||||
this._exportObject(child, parent);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!conditionSetIdentifier && !hasItemConditionSetIdentifiers) {
|
if (!conditionSetIdentifier && !hasItemConditionSetIdentifiers) {
|
||||||
this._decrementCallsAndSave();
|
this.#decrementCallsAndSave();
|
||||||
} else {
|
} else {
|
||||||
const conditionSetObjects = [];
|
let conditionSetObjects = [];
|
||||||
|
|
||||||
// conditionSetIdentifiers directly in objectStyles object
|
// conditionSetIdentifiers directly in objectStyles object
|
||||||
if (conditionSetIdentifier) {
|
if (conditionSetIdentifier) {
|
||||||
conditionSetObjects.push(await this.openmct.objects.get(conditionSetIdentifier));
|
const conditionSetObject = this.#openmct.objects.get(conditionSetIdentifier);
|
||||||
|
conditionSetObjects.push(conditionSetObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
// conditionSetIdentifiers stored on item ids in the objectStyles object
|
// conditionSetIdentifiers stored on item ids in the objectStyles object
|
||||||
if (hasItemConditionSetIdentifiers) {
|
if (hasItemConditionSetIdentifiers) {
|
||||||
const itemConditionSetIdentifiers = this._getItemConditionSetIdentifiers(parent);
|
const itemConditionSetIdentifiers = this.#getItemConditionSetIdentifiers(parent);
|
||||||
|
const itemConditionSetObjects = itemConditionSetIdentifiers.map((id) =>
|
||||||
|
this.#openmct.objects.get(id)
|
||||||
|
);
|
||||||
|
conditionSetObjects = conditionSetObjects.concat(itemConditionSetObjects);
|
||||||
|
|
||||||
for (const itemConditionSetIdentifier of itemConditionSetIdentifiers) {
|
for (const itemConditionSetIdentifier of itemConditionSetIdentifiers) {
|
||||||
conditionSetObjects.push(await this.openmct.objects.get(itemConditionSetIdentifier));
|
conditionSetObjects.push(this.#openmct.objects.get(itemConditionSetIdentifier));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const conditionSetObject of conditionSetObjects) {
|
if (conditionSetObjects.length > 0) {
|
||||||
this._exportObject(conditionSetObject, parent);
|
const resolvedConditionSetObjects = await Promise.all(conditionSetObjects);
|
||||||
|
const exportConditionSetPromises = resolvedConditionSetObjects.map((obj) =>
|
||||||
|
this.#exportObject(obj, parent)
|
||||||
|
);
|
||||||
|
await Promise.all(exportConditionSetPromises);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._decrementCallsAndSave();
|
this.#decrementCallsAndSave();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_exportObject(child, parent) {
|
#updateProgress() {
|
||||||
const originalKeyString = this._getKeystring(child);
|
this.progressPerc = Math.ceil((100 * this.exportedCount) / this.totalToExport);
|
||||||
const createable = this._isCreatableAndPersistable(child);
|
this.dialog?.updateProgress(
|
||||||
|
this.progressPerc,
|
||||||
|
`Exporting ${this.exportedCount} / ${this.totalToExport} objects.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#exportObject(child, parent) {
|
||||||
|
const originalKeyString = this.#getKeystring(child);
|
||||||
|
const createable = this.#isCreatableAndPersistable(child);
|
||||||
const isNotInfinite = !Object.prototype.hasOwnProperty.call(this.tree, originalKeyString);
|
const isNotInfinite = !Object.prototype.hasOwnProperty.call(this.tree, originalKeyString);
|
||||||
|
|
||||||
if (createable && isNotInfinite) {
|
if (createable && isNotInfinite) {
|
||||||
// for external or linked objects we generate new keys, if they don't exist already
|
// for external or linked objects we generate new keys, if they don't exist already
|
||||||
if (this._isLinkedObject(child, parent)) {
|
if (this.#isLinkedObject(child, parent)) {
|
||||||
child = this._rewriteLink(child, parent);
|
child = this.#rewriteLink(child, parent);
|
||||||
} else {
|
} else {
|
||||||
this.tree[originalKeyString] = child;
|
this.tree[originalKeyString] = child;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._write(child);
|
this.#write(child).then(() => {
|
||||||
|
this.exportedCount++;
|
||||||
|
this.#updateProgress();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,23 +195,23 @@ export default class ExportAsJSONAction {
|
|||||||
* @param {object} parent
|
* @param {object} parent
|
||||||
* @returns {object}
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
_rewriteLink(child, parent) {
|
#rewriteLink(child, parent) {
|
||||||
const originalKeyString = this._getKeystring(child);
|
const originalKeyString = this.#getKeystring(child);
|
||||||
const parentKeyString = this._getKeystring(parent);
|
const parentKeyString = this.#getKeystring(parent);
|
||||||
const conditionSetIdentifier = this._getConditionSetIdentifier(parent);
|
const conditionSetIdentifier = this.#getConditionSetIdentifier(parent);
|
||||||
const hasItemConditionSetIdentifiers = this._hasItemConditionSetIdentifiers(parent);
|
const hasItemConditionSetIdentifiers = this.#hasItemConditionSetIdentifiers(parent);
|
||||||
const existingMappedKeyString = this.idMap[originalKeyString];
|
const existingMappedKeyString = this.idMap[originalKeyString];
|
||||||
let copy;
|
let copy;
|
||||||
|
|
||||||
if (!existingMappedKeyString) {
|
if (!existingMappedKeyString) {
|
||||||
copy = this._copy(child);
|
copy = this.#copy(child);
|
||||||
copy.identifier.key = uuid();
|
copy.identifier.key = uuid();
|
||||||
|
|
||||||
if (!conditionSetIdentifier && !hasItemConditionSetIdentifiers) {
|
if (!conditionSetIdentifier && !hasItemConditionSetIdentifiers) {
|
||||||
copy.location = parentKeyString;
|
copy.location = parentKeyString;
|
||||||
}
|
}
|
||||||
|
|
||||||
let newKeyString = this._getKeystring(copy);
|
let newKeyString = this.#getKeystring(copy);
|
||||||
this.idMap[originalKeyString] = newKeyString;
|
this.idMap[originalKeyString] = newKeyString;
|
||||||
this.tree[newKeyString] = copy;
|
this.tree[newKeyString] = copy;
|
||||||
} else {
|
} else {
|
||||||
@ -166,7 +221,7 @@ export default class ExportAsJSONAction {
|
|||||||
if (conditionSetIdentifier || hasItemConditionSetIdentifiers) {
|
if (conditionSetIdentifier || hasItemConditionSetIdentifiers) {
|
||||||
// update objectStyle object
|
// update objectStyle object
|
||||||
if (conditionSetIdentifier) {
|
if (conditionSetIdentifier) {
|
||||||
const directObjectStylesIdentifier = this.openmct.objects.areIdsEqual(
|
const directObjectStylesIdentifier = this.#openmct.objects.areIdsEqual(
|
||||||
parent.configuration.objectStyles.conditionSetIdentifier,
|
parent.configuration.objectStyles.conditionSetIdentifier,
|
||||||
child.identifier
|
child.identifier
|
||||||
);
|
);
|
||||||
@ -187,7 +242,7 @@ export default class ExportAsJSONAction {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
itemConditionSetIdentifier &&
|
itemConditionSetIdentifier &&
|
||||||
this.openmct.objects.areIdsEqual(itemConditionSetIdentifier, child.identifier)
|
this.#openmct.objects.areIdsEqual(itemConditionSetIdentifier, child.identifier)
|
||||||
) {
|
) {
|
||||||
parent.configuration.objectStyles[itemId].conditionSetIdentifier = copy.identifier;
|
parent.configuration.objectStyles[itemId].conditionSetIdentifier = copy.identifier;
|
||||||
this.tree[parentKeyString].configuration.objectStyles[itemId].conditionSetIdentifier =
|
this.tree[parentKeyString].configuration.objectStyles[itemId].conditionSetIdentifier =
|
||||||
@ -199,7 +254,7 @@ export default class ExportAsJSONAction {
|
|||||||
} else {
|
} else {
|
||||||
// just update parent
|
// just update parent
|
||||||
const index = parent.composition.findIndex((identifier) => {
|
const index = parent.composition.findIndex((identifier) => {
|
||||||
return this.openmct.objects.areIdsEqual(child.identifier, identifier);
|
return this.#openmct.objects.areIdsEqual(child.identifier, identifier);
|
||||||
});
|
});
|
||||||
|
|
||||||
parent.composition[index] = copy.identifier;
|
parent.composition[index] = copy.identifier;
|
||||||
@ -214,8 +269,8 @@ export default class ExportAsJSONAction {
|
|||||||
* @param {object} domainObject
|
* @param {object} domainObject
|
||||||
* @returns {string} A string representation of the given identifier, including namespace and key
|
* @returns {string} A string representation of the given identifier, including namespace and key
|
||||||
*/
|
*/
|
||||||
_getKeystring(domainObject) {
|
#getKeystring(domainObject) {
|
||||||
return this.openmct.objects.makeKeyString(domainObject.identifier);
|
return this.#openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -223,9 +278,9 @@ export default class ExportAsJSONAction {
|
|||||||
* @param {object} domainObject
|
* @param {object} domainObject
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
_isCreatableAndPersistable(domainObject) {
|
#isCreatableAndPersistable(domainObject) {
|
||||||
const type = this.openmct.types.get(domainObject.type);
|
const type = this.#openmct.types.get(domainObject.type);
|
||||||
const isPersistable = this.openmct.objects.isPersistable(domainObject.identifier);
|
const isPersistable = this.#openmct.objects.isPersistable(domainObject.identifier);
|
||||||
|
|
||||||
return type && type.definition.creatable && isPersistable;
|
return type && type.definition.creatable && isPersistable;
|
||||||
}
|
}
|
||||||
@ -236,10 +291,10 @@ export default class ExportAsJSONAction {
|
|||||||
* @param {object} parent
|
* @param {object} parent
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
_isLinkedObject(child, parent) {
|
#isLinkedObject(child, parent) {
|
||||||
const rootKeyString = this._getKeystring(this.root);
|
const rootKeyString = this.#getKeystring(this.root);
|
||||||
const childKeyString = this._getKeystring(child);
|
const childKeyString = this.#getKeystring(child);
|
||||||
const parentKeyString = this._getKeystring(parent);
|
const parentKeyString = this.#getKeystring(parent);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(child.location !== parentKeyString &&
|
(child.location !== parentKeyString &&
|
||||||
@ -249,11 +304,11 @@ export default class ExportAsJSONAction {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getConditionSetIdentifier(object) {
|
#getConditionSetIdentifier(object) {
|
||||||
return object.configuration?.objectStyles?.conditionSetIdentifier;
|
return object.configuration?.objectStyles?.conditionSetIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
_hasItemConditionSetIdentifiers(parent) {
|
#hasItemConditionSetIdentifiers(parent) {
|
||||||
const objectStyles = parent.configuration?.objectStyles;
|
const objectStyles = parent.configuration?.objectStyles;
|
||||||
|
|
||||||
for (const itemId in objectStyles) {
|
for (const itemId in objectStyles) {
|
||||||
@ -265,7 +320,7 @@ export default class ExportAsJSONAction {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getItemConditionSetIdentifiers(parent) {
|
#getItemConditionSetIdentifiers(parent) {
|
||||||
const objectStyles = parent.configuration?.objectStyles;
|
const objectStyles = parent.configuration?.objectStyles;
|
||||||
let identifiers = new Set();
|
let identifiers = new Set();
|
||||||
|
|
||||||
@ -280,10 +335,15 @@ export default class ExportAsJSONAction {
|
|||||||
return Array.from(identifiers);
|
return Array.from(identifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#resetCounts() {
|
||||||
|
this.totalToExport = 0;
|
||||||
|
this.exportedCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_rewriteReferences() {
|
#rewriteReferences() {
|
||||||
const oldKeyStrings = Object.keys(this.idMap);
|
const oldKeyStrings = Object.keys(this.idMap);
|
||||||
let treeString = JSON.stringify(this.tree);
|
let treeString = JSON.stringify(this.tree);
|
||||||
|
|
||||||
@ -291,8 +351,8 @@ export default class ExportAsJSONAction {
|
|||||||
// this will cover keyStrings, identifiers and identifiers created
|
// this will cover keyStrings, identifiers and identifiers created
|
||||||
// by hand that may be structured differently from those created with 'makeKeyString'
|
// by hand that may be structured differently from those created with 'makeKeyString'
|
||||||
const newKeyString = this.idMap[oldKeyString];
|
const newKeyString = this.idMap[oldKeyString];
|
||||||
const newIdentifier = JSON.stringify(this.openmct.objects.parseKeyString(newKeyString));
|
const newIdentifier = JSON.stringify(this.#openmct.objects.parseKeyString(newKeyString));
|
||||||
const oldIdentifier = this.openmct.objects.parseKeyString(oldKeyString);
|
const oldIdentifier = this.#openmct.objects.parseKeyString(oldKeyString);
|
||||||
const oldIdentifierNamespaceFirst = JSON.stringify(oldIdentifier);
|
const oldIdentifierNamespaceFirst = JSON.stringify(oldIdentifier);
|
||||||
const oldIdentifierKeyFirst = JSON.stringify({
|
const oldIdentifierKeyFirst = JSON.stringify({
|
||||||
key: oldIdentifier.key,
|
key: oldIdentifier.key,
|
||||||
@ -318,29 +378,35 @@ export default class ExportAsJSONAction {
|
|||||||
* @private
|
* @private
|
||||||
* @param {object} completedTree
|
* @param {object} completedTree
|
||||||
*/
|
*/
|
||||||
_saveAs(completedTree) {
|
saveAs(completedTree) {
|
||||||
this.JSONExportService.export(completedTree, { filename: this.root.name + '.json' });
|
this.JSONExportService.export(completedTree, { filename: this.root.name + '.json' });
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @returns {object}
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
_wrapTree() {
|
#wrapTree() {
|
||||||
return {
|
return {
|
||||||
openmct: this.tree,
|
openmct: this.tree,
|
||||||
rootId: this._getKeystring(this.root)
|
rootId: this.#getKeystring(this.root)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_decrementCallsAndSave() {
|
#decrementCallsAndSave() {
|
||||||
this.calls--;
|
this.calls--;
|
||||||
|
this.#updateProgress();
|
||||||
if (this.calls === 0) {
|
if (this.calls === 0) {
|
||||||
this._rewriteReferences();
|
this.#rewriteReferences();
|
||||||
this._saveAs(this._wrapTree());
|
this.dialog.dismiss();
|
||||||
|
|
||||||
|
this.#resetCounts();
|
||||||
|
this.saveAs(this.#wrapTree());
|
||||||
|
|
||||||
|
this.dialog = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_copy(object) {
|
#copy(object) {
|
||||||
return JSON.parse(JSON.stringify(object));
|
return JSON.parse(JSON.stringify(object));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ describe('Export as JSON plugin', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
spyOn(exportAsJSONAction, '_saveAs').and.callFake((completedTree) => {
|
spyOn(exportAsJSONAction, 'saveAs').and.callFake((completedTree) => {
|
||||||
expect(Object.keys(completedTree).length).toBe(2);
|
expect(Object.keys(completedTree).length).toBe(2);
|
||||||
expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
|
||||||
expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
|
||||||
@ -195,7 +195,7 @@ describe('Export as JSON plugin', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
spyOn(exportAsJSONAction, '_saveAs').and.callFake((completedTree) => {
|
spyOn(exportAsJSONAction, 'saveAs').and.callFake((completedTree) => {
|
||||||
expect(Object.keys(completedTree).length).toBe(2);
|
expect(Object.keys(completedTree).length).toBe(2);
|
||||||
expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
|
||||||
expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
|
||||||
@ -257,7 +257,7 @@ describe('Export as JSON plugin', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
spyOn(exportAsJSONAction, '_saveAs').and.callFake((completedTree) => {
|
spyOn(exportAsJSONAction, 'saveAs').and.callFake((completedTree) => {
|
||||||
expect(Object.keys(completedTree).length).toBe(2);
|
expect(Object.keys(completedTree).length).toBe(2);
|
||||||
expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
|
||||||
expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
|
||||||
@ -318,7 +318,7 @@ describe('Export as JSON plugin', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
spyOn(exportAsJSONAction, '_saveAs').and.callFake((completedTree) => {
|
spyOn(exportAsJSONAction, 'saveAs').and.callFake((completedTree) => {
|
||||||
expect(Object.keys(completedTree).length).toBe(2);
|
expect(Object.keys(completedTree).length).toBe(2);
|
||||||
expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
|
||||||
expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'rootId')).toBeTruthy();
|
||||||
@ -373,7 +373,7 @@ describe('Export as JSON plugin', () => {
|
|||||||
return Promise.resolve(child);
|
return Promise.resolve(child);
|
||||||
});
|
});
|
||||||
|
|
||||||
spyOn(exportAsJSONAction, '_saveAs').and.callFake((completedTree) => {
|
spyOn(exportAsJSONAction, 'saveAs').and.callFake((completedTree) => {
|
||||||
expect(Object.keys(completedTree).length).toBe(2);
|
expect(Object.keys(completedTree).length).toBe(2);
|
||||||
const conditionSetId = Object.keys(completedTree.openmct)[1];
|
const conditionSetId = Object.keys(completedTree.openmct)[1];
|
||||||
expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
|
expect(Object.prototype.hasOwnProperty.call(completedTree, 'openmct')).toBeTruthy();
|
||||||
|
@ -71,14 +71,14 @@ export default class LocalStorageObjectProvider {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
persistSpace(space) {
|
persistSpace(space) {
|
||||||
this.localStorage[this.spaceKey] = JSON.stringify(space);
|
this.localStorage.setItem(this.spaceKey, JSON.stringify(space));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
getSpace() {
|
getSpace() {
|
||||||
return this.localStorage[this.spaceKey];
|
return this.localStorage.getItem(this.spaceKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,7 +93,7 @@ export default class LocalStorageObjectProvider {
|
|||||||
*/
|
*/
|
||||||
initializeSpace() {
|
initializeSpace() {
|
||||||
if (this.isEmpty()) {
|
if (this.isEmpty()) {
|
||||||
this.localStorage[this.spaceKey] = JSON.stringify({});
|
this.localStorage.setItem(this.spaceKey, JSON.stringify({}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,6 +101,6 @@ export default class LocalStorageObjectProvider {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
isEmpty() {
|
isEmpty() {
|
||||||
return this.getSpace() === undefined;
|
return this.getSpace() === null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
import { saveAs } from 'file-saver';
|
||||||
import Moment from 'moment';
|
import Moment from 'moment';
|
||||||
import { saveAs } from 'saveAs';
|
|
||||||
|
|
||||||
import { NOTEBOOK_TYPE, RESTRICTED_NOTEBOOK_TYPE } from '../notebook-constants.js';
|
import { NOTEBOOK_TYPE, RESTRICTED_NOTEBOOK_TYPE } from '../notebook-constants.js';
|
||||||
const UNKNOWN_USER = 'Unknown';
|
const UNKNOWN_USER = 'Unknown';
|
||||||
|
@ -23,14 +23,12 @@
|
|||||||
<div class="c-progress-bar">
|
<div class="c-progress-bar">
|
||||||
<div
|
<div
|
||||||
class="c-progress-bar__bar"
|
class="c-progress-bar__bar"
|
||||||
:class="{ '--indeterminate': !model.progressPerc }"
|
:class="{ '--indeterminate': !progressPerc }"
|
||||||
:style="styleBarWidth"
|
:style="styleBarWidth"
|
||||||
></div>
|
></div>
|
||||||
<div v-if="model.progressText !== undefined" class="c-progress-bar__text">
|
<div v-if="progressText !== ''" class="c-progress-bar__text">
|
||||||
<span v-if="model.progressPerc && model.progressPerc > 0"
|
<span v-if="progressPerc > 0">{{ progressPerc }}% complete.</span>
|
||||||
>{{ model.progressPerc }}% complete.</span
|
{{ progressText }}
|
||||||
>
|
|
||||||
{{ model.progressText }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -38,14 +36,18 @@
|
|||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
model: {
|
progressPerc: {
|
||||||
type: Object,
|
type: Number,
|
||||||
required: true
|
default: 0
|
||||||
|
},
|
||||||
|
progressText: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
styleBarWidth() {
|
styleBarWidth() {
|
||||||
return this.model.progressPerc ? `width: ${this.model.progressPerc}%;` : '';
|
return this.progressPerc ? `width: ${this.progressPerc}%;` : '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
import mount from 'utils/mount';
|
import mount from 'utils/mount';
|
||||||
import { createOpenMct, resetApplicationState } from 'utils/testing';
|
import { createOpenMct, resetApplicationState } from 'utils/testing';
|
||||||
import { mockLocalStorage } from 'utils/testing/mockLocalStorage';
|
import { mockLocalStorage } from 'utils/testing/mockLocalStorage';
|
||||||
|
import { nextTick } from 'vue';
|
||||||
|
|
||||||
import StylesView from '@/plugins/condition/components/inspector/StylesView.vue';
|
import StylesView from '@/plugins/condition/components/inspector/StylesView.vue';
|
||||||
|
|
||||||
@ -67,7 +68,7 @@ describe('the inspector', () => {
|
|||||||
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(1);
|
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display all saved styles', () => {
|
it('should display all saved styles', async () => {
|
||||||
selection = mockTelemetryTableSelection;
|
selection = mockTelemetryTableSelection;
|
||||||
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
|
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
|
||||||
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
|
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
|
||||||
@ -75,10 +76,9 @@ describe('the inspector', () => {
|
|||||||
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(0);
|
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(0);
|
||||||
stylesViewComponent.$refs.root.saveStyle(mockStyle);
|
stylesViewComponent.$refs.root.saveStyle(mockStyle);
|
||||||
|
|
||||||
return stylesViewComponent.$nextTick().then(() => {
|
await nextTick();
|
||||||
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(1);
|
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
xit('should allow a saved style to be applied', () => {
|
xit('should allow a saved style to be applied', () => {
|
||||||
spyOn(openmct.editor, 'isEditing').and.returnValue(true);
|
spyOn(openmct.editor, 'isEditing').and.returnValue(true);
|
||||||
@ -139,14 +139,14 @@ describe('the inspector', () => {
|
|||||||
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(20);
|
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(20);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow styles from multi-selections to be saved', () => {
|
it('should allow styles from multi-selections to be saved', async () => {
|
||||||
spyOn(openmct.editor, 'isEditing').and.returnValue(true);
|
spyOn(openmct.editor, 'isEditing').and.returnValue(true);
|
||||||
|
|
||||||
selection = mockMultiSelectionSameStyles;
|
selection = mockMultiSelectionSameStyles;
|
||||||
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
|
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
|
||||||
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
|
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
|
||||||
|
|
||||||
return stylesViewComponent.$nextTick().then(() => {
|
await nextTick();
|
||||||
const styleEditorComponent = stylesViewComponent.$refs.root.$refs.styleEditor;
|
const styleEditorComponent = stylesViewComponent.$refs.root.$refs.styleEditor;
|
||||||
const saveStyleButton = styleEditorComponent.$refs.saveStyleButton;
|
const saveStyleButton = styleEditorComponent.$refs.saveStyleButton;
|
||||||
|
|
||||||
@ -156,39 +156,36 @@ describe('the inspector', () => {
|
|||||||
|
|
||||||
expect(savedStylesViewComponent.$refs.root.$data.savedStyles.length).toBe(1);
|
expect(savedStylesViewComponent.$refs.root.$data.savedStyles.length).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('should prevent mixed styles from being saved', () => {
|
it('should prevent mixed styles from being saved', async () => {
|
||||||
spyOn(openmct.editor, 'isEditing').and.returnValue(true);
|
spyOn(openmct.editor, 'isEditing').and.returnValue(true);
|
||||||
|
|
||||||
selection = mockMultiSelectionMixedStyles;
|
selection = mockMultiSelectionMixedStyles;
|
||||||
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
|
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
|
||||||
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
|
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
|
||||||
|
|
||||||
return stylesViewComponent.$nextTick().then(() => {
|
await nextTick();
|
||||||
const styleEditorComponent = stylesViewComponent.$refs.root.$refs.styleEditor;
|
const styleEditorComponent = stylesViewComponent.$refs.root.$refs.styleEditor;
|
||||||
const saveStyleButton = styleEditorComponent.$refs.saveStyleButton;
|
const saveStyleButton = styleEditorComponent.$refs.saveStyleButton;
|
||||||
|
|
||||||
// Saving should not be enabled, thus the button ref should be undefined
|
// Saving should not be enabled, thus the button ref should be undefined
|
||||||
expect(saveStyleButton).toBe(undefined);
|
expect(saveStyleButton).toBe(undefined);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('should prevent non-specific styles from being saved', () => {
|
it('should prevent non-specific styles from being saved', async () => {
|
||||||
spyOn(openmct.editor, 'isEditing').and.returnValue(true);
|
spyOn(openmct.editor, 'isEditing').and.returnValue(true);
|
||||||
|
|
||||||
selection = mockMultiSelectionNonSpecificStyles;
|
selection = mockMultiSelectionNonSpecificStyles;
|
||||||
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
|
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
|
||||||
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
|
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
|
||||||
|
|
||||||
return stylesViewComponent.$nextTick().then(() => {
|
await nextTick();
|
||||||
const styleEditorComponent = stylesViewComponent.$refs.root.$refs.styleEditor;
|
const styleEditorComponent = stylesViewComponent.$refs.root.$refs.styleEditor;
|
||||||
const saveStyleButton = styleEditorComponent.$refs.saveStyleButton;
|
const saveStyleButton = styleEditorComponent.$refs.saveStyleButton;
|
||||||
|
|
||||||
// Saving should not be enabled, thus the button ref should be undefined
|
// Saving should not be enabled, thus the button ref should be undefined
|
||||||
expect(saveStyleButton).toBe(undefined);
|
expect(saveStyleButton).toBe(undefined);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
function createViewComponent(component) {
|
function createViewComponent(component) {
|
||||||
const element = document.createElement('div');
|
const element = document.createElement('div');
|
||||||
|
@ -10,7 +10,7 @@ export function mockLocalStorage() {
|
|||||||
store = {};
|
store = {};
|
||||||
|
|
||||||
function getItem(key) {
|
function getItem(key) {
|
||||||
return store[key];
|
return store[key] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setItem(key, value) {
|
function setItem(key, value) {
|
||||||
|
Loading…
Reference in New Issue
Block a user