Reducing wrapping divs from RenderingService (#97017)

* Reducing wrapping divs from RenderingService

* Applying more styles to .kbnAppWrapper

Some being temporary and will need a better solution when introducing the page layout component

* Almost fixing tests for rendering service

Can’t figure out how to have a optional Observable
`Received: "kbnAppWrapper class-name”`

* Adding some comments

* [Dashboard] Using the APP_WRAPPER_CLASS

* fix test & ts types

* Fixin a few more tests that were using `.app-wrapper`

* Creating docs for new var and cleaning up some selectors

* Fixing reporting

* Fixing banner position and truncation

* Fixed CSS error in loading screen and jump in animation

* Fixing selectors in Canvas

* Remove unused var

* Added `APP_WRAPPER_CLASS` export from `server` and updated reporting to use it

* Fix monitoring icon clicks

* move APP_WRAPPER_CLASS definition to src/core/common

* Fixing Monitoring snapshots and wrapper class

* Moved `APP_WRAPPER_CLASS` utils but exported from `public` and `server`

* Remove old folder

* Fix dashboard test by only showing HR in edit mode

Co-authored-by: pgayvallet
Co-authored-by:  tsullivan
This commit is contained in:
Caroline Horn 2021-04-26 16:06:06 -04:00 committed by GitHub
parent 509d75bcfe
commit f07ebc822f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 239 additions and 200 deletions

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [APP\_WRAPPER\_CLASS](./kibana-plugin-core-public.app_wrapper_class.md)
## APP\_WRAPPER\_CLASS variable
The class name for top level \*and\* nested application wrappers to ensure proper layout
<b>Signature:</b>
```typescript
APP_WRAPPER_CLASS = "kbnAppWrapper"
```

View file

@ -138,6 +138,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| Variable | Description | | Variable | Description |
| --- | --- | | --- | --- |
| [APP\_WRAPPER\_CLASS](./kibana-plugin-core-public.app_wrapper_class.md) | The class name for top level \*and\* nested application wrappers to ensure proper layout |
| [URL\_MAX\_LENGTH](./kibana-plugin-core-public.url_max_length.md) | The max URL length allowed by the current browser. Should be used to display warnings to users when query parameters cause URL to exceed this limit. | | [URL\_MAX\_LENGTH](./kibana-plugin-core-public.url_max_length.md) | The max URL length allowed by the current browser. Should be used to display warnings to users when query parameters cause URL to exceed this limit. |
## Type Aliases ## Type Aliases

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [APP\_WRAPPER\_CLASS](./kibana-plugin-core-server.app_wrapper_class.md)
## APP\_WRAPPER\_CLASS variable
The class name for top level \*and\* nested application wrappers to ensure proper layout
<b>Signature:</b>
```typescript
APP_WRAPPER_CLASS = "kbnAppWrapper"
```

View file

@ -230,6 +230,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| Variable | Description | | Variable | Description |
| --- | --- | | --- | --- |
| [APP\_WRAPPER\_CLASS](./kibana-plugin-core-server.app_wrapper_class.md) | The class name for top level \*and\* nested application wrappers to ensure proper layout |
| [kibanaResponseFactory](./kibana-plugin-core-server.kibanaresponsefactory.md) | Set of helpers used to create <code>KibanaResponse</code> to form HTTP response on an incoming request. Should be returned as a result of [RequestHandler](./kibana-plugin-core-server.requesthandler.md) execution. | | [kibanaResponseFactory](./kibana-plugin-core-server.kibanaresponsefactory.md) | Set of helpers used to create <code>KibanaResponse</code> to form HTTP response on an incoming request. Should be returned as a result of [RequestHandler](./kibana-plugin-core-server.requesthandler.md) execution. |
| [ServiceStatusLevels](./kibana-plugin-core-server.servicestatuslevels.md) | The current "level" of availability of a service. | | [ServiceStatusLevels](./kibana-plugin-core-server.servicestatuslevels.md) | The current "level" of availability of a service. |
| [validBodyOutput](./kibana-plugin-core-server.validbodyoutput.md) | The set of valid body.output | | [validBodyOutput](./kibana-plugin-core-server.validbodyoutput.md) | The set of valid body.output |

View file

@ -1,6 +1,7 @@
.header__topBanner { .header__topBanner {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0;
height: $kbnHeaderBannerHeight; height: $kbnHeaderBannerHeight;
width: 100%; width: 100%;
z-index: $euiZHeader; z-index: $euiZHeader;

View file

@ -199,7 +199,7 @@ describe('#start()', () => {
root.innerHTML = '<p>foo bar</p>'; root.innerHTML = '<p>foo bar</p>';
await startCore(root); await startCore(root);
expect(root.innerHTML).toMatchInlineSnapshot( expect(root.innerHTML).toMatchInlineSnapshot(
`"<div id=\\"kibana-body\\"></div><div></div><div></div>"` `"<div id=\\"kibana-body\\" data-test-subj=\\"kibanaChrome\\"></div><div></div><div></div>"`
); );
}); });

View file

@ -176,6 +176,7 @@ export class CoreSystem {
const coreUiTargetDomElement = document.createElement('div'); const coreUiTargetDomElement = document.createElement('div');
coreUiTargetDomElement.id = 'kibana-body'; coreUiTargetDomElement.id = 'kibana-body';
coreUiTargetDomElement.dataset.testSubj = 'kibanaChrome';
const notificationsTargetDomElement = document.createElement('div'); const notificationsTargetDomElement = document.createElement('div');
const overlayTargetDomElement = document.createElement('div'); const overlayTargetDomElement = document.createElement('div');

View file

@ -74,7 +74,7 @@ export type {
DomainDeprecationDetails, DomainDeprecationDetails,
} from '../server/types'; } from '../server/types';
export type { CoreContext, CoreSystem } from './core_system'; export type { CoreContext, CoreSystem } from './core_system';
export { DEFAULT_APP_CATEGORIES } from '../utils'; export { DEFAULT_APP_CATEGORIES, APP_WRAPPER_CLASS } from '../utils';
export type { export type {
AppCategory, AppCategory,
UiSettingsParams, UiSettingsParams,

View file

@ -1,7 +1,3 @@
.kbnGlobalBannerList {
padding: $euiSize;
}
.kbnGlobalBannerList__item + .kbnGlobalBannerList__item { .kbnGlobalBannerList__item + .kbnGlobalBannerList__item {
margin-top: $euiSizeS; margin-top: $euiSizeS;
} }

View file

@ -73,6 +73,9 @@ export interface App<HistoryLocationState = unknown> {
updater$?: Observable<AppUpdater>; updater$?: Observable<AppUpdater>;
} }
// @public
export const APP_WRAPPER_CLASS = "kbnAppWrapper";
// @public // @public
export interface AppCategory { export interface AppCategory {
ariaLabel?: string; ariaLabel?: string;

View file

@ -1,16 +1,20 @@
@import '../mixins'; @import '../mixins';
/** /**
* stretch the root element of the Kibana application to set the base-size that * Stretch the root element of the Kibana application to set the base-size that
* flexed children should keep. Only works when paired with root styles applied * flexed children should keep. Only works when paired with root styles applied
* by core service from new platform * by core service from new platform
*/ */
// SASSTODO: Naming here is too embedded and high up that changing them could cause major breaks
#kibana-body { #kibana-body {
overflow-x: hidden; // DO NOT ADD ANY OVERFLOW BEHAVIORS HERE
// It will break the sticky navigation
min-height: 100%; min-height: 100%;
display: flex;
flex-direction: column;
} }
// Affixes a div to restrict the position of charts tooltip to the visible viewport minus the header
#app-fixed-viewport { #app-fixed-viewport {
pointer-events: none; pointer-events: none;
visibility: hidden; visibility: hidden;
@ -21,26 +25,17 @@
left: 0; left: 0;
} }
.app-wrapper { .kbnAppWrapper {
// DO NOT ADD ANY OTHER STYLES TO THIS SELECTOR
// This a very nested dependency happnening in "all" apps
display: flex; display: flex;
flex-flow: column nowrap; flex-flow: column nowrap;
margin: 0 auto;
@include kibanaFullBodyMinHeight();
}
.app-wrapper-panel {
display: flex;
flex-grow: 1; flex-grow: 1;
flex-shrink: 0; z-index: 0; // This effectively puts every high z-index inside the scope of this wrapper to it doesn't interfere with the header and/or overlay mask
flex-basis: auto; position: relative; // This is temporary for apps that relied on this being present on `.application`
flex-direction: column;
> * {
flex-shrink: 0;
}
} }
// TODO: This is problematic because it doesn't stay in line with EUI:
// adapted from euiHeaderAffordForFixed as we need to handle the top banner // adapted from euiHeaderAffordForFixed as we need to handle the top banner
@mixin kbnAffordForHeader($headerHeight) { @mixin kbnAffordForHeader($headerHeight) {
padding-top: $headerHeight; padding-top: $headerHeight;

View file

@ -6,21 +6,25 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject, of } from 'rxjs';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import React from 'react'; import React from 'react';
import { AppWrapper, AppContainer } from './app_containers'; import { AppWrapper } from './app_containers';
describe('AppWrapper', () => { describe('AppWrapper', () => {
it('toggles the `hidden-chrome` class depending on the chrome visibility state', () => { it('toggles the `hidden-chrome` class depending on the chrome visibility state', () => {
const chromeVisible$ = new BehaviorSubject<boolean>(true); const chromeVisible$ = new BehaviorSubject<boolean>(true);
const component = mount(<AppWrapper chromeVisible$={chromeVisible$}>app-content</AppWrapper>); const component = mount(
<AppWrapper chromeVisible$={chromeVisible$} classes$={of([])}>
app-content
</AppWrapper>
);
expect(component.getDOMNode()).toMatchInlineSnapshot(` expect(component.getDOMNode()).toMatchInlineSnapshot(`
<div <div
class="app-wrapper" class="kbnAppWrapper"
> >
app-content app-content
</div> </div>
@ -30,7 +34,7 @@ describe('AppWrapper', () => {
component.update(); component.update();
expect(component.getDOMNode()).toMatchInlineSnapshot(` expect(component.getDOMNode()).toMatchInlineSnapshot(`
<div <div
class="app-wrapper hidden-chrome" class="kbnAppWrapper kbnAppWrapper--hiddenChrome"
> >
app-content app-content
</div> </div>
@ -40,22 +44,25 @@ describe('AppWrapper', () => {
component.update(); component.update();
expect(component.getDOMNode()).toMatchInlineSnapshot(` expect(component.getDOMNode()).toMatchInlineSnapshot(`
<div <div
class="app-wrapper" class="kbnAppWrapper"
> >
app-content app-content
</div> </div>
`); `);
}); });
});
describe('AppContainer', () => {
it('adds classes supplied by chrome', () => { it('adds classes supplied by chrome', () => {
const chromeVisible$ = new BehaviorSubject<boolean>(true);
const appClasses$ = new BehaviorSubject<string[]>([]); const appClasses$ = new BehaviorSubject<string[]>([]);
const component = mount(<AppContainer classes$={appClasses$}>app-content</AppContainer>); const component = mount(
<AppWrapper chromeVisible$={chromeVisible$} classes$={appClasses$}>
app-content
</AppWrapper>
);
expect(component.getDOMNode()).toMatchInlineSnapshot(` expect(component.getDOMNode()).toMatchInlineSnapshot(`
<div <div
class="application" class="kbnAppWrapper"
> >
app-content app-content
</div> </div>
@ -65,7 +72,7 @@ describe('AppContainer', () => {
component.update(); component.update();
expect(component.getDOMNode()).toMatchInlineSnapshot(` expect(component.getDOMNode()).toMatchInlineSnapshot(`
<div <div
class="application classA classB" class="kbnAppWrapper classA classB"
> >
app-content app-content
</div> </div>
@ -75,7 +82,7 @@ describe('AppContainer', () => {
component.update(); component.update();
expect(component.getDOMNode()).toMatchInlineSnapshot(` expect(component.getDOMNode()).toMatchInlineSnapshot(`
<div <div
class="application classC" class="kbnAppWrapper classC"
> >
app-content app-content
</div> </div>
@ -85,7 +92,7 @@ describe('AppContainer', () => {
component.update(); component.update();
expect(component.getDOMNode()).toMatchInlineSnapshot(` expect(component.getDOMNode()).toMatchInlineSnapshot(`
<div <div
class="application" class="kbnAppWrapper"
> >
app-content app-content
</div> </div>

View file

@ -10,17 +10,23 @@ import React from 'react';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import useObservable from 'react-use/lib/useObservable'; import useObservable from 'react-use/lib/useObservable';
import classNames from 'classnames'; import classNames from 'classnames';
import { APP_WRAPPER_CLASS } from '../../utils';
export const AppWrapper: React.FunctionComponent<{ export const AppWrapper: React.FunctionComponent<{
chromeVisible$: Observable<boolean>; chromeVisible$: Observable<boolean>;
}> = ({ chromeVisible$, children }) => {
const visible = useObservable(chromeVisible$);
return <div className={classNames('app-wrapper', { 'hidden-chrome': !visible })}>{children}</div>;
};
export const AppContainer: React.FunctionComponent<{
classes$: Observable<string[]>; classes$: Observable<string[]>;
}> = ({ classes$, children }) => { }> = ({ chromeVisible$, classes$, children }) => {
const classes = useObservable(classes$); const visible = useObservable(chromeVisible$);
return <div className={classNames('application', classes)}>{children}</div>; const classes = useObservable(classes$, ['']);
return (
<div
className={classNames(
APP_WRAPPER_CLASS,
{ 'kbnAppWrapper--hiddenChrome': !visible },
classes
)}
>
{children}
</div>
);
}; };

View file

@ -13,7 +13,7 @@ import { RenderingService } from './rendering_service';
import { applicationServiceMock } from '../application/application_service.mock'; import { applicationServiceMock } from '../application/application_service.mock';
import { chromeServiceMock } from '../chrome/chrome_service.mock'; import { chromeServiceMock } from '../chrome/chrome_service.mock';
import { overlayServiceMock } from '../overlays/overlay_service.mock'; import { overlayServiceMock } from '../overlays/overlay_service.mock';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject, of } from 'rxjs';
describe('RenderingService#start', () => { describe('RenderingService#start', () => {
let application: ReturnType<typeof applicationServiceMock.createInternalStartContract>; let application: ReturnType<typeof applicationServiceMock.createInternalStartContract>;
@ -28,6 +28,7 @@ describe('RenderingService#start', () => {
chrome = chromeServiceMock.createStartContract(); chrome = chromeServiceMock.createStartContract();
chrome.getHeaderComponent.mockReturnValue(<div>Hello chrome!</div>); chrome.getHeaderComponent.mockReturnValue(<div>Hello chrome!</div>);
chrome.getApplicationClasses$.mockReturnValue(of([]));
overlays = overlayServiceMock.createStartContract(); overlays = overlayServiceMock.createStartContract();
overlays.banners.getComponent.mockReturnValue(<div>I&apos;m a banner!</div>); overlays.banners.getComponent.mockReturnValue(<div>I&apos;m a banner!</div>);
@ -48,10 +49,13 @@ describe('RenderingService#start', () => {
it('renders application service into provided DOM element', () => { it('renders application service into provided DOM element', () => {
startService(); startService();
expect(targetDomElement.querySelector('div.application')).toMatchInlineSnapshot(` expect(targetDomElement.querySelector('div.kbnAppWrapper')).toMatchInlineSnapshot(`
<div <div
class="application class-name" class="kbnAppWrapper kbnAppWrapper--hiddenChrome"
> >
<div
id="app-fixed-viewport"
/>
<div> <div>
Hello application! Hello application!
</div> </div>
@ -59,43 +63,44 @@ describe('RenderingService#start', () => {
`); `);
}); });
it('adds the `chrome-hidden` class to the AppWrapper when chrome is hidden', () => { it('adds the `kbnAppWrapper--hiddenChrome` class to the AppWrapper when chrome is hidden', () => {
const isVisible$ = new BehaviorSubject(true); const isVisible$ = new BehaviorSubject(true);
chrome.getIsVisible$.mockReturnValue(isVisible$); chrome.getIsVisible$.mockReturnValue(isVisible$);
startService(); startService();
const appWrapper = targetDomElement.querySelector('div.app-wrapper')!; const appWrapper = targetDomElement.querySelector('div.kbnAppWrapper')!;
expect(appWrapper.className).toEqual('app-wrapper'); expect(appWrapper.className).toEqual('kbnAppWrapper');
act(() => isVisible$.next(false)); act(() => isVisible$.next(false));
expect(appWrapper.className).toEqual('app-wrapper hidden-chrome'); expect(appWrapper.className).toEqual('kbnAppWrapper kbnAppWrapper--hiddenChrome');
act(() => isVisible$.next(true)); act(() => isVisible$.next(true));
expect(appWrapper.className).toEqual('app-wrapper'); expect(appWrapper.className).toEqual('kbnAppWrapper');
}); });
it('adds the application classes to the AppContainer', () => { it('adds the application classes to the AppWrapper', () => {
const applicationClasses$ = new BehaviorSubject<string[]>([]); const applicationClasses$ = new BehaviorSubject<string[]>([]);
const isVisible$ = new BehaviorSubject(true);
chrome.getIsVisible$.mockReturnValue(isVisible$);
chrome.getApplicationClasses$.mockReturnValue(applicationClasses$); chrome.getApplicationClasses$.mockReturnValue(applicationClasses$);
startService(); startService();
const appContainer = targetDomElement.querySelector('div.application')!; const appContainer = targetDomElement.querySelector('div.kbnAppWrapper')!;
expect(appContainer.className).toEqual('application'); expect(appContainer.className).toEqual('kbnAppWrapper');
act(() => applicationClasses$.next(['classA', 'classB'])); act(() => applicationClasses$.next(['classA', 'classB']));
expect(appContainer.className).toEqual('application classA classB'); expect(appContainer.className).toEqual('kbnAppWrapper classA classB');
act(() => applicationClasses$.next(['classC'])); act(() => applicationClasses$.next(['classC']));
expect(appContainer.className).toEqual('application classC'); expect(appContainer.className).toEqual('kbnAppWrapper classC');
act(() => applicationClasses$.next([])); act(() => applicationClasses$.next([]));
expect(appContainer.className).toEqual('application'); expect(appContainer.className).toEqual('kbnAppWrapper');
}); });
it('contains wrapper divs', () => { it('contains wrapper divs', () => {
startService(); startService();
expect(targetDomElement.querySelector('div.app-wrapper')).toBeDefined(); expect(targetDomElement.querySelector('div.kbnAppWrapper')).toBeDefined();
expect(targetDomElement.querySelector('div.app-wrapper-pannel')).toBeDefined();
}); });
it('renders the banner UI', () => { it('renders the banner UI', () => {

View file

@ -14,7 +14,7 @@ import { pairwise, startWith } from 'rxjs/operators';
import { InternalChromeStart } from '../chrome'; import { InternalChromeStart } from '../chrome';
import { InternalApplicationStart } from '../application'; import { InternalApplicationStart } from '../application';
import { OverlayStart } from '../overlays'; import { OverlayStart } from '../overlays';
import { AppWrapper, AppContainer } from './app_containers'; import { AppWrapper } from './app_containers';
interface StartDeps { interface StartDeps {
application: InternalApplicationStart; application: InternalApplicationStart;
@ -48,16 +48,25 @@ export class RenderingService {
ReactDOM.render( ReactDOM.render(
<I18nProvider> <I18nProvider>
<div className="content" data-test-subj="kibanaChrome"> <>
{/* Fixed headers */}
{chromeHeader} {chromeHeader}
<AppWrapper chromeVisible$={chrome.getIsVisible$()}>
<div className="app-wrapper-panel"> {/* banners$.subscribe() for things like the No data banner */}
<div id="app-fixed-viewport" />
<div id="globalBannerList">{bannerComponent}</div> <div id="globalBannerList">{bannerComponent}</div>
<AppContainer classes$={chrome.getApplicationClasses$()}>{appComponent}</AppContainer>
</div> {/* The App Wrapper outside of the fixed headers that accepts custom class names from apps */}
<AppWrapper
chromeVisible$={chrome.getIsVisible$()}
classes$={chrome.getApplicationClasses$()}
>
{/* Affixes a div to restrict the position of charts tooltip to the visible viewport minus the header */}
<div id="app-fixed-viewport" />
{/* The actual plugin/app */}
{appComponent}
</AppWrapper> </AppWrapper>
</div> </>
</I18nProvider>, </I18nProvider>,
targetDomElement targetDomElement
); );

View file

@ -6,7 +6,7 @@
// In order to override the TM (Textmate) theme of Ace/Brace, everywhere, // In order to override the TM (Textmate) theme of Ace/Brace, everywhere,
// it is being scoped by a known outer selector // it is being scoped by a known outer selector
.application { .kbnBody {
.ace-tm { .ace-tm {
$aceBackground: tintOrShade($euiColorLightShade, 50%, 0); $aceBackground: tintOrShade($euiColorLightShade, 50%, 0);

View file

@ -5,29 +5,6 @@
// Grab some nav-specific EUI vars // Grab some nav-specific EUI vars
@import '@elastic/eui/src/components/collapsible_nav/variables'; @import '@elastic/eui/src/components/collapsible_nav/variables';
// Application Layout
.application,
.app-container {
> * {
position: relative;
}
}
.application {
position: relative;
z-index: 0;
display: flex;
flex-grow: 1;
flex-shrink: 0;
flex-basis: auto;
flex-direction: column;
> * {
flex-shrink: 0;
}
}
// We apply brute force focus states to anything not coming from Eui // We apply brute force focus states to anything not coming from Eui
// which has focus states designed at the component level. // which has focus states designed at the component level.
// You can also use "kbn-resetFocusState" to not apply the default focus // You can also use "kbn-resetFocusState" to not apply the default focus

View file

@ -397,7 +397,7 @@ export type {
} from './deprecations'; } from './deprecations';
export type { AppCategory } from '../types'; export type { AppCategory } from '../types';
export { DEFAULT_APP_CATEGORIES } from '../utils'; export { DEFAULT_APP_CATEGORIES, APP_WRAPPER_CLASS } from '../utils';
export type { export type {
SavedObject, SavedObject,

View file

@ -89,8 +89,7 @@ const InlineStyles: FC<{ darkMode: boolean }> = ({ darkMode }) => {
} }
.kbnWelcomeText { .kbnWelcomeText {
font-family: display: block;
display: inline-block;
font-size: 14px; font-size: 14px;
font-family: sans-serif; font-family: sans-serif;
line-height: 40px !important; line-height: 40px !important;
@ -103,7 +102,7 @@ const InlineStyles: FC<{ darkMode: boolean }> = ({ darkMode }) => {
text-align: center; text-align: center;
line-height: 1; line-height: 1;
text-align: center; text-align: center;
font-faimily: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial !important; font-family: sans-serif;
letter-spacing: -.005em; letter-spacing: -.005em;
-webkit-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%; -ms-text-size-adjust: 100%;

View file

@ -175,6 +175,9 @@ import { URL } from 'url';
export { AddConfigDeprecation } export { AddConfigDeprecation }
// @public
export const APP_WRAPPER_CLASS = "kbnAppWrapper";
// @public // @public
export interface AppCategory { export interface AppCategory {
ariaLabel?: string; ariaLabel?: string;

View file

@ -0,0 +1,13 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
/**
* The class name for top level *and* nested application wrappers to ensure proper layout
* @public
*/
export const APP_WRAPPER_CLASS = 'kbnAppWrapper';

View file

@ -7,3 +7,4 @@
*/ */
export { DEFAULT_APP_CATEGORIES } from './default_app_categories'; export { DEFAULT_APP_CATEGORIES } from './default_app_categories';
export { APP_WRAPPER_CLASS } from './app_wrapper_class';

View file

@ -1,9 +1,3 @@
.dshAppContainer {
display: flex;
flex-direction: column;
flex: 1;
}
.dashboardViewport { .dashboardViewport {
flex: 1; flex: 1;
display: flex; display: flex;

View file

@ -303,7 +303,7 @@ export function DashboardApp({
}, [data.search.session]); }, [data.search.session]);
return ( return (
<div className="app-container dshAppContainer"> <>
{savedDashboard && dashboardStateManager && dashboardContainer && viewMode && ( {savedDashboard && dashboardStateManager && dashboardContainer && viewMode && (
<> <>
<DashboardTopNav <DashboardTopNav
@ -334,6 +334,6 @@ export function DashboardApp({
</div> </div>
</> </>
)} )}
</div> </>
); );
} }

View file

@ -620,8 +620,9 @@ export function DashboardTopNav({
return ( return (
<> <>
<TopNavMenu {...getNavBarProps()} /> <TopNavMenu {...getNavBarProps()} />
<EuiHorizontalRule margin="none" />
{viewMode !== ViewMode.VIEW ? ( {viewMode !== ViewMode.VIEW ? (
<>
<EuiHorizontalRule margin="none" />
<SolutionToolbar isDarkModeEnabled={IS_DARK_THEME}> <SolutionToolbar isDarkModeEnabled={IS_DARK_THEME}>
{{ {{
primaryActionButton: ( primaryActionButton: (
@ -650,6 +651,7 @@ export function DashboardTopNav({
], ],
}} }}
</SolutionToolbar> </SolutionToolbar>
</>
) : null} ) : null}
</> </>
); );

View file

@ -12,6 +12,7 @@ import { filter, map } from 'rxjs/operators';
import { Start as InspectorStartContract } from 'src/plugins/inspector/public'; import { Start as InspectorStartContract } from 'src/plugins/inspector/public';
import { UrlForwardingSetup, UrlForwardingStart } from 'src/plugins/url_forwarding/public'; import { UrlForwardingSetup, UrlForwardingStart } from 'src/plugins/url_forwarding/public';
import { APP_WRAPPER_CLASS } from '../../../core/public';
import { import {
App, App,
Plugin, Plugin,
@ -292,7 +293,7 @@ export class DashboardPlugin
category: DEFAULT_APP_CATEGORIES.kibana, category: DEFAULT_APP_CATEGORIES.kibana,
mount: async (params: AppMountParameters) => { mount: async (params: AppMountParameters) => {
this.currentHistory = params.history; this.currentHistory = params.history;
params.element.classList.add('dshAppContainer'); params.element.classList.add(APP_WRAPPER_CLASS);
const { mountApp } = await import('./application/dashboard_router'); const { mountApp } = await import('./application/dashboard_router');
appMounted(); appMounted();
return mountApp({ return mountApp({

View file

@ -25,7 +25,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide
expect(await testSubjects.exists('kbnLoadingMessage')).to.be(false); expect(await testSubjects.exists('kbnLoadingMessage')).to.be(false);
const getAppWrapperHeight = async () => { const getAppWrapperHeight = async () => {
const wrapper = await find.byClassName('app-wrapper'); const wrapper = await find.byClassName('kbnAppWrapper');
return (await wrapper.getSize()).height; return (await wrapper.getSize()).height;
}; };

View file

@ -25,7 +25,7 @@ export const Banner: FC<BannerProps> = ({ bannerConfig }) => {
color: textColor, color: textColor,
}} }}
> >
<div data-test-subj="bannerInnerWrapper"> <div className="eui-textTruncate" data-test-subj="bannerInnerWrapper">
<Markdown markdown={textContent} openLinksInNewTab={true} /> <Markdown markdown={textContent} openLinksInNewTab={true} />
</div> </div>
</div> </div>

View file

@ -5,10 +5,6 @@ body.canvas-isFullscreen {
padding-top: 0; padding-top: 0;
} }
.headerWrapper ~ .app-wrapper {
min-height: 100vh;
}
// following rule is for docked navigation // following rule is for docked navigation
&.euiBody--collapsibleNavIsDocked { &.euiBody--collapsibleNavIsDocked {
padding-left: 0 !important; // sass-lint:disable-line no-important padding-left: 0 !important; // sass-lint:disable-line no-important

View file

@ -86,7 +86,7 @@ export class Popover extends Component<Props, State> {
return button(handleClick); return button(handleClick);
}; };
const appWrapper = document.querySelector('.app-wrapper'); const appWrapper = document.querySelector('.kbnAppWrapper');
const EuiPopoverAny = (EuiPopover as any) as React.FC<any>; const EuiPopoverAny = (EuiPopover as any) as React.FC<any>;
return ( return (

View file

@ -10,10 +10,10 @@ import { uiRoutes } from './helpers/routes';
import { Legacy } from '../legacy_shims'; import { Legacy } from '../legacy_shims';
import { configureAppAngularModule } from '../../../../../src/plugins/kibana_legacy/public'; import { configureAppAngularModule } from '../../../../../src/plugins/kibana_legacy/public';
import { localAppModule, appModuleName } from './app_modules'; import { localAppModule, appModuleName } from './app_modules';
import { APP_WRAPPER_CLASS } from '../../../../../src/core/public';
import { MonitoringStartPluginDependencies } from '../types'; import { MonitoringStartPluginDependencies } from '../types';
const APP_WRAPPER_CLASS = 'monApplicationWrapper';
export class AngularApp { export class AngularApp {
private injector?: angular.auto.IInjectorService; private injector?: angular.auto.IInjectorService;

View file

@ -31,13 +31,19 @@ exports[`Node Listing Metric Cell should format a non-percentage metric 1`] = `
<div <div
class="euiPopover__anchor" class="euiPopover__anchor"
> >
<span <button
data-euiicon-type="arrowDown" aria-label="More information about this metric"
class="euiButtonIcon euiButtonIcon--text euiButtonIcon--empty euiButtonIcon--xSmall"
data-test-subj="monitoringCellIcon-testCell2" data-test-subj="monitoringCellIcon-testCell2"
role="button"
tabindex="0"
title="More information about this metric" title="More information about this metric"
type="button"
>
<span
aria-hidden="true"
class="euiButtonIcon__icon"
data-euiicon-type="sortDown"
/> />
</button>
</div> </div>
</div> </div>
</div> </div>
@ -78,13 +84,19 @@ exports[`Node Listing Metric Cell should format a percentage metric 1`] = `
<div <div
class="euiPopover__anchor" class="euiPopover__anchor"
> >
<span <button
data-euiicon-type="arrowDown" aria-label="More information about this metric"
class="euiButtonIcon euiButtonIcon--text euiButtonIcon--empty euiButtonIcon--xSmall"
data-test-subj="monitoringCellIcon-testCell" data-test-subj="monitoringCellIcon-testCell"
role="button"
tabindex="0"
title="More information about this metric" title="More information about this metric"
type="button"
>
<span
aria-hidden="true"
class="euiButtonIcon__icon"
data-euiicon-type="sortDown"
/> />
</button>
</div> </div>
</div> </div>
</div> </div>

View file

@ -11,10 +11,9 @@ import { formatMetric } from '../../../lib/format_number';
import { import {
EuiText, EuiText,
EuiPopover, EuiPopover,
EuiIcon, EuiButtonIcon,
EuiDescriptionList, EuiDescriptionList,
EuiSpacer, EuiSpacer,
EuiKeyboardAccessible,
EuiFlexGroup, EuiFlexGroup,
EuiFlexItem, EuiFlexItem,
} from '@elastic/eui'; } from '@elastic/eui';
@ -40,7 +39,7 @@ const getDirection = (slope) => {
const getIcon = (slope) => { const getIcon = (slope) => {
if (slope || slope === 0) { if (slope || slope === 0) {
return slope > 0 ? 'arrowUp' : 'arrowDown'; return slope > 0 ? 'sortUp' : 'sortDown';
} }
return null; return null;
}; };
@ -83,17 +82,22 @@ function MetricCell({ isOnline, metric = {}, isPercent, ...props }) {
}, },
]; ];
const button = ( const iconLabel = i18n.translate(
<EuiKeyboardAccessible> 'xpack.monitoring.elasticsearch.node.cells.tooltip.iconLabel',
<EuiIcon {
onClick={onButtonClick}
type={getIcon(slope)}
data-test-subj={`monitoringCellIcon-${props['data-test-subj']}`}
title={i18n.translate('xpack.monitoring.elasticsearch.node.cells.tooltip.iconLabel', {
defaultMessage: 'More information about this metric', defaultMessage: 'More information about this metric',
})} }
);
const button = (
<EuiButtonIcon
color="text"
onClick={onButtonClick}
iconType={getIcon(slope)}
data-test-subj={`monitoringCellIcon-${props['data-test-subj']}`}
title={iconLabel}
aria-label={iconLabel}
/> />
</EuiKeyboardAccessible>
); );
return ( return (

View file

@ -6,9 +6,3 @@
// monChart__legend // monChart__legend
// monChart__legend--small // monChart__legend--small
// monChart__legend-isLoading // monChart__legend-isLoading
.monApplicationWrapper {
display: flex;
flex-direction: column;
flex-grow: 1;
}

View file

@ -23,11 +23,6 @@ filter-bar,
display: none !important; display: none !important;
} }
/* override open/closed positioning of the app wrapper/nav */
.app-wrapper {
left: 0px !important;
}
/** /**
* Discover Tweaks * Discover Tweaks
*/ */

View file

@ -23,11 +23,6 @@ filter-bar,
display: none !important; display: none !important;
} }
/* override open/closed positioning of the app wrapper/nav */
.app-wrapper {
left: 0px !important;
}
/** /**
* Discover Tweaks * Discover Tweaks
*/ */

View file

@ -5,7 +5,8 @@
* 2.0. * 2.0.
*/ */
export const DEFAULT_PAGELOAD_SELECTOR = '.application'; import { APP_WRAPPER_CLASS } from '../../../../../../src/core/server';
export const DEFAULT_PAGELOAD_SELECTOR = `.${APP_WRAPPER_CLASS}`;
export const CONTEXT_GETNUMBEROFITEMS = 'GetNumberOfItems'; export const CONTEXT_GETNUMBEROFITEMS = 'GetNumberOfItems';
export const CONTEXT_GETBROWSERDIMENSIONS = 'GetBrowserDimensions'; export const CONTEXT_GETBROWSERDIMENSIONS = 'GetBrowserDimensions';

View file

@ -204,7 +204,7 @@ describe('Screenshot Observable Pipeline', () => {
expect(mockOpen.mock.calls.length).toBe(2); expect(mockOpen.mock.calls.length).toBe(2);
const firstSelector = mockOpen.mock.calls[0][1].waitForSelector; const firstSelector = mockOpen.mock.calls[0][1].waitForSelector;
expect(firstSelector).toBe('.application'); expect(firstSelector).toBe('.kbnAppWrapper');
const secondSelector = mockOpen.mock.calls[1][1].waitForSelector; const secondSelector = mockOpen.mock.calls[1][1].waitForSelector;
expect(secondSelector).toBe('[data-shared-page="2"]'); expect(secondSelector).toBe('[data-shared-page="2"]');

View file

@ -7,6 +7,7 @@
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { ReportingCore } from '../..'; import { ReportingCore } from '../..';
import { APP_WRAPPER_CLASS } from '../../../../../../src/core/server';
import { API_DIAGNOSE_URL } from '../../../common/constants'; import { API_DIAGNOSE_URL } from '../../../common/constants';
import { omitBlockedHeaders } from '../../export_types/common'; import { omitBlockedHeaders } from '../../export_types/common';
import { getAbsoluteUrlFactory } from '../../export_types/common/get_absolute_url'; import { getAbsoluteUrlFactory } from '../../export_types/common/get_absolute_url';
@ -47,8 +48,8 @@ export const registerDiagnoseScreenshot = (reporting: ReportingCore, logger: Log
height: 2024, height: 2024,
}, },
selectors: { selectors: {
screenshot: '.application', screenshot: `.${APP_WRAPPER_CLASS}`,
renderComplete: '.application', renderComplete: `.${APP_WRAPPER_CLASS}`,
itemsCountAttribute: 'data-test-subj="kibanaChrome"', itemsCountAttribute: 'data-test-subj="kibanaChrome"',
timefilterDurationAttribute: 'data-test-subj="kibanaChrome"', timefilterDurationAttribute: 'data-test-subj="kibanaChrome"',
}, },

View file

@ -32,7 +32,7 @@ export const AppGlobalStyle = createGlobalStyle<{ theme: { eui: { euiColorPrimar
overflow: hidden; overflow: hidden;
} }
div.app-wrapper { div.kbnAppWrapper {
background-color: rgba(0,0,0,0); background-color: rgba(0,0,0,0);
} }

View file

@ -118,7 +118,7 @@ const Application = (props: UptimeAppProps) => {
<UptimeSettingsContextProvider {...props}> <UptimeSettingsContextProvider {...props}>
<UptimeThemeContextProvider darkMode={darkMode}> <UptimeThemeContextProvider darkMode={darkMode}>
<UptimeStartupPluginsContextProvider {...startPlugins}> <UptimeStartupPluginsContextProvider {...startPlugins}>
<EuiPage className="app-wrapper-panel " data-test-subj="uptimeApp"> <EuiPage data-test-subj="uptimeApp">
<RedirectAppLinks application={core.application}> <RedirectAppLinks application={core.application}>
<main> <main>
<UptimeAlertsFlyoutWrapper /> <UptimeAlertsFlyoutWrapper />

View file

@ -108,7 +108,7 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider
if (expectedResult === 'chrome') { if (expectedResult === 'chrome') {
await find.byCssSelector( await find.byCssSelector(
'[data-test-subj="kibanaChrome"] .app-wrapper:not(.hidden-chrome)', '[data-test-subj="kibanaChrome"] .kbnAppWrapper:not(.kbnAppWrapper--hiddenChrome)',
20000 20000
); );
log.debug(`Finished login process currentUrl = ${await browser.getCurrentUrl()}`); log.debug(`Finished login process currentUrl = ${await browser.getCurrentUrl()}`);

View file

@ -43,7 +43,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
); );
await find.byCssSelector( await find.byCssSelector(
'[data-test-subj="kibanaChrome"] .app-wrapper:not(.hidden-chrome)', '[data-test-subj="kibanaChrome"] .kbnAppWrapper:not(.kbnAppWrapper--hiddenChrome)',
20000 20000
); );

View file

@ -43,7 +43,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
); );
await find.byCssSelector( await find.byCssSelector(
'[data-test-subj="kibanaChrome"] .app-wrapper:not(.hidden-chrome)', '[data-test-subj="kibanaChrome"] .kbnAppWrapper:not(.kbnAppWrapper--hiddenChrome)',
20000 20000
); );