Compare commits
	
		
			5 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					484226b229 | ||
| 
						 | 
					abfa971dd6 | ||
| 
						 | 
					6d50dd571a | ||
| 
						 | 
					b58e38ee15 | ||
| 
						 | 
					d2923a672c | 
@@ -22,14 +22,14 @@
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Collection of Visual Tests set to run in a default context. The tests within this suite
 | 
			
		||||
are only meant to run against openmct's app.js started by `npm run start` within the 
 | 
			
		||||
are only meant to run against openmct's app.js started by `npm run start` within the
 | 
			
		||||
`./e2e/playwright-visual.config.js` file.
 | 
			
		||||
 | 
			
		||||
These should only use functional expect statements to verify assumptions about the state 
 | 
			
		||||
These should only use functional expect statements to verify assumptions about the state
 | 
			
		||||
in a test and not for functional verification of correctness. Visual tests are not supposed
 | 
			
		||||
to "fail" on assertions. Instead, they should be used to detect changes between builds or branches.
 | 
			
		||||
 | 
			
		||||
Note: Larger testsuite sizes are OK due to the setup time associated with these tests. 
 | 
			
		||||
Note: Larger testsuite sizes are OK due to the setup time associated with these tests.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
const { test, expect } = require('@playwright/test');
 | 
			
		||||
