[Flexible Layouts] Flexible Layout styling fixes (#7319)

This commit is contained in:
Khalid Adil 2024-01-03 19:11:35 -06:00 committed by GitHub
parent 6f3bb5fc6f
commit 70de7363d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 1222 additions and 58 deletions

View File

@ -491,7 +491,9 @@
"oger", "oger",
"lcovonly", "lcovonly",
"gcov", "gcov",
"WCAG" "WCAG",
"stackedplot",
"Andale"
], ],
"dictionaries": ["npm", "softwareTerms", "node", "html", "css", "bash", "en_US"], "dictionaries": ["npm", "softwareTerms", "node", "html", "css", "bash", "en_US"],
"ignorePaths": [ "ignorePaths": [

View File

@ -4,6 +4,10 @@
# Requires Git > 2.23 # Requires Git > 2.23
# See https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-fileltfilegt # See https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-fileltfilegt
# vue-eslint update 2019
14a0f84c1bcd56886d7c9e4e6afa8f7d292734e5
# eslint changes 2022
d80b6923541704ab925abf0047cbbc58735c27e2
# Copyright year update 2022 # Copyright year update 2022
4a9744e916d24122a81092f6b7950054048ba860 4a9744e916d24122a81092f6b7950054048ba860
# Copyright year update 2023 # Copyright year update 2023

View File

@ -25,4 +25,9 @@ snapshot:
/* Time Conductor Start Time */ /* Time Conductor Start Time */
.c-compact-tc__setting-value{ .c-compact-tc__setting-value{
opacity: 0 !important; opacity: 0 !important;
} }
/* Chart Area for Plots */
.gl-plot-chart-area{
opacity: 0 !important;
}

View File

@ -25,4 +25,8 @@ snapshot:
/* Time Conductor Start Time */ /* Time Conductor Start Time */
.c-compact-tc__setting-value{ .c-compact-tc__setting-value{
opacity: 0 !important; opacity: 0 !important;
}
/* Chart Area for Plots */
.gl-plot-chart-area{
opacity: 0 !important;
} }

104
e2e/helper/stylingUtils.js Normal file
View File

@ -0,0 +1,104 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import { expect } from '../pluginFixtures.js';
/**
* Converts a hex color value to its RGB equivalent.
*
* @param {string} hex - The hex color value. i.e. '#5b0f00'
* @returns {string} The RGB equivalent of the hex color.
*/
function hexToRGB(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? `rgb(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)})`
: null;
}
/**
* Sets the background and text color of a given element.
*
* @param {import('@playwright/test').Page} page - The Playwright page object.
* @param {string} borderColorHex - The hex value of the border color to set, or 'No Style'.
* @param {string} backgroundColorHex - The hex value of the background color to set, or 'No Style'.
* @param {string} textColorHex - The hex value of the text color to set, or 'No Style'.
* @param {import('@playwright/test').Locator} locator - The Playwright locator for the element whose style is to be set.
*/
async function setStyles(page, borderColorHex, backgroundColorHex, textColorHex, locator) {
await locator.click(); // Assuming the locator is clickable and opens the style setting UI
await page.getByLabel('Set border color').click();
await page.getByLabel(borderColorHex).click();
await page.getByLabel('Set background color').click();
await page.getByLabel(backgroundColorHex).click();
await page.getByLabel('Set text color').click();
await page.getByLabel(textColorHex).click();
}
/**
* Checks if the styles of an element match the expected values.
*
* @param {string} expectedBorderColor - The expected border color in RGB format. Default is '#e6b8af' or 'rgb(230, 184, 175)'
* @param {string} expectedBackgroundColor - The expected background color in RGB format.
* @param {string} expectedTextColor - The expected text color in RGB format. Default is #aaaaaa or 'rgb(170, 170, 170)'
* @param {import('@playwright/test').Locator} locator - The Playwright locator for the element whose style is to be checked.
*/
async function checkStyles(
expectedBorderColor,
expectedBackgroundColor,
expectedTextColor,
locator
) {
const layoutStyles = await locator.evaluate((el) => {
return {
border: window.getComputedStyle(el).getPropertyValue('border-top-color'), //infer the left, right, and bottom
background: window.getComputedStyle(el).getPropertyValue('background-color'),
fontColor: window.getComputedStyle(el).getPropertyValue('color')
};
});
expect(layoutStyles.border).toContain(expectedBorderColor);
expect(layoutStyles.background).toContain(expectedBackgroundColor);
expect(layoutStyles.fontColor).toContain(expectedTextColor);
}
/**
* Checks if the font Styles of an element match the expected values.
*
* @param {string} expectedFontSize - The expected font size in '72px' format. Default is 'Default'
* @param {string} expectedFontWeight - The expected font Type. Format as '700' for bold. Default is 'Default'
* @param {string} expectedFontFamily - The expected font Type. Format as "\"Andale Mono\", sans-serif". Default is 'Default'
* @param {import('@playwright/test').Locator} locator - The Playwright locator for the element whose style is to be checked.
*/
async function checkFontStyles(expectedFontSize, expectedFontWeight, expectedFontFamily, locator) {
const layoutStyles = await locator.evaluate((el) => {
return {
fontSize: window.getComputedStyle(el).getPropertyValue('font-size'),
fontWeight: window.getComputedStyle(el).getPropertyValue('font-weight'),
fontFamily: window.getComputedStyle(el).getPropertyValue('font-family')
};
});
expect(layoutStyles.fontSize).toContain(expectedFontSize);
expect(layoutStyles.fontWeight).toContain(expectedFontWeight);
expect(layoutStyles.fontFamily).toContain(expectedFontFamily);
}
export { checkFontStyles, checkStyles, hexToRGB, setStyles };

View File

@ -164,7 +164,7 @@ async function renameTimerFrom3DotMenu(page, timerUrl, newNameForTimer) {
await page.goto(timerUrl); await page.goto(timerUrl);
// Click on 3 Dot Menu // Click on 3 Dot Menu
await page.locator('button[title="More options"]').click(); await page.locator('button[title="More actions"]').click();
// Click text=Edit Properties... // Click text=Edit Properties...
await page.locator('text=Edit Properties...').click(); await page.locator('text=Edit Properties...').click();

View File

@ -131,7 +131,7 @@ test.describe('Generate Visual Test Data @localStorage @generatedata', () => {
const exampleTelemetry = await createExampleTelemetryObject(page); const exampleTelemetry = await createExampleTelemetryObject(page);
// Make Link from Telemetry Object to Overlay Plot // Make Link from Telemetry Object to Overlay Plot
await page.locator('button[title="More options"]').click(); await page.locator('button[title="More actions"]').click();
// Select 'Create Link' from dropdown // Select 'Create Link' from dropdown
await page.getByRole('menuitem', { name: ' Create Link' }).click(); await page.getByRole('menuitem', { name: ' Create Link' }).click();
@ -206,7 +206,7 @@ test.describe('Generate Visual Test Data @localStorage @generatedata', () => {
const swgWith5sDelay = await createExampleTelemetryObject(page, overlayPlot.uuid); const swgWith5sDelay = await createExampleTelemetryObject(page, overlayPlot.uuid);
await page.goto(swgWith5sDelay.url); await page.goto(swgWith5sDelay.url);
await page.getByTitle('More options').click(); await page.getByTitle('More actions').click();
await page.getByRole('menuitem', { name: ' Edit Properties...' }).click(); await page.getByRole('menuitem', { name: ' Edit Properties...' }).click();
//Edit Example Telemetry Object to include 5s loading Delay //Edit Example Telemetry Object to include 5s loading Delay

View File

@ -45,7 +45,7 @@ test.describe('Clear Data Action', () => {
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); await 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 options').click(); await page.getByTitle('More actions').click();
const clearDataMenuItem = page.getByRole('menuitem', { const clearDataMenuItem = page.getByRole('menuitem', {
name: 'Clear Data' name: 'Clear Data'
}); });

View File

@ -158,7 +158,7 @@ test.describe('Persistence operations @couchdb', () => {
}); });
// Open the edit form for the clock object // Open the edit form for the clock object
await page.click('button[title="More options"]'); await page.click('button[title="More actions"]');
await page.click('li[title="Edit properties of this object."]'); await page.click('li[title="Edit properties of this object."]');
// Modify the display format from default 12hr -> 24hr and click 'Save' // Modify the display format from default 12hr -> 24hr and click 'Save'

View File

@ -196,7 +196,7 @@ test.describe.serial('Condition Set CRUD Operations on @localStorage', () => {
.first() .first()
.click(); .click();
// Click hamburger button // Click hamburger button
await page.locator('[title="More options"]').click(); await page.locator('[title="More actions"]').click();
// Click 'Remove' and press OK // Click 'Remove' and press OK
await page.locator('li[role="menuitem"]:has-text("Remove")').click(); await page.locator('li[role="menuitem"]:has-text("Remove")').click();
@ -366,7 +366,7 @@ test.describe('Basic Condition Set Use', () => {
// Edit SWG to add 8 second loading delay to simulate the case // Edit SWG to add 8 second loading delay to simulate the case
// where telemetry is not available. // where telemetry is not available.
await page.getByTitle('More options').click(); await page.getByTitle('More actions').click();
await page.getByRole('menuitem', { name: 'Edit Properties...' }).click(); await page.getByRole('menuitem', { name: 'Edit Properties...' }).click();
await page.getByRole('spinbutton', { name: 'Loading Delay (ms)' }).fill('8000'); await page.getByRole('spinbutton', { name: 'Loading Delay (ms)' }).fill('8000');
await page.getByLabel('Save').click(); await page.getByLabel('Save').click();

View File

@ -283,16 +283,18 @@ test.describe('Flexible Layout Toolbar Actions @localStorage', () => {
type: 'issue', type: 'issue',
description: 'https://github.com/nasa/openmct/issues/7234' description: 'https://github.com/nasa/openmct/issues/7234'
}); });
expect(await page.getByRole('group', { name: 'Container' }).count()).toEqual(2);
await page.getByRole('group', { name: 'Container' }).nth(1).click(); const containerHandles = page.getByRole('columnheader', { name: 'Handle' });
expect(await containerHandles.count()).toEqual(2);
await page.getByRole('columnheader', { name: 'Container Handle 1' }).click();
await page.getByTitle('Add Container').click(); await page.getByTitle('Add Container').click();
expect(await page.getByRole('group', { name: 'Container' }).count()).toEqual(3); expect(await containerHandles.count()).toEqual(3);
await page.getByTitle('Remove Container').click(); await page.getByTitle('Remove Container').click();
await expect(page.getByRole('dialog')).toHaveText( await expect(page.getByRole('dialog')).toHaveText(
'This action will permanently delete this container from this Flexible Layout. Do you want to continue?' 'This action will permanently delete this container from this Flexible Layout. Do you want to continue?'
); );
await page.getByRole('button', { name: 'OK' }).click(); await page.getByRole('button', { name: 'OK' }).click();
expect(await page.getByRole('group', { name: 'Container' }).count()).toEqual(2); expect(await containerHandles.count()).toEqual(2);
}); });
test('Remove Frame', async ({ page }) => { test('Remove Frame', async ({ page }) => {
expect(await page.getByRole('group', { name: 'Frame' }).count()).toEqual(2); expect(await page.getByRole('group', { name: 'Frame' }).count()).toEqual(2);
@ -305,11 +307,12 @@ test.describe('Flexible Layout Toolbar Actions @localStorage', () => {
expect(await page.getByRole('group', { name: 'Frame' }).count()).toEqual(1); expect(await page.getByRole('group', { name: 'Frame' }).count()).toEqual(1);
}); });
test('Columns/Rows Layout Toggle', async ({ page }) => { test('Columns/Rows Layout Toggle', async ({ page }) => {
await page.getByRole('group', { name: 'Container' }).nth(1).click(); await page.getByRole('columnheader', { name: 'Container Handle 1' }).click();
expect(await page.locator('.c-fl--rows').count()).toEqual(0); const flexRows = page.getByLabel('Flexible Layout Row');
expect(await flexRows.count()).toEqual(0);
await page.getByTitle('Columns layout').click(); await page.getByTitle('Columns layout').click();
expect(await page.locator('.c-fl--rows').count()).toEqual(1); expect(await flexRows.count()).toEqual(1);
await page.getByTitle('Rows layout').click(); await page.getByTitle('Rows layout').click();
expect(await page.locator('.c-fl--rows').count()).toEqual(0); expect(await flexRows.count()).toEqual(0);
}); });
}); });

View File

@ -128,7 +128,7 @@ test.describe('Gauge', () => {
// Create the gauge with defaults // Create the gauge with defaults
await createDomainObjectWithDefaults(page, { type: 'Gauge' }); await createDomainObjectWithDefaults(page, { type: 'Gauge' });
await page.click('button[title="More options"]'); await page.click('button[title="More actions"]');
await page.click('li[role="menuitem"]:has-text("Edit Properties")'); await page.click('li[role="menuitem"]:has-text("Edit Properties")');
// FIXME: We need better selectors for these custom form controls // FIXME: We need better selectors for these custom form controls
const displayCurrentValueSwitch = page.locator('.c-toggle-switch__slider >> nth=0'); const displayCurrentValueSwitch = page.locator('.c-toggle-switch__slider >> nth=0');
@ -148,7 +148,7 @@ test.describe('Gauge', () => {
const swgWith5sDelay = await createExampleTelemetryObject(page, gauge.uuid); const swgWith5sDelay = await createExampleTelemetryObject(page, gauge.uuid);
await page.goto(swgWith5sDelay.url); await page.goto(swgWith5sDelay.url);
await page.getByTitle('More options').click(); await page.getByTitle('More actions').click();
await page.getByRole('menuitem', { name: /Edit Properties.../ }).click(); await page.getByRole('menuitem', { name: /Edit Properties.../ }).click();
//Edit Example Telemetry Object to include 5s loading Delay //Edit Example Telemetry Object to include 5s loading Delay

View File

@ -247,7 +247,7 @@ test.describe('Notebook export tests', () => {
test('can export notebook as text', async ({ page }) => { test('can export notebook as text', async ({ page }) => {
await nbUtils.enterTextEntry(page, `Foo bar entry`); await nbUtils.enterTextEntry(page, `Foo bar entry`);
// Click on 3 Dot Menu // Click on 3 Dot Menu
await page.locator('button[title="More options"]').click(); await page.locator('button[title="More actions"]').click();
const downloadPromise = page.waitForEvent('download'); const downloadPromise = page.waitForEvent('download');
await page.getByRole('menuitem', { name: /Export Notebook as Text/ }).click(); await page.getByRole('menuitem', { name: /Export Notebook as Text/ }).click();

View File

@ -105,7 +105,7 @@ test.describe('Snapshot Container tests', () => {
test.fixme( test.fixme(
'A snapshot can be Viewed, Annotated, display deleted, and saved from Container with 3 dot action menu', 'A snapshot can be Viewed, Annotated, display deleted, and saved from Container with 3 dot action menu',
async ({ page }) => { async ({ page }) => {
await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More options').click(); await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More actions').click();
await page.getByRole('menuitem', { name: ' View Snapshot' }).click(); await page.getByRole('menuitem', { name: ' View Snapshot' }).click();
await expect(page.locator('.c-overlay__outer')).toBeVisible(); await expect(page.locator('.c-overlay__outer')).toBeVisible();
await page.getByTitle('Annotate').click(); await page.getByTitle('Annotate').click();
@ -118,7 +118,7 @@ test.describe('Snapshot Container tests', () => {
} }
); );
test('A snapshot can be Quick Viewed from Container with 3 dot action menu', async ({ page }) => { test('A snapshot can be Quick Viewed from Container with 3 dot action menu', async ({ page }) => {
await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More options').click(); await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More actions').click();
await page.getByRole('menuitem', { name: 'Quick View' }).click(); await page.getByRole('menuitem', { name: 'Quick View' }).click();
await expect(page.locator('.c-overlay__outer')).toBeVisible(); await expect(page.locator('.c-overlay__outer')).toBeVisible();
}); });
@ -212,7 +212,7 @@ test.describe('Snapshot image tests', () => {
// expect two embedded images now // expect two embedded images now
expect(await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).count()).toBe(2); expect(await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).count()).toBe(2);
await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More options').click(); await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More actions').click();
await page.getByRole('menuitem', { name: /Remove This Embed/ }).click(); await page.getByRole('menuitem', { name: /Remove This Embed/ }).click();
await page.getByRole('button', { name: 'Ok', exact: true }).click(); await page.getByRole('button', { name: 'Ok', exact: true }).click();

View File

@ -152,18 +152,18 @@ test.describe('Restricted Notebook with a page locked and with an embed @addInit
test('Allows embeds to be deleted if page unlocked @addInit', async ({ page }) => { test('Allows embeds to be deleted if page unlocked @addInit', async ({ page }) => {
// Click embed popup menu // Click embed popup menu
await page.locator('.c-ne__embed__name .c-icon-button').click(); await page.getByLabel('Notebook Entry').getByLabel('More actions').click();
const embedMenu = page.locator('body >> .c-menu'); const embedMenu = page.getByLabel('Super Menu');
await expect(embedMenu).toContainText('Remove This Embed'); await expect(embedMenu).toContainText('Remove This Embed');
}); });
test('Disallows embeds to be deleted if page locked @addInit', async ({ page }) => { test('Disallows embeds to be deleted if page locked @addInit', async ({ page }) => {
await lockPage(page); await lockPage(page);
// Click embed popup menu // Click embed popup menu
await page.locator('.c-ne__embed__name .c-icon-button').click(); await page.getByLabel('Notebook Entry').getByLabel('More actions').click();
const embedMenu = page.locator('body >> .c-menu'); const embedMenu = page.getByLabel('Super Menu');
await expect(embedMenu).not.toContainText('Remove This Embed'); await expect(embedMenu).not.toContainText('Remove This Embed');
}); });
}); });
@ -176,7 +176,7 @@ test.describe('can export restricted notebook as text', () => {
test('basic functionality ', async ({ page }) => { test('basic functionality ', async ({ page }) => {
await enterTextEntry(page, `Foo bar entry`); await enterTextEntry(page, `Foo bar entry`);
// Click on 3 Dot Menu // Click on 3 Dot Menu
await page.locator('button[title="More options"]').click(); await page.locator('button[title="More actions"]').click();
const downloadPromise = page.waitForEvent('download'); const downloadPromise = page.waitForEvent('download');
await page.getByRole('menuitem', { name: /Export Notebook as Text/ }).click(); await page.getByRole('menuitem', { name: /Export Notebook as Text/ }).click();

View File

@ -167,7 +167,7 @@ test.describe('Tagging in Notebooks @addInit', () => {
test('Can delete objects with tags and neither return in search', async ({ page }) => { test('Can delete objects with tags and neither return in search', async ({ page }) => {
await createNotebookEntryAndTags(page); await createNotebookEntryAndTags(page);
// Delete Notebook // Delete Notebook
await page.locator('button[title="More options"]').click(); await page.locator('button[title="More actions"]').click();
await page.locator('li[title="Remove this object from its containing object."]').click(); await page.locator('li[title="Remove this object from its containing object."]').click();
await page.locator('button:has-text("OK")').click(); await page.locator('button:has-text("OK")').click();
await page.goto('./', { waitUntil: 'domcontentloaded' }); await page.goto('./', { waitUntil: 'domcontentloaded' });

View File

@ -73,7 +73,7 @@ test.describe('Plot Rendering', () => {
async function editSineWaveToUseInfinityOption(page, sineWaveGeneratorObject) { async function editSineWaveToUseInfinityOption(page, sineWaveGeneratorObject) {
await page.goto(sineWaveGeneratorObject.url); await page.goto(sineWaveGeneratorObject.url);
// Edit SWG properties to include infinity values // Edit SWG properties to include infinity values
await page.locator('[title="More options"]').click(); await page.locator('[title="More actions"]').click();
await page.locator('[title="Edit properties of this object."]').click(); await page.locator('[title="Edit properties of this object."]').click();
await page await page
.getByRole('switch', { .getByRole('switch', {

View File

@ -0,0 +1,48 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* This test is dedicated to test conditional styling
*/
import { test } from '../../../../pluginFixtures.js';
test.describe('Conditional Styling', () => {
test.fixme(
'Conditional Styling can be applied to Flex Layout and its children',
async ({ page }) => {
//test
}
);
test.fixme(
'Conditional Styling can be applied to Overlay Plot and its children',
async ({ page }) => {
//test
}
);
test.fixme(
'Conditional Styling changes the styling of the object the condition changes state',
async ({ page }) => {
//test
}
);
});

View File

@ -0,0 +1,414 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* This test is dedicated to test styling of flex layouts
*/
import { createDomainObjectWithDefaults } from '../../../../appActions.js';
import { checkStyles, hexToRGB, setStyles } from '../../../../helper/stylingUtils.js';
import { test } from '../../../../pluginFixtures.js';
const setBorderColor = '#ff00ff';
const setBackgroundColor = '#5b0f00';
const setTextColor = '#e6b8af';
const defaultFrameBorderColor = '#e6b8af'; //default border color
const defaultBorderTargetColor = '#aaaaaa';
const defaultTextColor = '#aaaaaa'; // default text color
const inheritedColor = '#aaaaaa'; // inherited from the body style
const pukeGreen = '#6aa84f'; //Ugliest green known to man
const NO_STYLE_RGBA = 'rgba(0, 0, 0, 0)'; //default background color value
test.describe('Flexible Layout styling', () => {
let stackedPlot;
let flexibleLayout;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create a Flexible Layout and attach to the Stacked Plot
flexibleLayout = await createDomainObjectWithDefaults(page, {
type: 'Flexible Layout',
name: 'Flexible Layout'
});
// Create a Stacked Plot and attach to the Flexible Layout
stackedPlot = await createDomainObjectWithDefaults(page, {
type: 'Stacked Plot',
name: 'StackedPlot1',
parent: flexibleLayout.uuid
});
// Create a Stacked Plot and attach to the Flexible Layout
await createDomainObjectWithDefaults(page, {
type: 'Stacked Plot',
name: 'StackedPlot2',
parent: flexibleLayout.uuid
});
});
test('styling the flexible layout properly applies the styles to flex layout', async ({
page
}) => {
// Directly navigate to the flexible layout
await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout
await page.getByLabel('Edit').click();
// Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click();
// Set styles using setStyles function
await setStyles(
page,
setBorderColor,
setBackgroundColor,
setTextColor,
page.getByLabel('Flexible Layout Column')
);
// Flex Layout Column matches set styles
await checkStyles(
hexToRGB(setBorderColor),
hexToRGB(setBackgroundColor),
hexToRGB(setTextColor),
page.getByLabel('Flexible Layout Column')
);
// Save Flexible Layout
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// Reload page
await page.reload({ waitUntil: 'domcontentloaded' });
// Check styles of overall Flex Layout
await checkStyles(
hexToRGB(setBorderColor),
hexToRGB(setBackgroundColor),
hexToRGB(setTextColor),
page.getByLabel('Flexible Layout Column')
);
// Check styles on StackedPlot1. Note: https://github.com/nasa/openmct/issues/7337
await checkStyles(
hexToRGB(defaultFrameBorderColor),
NO_STYLE_RGBA,
hexToRGB(setTextColor),
page.getByLabel('StackedPlot1 Frame').getByLabel('Stacked Plot Style Target')
);
// Check styles on StackedPlot2. Note: https://github.com/nasa/openmct/issues/7337
await checkStyles(
hexToRGB(defaultFrameBorderColor),
NO_STYLE_RGBA,
hexToRGB(setTextColor),
page.getByLabel('StackedPlot2 Frame').getByLabel('Stacked Plot Style Target')
);
});
test('styling a child object of the flexible layout properly applies that style to only that child', async ({
page
}) => {
// Directly navigate to the flexible layout
await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout
await page.getByLabel('Edit').click();
// Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click();
// Check styles on StackedPlot1
await checkStyles(
hexToRGB(defaultBorderTargetColor),
NO_STYLE_RGBA,
hexToRGB(defaultTextColor),
page.getByLabel('StackedPlot1 Frame').getByLabel('Stacked Plot Style Target')
);
// Check styles on StackedPlot2
await checkStyles(
hexToRGB(defaultBorderTargetColor),
NO_STYLE_RGBA,
hexToRGB(defaultTextColor),
page.getByLabel('StackedPlot2 Frame').getByLabel('Stacked Plot Style Target')
);
// Set styles using setStyles function on StackedPlot1 but not StackedPlot2
await setStyles(
page,
setBorderColor,
setBackgroundColor,
setTextColor,
page.getByLabel('StackedPlot1 Frame')
);
// Check styles on StackedPlot1
await checkStyles(
hexToRGB(setBorderColor),
hexToRGB(setBackgroundColor),
hexToRGB(setTextColor),
page.getByLabel('StackedPlot1 Frame').getByLabel('Stacked Plot Style Target')
);
// Check styles on StackedPlot2
await checkStyles(
hexToRGB(defaultBorderTargetColor),
NO_STYLE_RGBA,
hexToRGB(defaultTextColor),
page.getByLabel('StackedPlot2 Frame').getByLabel('Stacked Plot Style Target')
);
// Save Flexible Layout
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// Reload page and verify that styles persist
await page.reload({ waitUntil: 'domcontentloaded' });
// Check styles on StackedPlot1
await checkStyles(
hexToRGB(setBorderColor),
hexToRGB(setBackgroundColor),
hexToRGB(setTextColor),
page.getByLabel('StackedPlot1 Frame').getByLabel('Stacked Plot Style Target')
);
// Check styles on StackedPlot2
await checkStyles(
hexToRGB(defaultBorderTargetColor),
NO_STYLE_RGBA,
hexToRGB(defaultTextColor),
page.getByLabel('StackedPlot2 Frame').getByLabel('Stacked Plot Style Target')
);
});
test('if an overall style has been applied to the parent layout or plot, the individual styling should be able to coexist with that', async ({
page
}) => {
//Navigate to stackedPlot
await page.goto(stackedPlot.url, { waitUntil: 'domcontentloaded' });
// Edit stackedPlot
await page.getByLabel('Edit').click();
await page.getByRole('tab', { name: 'Styles' }).click();
// Set styles using setStyles function on StackedPlot1 but not StackedPlot2
await setStyles(
page,
setBorderColor,
setBackgroundColor,
setTextColor,
page.getByLabel('Stacked Plot Style Target').locator('div')
);
// Save Flexible Layout
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// Directly navigate to the flexible layout
await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout
await page.getByLabel('Edit').click();
// Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click();
// Check styles on StackedPlot1 to match the set colors
await checkStyles(
hexToRGB(setBorderColor),
hexToRGB(setBackgroundColor),
hexToRGB(setTextColor),
page.getByLabel('StackedPlot1 Frame').getByLabel('Stacked Plot Style Target')
);
// Check styles on StackedPlot2 to verify they are the default
await checkStyles(
hexToRGB(defaultBorderTargetColor),
NO_STYLE_RGBA,
hexToRGB(defaultTextColor),
page.getByLabel('StackedPlot2 Frame').getByLabel('Stacked Plot Style Target')
);
// Set styles using setStyles function on StackedPlot2
await setStyles(
page,
setBorderColor,
setBackgroundColor,
setTextColor,
page.getByLabel('StackedPlot2 Frame')
);
// Check styles on StackedPlot2
await checkStyles(
hexToRGB(setBorderColor),
hexToRGB(setBackgroundColor),
hexToRGB(setTextColor),
page.getByLabel('StackedPlot2 Frame').getByLabel('Stacked Plot Style Target')
);
// Save Flexible Layout
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// Reload page and verify that styles persist
await page.reload({ waitUntil: 'domcontentloaded' });
// Check styles on StackedPlot1
await checkStyles(
hexToRGB(setBorderColor),
hexToRGB(setBackgroundColor),
hexToRGB(setTextColor),
page.getByLabel('StackedPlot1 Frame').getByLabel('Stacked Plot Style Target')
);
// Check styles on StackedPlot2
await checkStyles(
hexToRGB(setBorderColor),
hexToRGB(setBackgroundColor),
hexToRGB(setTextColor),
page.getByLabel('StackedPlot2 Frame').getByLabel('Stacked Plot Style Target')
);
// Directly navigate to the flexible layout
await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout
await page.getByLabel('Edit').click();
// Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click();
// Set Flex Layout Column to puke green
await setStyles(
page,
pukeGreen,
pukeGreen,
pukeGreen,
page.getByLabel('Flexible Layout Column')
);
// Save Flexible Layout
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// Flex Layout Column matches set styles
await checkStyles(
hexToRGB(pukeGreen),
hexToRGB(pukeGreen),
hexToRGB(pukeGreen),
page.getByLabel('Flexible Layout Column')
);
// Check styles on StackedPlot1 matches previously set colors
await checkStyles(
hexToRGB(setBorderColor),
hexToRGB(setBackgroundColor),
hexToRGB(setTextColor),
page.getByLabel('StackedPlot1 Frame').getByLabel('Stacked Plot Style Target')
);
// Check styles on StackedPlot2 matches previous set colors
await checkStyles(
hexToRGB(setBorderColor),
hexToRGB(setBackgroundColor),
hexToRGB(setTextColor),
page.getByLabel('StackedPlot2 Frame').getByLabel('Stacked Plot Style Target')
);
});
test('when the "no style" option is selected, background and text should be reset to inherited styles', async ({
page
}) => {
// Directly navigate to the flexible layout
await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout
await page.getByLabel('Edit').click();
// Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click();
// Set styles using setStyles function
await setStyles(
page,
setBorderColor,
setBackgroundColor,
setTextColor,
page.getByLabel('StackedPlot1 Frame')
);
// Check styles using checkStyles function
await checkStyles(
hexToRGB(setBorderColor),
hexToRGB(setBackgroundColor),
hexToRGB(setTextColor),
page.getByLabel('StackedPlot1 Frame').getByLabel('Stacked Plot Style Target')
);
// Save Flexible Layout
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// Reload page and set Styles to 'None'
await page.reload({ waitUntil: 'domcontentloaded' });
// Edit Flexible Layout
await page.getByLabel('Edit').click();
// Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click();
//Select the 'No Style' option
await setStyles(
page,
'No Style',
'No Style',
'No Style',
page.getByLabel('StackedPlot1 Frame')
);
// Check styles using checkStyles function
await checkStyles(
hexToRGB(defaultBorderTargetColor),
NO_STYLE_RGBA,
hexToRGB(inheritedColor),
page.getByLabel('StackedPlot1 Frame').getByLabel('Stacked Plot Style Target')
);
// Save Flexible Layout
await page.locator('button[title="Save"]').click();
await page.locator('text=Save and Finish Editing').click();
// Reload page and verify that styles persist
await page.reload({ waitUntil: 'domcontentloaded' });
// Check styles using checkStyles function
await checkStyles(
hexToRGB(defaultBorderTargetColor),
NO_STYLE_RGBA,
hexToRGB(inheritedColor),
page.getByLabel('StackedPlot1 Frame').getByLabel('Stacked Plot Style Target')
);
});
});

View File

@ -0,0 +1,246 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* This test is dedicated to test styling of stacked plots
*/
import { createDomainObjectWithDefaults } from '../../../../appActions.js';
import {
checkFontStyles,
checkStyles,
hexToRGB,
setStyles
} from '../../../../helper/stylingUtils.js';
import { test } from '../../../../pluginFixtures.js';
const setBorderColor = '#ff00ff';
const setBackgroundColor = '#5b0f00';
const setTextColor = '#e6b8af';
const defaultTextColor = '#aaaaaa'; // default text color
const NO_STYLE_RGBA = 'rgba(0, 0, 0, 0)'; //default background color value
const setFontSize = '72px';
const setFontWeight = '700'; //bold for monospace bold
// eslint-disable-next-line prettier/prettier
const setFontFamily = "\"Andale Mono\", sans-serif";
test.describe('Stacked Plot styling', () => {
let stackedPlot;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create a Stacked Plot
stackedPlot = await createDomainObjectWithDefaults(page, {
type: 'Stacked Plot',
name: 'StackedPlot1'
});
// Create two SWGs and attach them to the Stacked Plot
await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: 'Sine Wave Generator 1',
parent: stackedPlot.uuid
});
await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: 'Sine Wave Generator 2',
parent: stackedPlot.uuid
});
});
test('styling the overlay plot properly applies the styles to all containers', async ({
page
}) => {
// Directly navigate to the stacked plot
await page.goto(stackedPlot.url, { waitUntil: 'domcontentloaded' });
await page.getByLabel('Edit').click();
await page.getByRole('tab', { name: 'Styles' }).click();
//Set styles on overall stacked plot
await setStyles(
page,
setBorderColor,
setBackgroundColor,
setTextColor,
page.getByRole('tab', { name: 'Styles' }) //Workaround for https://github.com/nasa/openmct/issues/7229
);
//Set Font Size to 72
await page.getByLabel('Set Font Size').click();
await page.getByRole('menuitem', { name: '72px' }).click();
//Set Font Type to Monospace Bold. See setFontWeight and setFontFamily variables
await page.getByLabel('Set Font Type').click();
await page.getByRole('menuitem', { name: 'Monospace Bold' }).click();
//Check styles of stacked plot
await checkStyles(
hexToRGB(setBorderColor),
hexToRGB(setBackgroundColor),
hexToRGB(setTextColor),
page.getByLabel('Stacked Plot Style Target')
);
//Check font styles of stacked plot
await checkFontStyles(
setFontSize,
setFontWeight,
setFontFamily,
page.getByLabel('Stacked Plot Style Target')
);
//Save
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// Reload page
await page.reload({ waitUntil: 'domcontentloaded' });
//Verify styles are correct after reload
await checkStyles(
hexToRGB(setBorderColor),
hexToRGB(setBackgroundColor),
hexToRGB(setTextColor),
page.getByLabel('Stacked Plot Style Target')
);
await checkFontStyles(
setFontSize,
setFontWeight,
setFontFamily,
page.getByLabel('Stacked Plot Style Target')
);
//Verify that stacked Plot Items inherit only text properties
await checkStyles(
NO_STYLE_RGBA,
NO_STYLE_RGBA,
hexToRGB(setTextColor),
page.getByLabel('Stacked Plot Item Sine Wave Generator 1')
);
await checkStyles(
NO_STYLE_RGBA,
NO_STYLE_RGBA,
hexToRGB(setTextColor),
page.getByLabel('Stacked Plot Item Sine Wave Generator 2')
);
await checkFontStyles(
setFontSize,
setFontWeight,
setFontFamily,
page.getByLabel('Stacked Plot Item Sine Wave Generator 1')
);
});
test.fixme(
'styling a child object of the flexible layout properly applies that style to only that child',
async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/nasa/openmct/issues/7338'
});
await page.goto(stackedPlot.url, { waitUntil: 'domcontentloaded' });
await page.getByLabel('Edit').click();
await page.getByRole('tab', { name: 'Styles' }).click();
//Check default styles for SWG1 and SWG2
await checkStyles(
NO_STYLE_RGBA,
NO_STYLE_RGBA,
hexToRGB(defaultTextColor),
page.getByLabel('Stacked Plot Item Sine Wave Generator 1')
);
await checkStyles(
NO_STYLE_RGBA,
NO_STYLE_RGBA,
hexToRGB(defaultTextColor),
page.getByLabel('Stacked Plot Item Sine Wave Generator 2')
);
// Set styles using setStyles function on StackedPlot1 but not StackedPlot2
await setStyles(
page,
setBorderColor,
setBackgroundColor,
setTextColor,
page.getByLabel('Stacked Plot Item Sine Wave Generator 1')
);
//Set Font Styles on SWG1 but not SWG2
await page.getByLabel('Stacked Plot Item Sine Wave Generator 1').click();
//Set Font Size to 72
await page.getByLabel('Set Font Size').click();
await page.getByRole('menuitem', { name: '72px' }).click();
//Set Font Type to Monospace Bold. See setFontWeight and setFontFamily variables
await page.getByLabel('Set Font Type').click();
await page.getByRole('menuitem', { name: 'Monospace Bold' }).click();
// Save Flexible Layout
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
// Check styles on StackedPlot1
await checkStyles(
hexToRGB(setBorderColor),
hexToRGB(setBackgroundColor),
hexToRGB(setTextColor),
page.getByLabel('Stacked Plot Item Sine Wave Generator 1')
);
// Check styles on StackedPlot2
await checkStyles(
NO_STYLE_RGBA,
NO_STYLE_RGBA,
hexToRGB(defaultTextColor),
page.getByLabel('Stacked Plot Item Sine Wave Generator 2')
);
// Reload page and verify that styles persist
await page.reload({ waitUntil: 'domcontentloaded' });
// Check styles on StackedPlot1
await checkStyles(
hexToRGB(setBorderColor),
hexToRGB(setBackgroundColor),
hexToRGB(setTextColor),
page.getByLabel('Stacked Plot Item Sine Wave Generator 1')
);
// Check styles on StackedPlot2
await checkStyles(
NO_STYLE_RGBA,
NO_STYLE_RGBA,
hexToRGB(defaultTextColor),
page.getByLabel('Stacked Plot Item Sine Wave Generator 2')
);
}
);
});

View File

@ -0,0 +1,90 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* This test is dedicated to test styling changes in the inspector tool
*/
import { createDomainObjectWithDefaults } from '../../../../appActions.js';
import { expect, test } from '../../../../pluginFixtures.js';
test.describe('Style Inspector Options', () => {
let flexibleLayout;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create a Flexible Layout and attach to the Stacked Plot
flexibleLayout = await createDomainObjectWithDefaults(page, {
type: 'Flexible Layout',
name: 'Flexible Layout'
});
// Create a Stacked Plot and attach to the Flexible Layout
await createDomainObjectWithDefaults(page, {
type: 'Stacked Plot',
name: 'Stacked Plot',
parent: flexibleLayout.uuid
});
});
test('styles button only appears when appropriate component selected', async ({ page }) => {
await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout
await page.getByLabel('Edit').click();
// The overall Flex Layout or Stacked Plot itself MUST be style-able.
await expect(page.getByRole('tab', { name: 'Styles' })).toBeVisible();
// Select flexible layout column
await page.getByLabel('Container Handle 1').click();
// Flex Layout containers should NOT be style-able.
await expect(page.getByRole('tab', { name: 'Styles' })).toBeHidden();
// Select Flex Layout Column
await page.getByLabel('Flexible Layout Column').click();
// The overall Flex Layout or Stacked Plot itself MUST be style-able.
await expect(page.getByRole('tab', { name: 'Styles' })).toBeVisible();
// Select Stacked Layout Column
await page.getByLabel('Stacked Plot Frame').click();
// The overall Flex Layout or Stacked Plot itself MUST be style-able.
await expect(page.getByRole('tab', { name: 'Styles' })).toBeVisible();
});
});
test.describe('Saved Styles', () => {
test.fixme('historical styles appear as an option once used', async ({ page }) => {
//test
});
test.fixme('at least 5 saved styles appear in the saved styles list', async ({ page }) => {
//test
});
test.fixme('Saved Styles can be deleted once used', async ({ page }) => {
//test
});
test.fixme('can apply a saved style to the currently selected target', async ({ page }) => {
//test
});
});

View File

@ -85,7 +85,7 @@ test.describe('Timer with target date', () => {
test('Can count down to a target date', async ({ page }) => { test('Can count down to a target date', async ({ page }) => {
// Set the target date to 2024-11-24 03:30:00 // Set the target date to 2024-11-24 03:30:00
await page.getByTitle('More options').click(); await page.getByTitle('More actions').click();
await page.getByRole('menuitem', { name: /Edit Properties.../ }).click(); await page.getByRole('menuitem', { name: /Edit Properties.../ }).click();
await page.getByPlaceholder('YYYY-MM-DD').fill('2024-11-24'); await page.getByPlaceholder('YYYY-MM-DD').fill('2024-11-24');
await page.locator('input[name="hour"]').fill('3'); await page.locator('input[name="hour"]').fill('3');
@ -108,7 +108,7 @@ test.describe('Timer with target date', () => {
test('Can count up from a target date', async ({ page }) => { test('Can count up from a target date', async ({ page }) => {
// Set the target date to 2020-11-23 03:30:00 // Set the target date to 2020-11-23 03:30:00
await page.getByTitle('More options').click(); await page.getByTitle('More actions').click();
await page.getByRole('menuitem', { name: /Edit Properties.../ }).click(); await page.getByRole('menuitem', { name: /Edit Properties.../ }).click();
await page.getByPlaceholder('YYYY-MM-DD').fill('2020-11-23'); await page.getByPlaceholder('YYYY-MM-DD').fill('2020-11-23');
await page.locator('input[name="hour"]').fill('3'); await page.locator('input[name="hour"]').fill('3');
@ -159,7 +159,7 @@ async function triggerTimerContextMenuAction(page, timerUrl, action) {
*/ */
async function triggerTimer3dotMenuAction(page, action) { async function triggerTimer3dotMenuAction(page, action) {
const menuAction = `.c-menu ul li >> text="${action}"`; const menuAction = `.c-menu ul li >> text="${action}"`;
const threeDotMenuButton = 'button[title="More options"]'; const threeDotMenuButton = 'button[title="More actions"]';
let isActionAvailable = false; let isActionAvailable = false;
let iterations = 0; let iterations = 0;
// Dismiss/open the 3dot menu until the action is available // Dismiss/open the 3dot menu until the action is available

View File

@ -46,7 +46,7 @@ test.describe('Visual - LAD Table', () => {
}); });
//Modify SWG to create a really stable SWG //Modify SWG to create a really stable SWG
await page.locator('button[title="More options"]').click(); await page.locator('button[title="More actions"]').click();
await page.getByRole('menuitem', { name: ' Edit Properties...' }).click(); await page.getByRole('menuitem', { name: ' Edit Properties...' }).click();

View File

@ -0,0 +1,194 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2023, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* This test is dedicated to test notification banner functionality and its accessibility attributes.
*/
import percySnapshot from '@percy/playwright';
import { createDomainObjectWithDefaults } from '../../appActions.js';
import { scanForA11yViolations, test } from '../../avpFixtures.js';
import { setStyles } from '../../helper/stylingUtils.js';
const setBorderColor = '#ff00ff';
const setBackgroundColor = '#5b0f00';
const setTextColor = '#e6b8af';
test.describe('Flexible Layout styling @a11y', () => {
let flexibleLayout;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create a Flexible Layout and attach to the Stacked Plot
flexibleLayout = await createDomainObjectWithDefaults(page, {
type: 'Flexible Layout',
name: 'Flexible Layout'
});
// Create a Stacked Plot and attach to the Flexible Layout
await createDomainObjectWithDefaults(page, {
type: 'Stacked Plot',
name: 'StackedPlot1',
parent: flexibleLayout.uuid
});
// Create a Stacked Plot and attach to the Flexible Layout
await createDomainObjectWithDefaults(page, {
type: 'Stacked Plot',
name: 'StackedPlot2',
parent: flexibleLayout.uuid
});
});
test('styling the flexible layout properly applies the styles to flex layout', async ({
page,
theme
}) => {
// Directly navigate to the flexible layout
await page.goto(flexibleLayout.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout
await page.getByLabel('Edit').click();
// Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click();
await percySnapshot(page, `Flex Layout with 2 children (theme: '${theme}')`);
// Set styles using setStyles function
await setStyles(
page,
setBorderColor,
setBackgroundColor,
setTextColor,
page.getByLabel('Flexible Layout Column')
);
await percySnapshot(page, `Edit Mode Styled Flex Layout Column (theme: '${theme}')`);
await setStyles(
page,
setBorderColor,
setBackgroundColor,
setTextColor,
page.getByLabel('StackedPlot1 Frame')
);
await percySnapshot(
page,
`Edit Mode Styled Flex Layout with Styled StackedPlot (theme: '${theme}')`
);
// Save Flexible Layout
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
await percySnapshot(
page,
`Saved Styled Flex Layout with Styled StackedPlot (theme: '${theme}')`
);
});
test.afterEach(async ({ page }, testInfo) => {
await scanForA11yViolations(page, testInfo.title);
});
});
test.describe('Stacked Plot styling @a11y', () => {
let stackedPlot;
test.beforeEach(async ({ page }) => {
await page.goto('./', { waitUntil: 'domcontentloaded' });
// Create a Stacked Plot
stackedPlot = await createDomainObjectWithDefaults(page, {
type: 'Stacked Plot',
name: 'StackedPlot1'
});
// Create two SWGs and attach them to the Stacked Plot
await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: 'Sine Wave Generator 1',
parent: stackedPlot.uuid
});
await createDomainObjectWithDefaults(page, {
type: 'Sine Wave Generator',
name: 'Sine Wave Generator 2',
parent: stackedPlot.uuid
});
});
test('styling the flexible layout properly applies the styles to flex layout', async ({
page,
theme
}) => {
// Directly navigate to the flexible layout
await page.goto(stackedPlot.url, { waitUntil: 'domcontentloaded' });
// Edit Flexible Layout
await page.getByLabel('Edit').click();
// Select styles tab
await page.getByRole('tab', { name: 'Styles' }).click();
await percySnapshot(page, `StackedPlot with 2 SWG (theme: '${theme}')`);
// Set styles using setStyles function
await setStyles(
page,
setBorderColor,
setBackgroundColor,
setTextColor,
page.getByRole('tab', { name: 'Styles' }) //Workaround for https://github.com/nasa/openmct/issues/7229
);
//Set Font Size to 72
await page.getByLabel('Set Font Size').click();
await page.getByRole('menuitem', { name: '72px' }).click();
//Set Font Type to Monospace Bold
await page.getByLabel('Set Font Type').click();
await page.getByRole('menuitem', { name: 'Monospace Bold' }).click();
await percySnapshot(page, `Edit Mode StackedPlot Styled (theme: '${theme}')`);
await setStyles(
page,
setBorderColor,
setBackgroundColor,
setTextColor,
page.getByLabel('Stacked Plot Item Sine Wave Generator 1')
);
await percySnapshot(page, `Edit Mode StackedPlot with Styled SWG (theme: '${theme}')`);
// Save Flexible Layout
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
await percySnapshot(page, `Saved Styled StackedPlot (theme: '${theme}')`);
});
test.afterEach(async ({ page }, testInfo) => {
await scanForA11yViolations(page, testInfo.title);
});
});

View File

@ -20,7 +20,7 @@
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<template> <template>
<div class="c-menu" :class="options.menuClass" :style="styleObject"> <div :aria-label="optionsLabel" class="c-menu" :class="options.menuClass" :style="styleObject">
<ul v-if="options.actions.length && options.actions[0].length" role="menu"> <ul v-if="options.actions.length && options.actions[0].length" role="menu">
<template v-for="(actionGroups, index) in options.actions" :key="index"> <template v-for="(actionGroups, index) in options.actions" :key="index">
<div role="group"> <div role="group">
@ -65,6 +65,12 @@
import popupMenuMixin from '../mixins/popupMenuMixin.js'; import popupMenuMixin from '../mixins/popupMenuMixin.js';
export default { export default {
mixins: [popupMenuMixin], mixins: [popupMenuMixin],
inject: ['options'] inject: ['options'],
computed: {
optionsLabel() {
const label = this.options.label ? `${this.options.label} Menu` : 'Menu';
return label;
}
}
}; };
</script> </script>

View File

@ -20,7 +20,12 @@
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<template> <template>
<div class="c-menu" :class="[options.menuClass, 'c-super-menu']" :style="styleObject"> <div
:aria-label="optionsLabel"
class="c-menu"
:class="[options.menuClass, 'c-super-menu']"
:style="styleObject"
>
<ul <ul
v-if="options.actions.length && options.actions[0].length" v-if="options.actions.length && options.actions[0].length"
role="menu" role="menu"
@ -88,6 +93,12 @@ export default {
hoveredItem: {} hoveredItem: {}
}; };
}, },
computed: {
optionsLabel() {
const label = this.options.label ? `${this.options.label} Super Menu` : 'Super Menu';
return label;
}
},
methods: { methods: {
toggleItemDescription(action = {}) { toggleItemDescription(action = {}) {
const hoveredItem = { const hoveredItem = {

View File

@ -25,13 +25,14 @@
class="c-fl-container" class="c-fl-container"
:style="[{ 'flex-basis': sizeString }]" :style="[{ 'flex-basis': sizeString }]"
:class="{ 'is-empty': !frames.length }" :class="{ 'is-empty': !frames.length }"
role="group" role="grid"
:aria-label="`Container ${container.id}`"
> >
<div <div
v-show="isEditing" v-show="isEditing"
class="c-fl-container__header" class="c-fl-container__header"
draggable="true" draggable="true"
role="columnheader"
:aria-label="`Container Handle ${index + 1}`"
@dragstart="startContainerDrag" @dragstart="startContainerDrag"
> >
<span class="c-fl-container__size-indicator">{{ sizeString }}</span> <span class="c-fl-container__size-indicator">{{ sizeString }}</span>
@ -44,12 +45,14 @@
@object-drop-to="moveOrCreateNewFrame" @object-drop-to="moveOrCreateNewFrame"
/> />
<div class="c-fl-container__frames-holder"> <div role="row" class="c-fl-container__frames-holder">
<template v-for="(frame, i) in frames" :key="frame.id"> <template v-for="(frame, i) in frames" :key="frame.id">
<frame-component <frame-component
class="c-fl-container__frame" class="c-fl-container__frame"
:frame="frame" :frame="frame"
:index="i" :index="i"
role="gridcell"
:aria-label="`Container Frame ${index}`"
:container-index="index" :container-index="index"
:is-editing="isEditing" :is-editing="isEditing"
:object-path="objectPath" :object-path="objectPath"

View File

@ -29,10 +29,11 @@
</div> </div>
<div <div
class="c-fl__container-holder" class="c-fl__container-holder u-style-receiver js-style-receiver"
:class="{ :class="{
'c-fl--rows': rowsLayout === true 'c-fl--rows': rowsLayout === true
}" }"
:aria-label="`Flexible Layout ${rowsLayout ? 'Row' : 'Column'}`"
> >
<template v-for="(container, index) in containers" :key="`component-${container.id}`"> <template v-for="(container, index) in containers" :key="`component-${container.id}`">
<drop-hint <drop-hint

View File

@ -29,7 +29,8 @@
</span> </span>
<button <button
ref="menu-button" ref="menu-button"
title="More options" title="More actions"
aria-label="More actions"
class="l-browse-bar__actions c-icon-button icon-3-dots" class="l-browse-bar__actions c-icon-button icon-3-dots"
@click="toggleMenu" @click="toggleMenu"
></button> ></button>

View File

@ -24,13 +24,18 @@
<div ref="fontSizeMenu" class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left"> <div ref="fontSizeMenu" class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left">
<button <button
class="c-icon-button c-button--menu icon-font-size" class="c-icon-button c-button--menu icon-font-size"
aria-label="Set Font Size"
@click.prevent.stop="showFontSizeMenu" @click.prevent.stop="showFontSizeMenu"
> >
<span class="c-button__label">{{ fontSizeLabel }}</span> <span class="c-button__label">{{ fontSizeLabel }}</span>
</button> </button>
</div> </div>
<div ref="fontMenu" class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left"> <div ref="fontMenu" class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left">
<button class="c-icon-button c-button--menu icon-font" @click.prevent.stop="showFontMenu"> <button
class="c-icon-button c-button--menu icon-font"
aria-label="Set Font Type"
@click.prevent.stop="showFontMenu"
>
<span class="c-button__label">{{ fontTypeLabel }}</span> <span class="c-button__label">{{ fontTypeLabel }}</span>
</button> </button>
</div> </div>

View File

@ -46,14 +46,17 @@ export default function StylesInspectorViewProvider(openmct) {
glyph: 'icon-paint-bucket', glyph: 'icon-paint-bucket',
canView: function (selection) { canView: function (selection) {
const objectSelection = selection?.[0]; const objectSelection = selection?.[0];
const layoutItem = objectSelection?.[0]?.context?.layoutItem; const objectContext = objectSelection?.[0]?.context;
const domainObject = objectSelection?.[0]?.context?.item; const layoutItem = objectContext?.layoutItem;
const domainObject = objectContext?.item;
const isFlexibleLayoutContainer =
domainObject?.type === 'flexible-layout' && objectContext.type === 'container';
if (layoutItem) { if (layoutItem) {
return true; return true;
} }
if (!domainObject) { if (!domainObject || isFlexibleLayoutContainer) {
return false; return false;
} }

View File

@ -36,7 +36,8 @@
</a> </a>
<button <button
class="c-ne__embed__actions c-icon-button icon-3-dots" class="c-ne__embed__actions c-icon-button icon-3-dots"
title="More options" title="More actions"
aria-label="More actions"
@click.prevent.stop="showMenuItems($event)" @click.prevent.stop="showMenuItems($event)"
></button> ></button>
</div> </div>

View File

@ -23,8 +23,9 @@
<template> <template>
<div <div
v-if="loaded" v-if="loaded"
class="c-plot c-plot--stacked holder holder-plot has-control-bar" class="c-plot c-plot--stacked holder holder-plot has-control-bar u-style-receiver js-style-receiver"
:class="[plotLegendExpandedStateClass, plotLegendPositionClass]" :class="[plotLegendExpandedStateClass, plotLegendPositionClass]"
aria-label="Stacked Plot Style Target"
> >
<plot-legend <plot-legend
v-if="compositionObjectsConfigLoaded && showLegendsForChildren === false" v-if="compositionObjectsConfigLoaded && showLegendsForChildren === false"

View File

@ -236,7 +236,11 @@ export default {
if (elemToStyle) { if (elemToStyle) {
if (typeof styleObj[key] === 'string' && styleObj[key].indexOf('__no_value') > -1) { if (typeof styleObj[key] === 'string' && styleObj[key].indexOf('__no_value') > -1) {
if (elemToStyle.style[key]) { if (elemToStyle.style[key]) {
elemToStyle.style[key] = ''; if (key === 'background-color') {
elemToStyle.style[key] = 'transparent';
} else {
elemToStyle.style[key] = '';
}
} }
} else { } else {
if ( if (

View File

@ -128,7 +128,8 @@
></button> ></button>
<button <button
class="l-browse-bar__actions c-icon-button icon-3-dots" class="l-browse-bar__actions c-icon-button icon-3-dots"
title="More options" title="More actions"
aria-label="More actions"
@click.prevent.stop="showMenuItems($event)" @click.prevent.stop="showMenuItems($event)"
></button> ></button>
</div> </div>
@ -362,6 +363,10 @@ export default {
title: 'Saving' title: 'Saving'
}); });
const currentSelection = this.openmct.selection.selected[0];
const parentObject = currentSelection[currentSelection.length - 1];
this.openmct.selection.select(parentObject);
return this.openmct.editor return this.openmct.editor
.save() .save()
.then(() => { .then(() => {

View File

@ -48,7 +48,8 @@
></button> ></button>
<button <button
class="l-browse-bar__actions c-icon-button icon-3-dots" class="l-browse-bar__actions c-icon-button icon-3-dots"
title="More options" title="More actions"
aria-label="More actions"
@click.prevent.stop="showMenuItems($event)" @click.prevent.stop="showMenuItems($event)"
></button> ></button>
</div> </div>

View File

@ -20,7 +20,7 @@
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<template> <template>
<div class="c-toolbar"> <div role="menubar" class="c-toolbar">
<div class="c-toolbar__element-controls"> <div class="c-toolbar__element-controls">
<component <component
:is="item.control" :is="item.control"

View File

@ -24,6 +24,8 @@
<div <div
ref="button" ref="button"
class="c-icon-button" class="c-icon-button"
role="menuitem"
:aria-label="options.title"
:title="options.title" :title="options.title"
:class="{ :class="{
[options.icon]: true, [options.icon]: true,

View File

@ -21,23 +21,26 @@
--> -->
<template> <template>
<div class="c-ctrl-wrapper"> <div class="c-ctrl-wrapper">
<div <button
class="c-icon-button c-icon-button--swatched" class="c-icon-button c-icon-button--swatched"
:class="[options.icon, { 'c-icon-button--mixed': nonSpecific }]" :class="[options.icon, { 'c-icon-button--mixed': nonSpecific }]"
:title="options.title" :title="options.title"
:aria-label="options.title"
@click="handleClick" @click="handleClick"
> >
<div <div
class="c-swatch" class="c-swatch"
:style="{ :style="{ background: options.value }"
background: options.value role="img"
}" :aria-label="None"
></div> ></div>
</div> </button>
<div v-if="open" class="c-menu c-palette c-palette--color"> <div v-if="open" class="c-menu c-palette c-palette--color">
<div <div
v-if="!options.preventNone" v-if="!options.preventNone"
class="c-palette__item-none" class="c-palette__item-none"
role="grid"
aria-label="No Style"
@click="select({ value: 'transparent' })" @click="select({ value: 'transparent' })"
> >
<div class="c-palette__item"></div> <div class="c-palette__item"></div>
@ -47,8 +50,11 @@
<div <div
v-for="(color, index) in colorPalette" v-for="(color, index) in colorPalette"
:key="index" :key="index"
role="gridcell"
class="c-palette__item" class="c-palette__item"
:style="{ background: color.value }" :style="{ background: color.value }"
:title="color.value"
:aria-label="color.value"
@click="select(color)" @click="select(color)"
></div> ></div>
</div> </div>

View File

@ -20,7 +20,7 @@
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<template> <template>
<div class="c-toolbar__separator"></div> <div role="separator" class="c-toolbar__separator"></div>
</template> </template>
<script> <script>