DashboardLinks: do not over-query search endpoint (#26311)

* DashboardLinks: WIP fix for dashboard links issue

* Make the dashboard links update on change(hacky)

* Replace dashboard links with new array when updating/adding dash links

* Update snaps

* Deep clone dashboard links on save

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
(cherry picked from commit 23e93175d1)
This commit is contained in:
Dominik Prokop
2020-07-16 13:04:17 +02:00
parent 10a9031b1b
commit 2747607805
7 changed files with 53 additions and 41 deletions
@@ -54,7 +54,7 @@ export class DashLinksEditorCtrl {
};
addLink() {
this.dashboard.links.push(this.link);
this.dashboard.links = [...this.dashboard.links, this.link];
this.mode = 'list';
this.dashboard.updateSubmenuVisibility();
}
@@ -65,6 +65,7 @@ export class DashLinksEditorCtrl {
}
saveLink() {
this.dashboard.links = _.cloneDeep(this.dashboard.links);
this.backToList();
}
@@ -10,38 +10,37 @@ import { iconMap } from '../DashLinks/DashLinksEditorCtrl';
export interface Props {
dashboard: DashboardModel;
links: DashboardLink[];
}
export const DashboardLinks: FC<Props> = ({ dashboard }) => {
export const DashboardLinks: FC<Props> = ({ dashboard, links }) => {
if (!links.length) {
return null;
}
return (
dashboard.links.length > 0 && (
<>
{dashboard.links.map((link: DashboardLink, index: number) => {
const linkInfo = getLinkSrv().getAnchorInfo(link);
const key = `${link.title}-$${index}`;
<>
{links.map((link: DashboardLink, index: number) => {
const linkInfo = getLinkSrv().getAnchorInfo(link);
const key = `${link.title}-$${index}`;
if (link.type === 'dashboards') {
return <DashboardLinksDashboard key={key} link={link} linkInfo={linkInfo} dashboardId={dashboard.id} />;
}
if (link.type === 'dashboards') {
return <DashboardLinksDashboard key={key} link={link} linkInfo={linkInfo} dashboardId={dashboard.id} />;
}
const linkElement = (
<a
className="gf-form-label"
href={sanitizeUrl(linkInfo.href)}
target={link.targetBlank ? '_blank' : '_self'}
>
<Icon name={iconMap[link.icon] as IconName} style={{ marginRight: '4px' }} />
<span>{sanitize(linkInfo.title)}</span>
</a>
);
const linkElement = (
<a className="gf-form-label" href={sanitizeUrl(linkInfo.href)} target={link.targetBlank ? '_blank' : '_self'}>
<Icon name={iconMap[link.icon] as IconName} style={{ marginRight: '4px' }} />
<span>{sanitize(linkInfo.title)}</span>
</a>
);
return (
<div key={key} className="gf-form">
{link.tooltip ? <Tooltip content={link.tooltip}>{linkElement}</Tooltip> : linkElement}
</div>
);
})}
</>
)
return (
<div key={key} className="gf-form">
{link.tooltip ? <Tooltip content={link.tooltip}>{linkElement}</Tooltip> : linkElement}
</div>
);
})}
</>
);
};
@@ -19,13 +19,17 @@ interface State {
export class DashboardLinksDashboard extends PureComponent<Props, State> {
state: State = { resolvedLinks: [] };
componentDidMount() {
this.searchForDashboards();
}
componentDidUpdate(prevProps: Readonly<Props>) {
if (!this.props.link.asDropdown && prevProps.linkInfo !== this.props.linkInfo) {
this.onResolveLinks();
if (this.props.link !== prevProps.link) {
this.searchForDashboards();
}
}
onResolveLinks = async () => {
searchForDashboards = async () => {
const { dashboardId, link } = this.props;
const searchHits = await searchForTags(link);
@@ -73,7 +77,7 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> {
<>
<a
className="gf-form-label pointer"
onClick={this.onResolveLinks}
onClick={this.searchForDashboards}
data-placement="bottom"
data-toggle="dropdown"
>
@@ -7,9 +7,11 @@ import { DashboardModel } from '../../state';
import { DashboardLinks } from './DashboardLinks';
import { Annotations } from './Annotations';
import { SubMenuItems } from './SubMenuItems';
import { DashboardLink } from '../../state/DashboardModel';
interface OwnProps {
dashboard: DashboardModel;
links: DashboardLink[];
}
interface ConnectedProps {
@@ -49,7 +51,8 @@ class SubMenuUnConnected extends PureComponent<Props> {
};
render() {
const { dashboard, variables } = this.props;
const { dashboard, variables, links } = this.props;
if (!this.isSubMenuVisible()) {
return null;
}
@@ -59,16 +62,18 @@ class SubMenuUnConnected extends PureComponent<Props> {
<SubMenuItems variables={variables} />
<Annotations annotations={dashboard.annotations.list} onAnnotationChanged={this.onAnnotationStateChanged} />
<div className="gf-form gf-form--grow" />
{dashboard && <DashboardLinks dashboard={dashboard} />}
{dashboard && <DashboardLinks dashboard={dashboard} links={links} />}
<div className="clearfix" />
</div>
);
}
}
const mapStateToProps: MapStateToProps<ConnectedProps, OwnProps, StoreState> = state => ({
variables: getSubMenuVariables(state),
});
const mapStateToProps: MapStateToProps<ConnectedProps, OwnProps, StoreState> = state => {
return {
variables: getSubMenuVariables(state.templating.variables),
};
};
export const SubMenu = connect(mapStateToProps)(SubMenuUnConnected);
SubMenu.displayName = 'SubMenu';
@@ -309,7 +309,7 @@ export class DashboardPage extends PureComponent<Props, State> {
>
<div className="dashboard-content">
{initError && this.renderInitFailedState()}
{!editPanel && <SubMenu dashboard={dashboard} />}
{!editPanel && <SubMenu dashboard={dashboard} links={dashboard.links} />}
<DashboardGrid
dashboard={dashboard}
@@ -211,6 +211,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
"version": 0,
}
}
links={Array []}
/>
<DashboardGrid
dashboard={
@@ -569,6 +570,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
"version": 0,
}
}
links={Array []}
/>
<DashboardGrid
dashboard={
@@ -2,6 +2,7 @@ import { StoreState } from '../../../types';
import { VariableModel } from '../types';
import { getState } from '../../../store/store';
import { NEW_VARIABLE_ID } from './types';
import memoizeOne from 'memoize-one';
export const getVariable = <T extends VariableModel = VariableModel>(
id: string,
@@ -42,9 +43,9 @@ export const getVariables = (state: StoreState = getState(), includeNewVariable
return getFilteredVariables(filter, state);
};
export const getSubMenuVariables = (state: StoreState): VariableModel[] => {
return getVariables(state);
};
export const getSubMenuVariables = memoizeOne((variables: Record<string, VariableModel>): VariableModel[] => {
return getVariables(getState());
});
export const getEditorVariables = (state: StoreState): VariableModel[] => {
return getVariables(state, true);