@@ -111,3 +111,50 @@ test('Visual - Default Condition Widget', async ({ page }) => {
 | 
			
		||||
    await page.waitForTimeout(VISUAL_GRACE_PERIOD);
 | 
			
		||||
    await percySnapshot(page, 'Default Condition Widget');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test.skip('Visual - Display layout items view', async ({ page }) => {
 | 
			
		||||
    //Go to baseURL
 | 
			
		||||
    await page.goto('/', { waitUntil: 'networkidle' });
 | 
			
		||||
 | 
			
		||||
    //Click the Create button
 | 
			
		||||
    await page.click('button:has-text("Create")');
 | 
			
		||||
 | 
			
		||||
    // Click text=Display Layout
 | 
			
		||||
    await page.click('text=Display Layout');
 | 
			
		||||
 | 
			
		||||
    // Click text=OK
 | 
			
		||||
    await page.click('text=OK');
 | 
			
		||||
 | 
			
		||||
    // Take a snapshot of the newly created Display Layout object
 | 
			
		||||
    await page.waitForTimeout(VISUAL_GRACE_PERIOD);
 | 
			
		||||
    await percySnapshot(page, 'Default Display Layout');
 | 
			
		||||
 | 
			
		||||
    // Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1
 | 
			
		||||
    await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
 | 
			
		||||
 | 
			
		||||
    // Click text=Save and Finish Editing
 | 
			
		||||
    await page.locator('text=Save and Finish Editing').click();
 | 
			
		||||
 | 
			
		||||
    //Click the Create button
 | 
			
		||||
    await page.click('button:has-text("Create")');
 | 
			
		||||
 | 
			
		||||
    // Click text=Sine Wave Generator
 | 
			
		||||
    await page.click('text=Sine Wave Generator');
 | 
			
		||||
 | 
			
		||||
    // Click text=Save In Open MCT No items >> input[type="search"]
 | 
			
		||||
    await page.locator('text=Save In Open MCT No items >> input[type="search"]').click();
 | 
			
		||||
 | 
			
		||||
    // Fill text=Save In Open MCT No items >> input[type="search"]
 | 
			
		||||
    await page.locator('text=Save In Open MCT No items >> input[type="search"]').fill('Unnamed Display Layout');
 | 
			
		||||
 | 
			
		||||
    // Click text=OK Cancel
 | 
			
		||||
    await page.locator('text=OK Cancel').click();
 | 
			
		||||
 | 
			
		||||
    // Click text=OK
 | 
			
		||||
    await page.click('text=OK');
 | 
			
		||||
 | 
			
		||||
    // Take a snapshot of the newly created Display Layout object
 | 
			
		||||
    await page.waitForTimeout(VISUAL_GRACE_PERIOD);
 | 
			
		||||
    await percySnapshot(page, 'Default Sine Wave Generator');
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "openmct",
 | 
			
		||||
  "version": "2.0.1-SNAPSHOT",
 | 
			
		||||
  "version": "2.0.1",
 | 
			
		||||
  "description": "The Open MCT core platform",
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@braintree/sanitize-url": "6.0.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -269,7 +269,6 @@ define([
 | 
			
		||||
        this.install(this.plugins.ViewDatumAction());
 | 
			
		||||
        this.install(this.plugins.ViewLargeAction());
 | 
			
		||||
        this.install(this.plugins.ObjectInterceptors());
 | 
			
		||||
        this.install(this.plugins.NonEditableFolder());
 | 
			
		||||
        this.install(this.plugins.DeviceClassifier());
 | 
			
		||||
        this.install(this.plugins.UserIndicator());
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,8 @@ export default function (folderName, couchPlugin, searchFilter) {
 | 
			
		||||
                    return Promise.resolve({
 | 
			
		||||
                        identifier,
 | 
			
		||||
                        type: 'folder',
 | 
			
		||||
                        name: folderName || "CouchDB Documents"
 | 
			
		||||
                        name: folderName || "CouchDB Documents",
 | 
			
		||||
                        location: 'ROOT'
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -85,7 +85,8 @@ describe('the plugin', function () {
 | 
			
		||||
            expect(object).toEqual({
 | 
			
		||||
                identifier,
 | 
			
		||||
                type: 'folder',
 | 
			
		||||
                name: "CouchDB Documents"
 | 
			
		||||
                name: 'CouchDB Documents',
 | 
			
		||||
                location: 'ROOT'
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -325,16 +325,7 @@ export default {
 | 
			
		||||
            return item && (item.type === type);
 | 
			
		||||
        },
 | 
			
		||||
        canPersistObject(item) {
 | 
			
		||||
            // for now the only way to tell if an object can be persisted is if it is creatable.
 | 
			
		||||
            let creatable = false;
 | 
			
		||||
            if (item) {
 | 
			
		||||
                const type = this.openmct.types.get(item.type);
 | 
			
		||||
                if (type && type.definition) {
 | 
			
		||||
                    creatable = (type.definition.creatable !== undefined && (type.definition.creatable === 'true' || type.definition.creatable === true));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return creatable;
 | 
			
		||||
            return this.openmct.objects.isPersistable(item.identifier);
 | 
			
		||||
        },
 | 
			
		||||
        hasConditionalStyle(domainObject, layoutItem) {
 | 
			
		||||
            const id = layoutItem ? layoutItem.id : undefined;
 | 
			
		||||
 
 | 
			
		||||
@@ -166,7 +166,7 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        gridSize() {
 | 
			
		||||
            return this.domainObject.configuration.layoutGrid;
 | 
			
		||||
            return this.domainObject.configuration.layoutGrid.map(Number);
 | 
			
		||||
        },
 | 
			
		||||
        layoutItems() {
 | 
			
		||||
            return this.domainObject.configuration.items;
 | 
			
		||||
 
 | 
			
		||||
@@ -97,13 +97,16 @@ export default class DuplicateAction {
 | 
			
		||||
 | 
			
		||||
    validate(currentParent) {
 | 
			
		||||
        return (data) => {
 | 
			
		||||
            const parentCandidatePath = data.value;
 | 
			
		||||
            const parentCandidate = parentCandidatePath[0];
 | 
			
		||||
            const parentCandidate = data.value[0];
 | 
			
		||||
 | 
			
		||||
            let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
 | 
			
		||||
            let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
 | 
			
		||||
            let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
 | 
			
		||||
 | 
			
		||||
            if (!this.openmct.objects.isPersistable(parentCandidate.identifier)) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!parentCandidateKeystring || !currentParentKeystring) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
@@ -122,13 +125,14 @@ export default class DuplicateAction {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    appliesTo(objectPath) {
 | 
			
		||||
        let parent = objectPath[1];
 | 
			
		||||
        let parentType = parent && this.openmct.types.get(parent.type);
 | 
			
		||||
        let child = objectPath[0];
 | 
			
		||||
        let childType = child && this.openmct.types.get(child.type);
 | 
			
		||||
        let locked = child.locked ? child.locked : parent && parent.locked;
 | 
			
		||||
        const parent = objectPath[1];
 | 
			
		||||
        const parentType = parent && this.openmct.types.get(parent.type);
 | 
			
		||||
        const child = objectPath[0];
 | 
			
		||||
        const childType = child && this.openmct.types.get(child.type);
 | 
			
		||||
        const locked = child.locked ? child.locked : parent && parent.locked;
 | 
			
		||||
        const isPersistable = this.openmct.objects.isPersistable(child.identifier);
 | 
			
		||||
 | 
			
		||||
        if (locked) {
 | 
			
		||||
        if (locked || !isPersistable) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,7 @@ export default class ExportAsJSONAction {
 | 
			
		||||
    appliesTo(objectPath) {
 | 
			
		||||
        let domainObject = objectPath[0];
 | 
			
		||||
 | 
			
		||||
        return this._isCreatable(domainObject);
 | 
			
		||||
        return this._isCreatableAndPersistable(domainObject);
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
@@ -80,10 +80,11 @@ export default class ExportAsJSONAction {
 | 
			
		||||
     * @param {object} domainObject
 | 
			
		||||
     * @returns {boolean}
 | 
			
		||||
     */
 | 
			
		||||
    _isCreatable(domainObject) {
 | 
			
		||||
    _isCreatableAndPersistable(domainObject) {
 | 
			
		||||
        const type = this.openmct.types.get(domainObject.type);
 | 
			
		||||
        const isPersistable = this.openmct.objects.isPersistable(domainObject.identifier);
 | 
			
		||||
 | 
			
		||||
        return type && type.definition.creatable;
 | 
			
		||||
        return type && type.definition.creatable && isPersistable;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * @private
 | 
			
		||||
@@ -170,7 +171,7 @@ export default class ExportAsJSONAction {
 | 
			
		||||
                .then((children) => {
 | 
			
		||||
                    children.forEach((child, index) => {
 | 
			
		||||
                        // Only export if object is creatable
 | 
			
		||||
                        if (this._isCreatable(child)) {
 | 
			
		||||
                        if (this._isCreatableAndPersistable(child)) {
 | 
			
		||||
                            // Prevents infinite export of self-contained objs
 | 
			
		||||
                            if (!Object.prototype.hasOwnProperty.call(this.tree, this._getId(child))) {
 | 
			
		||||
                                // If object is a link to something absent from
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,10 @@ describe('Export as JSON plugin', () => {
 | 
			
		||||
 | 
			
		||||
    it('ExportAsJSONAction applies to folder', () => {
 | 
			
		||||
        domainObject = {
 | 
			
		||||
            identifier: {
 | 
			
		||||
                key: 'export-testing',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            },
 | 
			
		||||
            composition: [],
 | 
			
		||||
            location: 'mine',
 | 
			
		||||
            modified: 1640115501237,
 | 
			
		||||
@@ -40,6 +44,10 @@ describe('Export as JSON plugin', () => {
 | 
			
		||||
 | 
			
		||||
    it('ExportAsJSONAction applies to telemetry.plot.overlay', () => {
 | 
			
		||||
        domainObject = {
 | 
			
		||||
            identifier: {
 | 
			
		||||
                key: 'export-testing',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            },
 | 
			
		||||
            composition: [],
 | 
			
		||||
            location: 'mine',
 | 
			
		||||
            modified: 1640115501237,
 | 
			
		||||
@@ -53,6 +61,10 @@ describe('Export as JSON plugin', () => {
 | 
			
		||||
 | 
			
		||||
    it('ExportAsJSONAction applies to telemetry.plot.stacked', () => {
 | 
			
		||||
        domainObject = {
 | 
			
		||||
            identifier: {
 | 
			
		||||
                key: 'export-testing',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            },
 | 
			
		||||
            composition: [],
 | 
			
		||||
            location: 'mine',
 | 
			
		||||
            modified: 1640115501237,
 | 
			
		||||
@@ -64,16 +76,24 @@ describe('Export as JSON plugin', () => {
 | 
			
		||||
        expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(true);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('ExportAsJSONAction applies does not applies to non-creatable objects', () => {
 | 
			
		||||
    it('ExportAsJSONAction does not applie to non-persistable objects', () => {
 | 
			
		||||
        domainObject = {
 | 
			
		||||
            identifier: {
 | 
			
		||||
                key: 'export-testing',
 | 
			
		||||
                namespace: ''
 | 
			
		||||
            },
 | 
			
		||||
            composition: [],
 | 
			
		||||
            location: 'mine',
 | 
			
		||||
            modified: 1640115501237,
 | 
			
		||||
            name: 'Non Editable Folder',
 | 
			
		||||
            persisted: 1640115501237,
 | 
			
		||||
            type: 'noneditable.folder'
 | 
			
		||||
            type: 'folder'
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        spyOn(openmct.objects, 'getProvider').and.callFake(() => {
 | 
			
		||||
            return { get: () => domainObject };
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        expect(exportAsJSONAction.appliesTo([domainObject])).toEqual(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@
 | 
			
		||||
        <template v-for="(container, index) in containers">
 | 
			
		||||
            <drop-hint
 | 
			
		||||
                v-if="index === 0 && containers.length > 1"
 | 
			
		||||
                :key="index"
 | 
			
		||||
                :key="`hint-top-${container.id}`"
 | 
			
		||||
                class="c-fl-frame__drop-hint"
 | 
			
		||||
                :index="-1"
 | 
			
		||||
                :allow-drop="allowContainerDrop"
 | 
			
		||||
@@ -51,7 +51,7 @@
 | 
			
		||||
            />
 | 
			
		||||
 | 
			
		||||
            <container-component
 | 
			
		||||
                :key="container.id"
 | 
			
		||||
                :key="`component-${container.id}`"
 | 
			
		||||
                class="c-fl__container"
 | 
			
		||||
                :index="index"
 | 
			
		||||
                :container="container"
 | 
			
		||||
@@ -66,7 +66,7 @@
 | 
			
		||||
 | 
			
		||||
            <resize-handle
 | 
			
		||||
                v-if="index !== (containers.length - 1)"
 | 
			
		||||
                :key="index"
 | 
			
		||||
                :key="`handle-${container.id}`"
 | 
			
		||||
                :index="index"
 | 
			
		||||
                :orientation="rowsLayout ? 'vertical' : 'horizontal'"
 | 
			
		||||
                :is-editing="isEditing"
 | 
			
		||||
@@ -77,7 +77,7 @@
 | 
			
		||||
 | 
			
		||||
            <drop-hint
 | 
			
		||||
                v-if="containers.length > 1"
 | 
			
		||||
                :key="index"
 | 
			
		||||
                :key="`hint-bottom-${container.id}`"
 | 
			
		||||
                class="c-fl-frame__drop-hint"
 | 
			
		||||
                :index="index"
 | 
			
		||||
                :allow-drop="allowContainerDrop"
 | 
			
		||||
 
 | 
			
		||||
@@ -101,7 +101,10 @@ export default class CreateWizard {
 | 
			
		||||
        // Ensure there is always a 'save in' section
 | 
			
		||||
        if (includeLocation) {
 | 
			
		||||
            function validateLocation(data) {
 | 
			
		||||
                return self.openmct.composition.checkPolicy(data.value[0], domainObject);
 | 
			
		||||
                const policyCheck = self.openmct.composition.checkPolicy(data.value[0], domainObject);
 | 
			
		||||
                const parentIsPersistable = self.openmct.objects.isPersistable(data.value[0].identifier);
 | 
			
		||||
 | 
			
		||||
                return policyCheck && parentIsPersistable;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            sections.push({
 | 
			
		||||
 
 | 
			
		||||
@@ -33,10 +33,7 @@ export default class LinkAction {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    appliesTo(objectPath) {
 | 
			
		||||
        let domainObject = objectPath[0];
 | 
			
		||||
        let type = domainObject && this.openmct.types.get(domainObject.type);
 | 
			
		||||
 | 
			
		||||
        return type && type.definition.creatable;
 | 
			
		||||
        return true; // link away!
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    invoke(objectPath) {
 | 
			
		||||
@@ -77,6 +74,7 @@ export default class LinkAction {
 | 
			
		||||
                        {
 | 
			
		||||
                            name: "location",
 | 
			
		||||
                            control: "locator",
 | 
			
		||||
                            parent: parentDomainObject,
 | 
			
		||||
                            required: true,
 | 
			
		||||
                            validate: this.validate(parentDomainObject),
 | 
			
		||||
                            key: 'location'
 | 
			
		||||
@@ -92,11 +90,26 @@ export default class LinkAction {
 | 
			
		||||
 | 
			
		||||
    validate(currentParent) {
 | 
			
		||||
        return (data) => {
 | 
			
		||||
 | 
			
		||||
            // default current parent to ROOT, if it's undefined, then it's a root level item
 | 
			
		||||
            if (currentParent === undefined) {
 | 
			
		||||
                currentParent = {
 | 
			
		||||
                    identifier: {
 | 
			
		||||
                        key: 'ROOT',
 | 
			
		||||
                        namespace: ''
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const parentCandidate = data.value[0];
 | 
			
		||||
            const currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
 | 
			
		||||
            const parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
 | 
			
		||||
            const objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
 | 
			
		||||
 | 
			
		||||
            if (!this.openmct.objects.isPersistable(parentCandidate.identifier)) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!parentCandidateKeystring || !currentParentKeystring) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -126,6 +126,7 @@ export default class MoveAction {
 | 
			
		||||
                        {
 | 
			
		||||
                            name: "Location",
 | 
			
		||||
                            control: "locator",
 | 
			
		||||
                            parent: parentDomainObject,
 | 
			
		||||
                            required: true,
 | 
			
		||||
                            validate: this.validate(parentDomainObject),
 | 
			
		||||
                            key: 'location'
 | 
			
		||||
@@ -144,6 +145,10 @@ export default class MoveAction {
 | 
			
		||||
            const parentCandidatePath = data.value;
 | 
			
		||||
            const parentCandidate = parentCandidatePath[0];
 | 
			
		||||
 | 
			
		||||
            if (!this.openmct.objects.isPersistable(parentCandidate.identifier)) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
 | 
			
		||||
            let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
 | 
			
		||||
            let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
 | 
			
		||||
@@ -174,8 +179,9 @@ export default class MoveAction {
 | 
			
		||||
        let parentType = parent && this.openmct.types.get(parent.type);
 | 
			
		||||
        let child = objectPath[0];
 | 
			
		||||
        let childType = child && this.openmct.types.get(child.type);
 | 
			
		||||
        let isPersistable = this.openmct.objects.isPersistable(child.identifier);
 | 
			
		||||
 | 
			
		||||
        if (child.locked || (parent && parent.locked)) {
 | 
			
		||||
        if (child.locked || (parent && parent.locked) || !isPersistable) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2022, 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
export default function () {
 | 
			
		||||
    return function (openmct) {
 | 
			
		||||
        openmct.types.addType("noneditable.folder", {
 | 
			
		||||
            name: "Non-Editable Folder",
 | 
			
		||||
            key: "noneditable.folder",
 | 
			
		||||
            description: "Create folders to organize other objects or links to objects without the ability to edit it's properties.",
 | 
			
		||||
            cssClass: "icon-folder",
 | 
			
		||||
            creatable: false
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
@@ -1,50 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2022, 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 {
 | 
			
		||||
    createOpenMct,
 | 
			
		||||
    resetApplicationState
 | 
			
		||||
} from 'utils/testing';
 | 
			
		||||
 | 
			
		||||
describe("the plugin", () => {
 | 
			
		||||
    const NON_EDITABLE_FOLDER_KEY = 'noneditable.folder';
 | 
			
		||||
    let openmct;
 | 
			
		||||
 | 
			
		||||
    beforeEach((done) => {
 | 
			
		||||
        openmct = createOpenMct();
 | 
			
		||||
        openmct.install(openmct.plugins.NonEditableFolder());
 | 
			
		||||
 | 
			
		||||
        openmct.on('start', done);
 | 
			
		||||
        openmct.startHeadless();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    afterEach(() => {
 | 
			
		||||
        return resetApplicationState(openmct);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('adds the new non-editable folder type', () => {
 | 
			
		||||
        const type = openmct.types.get(NON_EDITABLE_FOLDER_KEY);
 | 
			
		||||
 | 
			
		||||
        expect(type).toBeDefined();
 | 
			
		||||
        expect(type.definition.creatable).toBeFalse();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
@@ -61,7 +61,6 @@ define([
 | 
			
		||||
    './URLTimeSettingsSynchronizer/plugin',
 | 
			
		||||
    './notificationIndicator/plugin',
 | 
			
		||||
    './newFolderAction/plugin',
 | 
			
		||||
    './nonEditableFolder/plugin',
 | 
			
		||||
    './persistence/couch/plugin',
 | 
			
		||||
    './defaultRootName/plugin',
 | 
			
		||||
    './plan/plugin',
 | 
			
		||||
@@ -119,7 +118,6 @@ define([
 | 
			
		||||
    URLTimeSettingsSynchronizer,
 | 
			
		||||
    NotificationIndicator,
 | 
			
		||||
    NewFolderAction,
 | 
			
		||||
    NonEditableFolder,
 | 
			
		||||
    CouchDBPlugin,
 | 
			
		||||
    DefaultRootName,
 | 
			
		||||
    PlanLayout,
 | 
			
		||||
@@ -197,7 +195,6 @@ define([
 | 
			
		||||
    plugins.URLTimeSettingsSynchronizer = URLTimeSettingsSynchronizer.default;
 | 
			
		||||
    plugins.NotificationIndicator = NotificationIndicator.default;
 | 
			
		||||
    plugins.NewFolderAction = NewFolderAction.default;
 | 
			
		||||
    plugins.NonEditableFolder = NonEditableFolder.default;
 | 
			
		||||
    plugins.ISOTimeFormat = ISOTimeFormat.default;
 | 
			
		||||
    plugins.DefaultRootName = DefaultRootName.default;
 | 
			
		||||
    plugins.PlanLayout = PlanLayout.default;
 | 
			
		||||
 
 | 
			
		||||
@@ -92,20 +92,35 @@ export default class RemoveAction {
 | 
			
		||||
            this.openmct.editor.save();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
 | 
			
		||||
        const isAlias = parentKeyString !== child.location;
 | 
			
		||||
 | 
			
		||||
        if (!isAlias) {
 | 
			
		||||
        if (!this.isAlias(child, parent)) {
 | 
			
		||||
            this.openmct.objects.mutate(child, 'location', null);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isAlias(child, parent) {
 | 
			
		||||
        if (parent === undefined) {
 | 
			
		||||
            // then it's a root item, not an alias
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
 | 
			
		||||
        const childLocation = child.location;
 | 
			
		||||
 | 
			
		||||
        return childLocation !== parentKeyString;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    appliesTo(objectPath) {
 | 
			
		||||
        let parent = objectPath[1];
 | 
			
		||||
        let parentType = parent && this.openmct.types.get(parent.type);
 | 
			
		||||
        let child = objectPath[0];
 | 
			
		||||
        let locked = child.locked ? child.locked : parent && parent.locked;
 | 
			
		||||
        let isEditing = this.openmct.editor.isEditing();
 | 
			
		||||
        const parent = objectPath[1];
 | 
			
		||||
        const parentType = parent && this.openmct.types.get(parent.type);
 | 
			
		||||
        const child = objectPath[0];
 | 
			
		||||
        const locked = child.locked ? child.locked : parent && parent.locked;
 | 
			
		||||
        const isEditing = this.openmct.editor.isEditing();
 | 
			
		||||
        const isPersistable = this.openmct.objects.isPersistable(child.identifier);
 | 
			
		||||
        const isAlias = this.isAlias(child, parent);
 | 
			
		||||
 | 
			
		||||
        if (locked || (!isPersistable && !isAlias)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isEditing) {
 | 
			
		||||
            let currentItemInView = this.openmct.router.path[0];
 | 
			
		||||
@@ -116,10 +131,6 @@ export default class RemoveAction {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (locked) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return parentType
 | 
			
		||||
            && parentType.definition.creatable
 | 
			
		||||
            && Array.isArray(parent.composition);
 | 
			
		||||
 
 | 
			
		||||
@@ -31,9 +31,11 @@
 | 
			
		||||
>
 | 
			
		||||
    <div class="c-conductor__time-bounds">
 | 
			
		||||
        <conductor-inputs-fixed v-if="isFixed"
 | 
			
		||||
                                :input-bounds="viewBounds"
 | 
			
		||||
                                @updated="saveFixedOffsets"
 | 
			
		||||
        />
 | 
			
		||||
        <conductor-inputs-realtime v-else
 | 
			
		||||
                                   :input-bounds="viewBounds"
 | 
			
		||||
                                   @updated="saveClockOffsets"
 | 
			
		||||
        />
 | 
			
		||||
        <ConductorModeIcon class="c-conductor__mode-icon" />
 | 
			
		||||
 
 | 
			
		||||
@@ -71,6 +71,12 @@ export default {
 | 
			
		||||
            default() {
 | 
			
		||||
                return undefined;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        inputBounds: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            default() {
 | 
			
		||||
                return undefined;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
@@ -98,6 +104,12 @@ export default {
 | 
			
		||||
    watch: {
 | 
			
		||||
        keyString() {
 | 
			
		||||
            this.setTimeContext();
 | 
			
		||||
        },
 | 
			
		||||
        inputBounds: {
 | 
			
		||||
            handler(newBounds) {
 | 
			
		||||
                this.handleNewBounds(newBounds);
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
            ref="startOffset"
 | 
			
		||||
            class="c-button c-conductor__delta-button"
 | 
			
		||||
            title="Set the time offset after now"
 | 
			
		||||
            @click.prevent="showTimePopupStart"
 | 
			
		||||
            @click.prevent.stop="showTimePopupStart"
 | 
			
		||||
        >
 | 
			
		||||
            {{ offsets.start }}
 | 
			
		||||
        </button>
 | 
			
		||||
@@ -60,7 +60,7 @@
 | 
			
		||||
            ref="endOffset"
 | 
			
		||||
            class="c-button c-conductor__delta-button"
 | 
			
		||||
            title="Set the time offset preceding now"
 | 
			
		||||
            @click.prevent="showTimePopupEnd"
 | 
			
		||||
            @click.prevent.stop="showTimePopupEnd"
 | 
			
		||||
        >
 | 
			
		||||
            {{ offsets.end }}
 | 
			
		||||
        </button>
 | 
			
		||||
@@ -86,6 +86,12 @@ export default {
 | 
			
		||||
            default() {
 | 
			
		||||
                return undefined;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        inputBounds: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            default() {
 | 
			
		||||
                return undefined;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
@@ -118,6 +124,12 @@ export default {
 | 
			
		||||
    watch: {
 | 
			
		||||
        keyString() {
 | 
			
		||||
            this.setTimeContext();
 | 
			
		||||
        },
 | 
			
		||||
        inputBounds: {
 | 
			
		||||
            handler(newBounds) {
 | 
			
		||||
                this.handleNewBounds(newBounds);
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
 
 | 
			
		||||
@@ -122,6 +122,9 @@
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
        height: 0; // Chrome 73 overflow bug fix
 | 
			
		||||
        overflow: auto;
 | 
			
		||||
        //To accommodate independent time conductor controls
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
 | 
			
		||||
        .u-fills-container {
 | 
			
		||||
            // Expand component types that fill a container
 | 
			
		||||
 
 | 
			
		||||
@@ -258,10 +258,12 @@ export default {
 | 
			
		||||
        if (!this.isSelectorTree) {
 | 
			
		||||
            await this.syncTreeOpenItems();
 | 
			
		||||
        } else {
 | 
			
		||||
            const objectPath = await this.openmct.objects.getOriginalPath(this.initialSelection.identifier);
 | 
			
		||||
            const navigationPath = this.buildNavigationPath(objectPath);
 | 
			
		||||
            if (this.initialSelection.identifier) {
 | 
			
		||||
                const objectPath = await this.openmct.objects.getOriginalPath(this.initialSelection.identifier);
 | 
			
		||||
                const navigationPath = this.buildNavigationPath(objectPath);
 | 
			
		||||
 | 
			
		||||
            this.openAndScrollTo(navigationPath);
 | 
			
		||||
                this.openAndScrollTo(navigationPath);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    created() {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user