mirror of
https://github.com/grafana/grafana.git
synced 2025-02-10 23:55:47 -06:00
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>
This commit is contained in:
parent
55c306eb6d
commit
8709c9a8a5
@ -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();
|
||||
|
@ -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<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,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
||||
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,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
appEvents.on(CoreEvents.showDashSearch, openSearch);
|
||||
appEvents.on(CoreEvents.hideDashSearch, closeOnItemClick);
|
||||
return isOpen ? <DashboardSearch onCloseSearch={closeSearch} folder={folder} /> : null;
|
||||
});
|
||||
|
||||
return () => {
|
||||
appEvents.off(CoreEvents.showDashSearch, openSearch);
|
||||
appEvents.off(CoreEvents.hideDashSearch, closeOnItemClick);
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
return isOpen ? <DashboardSearch onCloseSearch={() => setIsOpen(false)} payload={payload} /> : 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(
|
||||
|
Loading…
Reference in New Issue
Block a user