From 01434ff2d51adda220713d24f275fad94b7c48e3 Mon Sep 17 00:00:00 2001 From: Jesse Mazzella Date: Thu, 18 Jan 2024 03:21:36 -0800 Subject: [PATCH] feat(#7367): ProgressBar for `ExportAsJSONAction` (#7374) * refactor(ExportAsJSONAction): use private methods * refactor: remove unnecessary webpack alias * refactor: lint * fix: tests for `ExportAsJSONAction` * test: stabilize `InspectorStylesSpec` tests * docs: fix jsdocs * chore: remove dead / redundant code * refactor(LocalStorageObjectProvider): use `getItem()` and `setItem()` * refactor(ExportAsJSONAction): use `Promise.all` where applicable * refactor(MenuAPI): one-liner * feat: add percentage ProgressBar to ExportAsJSONAction * fix(ProgressBar.vue): v-if conditionals * test(fix): update mockLocalStorage * test: fix locators * test: remove unneeded awaits * fix: example imagery urls (moved after NASA wordpress migration) * Revert "refactor(LocalStorageObjectProvider): use `getItem()` and `setItem()`" This reverts commit 4f8403adaba5f62ab3a83e5c28ae9c4856a6046b. * test(e2e): fix logPlot test * Revert "Revert "refactor(LocalStorageObjectProvider): use `getItem()` and `setItem()`"" This reverts commit 0de66401cdd0cbe3e88a89c9c6d2bfe6773257fb. * test(e2e): remove waitForNavigations --- .webpack/webpack.common.js | 15 +- .../functional/clearDataAction.e2e.spec.js | 4 +- .../plugins/plot/logPlot.e2e.spec.js | 39 ++-- example/imagery/plugin.js | 36 ++-- src/api/menu/MenuAPI.js | 4 +- src/api/overlays/Overlay.js | 3 +- src/api/overlays/OverlayAPI.js | 19 +- src/api/overlays/ProgressDialog.js | 17 +- .../components/ProgressDialogComponent.vue | 20 +- src/exporters/CSVExporter.js | 2 +- src/exporters/ImageExporter.js | 2 +- src/exporters/JSONExporter.js | 2 +- .../exportAsJSONAction/ExportAsJSONAction.js | 190 ++++++++++++------ .../ExportAsJSONActionSpec.js | 10 +- .../LocalStorageObjectProvider.js | 8 +- .../actions/ExportNotebookAsTextAction.js | 2 +- src/ui/components/ProgressBar.vue | 22 +- src/ui/inspector/InspectorStylesSpec.js | 49 +++-- src/utils/testing/mockLocalStorage.js | 2 +- 19 files changed, 249 insertions(+), 197 deletions(-) diff --git a/.webpack/webpack.common.js b/.webpack/webpack.common.js index cf729868ef..1dc3af7ba6 100644 --- a/.webpack/webpack.common.js +++ b/.webpack/webpack.common.js @@ -6,15 +6,15 @@ This is the OpenMCT common webpack file. It is imported by the other three webpa There are separate npm scripts to use these configurations, though simply running `npm install` will use the default production configuration. */ -import path from 'node:path'; -import CopyWebpackPlugin from 'copy-webpack-plugin'; -import webpack from 'webpack'; -import MiniCssExtractPlugin from 'mini-css-extract-plugin'; -import fs from 'node:fs'; import { execSync } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; import { fileURLToPath } from 'node:url'; +import CopyWebpackPlugin from 'copy-webpack-plugin'; +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; import { VueLoaderPlugin } from 'vue-loader'; +import webpack from 'webpack'; let gitRevision = 'error-retrieving-revision'; let gitBranch = 'error-retrieving-branch'; @@ -22,9 +22,7 @@ const packageDefinition = JSON.parse(fs.readFileSync(new URL('../package.json', try { gitRevision = execSync('git rev-parse HEAD').toString().trim(); - gitBranch = execSync('git rev-parse --abbrev-ref HEAD') - .toString() - .trim(); + gitBranch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim(); } catch (err) { console.warn(err); } @@ -67,7 +65,6 @@ const config = { alias: { '@': path.join(projectRootDir, 'src'), legacyRegistry: path.join(projectRootDir, 'src/legacyRegistry'), - saveAs: 'file-saver/src/FileSaver.js', csv: 'comma-separated-values', EventEmitter: 'eventemitter3', bourbon: 'bourbon.scss', diff --git a/e2e/tests/functional/clearDataAction.e2e.spec.js b/e2e/tests/functional/clearDataAction.e2e.spec.js index 9608c9d31e..56e6b2f274 100644 --- a/e2e/tests/functional/clearDataAction.e2e.spec.js +++ b/e2e/tests/functional/clearDataAction.e2e.spec.js @@ -43,7 +43,7 @@ test.describe('Clear Data Action', () => { await expect(page.locator(backgroundImageSelector)).toBeVisible(); }); test('works as expected with Example Imagery', async ({ page }) => { - await expect(await page.locator('.c-thumb__image').count()).toBeGreaterThan(0); + expect(await page.locator('.c-thumb__image').count()).toBeGreaterThan(0); // Click the "Clear Data" menu action await page.getByTitle('More actions').click(); await expect( @@ -59,6 +59,6 @@ test.describe('Clear Data Action', () => { // Verify that the background image is no longer visible await expect(page.locator(backgroundImageSelector)).toBeHidden(); - await expect(await page.locator('.c-thumb__image').count()).toBe(0); + expect(await page.locator('.c-thumb__image').count()).toBe(0); }); }); diff --git a/e2e/tests/functional/plugins/plot/logPlot.e2e.spec.js b/e2e/tests/functional/plugins/plot/logPlot.e2e.spec.js index 02b21ae20e..20e4443034 100644 --- a/e2e/tests/functional/plugins/plot/logPlot.e2e.spec.js +++ b/e2e/tests/functional/plugins/plot/logPlot.e2e.spec.js @@ -95,18 +95,15 @@ async function makeOverlayPlot(page, myItemsFolderName) { await page.locator('button.c-create-button').click(); await page.locator('li[role="menuitem"]:has-text("Overlay Plot")').click(); + // Click OK button and wait for Navigate event await Promise.all([ - page.waitForNavigation({ waitUntil: 'networkidle' }), - page.locator('button:has-text("OK")').click(), - //Wait for Save Banner to appear + page.waitForLoadState(), + await page.getByRole('button', { name: 'Save' }).click(), + // Wait for Save Banner to appear page.waitForSelector('.c-message-banner__message') ]); - //Wait until Save Banner is gone - await page.locator('.c-message-banner__close-button').click(); - await page.waitForSelector('.c-message-banner__message', { state: 'detached' }); // save the overlay plot - await saveOverlayPlot(page); // create a sinewave generator @@ -114,34 +111,24 @@ async function makeOverlayPlot(page, myItemsFolderName) { await page.locator('button.c-create-button').click(); await page.locator('li[role="menuitem"]:has-text("Sine Wave Generator")').click(); - // set amplitude to 6, offset 4, period 2 + // set amplitude to 6, offset 4, data rate 2 hz - await page.locator('div:nth-child(5) .c-form-row__controls .form-control .field input').click(); - await page.locator('div:nth-child(5) .c-form-row__controls .form-control .field input').fill('6'); - - await page.locator('div:nth-child(6) .c-form-row__controls .form-control .field input').click(); - await page.locator('div:nth-child(6) .c-form-row__controls .form-control .field input').fill('4'); - - await page.locator('div:nth-child(7) .c-form-row__controls .form-control .field input').click(); - await page.locator('div:nth-child(7) .c-form-row__controls .form-control .field input').fill('2'); - - // Click OK to make generator + await page.getByLabel('Amplitude').fill('6'); + await page.getByLabel('Offset').fill('4'); + await page.getByLabel('Data Rate (hz)').fill('2'); + // Click OK button and wait for Navigate event await Promise.all([ - page.waitForNavigation({ waitUntil: 'networkidle' }), - page.locator('button:has-text("OK")').click(), - //Wait for Save Banner to appear + page.waitForLoadState(), + await page.getByRole('button', { name: 'Save' }).click(), + // Wait for Save Banner to appear page.waitForSelector('.c-message-banner__message') ]); - //Wait until Save Banner is gone - await page.locator('.c-message-banner__close-button').click(); - await page.waitForSelector('.c-message-banner__message', { state: 'detached' }); // click on overlay plot - await page.locator(`text=Open MCT ${myItemsFolderName} >> span`).nth(3).click(); await Promise.all([ - page.waitForNavigation(), + page.waitForLoadState(), page.locator('text=Unnamed Overlay Plot').first().click() ]); } diff --git a/example/imagery/plugin.js b/example/imagery/plugin.js index da267c241b..9d37bbc925 100644 --- a/example/imagery/plugin.js +++ b/example/imagery/plugin.js @@ -21,24 +21,24 @@ *****************************************************************************/ const DEFAULT_IMAGE_SAMPLES = [ - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18731.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18732.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18733.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18734.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18735.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18736.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18737.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18738.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18739.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18740.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18741.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18742.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18743.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18744.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18745.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18746.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18747.jpg', - 'https://www.hq.nasa.gov/alsj/a16/AS16-117-18748.jpg' + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18731.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18732.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18733.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18734.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18735.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18736.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18737.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18738.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18739.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18740.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18741.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18742.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18743.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18744.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18745.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18746.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18747.jpg', + 'https://www.nasa.gov/wp-content/uploads/static/history/alsj/a16/AS16-117-18748.jpg' ]; const DEFAULT_IMAGE_LOAD_DELAY_IN_MILLISECONDS = 20000; const MIN_IMAGE_LOAD_DELAY_IN_MILLISECONDS = 5000; diff --git a/src/api/menu/MenuAPI.js b/src/api/menu/MenuAPI.js index ec7bf9181e..c66032229d 100644 --- a/src/api/menu/MenuAPI.js +++ b/src/api/menu/MenuAPI.js @@ -78,9 +78,7 @@ class MenuAPI { if (isActionGroup) { action = this.actionsToMenuItems(action, objectPath, view); } else { - action.onItemClicked = () => { - action.invoke(objectPath, view); - }; + action.onItemClicked = () => action.invoke(objectPath, view); } return action; diff --git a/src/api/overlays/Overlay.js b/src/api/overlays/Overlay.js index 94936e6e0e..33b4e7935c 100644 --- a/src/api/overlays/Overlay.js +++ b/src/api/overlays/Overlay.js @@ -32,7 +32,7 @@ class Overlay extends EventEmitter { const { destroy } = mount( { components: { - OverlayComponent: OverlayComponent + OverlayComponent }, provide: { dismiss: this.notifyAndDismiss.bind(this), @@ -60,7 +60,6 @@ class Overlay extends EventEmitter { dismiss() { this.emit('destroy'); - document.body.removeChild(this.container); this.destroy(); } diff --git a/src/api/overlays/OverlayAPI.js b/src/api/overlays/OverlayAPI.js index d45aaa34bc..762ad01077 100644 --- a/src/api/overlays/OverlayAPI.js +++ b/src/api/overlays/OverlayAPI.js @@ -82,14 +82,17 @@ class OverlayAPI { } /** - * A description of option properties that can be passed into the overlay - * @typedef options - * @property {object} element DOMElement that is to be inserted/shown on the overlay - * @property {string} size preferred size of the overlay (large, small, fit) - * @property {array} buttons optional button objects with label and callback properties - * @property {function} onDestroy callback to be called when overlay is destroyed - * @property {boolean} dismissable allow user to dismiss overlay by using esc, and clicking away - * from overlay. Unless set to false, all overlays will be dismissable by default. + * Creates and displays an overlay with the specified options. + * + * @typedef {Object} OverlayOptions + * @property {HTMLElement} element The DOM Element to be inserted or shown in the overlay. + * @property {'large'|'small'|'fit'} size The preferred size of the overlay. + * @property {Array<{label: string, callback: Function}>} [buttons] Optional array of button objects, each with 'label' and 'callback' properties. + * @property {Function} onDestroy Callback to be called when the overlay is destroyed. + * @property {boolean} [dismissable=true] Whether the overlay can be dismissed by pressing 'esc' or clicking outside of it. Defaults to true. + * + * @param {OverlayOptions} options - The configuration options for the overlay. + * @returns {Overlay} An instance of the Overlay class. */ overlay(options) { let overlay = new Overlay(options); diff --git a/src/api/overlays/ProgressDialog.js b/src/api/overlays/ProgressDialog.js index fc3b7fd305..8183135872 100644 --- a/src/api/overlays/ProgressDialog.js +++ b/src/api/overlays/ProgressDialog.js @@ -17,7 +17,7 @@ class ProgressDialog extends Overlay { }) { const { vNode, destroy } = mount({ components: { - ProgressDialogComponent: ProgressDialogComponent + ProgressDialogComponent }, provide: { iconClass, @@ -28,16 +28,15 @@ class ProgressDialog extends Overlay { }, data() { return { - model: { - progressPerc: progressPerc || 0, - progressText - } + progressPerc, + progressText }; }, - template: '' + template: + '' }); - component = vNode.componentInstance; + component = vNode.componentInstance; super({ element: vNode.el, size: 'fit', @@ -51,8 +50,8 @@ class ProgressDialog extends Overlay { } updateProgress(progressPerc, progressText) { - component.model.progressPerc = progressPerc; - component.model.progressText = progressText; + component.$data.progressPerc = progressPerc; + component.$data.progressText = progressText; } } diff --git a/src/api/overlays/components/ProgressDialogComponent.vue b/src/api/overlays/components/ProgressDialogComponent.vue index a306690844..f765a8b623 100644 --- a/src/api/overlays/components/ProgressDialogComponent.vue +++ b/src/api/overlays/components/ProgressDialogComponent.vue @@ -20,9 +20,9 @@ at runtime from the About dialog for additional information. -->