Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Dashboard] Scroll to new panel #152056

Merged
merged 29 commits into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
809ce55
Added scrollToPanel method on dashboard container
cqliu1 Feb 10, 2023
e050966
Add scroll to top on control add
cqliu1 Mar 1, 2023
23bce2c
Added panel highlight
cqliu1 Mar 1, 2023
db6bb73
Fix types
cqliu1 Mar 1, 2023
5069144
Added highlight styles
cqliu1 Mar 2, 2023
00d3cbb
Fix ts errors
cqliu1 Mar 2, 2023
4cea793
Removed extra empty line
cqliu1 Mar 2, 2023
8ead94d
Adjust highlight animation timing
cqliu1 Mar 20, 2023
5701506
Add scroll options to scrollToPanel
cqliu1 Mar 21, 2023
51f147b
Add scroll/highlight panel id to dashboard container component state
cqliu1 Apr 11, 2023
72847c5
Fix scroll behavior
cqliu1 Apr 17, 2023
efef24a
Fix ts errors
cqliu1 Apr 18, 2023
266ef94
Added comments
cqliu1 Apr 18, 2023
957dc0f
Fix controls scrolling
cqliu1 Apr 18, 2023
223ce8f
Merge branch 'main' into dashboard/scroll-to-new-panel
cqliu1 Apr 18, 2023
90ad114
Merge branch 'main' of https://github.com/elastic/kibana into dashboa…
cqliu1 Apr 21, 2023
acd82c9
Remove scrolling on minimize panel
cqliu1 Apr 21, 2023
348d5a6
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 21, 2023
7bb881b
BROKEN
cqliu1 Apr 21, 2023
1591e81
Fix ts errors
cqliu1 Apr 21, 2023
682b6eb
Merge branch 'dashboard/scroll-to-new-panel' of https://github.com/cq…
cqliu1 Apr 21, 2023
0b93ee8
Merge branch 'main' of https://github.com/elastic/kibana into dashboa…
cqliu1 Apr 21, 2023
2ec08ee
Remove console log
cqliu1 Apr 24, 2023
d91a587
Merge branch 'main' into dashboard/scroll-to-new-panel
cqliu1 Apr 24, 2023
f49cc45
Fix controls
cqliu1 Apr 24, 2023
369aaa4
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Apr 24, 2023
47dc6fb
Fix lint errors
cqliu1 Apr 24, 2023
20d1714
Merge branch 'dashboard/scroll-to-new-panel' of https://github.com/cq…
cqliu1 Apr 24, 2023
4e16c72
Added comments
cqliu1 Apr 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/controls_example/public/edit_example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export const EditExample = () => {
iconType="plusInCircle"
isDisabled={controlGroupAPI === undefined}
onClick={() => {
controlGroupAPI!.openAddDataControlFlyout(controlInputTransform);
controlGroupAPI!.openAddDataControlFlyout({ controlInputTransform });
}}
>
Add control
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import React from 'react';
import { toMountPoint } from '@kbn/kibana-react-plugin/public';
import { isErrorEmbeddable } from '@kbn/embeddable-plugin/public';

import {
ControlGroupContainer,
Expand All @@ -32,8 +33,12 @@ import { DataControlInput, OPTIONS_LIST_CONTROL, RANGE_SLIDER_CONTROL } from '..

export function openAddDataControlFlyout(
this: ControlGroupContainer,
controlInputTransform?: ControlInputTransform
options?: {
controlInputTransform?: ControlInputTransform;
onSave?: (id: string) => void;
}
) {
const { controlInputTransform, onSave } = options || {};
const {
overlays: { openFlyout, openConfirm },
controls: { getControlFactory },
Expand Down Expand Up @@ -71,7 +76,7 @@ export function openAddDataControlFlyout(
updateTitle={(newTitle) => (controlInput.title = newTitle)}
updateWidth={(defaultControlWidth) => this.updateInput({ defaultControlWidth })}
updateGrow={(defaultControlGrow: boolean) => this.updateInput({ defaultControlGrow })}
onSave={(type) => {
onSave={async (type) => {
this.closeAllFlyouts();
if (!type) {
return;
Expand All @@ -86,17 +91,28 @@ export function openAddDataControlFlyout(
controlInput = controlInputTransform({ ...controlInput }, type);
}

if (type === OPTIONS_LIST_CONTROL) {
this.addOptionsListControl(controlInput as AddOptionsListControlProps);
return;
}
let newControl;

if (type === RANGE_SLIDER_CONTROL) {
this.addRangeSliderControl(controlInput as AddRangeSliderControlProps);
return;
switch (type) {
case OPTIONS_LIST_CONTROL:
newControl = await this.addOptionsListControl(
controlInput as AddOptionsListControlProps
);
break;
case RANGE_SLIDER_CONTROL:
newControl = await this.addRangeSliderControl(
controlInput as AddRangeSliderControlProps
);
break;
default:
newControl = await this.addDataControlFromField(
controlInput as AddDataControlProps
);
}

this.addDataControlFromField(controlInput as AddDataControlProps);
if (onSave && !isErrorEmbeddable(newControl)) {
onSave(newControl.id);
}
}}
onCancel={onCancel}
onTypeEditorChange={(partialInput) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export class ClonePanelAction implements Action<ClonePanelActionContext> {
height: panelToClone.gridData.h,
currentPanels: dashboard.getInput().panels,
placeBesideId: panelToClone.explicitInput.id,
scrollToPanel: true,
} as IPanelPlacementBesideArgs
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,9 @@ export class ExpandPanelAction implements Action<ExpandPanelActionContext> {
}
const newValue = isExpanded(embeddable) ? undefined : embeddable.id;
(embeddable.parent as DashboardContainer).setExpandedPanelId(newValue);

if (!newValue) {
(embeddable.parent as DashboardContainer).setScrollToPanelId(embeddable.id);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { Toast } from '@kbn/core/public';
import { DashboardPanelState } from '../../common';
import { pluginServices } from '../services/plugin_services';
import { dashboardReplacePanelActionStrings } from './_dashboard_actions_strings';
import { DashboardContainer } from '../dashboard_container';

interface Props {
container: IContainer;
Expand Down Expand Up @@ -82,6 +83,7 @@ export class ReplacePanelFlyout extends React.Component<Props> {
},
});

(container as DashboardContainer).setHighlightPanelId(id);
this.showToast(name);
this.props.onClose();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import React from 'react';
import { EuiContextMenuItem } from '@elastic/eui';
import { ControlGroupContainer } from '@kbn/controls-plugin/public';
import { useDashboardContainer } from '../../../dashboard_container/embeddable/dashboard_container';
import { getAddControlButtonTitle } from '../../_dashboard_app_strings';

interface Props {
Expand All @@ -17,14 +18,19 @@ interface Props {
}

export const AddDataControlButton = ({ closePopover, controlGroup, ...rest }: Props) => {
const container = useDashboardContainer();
const onSave = () => {
container.scrollToTop();
};
Heenawter marked this conversation as resolved.
Show resolved Hide resolved

return (
<EuiContextMenuItem
{...rest}
icon="plusInCircle"
data-test-subj="controls-create-button"
aria-label={getAddControlButtonTitle()}
onClick={() => {
controlGroup.openAddDataControlFlyout();
controlGroup.openAddDataControlFlyout({ onSave });
closePopover();
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import React, { useEffect, useState } from 'react';
import { EuiContextMenuItem } from '@elastic/eui';
import { ControlGroupContainer, TIME_SLIDER_CONTROL } from '@kbn/controls-plugin/public';
import { useDashboardContainerContext } from '../../..';
import {
getAddTimeSliderControlButtonTitle,
getOnlyOneTimeSliderControlMsg,
Expand All @@ -21,6 +22,7 @@ interface Props {

export const AddTimeSliderControlButton = ({ closePopover, controlGroup, ...rest }: Props) => {
const [hasTimeSliderControl, setHasTimeSliderControl] = useState(false);
const { embeddableInstance: dashboardContainer } = useDashboardContainerContext();

useEffect(() => {
const subscription = controlGroup.getInput$().subscribe(() => {
Expand All @@ -42,8 +44,9 @@ export const AddTimeSliderControlButton = ({ closePopover, controlGroup, ...rest
<EuiContextMenuItem
{...rest}
icon="plusInCircle"
onClick={() => {
controlGroup.addTimeSliderControl();
onClick={async () => {
await controlGroup.addTimeSliderControl();
dashboardContainer.scrollToTop();
closePopover();
}}
data-test-subj="controls-create-timeslider-button"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ export function DashboardEditingToolbar() {
const newEmbeddable = await dashboard.addNewEmbeddable(embeddableFactory.type, explicitInput);

if (newEmbeddable) {
dashboard.setScrollToPanelId(newEmbeddable.id);
dashboard.setHighlightPanelId(newEmbeddable.id);
toasts.addSuccess({
title: dashboardReplacePanelActionStrings.getSuccessMessage(newEmbeddable.getTitle()),
'data-test-subj': 'addEmbeddableToDashboardSuccess',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@
}

/**
* When a single panel is expanded, all the other panels are hidden in the grid.
* When a single panel is expanded, all the other panels moved offscreen.
* Shifting the rendered panels offscreen prevents panel reloading on minimize
*/
.dshDashboardGrid__item--hidden {
display: none;
position: absolute;
top: -9999px;
left: -9999px;
Heenawter marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand All @@ -53,11 +56,12 @@
* 1. We need to mark this as important because react grid layout sets the width and height of the panels inline.
*/
.dshDashboardGrid__item--expanded {
position: absolute;
height: 100% !important; /* 1 */
width: 100% !important; /* 1 */
top: 0 !important; /* 1 */
left: 0 !important; /* 1 */
transform: translate(0, 0) !important; /* 1 */
transform: none !important;
padding: $euiSizeS;

// Altered panel styles can be found in ../panel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import 'react-grid-layout/css/styles.css';
import { pick } from 'lodash';
import classNames from 'classnames';
import { useEffectOnce } from 'react-use/lib';
import React, { useState, useMemo, useCallback } from 'react';
import React, { useState, useMemo, useCallback, useEffect } from 'react';
import { Layout, Responsive as ResponsiveReactGridLayout } from 'react-grid-layout';

import { ViewMode } from '@kbn/embeddable-plugin/public';
Expand All @@ -38,6 +38,14 @@ export const DashboardGrid = ({ viewportWidth }: { viewportWidth: number }) => {
setTimeout(() => setAnimatePanelTransforms(true), 500);
});

useEffect(() => {
if (expandedPanelId) {
setAnimatePanelTransforms(false);
} else {
setTimeout(() => setAnimatePanelTransforms(true), 0);
Heenawter marked this conversation as resolved.
Show resolved Hide resolved
}
}, [expandedPanelId]);

const { onPanelStatusChange } = useDashboardPerformanceTracker({
panelCount: Object.keys(panels).length,
});
Expand Down Expand Up @@ -98,7 +106,7 @@ export const DashboardGrid = ({ viewportWidth }: { viewportWidth: number }) => {
'dshLayout-withoutMargins': !useMargins,
'dshLayout--viewing': viewMode === ViewMode.VIEW,
'dshLayout--editing': viewMode !== ViewMode.VIEW,
'dshLayout--noAnimation': !animatePanelTransforms,
'dshLayout--noAnimation': !animatePanelTransforms || expandedPanelId,
'dshLayout-isMaximizedPanel': expandedPanelId !== undefined,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

import React, { useState, useRef, useEffect } from 'react';
import React, { useState, useRef, useEffect, useLayoutEffect } from 'react';
import { EuiLoadingChart } from '@elastic/eui';
import classNames from 'classnames';

Expand Down Expand Up @@ -56,6 +56,8 @@ const Item = React.forwardRef<HTMLDivElement, Props>(
embeddable: { EmbeddablePanel: PanelComponent },
} = pluginServices.getServices();
const container = useDashboardContainer();
const scrollToPanelId = container.select((state) => state.componentState.scrollToPanelId);
const highlightPanelId = container.select((state) => state.componentState.highlightPanelId);

const expandPanel = expandedPanelId !== undefined && expandedPanelId === id;
const hidePanel = expandedPanelId !== undefined && expandedPanelId !== id;
Expand All @@ -66,11 +68,24 @@ const Item = React.forwardRef<HTMLDivElement, Props>(
printViewport__vis: container.getInput().viewMode === ViewMode.PRINT,
});

useEffect(() => {
let panelElement;
Heenawter marked this conversation as resolved.
Show resolved Hide resolved
if (typeof ref !== 'function' && ref?.current) {
if (scrollToPanelId === id) {
container.scrollToPanel(ref.current);
}
if (highlightPanelId === id) {
container.highlightPanel(ref.current);
}
}
}, [id, container, scrollToPanelId, highlightPanelId, ref]);

return (
<div
style={{ ...style, zIndex: focusedPanelId === id ? 2 : 'auto' }}
className={[classes, className].join(' ')}
data-test-subj="dashboardPanel"
id={`panel-${id}`}
ref={ref}
{...rest}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
box-shadow: none;
border-radius: 0;
}

.dshDashboardGrid__item--highlighted {
border-radius: 0;
}
}

// Remove border color unless in editing mode
Expand All @@ -25,3 +29,24 @@
cursor: default;
}
}

@keyframes highlightOutline {
0% {
outline: solid $euiSizeXS transparentize($euiColorSuccess, 1);
}
25% {
outline: solid $euiSizeXS transparentize($euiColorSuccess, .5);
}
100% {
outline: solid $euiSizeXS transparentize($euiColorSuccess, 1);
}
}

.dshDashboardGrid__item--highlighted {
border-radius: $euiSizeXS;
animation-name: highlightOutline;
animation-duration: 4s;
animation-timing-function: ease-out;
// keeps outline from getting cut off by other panels without margins
z-index: 999 !important;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface IPanelPlacementArgs {
width: number;
height: number;
currentPanels: { [key: string]: DashboardPanelState };
scrollToPanel?: boolean;
}

export interface IPanelPlacementBesideArgs extends IPanelPlacementArgs {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export function addFromLibrary(this: DashboardContainer) {
notifications,
overlays,
theme,
onAddPanel: (id: string) => {
this.setScrollToPanelId(id);
this.setHighlightPanelId(id);
},
})
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,12 @@ export function showPlaceholderUntil<TPlacementMethodArgs extends IPanelPlacemen
// this is useful as sometimes panels can load faster than the placeholder one (i.e. by value embeddables)
this.untilEmbeddableLoaded(originalPanelState.explicitInput.id)
.then(() => newStateComplete)
.then((newPanelState: Partial<PanelState>) =>
this.replacePanel(placeholderPanelState, newPanelState)
);
.then(async (newPanelState: Partial<PanelState>) => {
const panelId = await this.replacePanel(placeholderPanelState, newPanelState);

if (placementArgs?.scrollToPanel) {
this.setScrollToPanelId(panelId);
this.setHighlightPanelId(panelId);
}
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,13 @@ export const createDashboard = async (
const incomingEmbeddable = creationOptions?.incomingEmbeddable;
if (incomingEmbeddable) {
initialInput.viewMode = ViewMode.EDIT; // view mode must always be edit to recieve an embeddable.
if (

const panelExists =
incomingEmbeddable.embeddableId &&
Boolean(initialInput.panels[incomingEmbeddable.embeddableId])
) {
Boolean(initialInput.panels[incomingEmbeddable.embeddableId]);
if (panelExists) {
// this embeddable already exists, we will update the explicit input.
const panelToUpdate = initialInput.panels[incomingEmbeddable.embeddableId];
const panelToUpdate = initialInput.panels[incomingEmbeddable.embeddableId as string];
const sameType = panelToUpdate.type === incomingEmbeddable.type;

panelToUpdate.type = incomingEmbeddable.type;
Expand All @@ -195,17 +196,22 @@ export const createDashboard = async (
...(sameType ? panelToUpdate.explicitInput : {}),

...incomingEmbeddable.input,
id: incomingEmbeddable.embeddableId,
id: incomingEmbeddable.embeddableId as string,

// maintain hide panel titles setting.
hidePanelTitles: panelToUpdate.explicitInput.hidePanelTitles,
};
} else {
// otherwise this incoming embeddable is brand new and can be added via the default method after the dashboard container is created.
untilDashboardReady().then((container) =>
container.addNewEmbeddable(incomingEmbeddable.type, incomingEmbeddable.input)
);
untilDashboardReady().then(async (container) => {
container.addNewEmbeddable(incomingEmbeddable.type, incomingEmbeddable.input);
});
}

untilDashboardReady().then(async (container) => {
container.setScrollToPanelId(incomingEmbeddable.embeddableId);
container.setHighlightPanelId(incomingEmbeddable.embeddableId);
});
}

// --------------------------------------------------------------------------------------
Expand Down
Loading