Skip to content

Commit

Permalink
[Dashboard Usability] Moves scrollbar to panel section (#145628)
Browse files Browse the repository at this point in the history
## Summary

Closes #145404.
Closes #134257.

Cloud deployment for testing:
https://kibana-pr-145628.kb.us-west2.gcp.elastic-cloud.com:9243
User: `elastic`
Password: `zuIno5Tuy4lVmhMwbt2C6NyY`

This moves the scrollbar from the entire app to only the panel section
of the dashboard app. The search/filter bar and editor toolbar will
remain at the top while controls and panels scroll. The controls
floating actions were extracted out into their own component for future
use for panel actions.

#### Before
![Nov-17-2022
12-53-43](https://user-images.githubusercontent.com/1697105/202557972-6963b9d3-4eb0-40a4-963d-19e16e652d95.gif)


#### After
![Nov-17-2022
12-41-29](https://user-images.githubusercontent.com/1697105/202557993-251a984e-8bce-4b50-94c7-a7a39410a624.gif)


### Checklist

Delete any items that are not applicable to this PR.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
cqliu1 and kibanamachine authored Feb 9, 2023
1 parent f439bdc commit 1f03570
Show file tree
Hide file tree
Showing 25 changed files with 283 additions and 152 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {

import { FormattedMessage } from '@kbn/i18n-react';
import { Markdown } from '@kbn/kibana-react-plugin/public';
import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public';
import { useReduxEmbeddableContext, FloatingActions } from '@kbn/presentation-util-plugin/public';
import { ControlGroupReduxState } from '../types';
import { pluginServices } from '../../services';
import { EditControlButton } from '../editor/edit_control';
Expand Down Expand Up @@ -127,12 +127,7 @@ export const ControlFrame = ({
}, [embeddable, embeddableRoot]);

const floatingActions = (
<div
className={classNames('controlFrameFloatingActions', {
'controlFrameFloatingActions--twoLine': usingTwoLineLayout,
'controlFrameFloatingActions--oneLine': !usingTwoLineLayout,
})}
>
<>
{!fatalError && embeddableType !== TIME_SLIDER_CONTROL && (
<EuiToolTip content={ControlGroupStrings.floatingActions.getEditButtonTitle()}>
<EditControlButton embeddableId={embeddableId} />
Expand All @@ -158,7 +153,7 @@ export const ControlFrame = ({
color="danger"
/>
</EuiToolTip>
</div>
</>
);

const embeddableParentClassNames = classNames('controlFrame__control', {
Expand Down Expand Up @@ -220,18 +215,27 @@ export const ControlFrame = ({

return (
<>
{embeddable && enableActions && floatingActions}
<EuiFormRow
data-test-subj="control-frame-title"
fullWidth
label={
usingTwoLineLayout
? title || ControlGroupStrings.emptyState.getTwoLineLoadingTitle()
: undefined
}
<FloatingActions
className={classNames('controlFrameFloatingActions', {
'controlFrameFloatingActions--twoLine': usingTwoLineLayout,
'controlFrameFloatingActions--oneLine': !usingTwoLineLayout,
})}
usingTwoLineLayout={usingTwoLineLayout}
actions={floatingActions}
isEnabled={embeddable && enableActions}
>
{form}
</EuiFormRow>
<EuiFormRow
data-test-subj="control-frame-title"
fullWidth
label={
usingTwoLineLayout
? title || ControlGroupStrings.emptyState.getTwoLineLoadingTitle()
: undefined
}
>
{form}
</EuiFormRow>
</FloatingActions>
</>
);
};
61 changes: 19 additions & 42 deletions src/plugins/controls/public/control_group/control_group.scss
Original file line number Diff line number Diff line change
Expand Up @@ -181,48 +181,6 @@ $controlMinWidth: $euiSize * 14;
}
}

.controlFrameFloatingActions {
visibility: hidden;
opacity: 0;

// slower transition on hover leave in case the user accidentally stops hover
transition: visibility .3s, opacity .3s;

z-index: 1;
position: absolute;

&--oneLine {
right: $euiSizeXS;
top: -$euiSizeL;
padding: $euiSizeXS;
border-radius: $euiBorderRadius;
background-color: $euiColorEmptyShade;
box-shadow: 0 0 0 1px $euiColorLightShade;
}

&--fatalError {
right: $euiSizeXS;
top: -$euiSizeL;
padding: $euiSizeXS;
border-radius: $euiBorderRadius;
background-color: $euiColorEmptyShade;
box-shadow: 0 0 0 1px $euiColorLightShade;
}

&--twoLine {
right: $euiSizeXS;
top: -$euiSizeXS;
}
}

&:hover {
.controlFrameFloatingActions {
transition: visibility .1s, opacity .1s;
visibility: visible;
opacity: 1;
}
}

&-isDragging {
.euiFormRow__labelWrapper {
opacity: 0;
Expand All @@ -232,3 +190,22 @@ $controlMinWidth: $euiSize * 14;
}
}
}

.controlFrameFloatingActions {
z-index: 1;
position: absolute;

&--oneLine {
padding: $euiSizeXS;
border-radius: $euiBorderRadius;
background-color: $euiColorEmptyShade;
box-shadow: 0 0 0 1px $euiColorLightShade;
}

&--fatalError {
padding: $euiSizeXS;
border-radius: $euiBorderRadius;
background-color: $euiColorEmptyShade;
box-shadow: 0 0 0 1px $euiColorLightShade;
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
@import 'src/core/public/mixins';

.dshAppWrapper {
@include kibanaFullBodyHeight();

display: flex;
flex-direction: column;
}

.dshUnsavedListingItem {
margin-top: $euiSizeM;
}
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/dashboard/public/dashboard_app/dashboard_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export function DashboardApp({
}, [dashboardContainer, kbnUrlStateStorage]);

return (
<>
<div className={'dshAppWrapper'}>
{showNoDataPage && (
<DashboardAppNoDataPage onDataViewCreated={() => setShowNoDataPage(false)} />
)}
Expand All @@ -204,6 +204,6 @@ export function DashboardApp({
/>
</>
)}
</>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
* Side Public License, v 1.
*/

import { EuiHorizontalRule } from '@elastic/eui';
import { METRIC_TYPE } from '@kbn/analytics';
import { EmbeddableFactory } from '@kbn/embeddable-plugin/public';
import {
Expand All @@ -19,6 +18,8 @@ import {
import { BaseVisType, VisTypeAlias } from '@kbn/visualizations-plugin/public';
import React from 'react';
import { useCallback } from 'react';
import { useEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
import { dashboardReplacePanelActionStrings } from '../../dashboard_actions/_dashboard_actions_strings';
import { DASHBOARD_APP_ID, DASHBOARD_UI_METRIC_ID } from '../../dashboard_constants';
import { useDashboardContainerContext } from '../../dashboard_container/dashboard_container_renderer';
Expand All @@ -36,6 +37,7 @@ export function DashboardEditingToolbar() {
embeddable: { getStateTransfer, getEmbeddableFactory },
visualizations: { get: getVisualization, getAliases: getVisTypeAliases },
} = pluginServices.getServices();
const { euiTheme } = useEuiTheme();

const { embeddableInstance: dashboardContainer } = useDashboardContainerContext();

Expand Down Expand Up @@ -178,8 +180,11 @@ export function DashboardEditingToolbar() {
}

return (
<>
<EuiHorizontalRule margin="none" />
<div
css={css`
padding: 0 ${euiTheme.size.s} ${euiTheme.size.s} ${euiTheme.size.s};
`}
>
<SolutionToolbar isDarkModeEnabled={IS_DARK_THEME}>
{{
primaryActionButton: (
Expand All @@ -195,6 +200,6 @@ export function DashboardEditingToolbar() {
extraButtons,
}}
</SolutionToolbar>
</>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ViewMode } from '@kbn/embeddable-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/public';
import type { TopNavMenuProps } from '@kbn/navigation-plugin/public';

import { EuiHorizontalRule } from '@elastic/eui';
import {
getDashboardTitle,
leaveConfirmStrings,
Expand Down Expand Up @@ -261,6 +262,7 @@ export function DashboardTopNav({ embedSettings, redirectTo }: DashboardTopNavPr
</PresentationUtilContextProvider>
) : null}
{viewMode === ViewMode.EDIT ? <DashboardEditingToolbar /> : null}
<EuiHorizontalRule margin="none" />
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
@import './component/panel/index';
@import './component/viewport/index';

.dashboardViewport {
flex: 1;
.dashboardContainer, .dashboardViewport {
display: flex;
flex: 1;
flex-direction: column;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@
*/
.dshLayout-isMaximizedPanel {
height: 100% !important; /* 1. */
width: 100%;
position: absolute !important; /* 1 */
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

.dshDashboardViewport-controls {
margin: 0 $euiSizeS 0 $euiSizeS;
padding-top: $euiSizeS;
padding-bottom: $euiSizeXS;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,21 @@ import React, { useEffect, useRef } from 'react';
import { ViewMode } from '@kbn/embeddable-plugin/public';
import { ExitFullScreenButton } from '@kbn/shared-ux-button-exit-full-screen';

import { css } from '@emotion/react';
import { EuiPortal } from '@elastic/eui';
import { DashboardGrid } from '../grid';
import { pluginServices } from '../../../services/plugin_services';
import { DashboardEmptyScreen } from '../empty_screen/dashboard_empty_screen';
import { useDashboardContainerContext } from '../../dashboard_container_renderer';

export const DashboardViewport = () => {
export const DashboardViewportComponent = () => {
const {
settings: { isProjectEnabledInLabs },
} = pluginServices.getServices();
const controlsRoot = useRef(null);

const {
useEmbeddableDispatch,
useEmbeddableSelector: select,
actions: { setFullScreenMode },
embeddableInstance: dashboardContainer,
} = useDashboardContainerContext();
const dispatch = useEmbeddableDispatch();
const { useEmbeddableSelector: select, embeddableInstance: dashboardContainer } =
useDashboardContainerContext();

/**
* Render Control group
Expand All @@ -47,9 +44,10 @@ export const DashboardViewport = () => {
const dashboardTitle = select((state) => state.explicitInput.title);
const useMargins = select((state) => state.explicitInput.useMargins);
const description = select((state) => state.explicitInput.description);
const isFullScreenMode = select((state) => state.componentState.fullScreenMode);
const isEmbeddedExternally = select((state) => state.componentState.isEmbeddedExternally);

const expandedPanelId = select((state) => state.componentState.expandedPanelId);
const expandedPanelStyles = css`
flex: 1;
`;
const controlsEnabled = isProjectEnabledInLabs('labs:dashboard:dashboardControls');

return (
Expand All @@ -66,13 +64,8 @@ export const DashboardViewport = () => {
data-title={dashboardTitle}
data-description={description}
className={useMargins ? 'dshDashboardViewport-withMargins' : 'dshDashboardViewport'}
css={expandedPanelId ? expandedPanelStyles : undefined}
>
{isFullScreenMode && (
<ExitFullScreenButton
onExit={() => dispatch(setFullScreenMode(false))}
toggleChrome={!isEmbeddedExternally}
/>
)}
{panelCount === 0 && (
<div className="dshDashboardEmptyScreen">
<DashboardEmptyScreen isEditMode={viewMode === ViewMode.EDIT} />
Expand All @@ -83,3 +76,38 @@ export const DashboardViewport = () => {
</>
);
};

// This fullscreen button HOC separates fullscreen button and dashboard content to reduce rerenders
// because ExitFullScreenButton sets isFullscreenMode to false on unmount while rerendering.
// This specifically fixed maximizing/minimizing panels without exiting fullscreen mode.
const WithFullScreenButton = ({ children }: { children: JSX.Element }) => {
const {
useEmbeddableDispatch,
useEmbeddableSelector: select,
actions: { setFullScreenMode },
} = useDashboardContainerContext();
const dispatch = useEmbeddableDispatch();

const isFullScreenMode = select((state) => state.componentState.fullScreenMode);
const isEmbeddedExternally = select((state) => state.componentState.isEmbeddedExternally);

return (
<>
{children}
{isFullScreenMode && (
<EuiPortal>
<ExitFullScreenButton
onExit={() => dispatch(setFullScreenMode(false))}
toggleChrome={!isEmbeddedExternally}
/>
</EuiPortal>
)}
</>
);
};

export const DashboardViewport = () => (
<WithFullScreenButton>
<DashboardViewportComponent />
</WithFullScreenButton>
);
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import './_dashboard_container.scss';

import { v4 as uuidv4 } from 'uuid';
import classNames from 'classnames';
import { EuiLoadingElastic, EuiLoadingSpinner } from '@elastic/eui';
import React, { useEffect, useMemo, useRef, useState } from 'react';

import useObservable from 'react-use/lib/useObservable';

import { EuiLoadingElastic, EuiLoadingSpinner, useEuiOverflowScroll } from '@elastic/eui';
import { css } from '@emotion/react';
import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public';

import {
Expand Down Expand Up @@ -110,13 +111,20 @@ export const DashboardContainerRenderer = ({
{ 'dashboardViewport--screenshotMode': isScreenshotMode() },
{ 'dashboardViewport--loading': loading }
);

const viewportStyles = css`
${useEuiOverflowScroll('y', false)}
`;

const loadingSpinner = showPlainSpinner ? (
<EuiLoadingSpinner size="xxl" />
) : (
<EuiLoadingElastic size="xxl" />
);
return (
<div className={viewportClasses}>{loading ? loadingSpinner : <div ref={dashboardRoot} />}</div>
<div className={viewportClasses} css={viewportStyles}>
{loading ? loadingSpinner : <div ref={dashboardRoot} />}
</div>
);
};

Expand Down
Loading

0 comments on commit 1f03570

Please sign in to comment.