From 8709c9a8a51d4f95b8abadf11d8e2aa17ecd8c7a Mon Sep 17 00:00:00 2001 From: Alex Khomenko Date: Mon, 20 Apr 2020 18:04:51 +0300 Subject: [PATCH] Search: Toggle Search based on search query (#23648) * Search: Toggle Search based on search query * Search: Fix types and closed search param * Search: Remove appEvents from SearchWrapper * Search: Reset folder on close Co-Authored-By: Alexander Zobnin * Search: Disable reloadOnSearch for manage dashboards urls Co-authored-by: Alexander Zobnin --- public/app/core/selectors/location.ts | 1 + public/app/core/services/keybindingSrv.ts | 3 +- .../dashboard/components/DashNav/DashNav.tsx | 14 ++-- .../search/components/DashboardSearch.tsx | 6 +- .../search/components/ManageDashboards.tsx | 6 +- .../features/search/components/SearchItem.tsx | 8 ++- .../search/components/SearchWrapper.tsx | 67 ++++++++++++------- public/app/features/search/index.ts | 2 +- public/app/routes/routes.ts | 3 + 9 files changed, 68 insertions(+), 42 deletions(-) diff --git a/public/app/core/selectors/location.ts b/public/app/core/selectors/location.ts index 97bb61c43cb..21c08fc13c8 100644 --- a/public/app/core/selectors/location.ts +++ b/public/app/core/selectors/location.ts @@ -3,3 +3,4 @@ import { LocationState } from 'app/types'; export const getRouteParamsId = (state: LocationState) => state.routeParams.id; export const getRouteParamsPage = (state: LocationState) => state.routeParams.page; export const getRouteParams = (state: LocationState) => state.routeParams; +export const getLocationQuery = (state: LocationState) => state.query; diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index d31f7edeff4..bd81034aad3 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -83,7 +83,8 @@ export class KeybindingSrv { } openSearch() { - appEvents.emit(CoreEvents.showDashSearch); + const search = _.extend(this.$location.search(), { search: 'open' }); + this.$location.search(search); } openAlerting() { diff --git a/public/app/features/dashboard/components/DashNav/DashNav.tsx b/public/app/features/dashboard/components/DashNav/DashNav.tsx index 424855eb643..0d7c686ba8b 100644 --- a/public/app/features/dashboard/components/DashNav/DashNav.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNav.tsx @@ -47,13 +47,17 @@ class DashNav extends PureComponent { this.playlistSrv = this.props.$injector.get('playlistSrv'); } - onDahboardNameClick = () => { - appEvents.emit(CoreEvents.showDashSearch); + onDashboardNameClick = () => { + this.props.updateLocation({ + query: { search: 'open' }, + partial: true, + }); }; onFolderNameClick = () => { - appEvents.emit(CoreEvents.showDashSearch, { - query: 'folder:current', + this.props.updateLocation({ + query: { search: 'open', folder: 'current' }, + partial: true, }); }; @@ -126,7 +130,7 @@ class DashNav extends PureComponent { )} - + {dashboard.title} diff --git a/public/app/features/search/components/DashboardSearch.tsx b/public/app/features/search/components/DashboardSearch.tsx index ae376358400..c6cd045abe9 100644 --- a/public/app/features/search/components/DashboardSearch.tsx +++ b/public/app/features/search/components/DashboardSearch.tsx @@ -5,7 +5,6 @@ import { GrafanaTheme } from '@grafana/data'; import { SearchSrv } from 'app/core/services/search_srv'; import { TagFilter } from 'app/core/components/TagFilter/TagFilter'; import { contextSrv } from 'app/core/services/context_srv'; -import { OpenSearchParams } from '../types'; import { useSearchQuery } from '../hooks/useSearchQuery'; import { useDashboardSearch } from '../hooks/useDashboardSearch'; import { SearchField } from './SearchField'; @@ -18,10 +17,11 @@ const canEdit = isEditor || hasEditPermissionInFolders; export interface Props { onCloseSearch: () => void; - payload?: OpenSearchParams; + folder?: string; } -export const DashboardSearch: FC = ({ onCloseSearch, payload = {} }) => { +export const DashboardSearch: FC = ({ onCloseSearch, folder }) => { + const payload = folder ? { query: `folder:${folder}` } : {}; const { query, onQueryChange, onClearFilters, onTagFilterChange, onTagAdd } = useSearchQuery(payload); const { results, loading, onToggleSection, onKeyDown } = useDashboardSearch(query, onCloseSearch); const theme = useTheme(); diff --git a/public/app/features/search/components/ManageDashboards.tsx b/public/app/features/search/components/ManageDashboards.tsx index 11e579f97f7..7b28c20f754 100644 --- a/public/app/features/search/components/ManageDashboards.tsx +++ b/public/app/features/search/components/ManageDashboards.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from 'react'; +import React, { FC, useState, memo } from 'react'; import { css } from 'emotion'; import { Icon, TagList, HorizontalGroup, stylesFactory, useTheme } from '@grafana/ui'; import { GrafanaTheme } from '@grafana/data'; @@ -20,7 +20,7 @@ export interface Props { const { isEditor } = contextSrv; -export const ManageDashboards: FC = ({ folderId, folderUid }) => { +export const ManageDashboards: FC = memo(({ folderId, folderUid }) => { const theme = useTheme(); const styles = getStyles(theme); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); @@ -152,7 +152,7 @@ export const ManageDashboards: FC = ({ folderId, folderUid }) => { /> ); -}; +}); const getStyles = stylesFactory((theme: GrafanaTheme) => { return { diff --git a/public/app/features/search/components/SearchItem.tsx b/public/app/features/search/components/SearchItem.tsx index 0cbad3e0616..29f27f42bac 100644 --- a/public/app/features/search/components/SearchItem.tsx +++ b/public/app/features/search/components/SearchItem.tsx @@ -3,8 +3,7 @@ import { css, cx } from 'emotion'; import { GrafanaTheme } from '@grafana/data'; import { e2e } from '@grafana/e2e'; import { Icon, useTheme, TagList, styleMixins, stylesFactory } from '@grafana/ui'; -import appEvents from 'app/core/app_events'; -import { CoreEvents } from 'app/types'; +import { updateLocation } from 'app/core/reducers/location'; import { DashboardSectionItem, OnToggleChecked } from '../types'; import { SearchCheckbox } from './SearchCheckbox'; @@ -38,7 +37,10 @@ export const SearchItem: FC = ({ item, editable, onToggleChecked, onTagSe const onItemClick = () => { //Check if one string can be found in the other if (window.location.pathname.includes(item.url) || item.url.includes(window.location.pathname)) { - appEvents.emit(CoreEvents.hideDashSearch, { target: 'search-item' }); + updateLocation({ + query: { search: null }, + partial: true, + }); } }; diff --git a/public/app/features/search/components/SearchWrapper.tsx b/public/app/features/search/components/SearchWrapper.tsx index 245f0055d37..016b261d8dc 100644 --- a/public/app/features/search/components/SearchWrapper.tsx +++ b/public/app/features/search/components/SearchWrapper.tsx @@ -1,34 +1,49 @@ -import React, { FC, useState, useEffect } from 'react'; -import { appEvents } from 'app/core/core'; -import { CoreEvents } from 'app/types'; +import React, { FC, memo } from 'react'; +import { MapDispatchToProps, MapStateToProps } from 'react-redux'; +import { getLocationQuery } from 'app/core/selectors/location'; +import { updateLocation } from 'app/core/reducers/location'; +import { connectWithStore } from 'app/core/utils/connectWithReduxStore'; +import { StoreState } from 'app/types'; import { DashboardSearch } from './DashboardSearch'; -import { OpenSearchParams } from '../types'; -export const SearchWrapper: FC = () => { - const [isOpen, setIsOpen] = useState(false); - const [payload, setPayload] = useState({}); +interface OwnProps { + search?: string | null; + folder?: string; + queryText?: string; + filter?: string; +} - useEffect(() => { - const openSearch = (payload: OpenSearchParams) => { - setIsOpen(true); - setPayload(payload); - }; +interface DispatchProps { + updateLocation: typeof updateLocation; +} - const closeOnItemClick = (payload: any) => { - // Detect if the event was emitted by clicking on search item - if (payload?.target === 'search-item' && isOpen) { - setIsOpen(false); - } - }; +export type Props = OwnProps & DispatchProps; - appEvents.on(CoreEvents.showDashSearch, openSearch); - appEvents.on(CoreEvents.hideDashSearch, closeOnItemClick); +export const SearchWrapper: FC = memo(({ search, folder, updateLocation }) => { + const isOpen = search === 'open'; - return () => { - appEvents.off(CoreEvents.showDashSearch, openSearch); - appEvents.off(CoreEvents.hideDashSearch, closeOnItemClick); - }; - }, [isOpen]); + const closeSearch = () => { + if (search === 'open') { + updateLocation({ + query: { + search: null, + folder: null, + }, + partial: true, + }); + } + }; - return isOpen ? setIsOpen(false)} payload={payload} /> : null; + return isOpen ? : null; +}); + +const mapStateToProps: MapStateToProps<{}, OwnProps, StoreState> = (state: StoreState) => { + const { search, folder } = getLocationQuery(state.location); + return { search, folder }; }; + +const mapDispatchToProps: MapDispatchToProps = { + updateLocation, +}; + +export default connectWithStore(SearchWrapper, mapStateToProps, mapDispatchToProps); diff --git a/public/app/features/search/index.ts b/public/app/features/search/index.ts index 17b3d44875a..ec97ee4de48 100644 --- a/public/app/features/search/index.ts +++ b/public/app/features/search/index.ts @@ -2,7 +2,7 @@ export { SearchResults } from './components/SearchResults'; export { SearchField } from './components/SearchField'; export { SearchItem } from './components/SearchItem'; export { SearchCheckbox } from './components/SearchCheckbox'; -export { SearchWrapper } from './components/SearchWrapper'; +export { default as SearchWrapper } from './components/SearchWrapper'; export { SearchResultsFilter } from './components/SearchResultsFilter'; export { ManageDashboards } from './components/ManageDashboards'; export { ConfirmDeleteModal } from './components/ConfirmDeleteModal'; diff --git a/public/app/routes/routes.ts b/public/app/routes/routes.ts index bc136115900..bc814bf7082 100644 --- a/public/app/routes/routes.ts +++ b/public/app/routes/routes.ts @@ -156,6 +156,7 @@ export function setupAngularRoutes($routeProvider: route.IRouteProvider, $locati }) .when('/dashboards', { template: '', + reloadOnSearch: false, resolve: { component: () => SafeDynamicImport( @@ -192,6 +193,7 @@ export function setupAngularRoutes($routeProvider: route.IRouteProvider, $locati }) .when('/dashboards/f/:uid/:slug', { template: '', + reloadOnSearch: false, resolve: { component: () => SafeDynamicImport( @@ -201,6 +203,7 @@ export function setupAngularRoutes($routeProvider: route.IRouteProvider, $locati }) .when('/dashboards/f/:uid', { template: '', + reloadOnSearch: false, resolve: { component: () => SafeDynamicImport(