Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into timeline-delete-note
Browse files Browse the repository at this point in the history
  • Loading branch information
kqualters-elastic committed Apr 24, 2023
2 parents cde1374 + 275c360 commit 431972a
Show file tree
Hide file tree
Showing 44 changed files with 583 additions and 412 deletions.
2 changes: 1 addition & 1 deletion examples/controls_example/public/edit_example.tsx
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
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
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
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);
}
}
}
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
Expand Up @@ -10,21 +10,27 @@ import React from 'react';
import { EuiContextMenuItem } from '@elastic/eui';
import { ControlGroupContainer } from '@kbn/controls-plugin/public';
import { getAddControlButtonTitle } from '../../_dashboard_app_strings';
import { useDashboardAPI } from '../../dashboard_app';

interface Props {
closePopover: () => void;
controlGroup: ControlGroupContainer;
}

export const AddDataControlButton = ({ closePopover, controlGroup, ...rest }: Props) => {
const dashboard = useDashboardAPI();
const onSave = () => {
dashboard.scrollToTop();
};

return (
<EuiContextMenuItem
{...rest}
icon="plusInCircle"
data-test-subj="controls-create-button"
aria-label={getAddControlButtonTitle()}
onClick={() => {
controlGroup.openAddDataControlFlyout();
controlGroup.openAddDataControlFlyout({ onSave });
closePopover();
}}
>
Expand Down
Expand Up @@ -13,6 +13,7 @@ import {
getAddTimeSliderControlButtonTitle,
getOnlyOneTimeSliderControlMsg,
} from '../../_dashboard_app_strings';
import { useDashboardAPI } from '../../dashboard_app';

interface Props {
closePopover: () => void;
Expand All @@ -21,6 +22,7 @@ interface Props {

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

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();
dashboard.scrollToTop();
closePopover();
}}
data-test-subj="controls-create-timeslider-button"
Expand Down
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
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 a quick flash when redrawing the panels on minimize
*/
.dshDashboardGrid__item--hidden {
display: none;
position: absolute;
top: -9999px;
left: -9999px;
}

/**
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
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,15 @@ export const DashboardGrid = ({ viewportWidth }: { viewportWidth: number }) => {
setTimeout(() => setAnimatePanelTransforms(true), 500);
});

useEffect(() => {
if (expandedPanelId) {
setAnimatePanelTransforms(false);
} else {
// delaying enabling CSS transforms to the next tick prevents a panel slide animation on minimize
setTimeout(() => setAnimatePanelTransforms(true), 0);
}
}, [expandedPanelId]);

const { onPanelStatusChange } = useDashboardPerformanceTracker({
panelCount: Object.keys(panels).length,
});
Expand Down Expand Up @@ -98,7 +107,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
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,23 @@ const Item = React.forwardRef<HTMLDivElement, Props>(
printViewport__vis: container.getInput().viewMode === ViewMode.PRINT,
});

useLayoutEffect(() => {
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
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;
}
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
Expand Up @@ -41,6 +41,10 @@ export function addFromLibrary(this: DashboardContainer) {
notifications,
overlays,
theme,
onAddPanel: (id: string) => {
this.setScrollToPanelId(id);
this.setHighlightPanelId(id);
},
})
);
}
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);
}
});
}
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

0 comments on commit 431972a

Please sign in to comment.