[Mobile] Make Time Conductor Usable Again (#7515)

* Initial changes to refactor Time Conductor

* Finish refactor using grid-template

* Finish total refactor of Time Conductor

* Initial mobile changes

* Fix TC on mobile by changing grid template

* Fix more mobile stuff

* Add ellipsize to TC popup options and rearrange popup inputs and labels

* Small final changes so TC is adaptive to extreme cases

* Add e2e mobile test

---------

Co-authored-by: John Hill <john.c.hill@nasa.gov>
This commit is contained in:
Rukmini Bose (Ruki) 2024-03-08 12:22:09 -08:00 committed by GitHub
parent ab49f3f3a1
commit 87ba9fcbc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 230 additions and 63 deletions

View File

@ -34,42 +34,62 @@ Make no assumptions about the order that elements appear in the DOM.
*/
import { expect, test } from '../../pluginFixtures.js';
test('Verify that My Items Tree appears @mobile', async ({ page, openmctConfig }) => {
const { myItemsFolderName } = openmctConfig;
//Go to baseURL
await page.goto('./');
//My Items to be visible
await expect(page.getByRole('treeitem', { name: `${myItemsFolderName}` })).toBeVisible();
});
test('Verify that user can search @mobile', async ({ page }) => {
//For now, this test is going to be hardcoded against './test-data/display_layout_with_child_layouts.json'
await page.goto('./');
await page.getByRole('searchbox', { name: 'Search Input' }).click();
await page.getByRole('searchbox', { name: 'Search Input' }).fill('Parent Display Layout');
//Search Results appear in search modal
await expect(page.getByLabel('Object Results').getByText('Parent Display Layout')).toBeVisible();
//Clicking on the search result takes you to the object
await page.getByLabel('Object Results').getByText('Parent Display Layout').click();
await page.getByTitle('Collapse Browse Pane').click();
await expect(page.getByRole('main').getByText('Parent Display Layout')).toBeVisible();
});
test('Remove Object and confirmation dialog @mobile', async ({ page }) => {
await page.goto('./');
await page.getByRole('searchbox', { name: 'Search Input' }).click();
await page.getByRole('searchbox', { name: 'Search Input' }).fill('Parent Display Layout');
//Search Results appear in search modal
//Clicking on the search result takes you to the object
await page.getByLabel('Object Results').getByText('Parent Display Layout').click();
await page.getByTitle('Collapse Browse Pane').click();
await expect(page.getByRole('main').getByText('Parent Display Layout')).toBeVisible();
//Verify both objects are in view
await expect(await page.getByLabel('Child Layout 1 Layout')).toBeVisible();
await expect(await page.getByLabel('Child Layout 2 Layout')).toBeVisible();
//Remove First Object to bring up confirmation dialog
await page.getByLabel('View menu items').nth(1).click();
await page.getByLabel('Remove').click();
await page.getByRole('button', { name: 'OK' }).click();
//Verify that the object is removed
await expect(await page.getByLabel('Child Layout 1 Layout')).toBeVisible();
expect(await page.getByLabel('Child Layout 2 Layout').count()).toBe(0);
test.describe('Smoke tests for @mobile', () => {
test.beforeEach(async ({ page }) => {
//For now, this test is going to be hardcoded against './test-data/display_layout_with_child_layouts.json'
await page.goto('./');
});
test('Verify that My Items Tree appears @mobile', async ({ page }) => {
//My Items to be visible
await expect(page.getByRole('treeitem', { name: 'My Items' })).toBeVisible();
});
test('Verify that user can search @mobile', async ({ page }) => {
await page.getByRole('searchbox', { name: 'Search Input' }).click();
await page.getByRole('searchbox', { name: 'Search Input' }).fill('Parent Display Layout');
//Search Results appear in search modal
await expect(
page.getByLabel('Object Results').getByText('Parent Display Layout')
).toBeVisible();
//Clicking on the search result takes you to the object
await page.getByLabel('Object Results').getByText('Parent Display Layout').click();
await page.getByTitle('Collapse Browse Pane').click();
await expect(page.getByRole('main').getByText('Parent Display Layout')).toBeVisible();
});
test('Verify that user can change time conductor @mobile', async ({ page }) => {
//Collapse Browse Pane to get more Time Conductor space
await page.getByLabel('Collapse Browse Pane').click();
//Open Time Conductor and change to Real Time Mode and set offset hour by 1 hour
// Disabling line because we're intentionally obscuring the text
// eslint-disable-next-line playwright/no-force-option
await page.getByLabel('Time Conductor Mode').click({ force: true });
await page.getByLabel('Time Conductor Mode Menu').click();
await page.getByLabel('Real-Time').click();
await page.getByLabel('Start offset hours').fill('01');
await page.getByLabel('Submit time offsets').click();
await expect(page.getByLabel('Start offset: 01:30:00')).toBeVisible();
});
test('Remove Object and confirmation dialog @mobile', async ({ page }) => {
await page.getByRole('searchbox', { name: 'Search Input' }).click();
await page.getByRole('searchbox', { name: 'Search Input' }).fill('Parent Display Layout');
//Search Results appear in search modal
//Clicking on the search result takes you to the object
await page.getByLabel('Object Results').getByText('Parent Display Layout').click();
await page.getByTitle('Collapse Browse Pane').click();
await expect(page.getByRole('main').getByText('Parent Display Layout')).toBeVisible();
//Verify both objects are in view
await expect(await page.getByLabel('Child Layout 1 Layout')).toBeVisible();
await expect(await page.getByLabel('Child Layout 2 Layout')).toBeVisible();
//Remove First Object to bring up confirmation dialog
await page.getByLabel('View menu items').nth(1).click();
await page.getByLabel('Remove').click();
await page.getByRole('button', { name: 'OK' }).click();
//Verify that the object is removed
await expect(await page.getByLabel('Child Layout 1 Layout')).toBeVisible();
expect(await page.getByLabel('Child Layout 2 Layout').count()).toBe(0);
});
});

View File

@ -1,14 +1,14 @@
<template>
<form ref="fixedDeltaInput">
<div class="c-tc-input-popup__input-grid">
<div class="pr-time-label"><em>Start</em> Date</div>
<div class="pr-time-label">Time</div>
<div class="pr-time-label"></div>
<div class="pr-time-label"><em>End</em> Date</div>
<div class="pr-time-label">Time</div>
<div class="pr-time-label"></div>
<div class="pr-time-label pr-time-label-start-date"><em>Start</em> Date</div>
<div class="pr-time-label pr-time-label-start-time">Time</div>
<div class="pr-time-label pr-time-label-end-date"><em>End</em> Date</div>
<div class="pr-time-label pr-time-label-end-time">Time</div>
<div class="pr-time-input pr-time-input--date pr-time-input--input-and-button">
<div
class="pr-time-input pr-time-input--date pr-time-input--input-and-button pr-time-input-start-date"
>
<input
ref="startDate"
v-model="formattedBounds.start"
@ -28,7 +28,7 @@
/>
</div>
<div class="pr-time-input pr-time-input--time">
<div class="pr-time-input pr-time-input--time pr-time-input-start-time">
<input
ref="startTime"
v-model="formattedBounds.startTime"
@ -43,7 +43,9 @@
<div class="pr-time-input pr-time-input__start-end-sep icon-arrows-right-left"></div>
<div class="pr-time-input pr-time-input--date pr-time-input--input-and-button">
<div
class="pr-time-input pr-time-input--date pr-time-input--input-and-button pr-time-input-end-date"
>
<input
ref="endDate"
v-model="formattedBounds.end"
@ -63,7 +65,7 @@
/>
</div>
<div class="pr-time-input pr-time-input--time">
<div class="pr-time-input pr-time-input--time pr-time-input-end-time">
<input
ref="endTime"
v-model="formattedBounds.endTime"

View File

@ -1,16 +1,14 @@
<template>
<form ref="deltaInput">
<div class="c-tc-input-popup__input-grid">
<div class="pr-time-label icon-minus">Hrs</div>
<div class="pr-time-label">Mins</div>
<div class="pr-time-label">Secs</div>
<div class="pr-time-label"></div>
<div class="pr-time-label icon-plus">Hrs</div>
<div class="pr-time-label">Mins</div>
<div class="pr-time-label">Secs</div>
<div class="pr-time-label"></div>
<div class="pr-time-label icon-minus pr-time-label-minus-hrs">Hrs</div>
<div class="pr-time-label pr-time-label-minus-mins">Mins</div>
<div class="pr-time-label pr-time-label-minus-secs">Secs</div>
<div class="pr-time-label icon-plus pr-time-label-plus-hrs">Hrs</div>
<div class="pr-time-label pr-time-label-plus-mins">Mins</div>
<div class="pr-time-label pr-time-label-plus-secs">Secs</div>
<div class="pr-time-input">
<div class="pr-time-input pr-time-input-minus-hrs">
<input
ref="startInputHrs"
v-model="startInputHrs"
@ -29,7 +27,7 @@
/>
<b>:</b>
</div>
<div class="pr-time-input">
<div class="pr-time-input pr-time-input-minus-mins">
<input
ref="startInputMins"
v-model="startInputMins"
@ -48,7 +46,7 @@
/>
<b>:</b>
</div>
<div class="pr-time-input">
<div class="pr-time-input pr-time-input-minus-secs">
<input
ref="startInputSecs"
v-model="startInputSecs"
@ -69,7 +67,7 @@
<div class="pr-time-input pr-time-input__start-end-sep icon-arrows-right-left"></div>
<div class="pr-time-input">
<div class="pr-time-input pr-time-input-plus-hrs">
<input
ref="endInputHrs"
v-model="endInputHrs"
@ -88,7 +86,7 @@
/>
<b>:</b>
</div>
<div class="pr-time-input">
<div class="pr-time-input pr-time-input-plus-mins">
<input
ref="endInputMins"
v-model="endInputMins"
@ -106,7 +104,7 @@
/>
<b>:</b>
</div>
<div class="pr-time-input">
<div class="pr-time-input pr-time-input-plus-secs">
<input
ref="endInputSecs"
v-model="endInputSecs"

View File

@ -604,23 +604,170 @@
padding: cButtonPadding($compact: true);
}
}
.pr-time{
// FIXED TIME MODE
&-label-start-date{
grid-area: sDate;
}
&-label-start-time{
grid-area: sTime;
}
&-input-start-date{
grid-area: sDateInput;
}
&-input-start-time{
grid-area: sTimeInput;
}
&-label-end-date{
grid-area: eDate;
}
&-label-end-time{
grid-area: eTime;
}
&-input-end-date{
grid-area: eDateInput;
}
&-input-end-time{
grid-area: eTimeInput;
}
&-label-blank-grid{
grid-area: blank;
}
//REAL TIME MODE
&-label-minus-hrs{
grid-area: labelMinusHrs;
}
&-label-minus-mins{
grid-area: labelMinusMins;
}
&-label-minus-secs{
grid-area: labelMinusSecs;
}
&-label-plus-hrs{
grid-area: labelPlusHrs;
}
&-label-plus-mins{
grid-area: labelPlusMins;
}
&-label-plus-secs{
grid-area: labelPlusSecs;
}
&-input-minus-hrs{
grid-area: inputMinusHrs;
}
&-input-minus-mins{
grid-area: inputMinusMins;
}
&-input-minus-secs{
grid-area: inputMinusSecs;
}
&-input-plus-hrs{
grid-area: inputPlusHrs;
}
&-input-plus-mins{
grid-area: inputPlusMins;
}
&-input-plus-secs{
grid-area: inputPlusSecs;
}
// USED FOR BOTH
&-label-blank-grid{
grid-area: empty;
}
&-input__start-end-sep{
grid-area: arrowIcon;
}
&-input--buttons{
grid-area: buttons;
}
}
&--fixed-mode {
.c-tc-input-popup__input-grid {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 2fr;
grid-template-areas:
"sDate sTime . eDate eTime ."
"sDateInput sTimeInput arrowIcon eDateInput eTimeInput buttons";
}
@include phonePortrait(){
.c-tc-input-popup__input-grid {
grid-template-columns: repeat(2, max-content) 1fr;
grid-template-areas:
"sDate sTime ."
"sDateInput sTimeInput ."
"eDate eTime ."
"eDateInput eTimeInput buttons";
padding: 2px;
overflow: hidden;
}
}
}
&--realtime-mode {
.c-tc-input-popup__input-grid {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 2fr;
grid-template-areas:
"labelMinusHrs labelMinusMins labelMinusSecs . labelPlusHrs labelPlusMins labelPlusSecs ."
"inputMinusHrs inputMinusMins inputMinusSecs arrowIcon inputPlusHrs inputPlusMins inputPlusSecs buttons";
}
@include phonePortrait(){
.c-tc-input-popup__input-grid {
grid-template-columns: repeat(3, max-content) 1fr;
grid-template-areas:
"labelMinusHrs labelMinusMins labelMinusSecs ."
"inputMinusHrs inputMinusMins inputMinusSecs ."
"labelPlusHrs labelPlusMins labelPlusSecs ."
"inputPlusHrs inputPlusMins inputPlusSecs buttons";
padding: 2px;
overflow: hidden;
}
}
}
&__input-grid {
display: grid;
grid-column-gap: 3px;
grid-row-gap: $interiorMargin;
grid-column-gap: $interiorMarginSm;
align-items: start;
}
}
@include phonePortrait(){ // Additional styling for mobile portrait.
.c-tc-input-popup{
width: 100%;
&__options{
> * {
overflow: hidden;
[class*= 'ctrl-wrapper']{
[class*='--menu'] {
width: 100%;
[class*='__label'] {
@include ellipsize();
}
}
}
}
}
}
.pr-time-input-end-time, .pr-time-input-start-time{
> * {
margin-right: $interiorMargin;
}
}
.pr-time-input--buttons{
justify-content: flex-end;
}
.pr-time-input__start-end-sep{
margin: auto;
}
.pr-time-input__start-end-sep{
display: none;
}
.pr-time-input-start-date, .pr-time-input-end-date{
width: max-content;
}
}