Commit 8709c9a8 by Alex Khomenko Committed by GitHub

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 <alexanderzobnin@gmail.com>

* Search: Disable reloadOnSearch for manage dashboards urls

Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com>
parent 55c306eb
......@@ -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;
......@@ -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() {
......
......@@ -47,13 +47,17 @@ class DashNav extends PureComponent<Props> {
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<Props> {
<Icon name="angle-right" className={iconClassName} />
</>
)}
<a onClick={this.onDahboardNameClick}>
<a onClick={this.onDashboardNameClick}>
{dashboard.title} <Icon name="angle-down" className={iconClassName} />
</a>
</div>
......
......@@ -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<Props> = ({ onCloseSearch, payload = {} }) => {
export const DashboardSearch: FC<Props> = ({ 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();
......
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<Props> = ({ folderId, folderUid }) => {
export const ManageDashboards: FC<Props> = memo(({ folderId, folderUid }) => {
const theme = useTheme();
const styles = getStyles(theme);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
......@@ -152,7 +152,7 @@ export const ManageDashboards: FC<Props> = ({ folderId, folderUid }) => {
/>
</div>
);
};
});
const getStyles = stylesFactory((theme: GrafanaTheme) => {
return {
......
......@@ -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<Props> = ({ 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,
});
}
};
......
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({});
useEffect(() => {
const openSearch = (payload: OpenSearchParams) => {
setIsOpen(true);
setPayload(payload);
};
const closeOnItemClick = (payload: any) => {
// Detect if the event was emitted by clicking on search item
if (payload?.target === 'search-item' && isOpen) {
setIsOpen(false);
}
};
appEvents.on(CoreEvents.showDashSearch, openSearch);
appEvents.on(CoreEvents.hideDashSearch, closeOnItemClick);
return () => {
appEvents.off(CoreEvents.showDashSearch, openSearch);
appEvents.off(CoreEvents.hideDashSearch, closeOnItemClick);
};
}, [isOpen]);
return isOpen ? <DashboardSearch onCloseSearch={() => setIsOpen(false)} payload={payload} /> : null;
interface OwnProps {
search?: string | null;
folder?: string;
queryText?: string;
filter?: string;
}
interface DispatchProps {
updateLocation: typeof updateLocation;
}
export type Props = OwnProps & DispatchProps;
export const SearchWrapper: FC<Props> = memo(({ search, folder, updateLocation }) => {
const isOpen = search === 'open';
const closeSearch = () => {
if (search === 'open') {
updateLocation({
query: {
search: null,
folder: null,
},
partial: true,
});
}
};
return isOpen ? <DashboardSearch onCloseSearch={closeSearch} folder={folder} /> : null;
});
const mapStateToProps: MapStateToProps<{}, OwnProps, StoreState> = (state: StoreState) => {
const { search, folder } = getLocationQuery(state.location);
return { search, folder };
};
const mapDispatchToProps: MapDispatchToProps<DispatchProps, OwnProps> = {
updateLocation,
};
export default connectWithStore(SearchWrapper, mapStateToProps, mapDispatchToProps);
......@@ -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';
......
......@@ -156,6 +156,7 @@ export function setupAngularRoutes($routeProvider: route.IRouteProvider, $locati
})
.when('/dashboards', {
template: '<react-container />',
reloadOnSearch: false,
resolve: {
component: () =>
SafeDynamicImport(
......@@ -192,6 +193,7 @@ export function setupAngularRoutes($routeProvider: route.IRouteProvider, $locati
})
.when('/dashboards/f/:uid/:slug', {
template: '<react-container />',
reloadOnSearch: false,
resolve: {
component: () =>
SafeDynamicImport(
......@@ -201,6 +203,7 @@ export function setupAngularRoutes($routeProvider: route.IRouteProvider, $locati
})
.when('/dashboards/f/:uid', {
template: '<react-container />',
reloadOnSearch: false,
resolve: {
component: () =>
SafeDynamicImport(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment