mirror of
https://github.com/nasa/openmct.git
synced 2025-03-21 03:25:44 +00:00
5391 Add preview and drag support to Grand Search (#5394)
* add preview and drag actions * added unit test, simplified remove action * do not hide search results in preview mode when clicking outside search results * add semantic aria labels to enable e2e tests * readd preview * add e2e test * remove commented out url * add percy snapshot and add search to ci * make percy stuff work * linting * fix percy again * move percy snapshots to a visual test * added separate visual test and changed test to fixtures * fix fixtures path * addressing review comments
This commit is contained in:
parent
5a1c329c66
commit
28dbd724d6
111
e2e/tests/ui/layout/search/grandsearch.e2e.spec.js
Normal file
111
e2e/tests/ui/layout/search/grandsearch.e2e.spec.js
Normal file
@ -0,0 +1,111 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
This test suite is dedicated to tests which verify search functionality.
|
||||
*/
|
||||
|
||||
const { expect } = require('@playwright/test');
|
||||
const { test } = require('../../../../fixtures');
|
||||
|
||||
/**
|
||||
* Creates a notebook object and adds an entry.
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function createClockAndDisplayLayout(page) {
|
||||
//Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
// Click button:has-text("Create")
|
||||
await page.locator('button:has-text("Create")').click();
|
||||
// Click li:has-text("Notebook")
|
||||
await page.locator('li:has-text("Clock")').click();
|
||||
// Click button:has-text("OK")
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('button:has-text("OK")').click()
|
||||
]);
|
||||
|
||||
// Click a:has-text("My Items")
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('a:has-text("My Items") >> nth=0').click()
|
||||
]);
|
||||
// Click button:has-text("Create")
|
||||
await page.locator('button:has-text("Create")').click();
|
||||
// Click li:has-text("Notebook")
|
||||
await page.locator('li:has-text("Display Layout")').click();
|
||||
// Click button:has-text("OK")
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('button:has-text("OK")').click()
|
||||
]);
|
||||
}
|
||||
|
||||
test.describe('Grand Search', () => {
|
||||
test('Can search for objects, and subsequent search dropdown behaves properly', async ({ page }) => {
|
||||
await createClockAndDisplayLayout(page);
|
||||
|
||||
// Click [aria-label="OpenMCT Search"] input[type="search"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
||||
// Fill [aria-label="OpenMCT Search"] input[type="search"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Cl');
|
||||
await expect(page.locator('[aria-label="Search Result"]')).toContainText('Clock');
|
||||
// Click text=Elements >> nth=0
|
||||
await page.locator('text=Elements').first().click();
|
||||
await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
|
||||
|
||||
// Click [aria-label="OpenMCT Search"] [aria-label="Search Input"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
|
||||
// Click [aria-label="Unnamed Clock clock result"] >> text=Unnamed Clock
|
||||
await page.locator('[aria-label="Unnamed Clock clock result"] >> text=Unnamed Clock').click();
|
||||
await expect(page.locator('.js-preview-window')).toBeVisible();
|
||||
|
||||
// Click [aria-label="Close"]
|
||||
await page.locator('[aria-label="Close"]').click();
|
||||
await expect(page.locator('[aria-label="Search Result"]')).toBeVisible();
|
||||
await expect(page.locator('[aria-label="Search Result"]')).toContainText('Cloc');
|
||||
|
||||
// Click [aria-label="OpenMCT Search"] a >> nth=0
|
||||
await page.locator('[aria-label="OpenMCT Search"] a').first().click();
|
||||
await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
|
||||
|
||||
// Fill [aria-label="OpenMCT Search"] input[type="search"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('foo');
|
||||
await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
|
||||
|
||||
// Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1
|
||||
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
|
||||
// Click text=Save and Finish Editing
|
||||
await page.locator('text=Save and Finish Editing').click();
|
||||
// Click [aria-label="OpenMCT Search"] [aria-label="Search Input"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
|
||||
// Fill [aria-label="OpenMCT Search"] [aria-label="Search Input"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').fill('Cl');
|
||||
// Click text=Unnamed Clock
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('text=Unnamed Clock').click()
|
||||
]);
|
||||
await expect(page.locator('.is-object-type-clock')).toBeVisible();
|
||||
});
|
||||
});
|
104
e2e/tests/visual/search.visual.spec.js
Normal file
104
e2e/tests/visual/search.visual.spec.js
Normal file
@ -0,0 +1,104 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
This test suite is dedicated to tests which verify search functionality.
|
||||
*/
|
||||
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const percySnapshot = require('@percy/playwright');
|
||||
|
||||
/**
|
||||
* Creates a notebook object and adds an entry.
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
async function createClockAndDisplayLayout(page) {
|
||||
//Go to baseURL
|
||||
await page.goto('/', { waitUntil: 'networkidle' });
|
||||
|
||||
// Click button:has-text("Create")
|
||||
await page.locator('button:has-text("Create")').click();
|
||||
// Click li:has-text("Notebook")
|
||||
await page.locator('li:has-text("Clock")').click();
|
||||
// Click button:has-text("OK")
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('button:has-text("OK")').click()
|
||||
]);
|
||||
|
||||
// Click a:has-text("My Items")
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('a:has-text("My Items") >> nth=0').click()
|
||||
]);
|
||||
// Click button:has-text("Create")
|
||||
await page.locator('button:has-text("Create")').click();
|
||||
// Click li:has-text("Notebook")
|
||||
await page.locator('li:has-text("Display Layout")').click();
|
||||
// Click button:has-text("OK")
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('button:has-text("OK")').click()
|
||||
]);
|
||||
}
|
||||
|
||||
test.describe('Grand Search', () => {
|
||||
test('Can search for objects, and subsequent search dropdown behaves properly', async ({ page }) => {
|
||||
await createClockAndDisplayLayout(page);
|
||||
|
||||
// Click [aria-label="OpenMCT Search"] input[type="search"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').click();
|
||||
// Fill [aria-label="OpenMCT Search"] input[type="search"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] input[type="search"]').fill('Cl');
|
||||
await expect(page.locator('[aria-label="Search Result"]')).toContainText('Clock');
|
||||
await percySnapshot(page, 'Searching for Clocks');
|
||||
// Click text=Elements >> nth=0
|
||||
await page.locator('text=Elements').first().click();
|
||||
await expect(page.locator('[aria-label="Search Result"]')).not.toBeVisible();
|
||||
|
||||
// Click [aria-label="OpenMCT Search"] [aria-label="Search Input"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
|
||||
// Click [aria-label="Unnamed Clock clock result"] >> text=Unnamed Clock
|
||||
await page.locator('[aria-label="Unnamed Clock clock result"] >> text=Unnamed Clock').click();
|
||||
await percySnapshot(page, 'Preview for clock should display when editing enabled and search item clicked');
|
||||
|
||||
// Click [aria-label="Close"]
|
||||
await page.locator('[aria-label="Close"]').click();
|
||||
await percySnapshot(page, 'Search should still be showing after preview closed');
|
||||
|
||||
// Click text=Snapshot Save and Finish Editing Save and Continue Editing >> button >> nth=1
|
||||
await page.locator('text=Snapshot Save and Finish Editing Save and Continue Editing >> button').nth(1).click();
|
||||
// Click text=Save and Finish Editing
|
||||
await page.locator('text=Save and Finish Editing').click();
|
||||
// Click [aria-label="OpenMCT Search"] [aria-label="Search Input"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').click();
|
||||
// Fill [aria-label="OpenMCT Search"] [aria-label="Search Input"]
|
||||
await page.locator('[aria-label="OpenMCT Search"] [aria-label="Search Input"]').fill('Cl');
|
||||
// Click text=Unnamed Clock
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.locator('text=Unnamed Clock').click()
|
||||
]);
|
||||
await percySnapshot(page, 'Clicking on search results should navigate to them if not editing');
|
||||
|
||||
});
|
||||
});
|
@ -89,7 +89,7 @@
|
||||
"test": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run",
|
||||
"test:firefox": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=FirefoxHeadless",
|
||||
"test:debug": "cross-env NODE_ENV=debug karma start --no-single-run",
|
||||
"test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome smoke branding default condition timeConductor clock exampleImagery persistence performance",
|
||||
"test:e2e:ci": "npx playwright test --config=e2e/playwright-ci.config.js --project=chrome smoke branding default condition timeConductor clock exampleImagery persistence performance grandsearch",
|
||||
"test:e2e:local": "npx playwright test --config=e2e/playwright-local.config.js --project=chrome",
|
||||
"test:e2e:updatesnapshots": "npx playwright test --config=e2e/playwright-local.config.js --project=chrome --grep @snapshot --update-snapshots",
|
||||
"test:e2e:visual": "percy exec --config ./e2e/.percy.yml -- npx playwright test --config=e2e/playwright-visual.config.js",
|
||||
|
@ -44,6 +44,7 @@
|
||||
<div
|
||||
v-if="!hideOptions"
|
||||
class="c-menu c-input--autocomplete__options"
|
||||
aria-label="Autocomplete Options"
|
||||
@blur="hideOptions = true"
|
||||
>
|
||||
<ul>
|
||||
|
@ -123,10 +123,7 @@ export default class RemoveAction {
|
||||
}
|
||||
|
||||
if (isEditing) {
|
||||
let currentItemInView = this.openmct.router.path[0];
|
||||
let domainObject = objectPath[0];
|
||||
|
||||
if (this.openmct.objects.areIdsEqual(currentItemInView.identifier, domainObject.identifier)) {
|
||||
if (this.openmct.router.isNavigatedObject(objectPath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
>
|
||||
<input
|
||||
class="c-search__input"
|
||||
aria-label="Search Input"
|
||||
tabindex="10000"
|
||||
type="search"
|
||||
v-bind="$attrs"
|
||||
|
@ -23,6 +23,8 @@
|
||||
<template>
|
||||
<div
|
||||
class="c-gsearch-result c-gsearch-result--annotation"
|
||||
aria-label="Search Result"
|
||||
role="presentation"
|
||||
>
|
||||
<div
|
||||
class="c-gsearch-result__type-icon"
|
||||
|
@ -135,8 +135,11 @@ export default {
|
||||
// dropdown is visible, this will collapse the dropdown.
|
||||
if (this.$refs.GrandSearch) {
|
||||
const clickedInsideDropdown = this.$refs.GrandSearch.contains(event.target);
|
||||
if (!clickedInsideDropdown && this.$refs.searchResultsDropDown._data.resultsShown) {
|
||||
this.$refs.searchResultsDropDown._data.resultsShown = false;
|
||||
const clickedPreviewClose = event.target.parentElement && event.target.parentElement.querySelector('.js-preview-window');
|
||||
const searchResultsDropDown = this.$refs.searchResultsDropDown._data;
|
||||
if (!clickedInsideDropdown && searchResultsDropDown.resultsShown && !searchResultsDropDown.previewVisible && !clickedPreviewClose) {
|
||||
searchResultsDropDown.resultsShown = false;
|
||||
document.body.removeEventListener('click', this.handleOutsideClick);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
204
src/ui/layout/search/GrandSearchSpec.js
Normal file
204
src/ui/layout/search/GrandSearchSpec.js
Normal file
@ -0,0 +1,204 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2022, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import {
|
||||
createOpenMct,
|
||||
resetApplicationState
|
||||
} from 'utils/testing';
|
||||
import Vue from 'vue';
|
||||
import GrandSearch from './GrandSearch.vue';
|
||||
import ExampleTagsPlugin from '../../../../example/exampleTags/plugin';
|
||||
import DisplayLayoutPlugin from '../../../plugins/displayLayout/plugin';
|
||||
|
||||
describe("GrandSearch", () => {
|
||||
let openmct;
|
||||
let grandSearchComponent;
|
||||
let viewContainer;
|
||||
let parent;
|
||||
let sharedWorkerToRestore;
|
||||
let mockDomainObject;
|
||||
let mockDisplayLayout;
|
||||
let mockFolderObject;
|
||||
let mockAnnotationObject;
|
||||
let originalRouterPath;
|
||||
|
||||
beforeEach((done) => {
|
||||
openmct = createOpenMct();
|
||||
originalRouterPath = openmct.router.path;
|
||||
openmct.router.path = [mockDisplayLayout];
|
||||
openmct.editor.edit();
|
||||
|
||||
openmct.install(new ExampleTagsPlugin());
|
||||
openmct.install(new DisplayLayoutPlugin());
|
||||
const availableTags = openmct.annotation.getAvailableTags();
|
||||
mockDomainObject = {
|
||||
type: 'notebook',
|
||||
name: 'fooRabbitNotebook',
|
||||
identifier: {
|
||||
key: 'some-object',
|
||||
namespace: 'fooNameSpace'
|
||||
},
|
||||
configuration: {
|
||||
entries: {
|
||||
someSection: {
|
||||
somePage: [
|
||||
{
|
||||
id: 'fooBarEntry',
|
||||
text: 'Foo Bar Text'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
mockFolderObject = {
|
||||
type: 'folder',
|
||||
name: 'Test Folder',
|
||||
identifier: {
|
||||
key: 'some-folder',
|
||||
namespace: 'fooNameSpace'
|
||||
}
|
||||
};
|
||||
mockDisplayLayout = {
|
||||
type: 'layout',
|
||||
name: 'Bar Layout',
|
||||
identifier: {
|
||||
key: 'some-layout',
|
||||
namespace: 'fooNameSpace'
|
||||
},
|
||||
configuration: {
|
||||
items: [],
|
||||
layoutGrid: [10, 10]
|
||||
}
|
||||
};
|
||||
mockAnnotationObject = {
|
||||
type: 'annotation',
|
||||
name: 'Some Notebook Annotation',
|
||||
annotationType: openmct.annotation.ANNOTATION_TYPES.NOTEBOOK,
|
||||
tags: [availableTags[0].id, availableTags[1].id],
|
||||
identifier: {
|
||||
key: 'anAnnotationKey',
|
||||
namespace: 'fooNameSpace'
|
||||
},
|
||||
targets: {
|
||||
'fooNameSpace:some-object': {
|
||||
entryId: 'fooBarEntry'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
openmct.router.isNavigatedObject = jasmine.createSpy().and.returnValue(false);
|
||||
|
||||
const mockObjectProvider = jasmine.createSpyObj("mock object provider", [
|
||||
"create",
|
||||
"update",
|
||||
"get"
|
||||
]);
|
||||
// eslint-disable-next-line require-await
|
||||
mockObjectProvider.get = async (identifier) => {
|
||||
if (identifier.key === mockDomainObject.identifier.key) {
|
||||
return mockDomainObject;
|
||||
} else if (identifier.key === mockAnnotationObject.identifier.key) {
|
||||
return mockAnnotationObject;
|
||||
} else if (identifier.key === mockDisplayLayout.identifier.key) {
|
||||
return mockDisplayLayout;
|
||||
} else if (identifier.key === mockFolderObject.identifier.key) {
|
||||
return mockFolderObject;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
mockObjectProvider.create.and.returnValue(Promise.resolve(true));
|
||||
mockObjectProvider.update.and.returnValue(Promise.resolve(true));
|
||||
|
||||
openmct.objects.addProvider('fooNameSpace', mockObjectProvider);
|
||||
|
||||
const mockViewProvider = jasmine.createSpyObj("mock view provider", [
|
||||
"key",
|
||||
"view",
|
||||
"canView"
|
||||
]);
|
||||
|
||||
openmct.objectViews.addProvider(mockViewProvider);
|
||||
|
||||
openmct.on('start', async () => {
|
||||
// use local worker
|
||||
sharedWorkerToRestore = openmct.objects.inMemorySearchProvider.worker;
|
||||
openmct.objects.inMemorySearchProvider.worker = null;
|
||||
await openmct.objects.inMemorySearchProvider.index(mockDomainObject);
|
||||
await openmct.objects.inMemorySearchProvider.index(mockDisplayLayout);
|
||||
await openmct.objects.inMemorySearchProvider.index(mockFolderObject);
|
||||
await openmct.objects.inMemorySearchProvider.index(mockAnnotationObject);
|
||||
parent = document.createElement('div');
|
||||
document.body.appendChild(parent);
|
||||
viewContainer = document.createElement('div');
|
||||
parent.append(viewContainer);
|
||||
grandSearchComponent = new Vue({
|
||||
el: viewContainer,
|
||||
components: {
|
||||
GrandSearch
|
||||
},
|
||||
provide: {
|
||||
openmct
|
||||
},
|
||||
template: '<GrandSearch/>'
|
||||
}).$mount();
|
||||
await Vue.nextTick();
|
||||
done();
|
||||
});
|
||||
openmct.startHeadless();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
openmct.objects.inMemorySearchProvider.worker = sharedWorkerToRestore;
|
||||
openmct.router.path = originalRouterPath;
|
||||
grandSearchComponent.$destroy();
|
||||
|
||||
return resetApplicationState(openmct);
|
||||
});
|
||||
|
||||
it("should render an object search result", async () => {
|
||||
await grandSearchComponent.$children[0].searchEverything('foo');
|
||||
await Vue.nextTick();
|
||||
const searchResult = document.querySelector('[aria-label="fooRabbitNotebook notebook result"]');
|
||||
expect(searchResult).toBeDefined();
|
||||
});
|
||||
|
||||
it("should render an annotation search result", async () => {
|
||||
await grandSearchComponent.$children[0].searchEverything('S');
|
||||
await Vue.nextTick();
|
||||
const annotationResult = document.querySelector('[aria-label="Search Result"]');
|
||||
expect(annotationResult).toBeDefined();
|
||||
});
|
||||
|
||||
it("should preview object search results in edit mode if object clicked", async () => {
|
||||
await grandSearchComponent.$children[0].searchEverything('Folder');
|
||||
grandSearchComponent._provided.openmct.router.path = [mockDisplayLayout];
|
||||
await Vue.nextTick();
|
||||
const searchResult = document.querySelector('[name="Test Folder"]');
|
||||
expect(searchResult).toBeDefined();
|
||||
searchResult.click();
|
||||
const previewWindow = document.querySelector('.js-preview-window');
|
||||
expect(previewWindow).toBeDefined();
|
||||
});
|
||||
});
|
@ -23,6 +23,7 @@
|
||||
<template>
|
||||
<div
|
||||
class="c-gsearch-result c-gsearch-result--object"
|
||||
aria-label="Search Result"
|
||||
role="presentation"
|
||||
>
|
||||
<div
|
||||
@ -37,6 +38,8 @@
|
||||
<div
|
||||
class="c-gsearch-result__title"
|
||||
:name="resultName"
|
||||
draggable="true"
|
||||
@dragstart="dragStart"
|
||||
@click="clickedResult"
|
||||
>
|
||||
{{ resultName }}
|
||||
@ -56,6 +59,7 @@
|
||||
<script>
|
||||
import ObjectPath from '../../components/ObjectPath.vue';
|
||||
import objectPathToUrl from '../../../tools/url';
|
||||
import PreviewAction from '../../preview/PreviewAction';
|
||||
|
||||
export default {
|
||||
name: 'ObjectSearchResult',
|
||||
@ -90,12 +94,43 @@ export default {
|
||||
}
|
||||
};
|
||||
this.$refs.objectpath.updateSelection([[selectionObject]]);
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.previewAction.on('isVisible', this.togglePreviewState);
|
||||
},
|
||||
destroyed() {
|
||||
this.previewAction.off('isVisible', this.togglePreviewState);
|
||||
},
|
||||
methods: {
|
||||
clickedResult() {
|
||||
clickedResult(event) {
|
||||
if (this.openmct.editor.isEditing()) {
|
||||
event.preventDefault();
|
||||
this.preview();
|
||||
} else {
|
||||
const objectPath = this.result.originalPath;
|
||||
const resultUrl = objectPathToUrl(this.openmct, objectPath);
|
||||
this.openmct.router.navigate(resultUrl);
|
||||
}
|
||||
},
|
||||
togglePreviewState(previewState) {
|
||||
this.$emit('preview-changed', previewState);
|
||||
},
|
||||
preview() {
|
||||
const objectPath = this.result.originalPath;
|
||||
const resultUrl = objectPathToUrl(this.openmct, objectPath);
|
||||
this.openmct.router.navigate(resultUrl);
|
||||
if (this.previewAction.appliesTo(objectPath)) {
|
||||
this.previewAction.invoke(objectPath);
|
||||
}
|
||||
},
|
||||
dragStart(event) {
|
||||
const navigatedObject = this.openmct.router.path[0];
|
||||
const objectPath = this.result.originalPath;
|
||||
const serializedPath = JSON.stringify(objectPath);
|
||||
const keyString = this.openmct.objects.makeKeyString(this.result.identifier);
|
||||
if (this.openmct.composition.checkPolicy(navigatedObject, this.result)) {
|
||||
event.dataTransfer.setData("openmct/composable-domain-object", JSON.stringify(this.result));
|
||||
}
|
||||
|
||||
event.dataTransfer.setData("openmct/domain-object-path", serializedPath);
|
||||
event.dataTransfer.setData(`openmct/domain-object/${keyString}`, this.result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -42,6 +42,7 @@
|
||||
v-for="(objectResult, index) in objectResults"
|
||||
:key="index"
|
||||
:result="objectResult"
|
||||
@preview-changed="previewChanged"
|
||||
@click.native="selectedResult"
|
||||
/>
|
||||
</div>
|
||||
@ -76,12 +77,18 @@ export default {
|
||||
return {
|
||||
resultsShown: false,
|
||||
annotationResults: [],
|
||||
objectResults: []
|
||||
objectResults: [],
|
||||
previewVisible: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
selectedResult() {
|
||||
this.resultsShown = false;
|
||||
if (!this.previewVisible) {
|
||||
this.resultsShown = false;
|
||||
}
|
||||
},
|
||||
previewChanged(changedPreviewState) {
|
||||
this.previewVisible = changedPreviewState;
|
||||
},
|
||||
showResults(passedAnnotationResults, passedObjectResults) {
|
||||
if ((passedAnnotationResults && passedAnnotationResults.length)
|
||||
|
@ -39,6 +39,7 @@
|
||||
padding: $interiorMarginLg;
|
||||
min-width: 500px;
|
||||
max-height: 500px;
|
||||
z-index: 60;
|
||||
}
|
||||
|
||||
&__results,
|
||||
|
@ -21,9 +21,11 @@
|
||||
*****************************************************************************/
|
||||
import Preview from './Preview.vue';
|
||||
import Vue from 'vue';
|
||||
import EventEmitter from 'EventEmitter';
|
||||
|
||||
export default class PreviewAction {
|
||||
export default class PreviewAction extends EventEmitter {
|
||||
constructor(openmct) {
|
||||
super();
|
||||
/**
|
||||
* Metadata
|
||||
*/
|
||||
@ -75,10 +77,12 @@ export default class PreviewAction {
|
||||
onDestroy: () => {
|
||||
PreviewAction.isVisible = false;
|
||||
preview.$destroy();
|
||||
this.emit('isVisible', false);
|
||||
}
|
||||
});
|
||||
|
||||
PreviewAction.isVisible = true;
|
||||
this.emit('isVisible', true);
|
||||
}
|
||||
|
||||
appliesTo(objectPath, view = {}) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user