Skip to content

Commit

Permalink
[TypeRegistry, Create Menu] implement selectable plugins for create menu
Browse files Browse the repository at this point in the history
  • Loading branch information
johnriedel committed Feb 2, 2025
1 parent 1fde0d9 commit 7ae8fae
Show file tree
Hide file tree
Showing 14 changed files with 704 additions and 16 deletions.
102 changes: 102 additions & 0 deletions e2e/tests/framework/createButton.e2e.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*****************************************************************************
* 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 { expect, test } from '../../pluginFixtures.js';

test.describe('Create button', () => {
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
});
test('plugins can be selected', async ({ page }) => {
await page.goto('./#/browse/mine');

//verify clicking the Create button will both open AND close the Create menu
await page.getByRole('button', { name: 'Create' }).click();
await expect(page.getByLabel('Select Plugins...')).toBeVisible();
await page.getByRole('button', { name: 'Create' }).click();
await expect(page.getByLabel('Select Plugins...')).toBeHidden();

//verify plugin selector form is visible
await page.getByRole('button', { name: 'Create' }).click();
await expect(page.getByRole('menuitem', { name: 'Clock' })).toBeVisible();
await page.getByLabel('Select Plugins...').click();
await expect(page.locator('.js-form-title')).toContainText('Plugin Selector');
await expect(page.getByRole('button', { name: 'Save' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Cancel' })).toBeVisible();

//disable clock plugin
await expect(page.locator('.plugin-selector-row').first()).toContainText('Clock');
await expect(page.getByLabel('Clock plugin checkbox')).toBeChecked();
await expect(page.getByLabel('Clock plugin checkbox')).toBeEnabled();
await page.getByLabel('Clock plugin checkbox').uncheck();
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.locator('.js-form-title')).toBeHidden();

//verify Clock plugin option is no longer in Create menu
await page.getByRole('button', { name: 'Create' }).click();
await expect(page.getByRole('menuitem', { name: 'Clock' })).toBeHidden();

//re-enable clock plugin
await page.getByLabel('Select Plugins...').click();
await expect(page.locator('.js-form-title')).toContainText('Plugin Selector');
await expect(page.getByLabel('Clock plugin checkbox')).not.toBeChecked();
await expect(page.getByLabel('Clock plugin checkbox')).toBeEnabled();
await page.getByLabel('Clock plugin checkbox').click();
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.locator('.js-form-title')).toBeHidden();

//verify Clock plugin option appears in Create menu
await page.getByRole('button', { name: 'Create' }).click();
await expect(page.getByRole('menuitem', { name: 'Clock' })).toBeVisible();

//activate clock plugin
await page.getByRole('menuitem', { name: 'Clock' }).click();
await page.getByRole('button', { name: 'Save' }).click();

//verify the active clock plugin cannot be disabled while in-use
await page.getByRole('button', { name: 'Create' }).click();
await page.getByLabel('Select Plugins...').click();
await expect(page.locator('.plugin-selector-row').first()).toContainText('Clock');
await expect(page.getByLabel('Clock plugin checkbox')).toBeChecked();
await expect(page.getByLabel('Clock plugin checkbox')).toBeDisabled();
await page.getByRole('button', { name: 'Cancel' }).click();

//remove active clock plugin
await page.getByLabel('Expand My Items folder').click();
await page.getByLabel('Navigate to Unnamed Clock clock Object').click({
button: 'right'
});
await page.getByLabel('Remove').click();
await expect(
page.getByText(
'Warning! This action will remove this object. Are you sure you want to continue?'
)
).toBeVisible();
await page.getByRole('button', { name: 'Ok', exact: true }).click();

//verify the active clock plugin can be disabled since the plugin is no longer in-use
await page.getByRole('button', { name: 'Create' }).click();
await page.getByLabel('Select Plugins...').click();
await expect(page.locator('.plugin-selector-row').first()).toContainText('Clock');
await expect(page.getByLabel('Clock plugin checkbox')).toBeChecked();
await expect(page.getByLabel('Clock plugin checkbox')).toBeEnabled();
});
});
2 changes: 1 addition & 1 deletion e2e/tests/functional/plugins/clocks/clock.e2e.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ test.describe('Clock Generator CRUD Operations', () => {
await page.getByRole('button', { name: 'Create' }).click();

// Click Clock
await page.getByRole('menuitem').first().click();
await page.getByRole('menuitem', { name: 'Clock' }).click();

// Click .icon-arrow-down
await page.locator('.icon-arrow-down').click();
Expand Down
22 changes: 20 additions & 2 deletions src/api/menu/components/SuperMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,29 @@
class="c-menu__section-separator"
></div>
<li v-if="actionGroups.length === 0" :key="index">No actions defined.</li>
</div></template
>
</div>
</template>
</ul>

<ul v-else class="c-super-menu__menu" role="menu">
<template v-if="options.menuHeaderActions">
<div>
<li
v-for="action in options.menuHeaderActions"
:key="action.name"
role="menuitem"
:class="action.cssClass"
:aria-label="action.name"
aria-describedby="item-description"
@click="action.onItemClicked"
@mouseover="toggleItemDescription(action)"
@mouseleave="toggleItemDescription()"
>
{{ action.name }}
</li>
<div role="separator" class="c-menu__section-separator"></div>
</div>
</template>
<li
v-for="action in options.actions"
:key="action.name"
Expand Down
36 changes: 34 additions & 2 deletions src/api/types/TypeRegistry.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,27 @@ export default class TypeRegistry {
* @type {Record<string, Type>}
*/
this.types = {};

/**
* @type {Record<string, Type>}
*/
this.deactivatedTypes = {};
}
/**
* Register a new object type.
*
* @param {string} typeKey a string identifier for this type
* @param {TypeDefinition} typeDef the type to add
* @param {boolean} isDeactivated if true, will load type in a deactivated state
*/
addType(typeKey, typeDef) {
addType(typeKey, typeDef, isDeactivated = false) {
this.standardizeType(typeDef);
this.types[typeKey] = new Type(typeDef);

if (isDeactivated) {
this.deactivatedTypes[typeKey] = new Type(typeDef);
} else {
this.types[typeKey] = new Type(typeDef);
}
}
/**
* Takes a typeDef, standardizes it, and logs warnings about unsupported
Expand All @@ -74,13 +85,26 @@ export default class TypeRegistry {
delete typeDef.label;
}
}
removeType(typeKey) {
delete this.types[typeKey];
}
removeDeactivatedType(typeKey) {
delete this.deactivatedTypes[typeKey];
}
/**
* List keys for all registered types.
* @returns {string[]} all registered type keys
*/
listKeys() {
return Object.keys(this.types);
}
/**
* List keys for all deactivated types.
* @returns {string[]} all deactivated type keys
*/
listDeactivatedKeys() {
return Object.keys(this.deactivatedTypes);
}
/**
* Retrieve a registered type by its key.
* @param {string} typeKey the key for this type
Expand All @@ -89,6 +113,14 @@ export default class TypeRegistry {
get(typeKey) {
return this.types[typeKey] || UNKNOWN_TYPE;
}
/**
* Retrieve a registered type that's deactivated by its key.
* @param {string} typeKey the key for this deactivated type
* @returns {Type} the registered type that's deactivated
*/
getDeactivated(typeKey) {
return this.deactivatedTypes[typeKey] || UNKNOWN_TYPE;
}
importLegacyTypes(types) {
types
.filter((t) => this.get(t.key) === UNKNOWN_TYPE)
Expand Down
59 changes: 59 additions & 0 deletions src/api/types/TypeRegistrySpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,63 @@ describe('The Type API', function () {
it('type registry contains new keys', function () {
expect(typeRegistryInstance.listKeys()).toContain('testType');
});

it('types can be removed', function () {
expect(typeRegistryInstance.get('testType').definition.name).toBe('Test Type');
typeRegistryInstance.removeType('testType');
expect(typeRegistryInstance.get('testType').definition.name).toBe('Unknown Type');
});

it('types can be added in a deactivated state', function () {
typeRegistryInstance.addType(
'deactivatedTestType',
{
name: 'Deactivated Test Type',
description: 'This is a deactivated test type.',
creatable: true
},
true
);

expect(typeRegistryInstance.get('deactivatedTestType').definition.name).toBe('Unknown Type');
expect(typeRegistryInstance.getDeactivated('deactivatedTestType').definition.name).toBe(
'Deactivated Test Type'
);
});

it('deactivated types contain keys', function () {
typeRegistryInstance.addType(
'deactivatedTestType',
{
name: 'Deactivated Test Type',
description: 'This is a deactivated test type.',
creatable: true
},
true
);

let deactivatedKeys = typeRegistryInstance.listDeactivatedKeys();
expect(deactivatedKeys.length).toBe(1);
expect(deactivatedKeys[0]).toBe('deactivatedTestType');
});

it('deactivated types can be removed', function () {
typeRegistryInstance.addType(
'deactivatedTestType',
{
name: 'Deactivated Test Type',
description: 'This is a deactivated test type.',
creatable: true
},
true
);

expect(typeRegistryInstance.getDeactivated('deactivatedTestType').definition.name).toBe(
'Deactivated Test Type'
);
typeRegistryInstance.removeDeactivatedType('deactivatedTestType');
expect(typeRegistryInstance.getDeactivated('deactivatedTestType').definition.name).toBe(
'Unknown Type'
);
});
});
Loading

0 comments on commit 7ae8fae

Please sign in to comment.