Skip to content

Commit 1a99df7

Browse files
author
Spencer
authored
[new platform] render legacy platform into a container (#21248)
* [core/public/legacyPlatform] render into a child of the rootDomElement * [kbn-chrome] use #kibana-body selector for ensuring root height * after feedback in #20752, clear elements by setting textContent * [browserTests] mount CoreSystem in a child, we require stuff in <body> * [core/public] ensure the rootDomElement is full-screen * add some comments tieing together root style rules * use `readonly` for unchanged properties * avoid unnecessary recreation of default param * comment about why coreSystem is not rendered into body in the tests * use more destructuring * rename argument in callback type signature * fix typo * [core/public/styles] switch from less to css
1 parent 5e5b2ce commit 1a99df7

File tree

8 files changed

+100
-51
lines changed

8 files changed

+100
-51
lines changed

src/core/public/core.css

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* designed to emulate root-element stretching and overflow
3+
* prevention previously handled in kbn_chrome.less
4+
*/
5+
.coreSystemRootDomElement {
6+
overflow-x: hidden;
7+
min-width: 100%;
8+
min-height: 100%;
9+
}

src/core/public/core_system.test.ts

+36-13
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ import { CoreSystem } from './core_system';
5656
jest.spyOn(CoreSystem.prototype, 'stop');
5757

5858
const defaultCoreSystemParams = {
59-
rootDomElement: null!,
59+
rootDomElement: document.createElement('div'),
6060
injectedMetadata: {} as any,
6161
requireLegacyFiles: jest.fn(),
6262
};
@@ -91,8 +91,8 @@ describe('constructor', () => {
9191
});
9292
});
9393

94-
it('passes rootDomElement, requireLegacyFiles, and useLegacyTestHarness to LegacyPlatformService', () => {
95-
const rootDomElement = { rootDomElement: true } as any;
94+
it('passes requireLegacyFiles, useLegacyTestHarness, and a dom element to LegacyPlatformService', () => {
95+
const rootDomElement = document.createElement('div');
9696
const requireLegacyFiles = { requireLegacyFiles: true } as any;
9797
const useLegacyTestHarness = { useLegacyTestHarness: true } as any;
9898

@@ -106,14 +106,14 @@ describe('constructor', () => {
106106

107107
expect(MockLegacyPlatformService).toHaveBeenCalledTimes(1);
108108
expect(MockLegacyPlatformService).toHaveBeenCalledWith({
109-
rootDomElement,
109+
targetDomElement: expect.any(HTMLElement),
110110
requireLegacyFiles,
111111
useLegacyTestHarness,
112112
});
113113
});
114114

115115
it('passes injectedMetadata, rootDomElement, and a stopCoreSystem function to FatalErrorsService', () => {
116-
const rootDomElement = { rootDomElement: true } as any;
116+
const rootDomElement = document.createElement('div');
117117
const injectedMetadata = { injectedMetadata: true } as any;
118118

119119
const coreSystem = new CoreSystem({
@@ -152,14 +152,22 @@ describe('#stop', () => {
152152
});
153153

154154
describe('#start()', () => {
155-
function startCore() {
155+
function startCore(rootDomElement = defaultCoreSystemParams.rootDomElement) {
156156
const core = new CoreSystem({
157157
...defaultCoreSystemParams,
158+
rootDomElement,
158159
});
159160

160161
core.start();
161162
}
162163

164+
it('clears the children of the rootDomElement and appends container for legacyPlatform', () => {
165+
const root = document.createElement('div');
166+
root.innerHTML = '<p>foo bar</p>';
167+
startCore(root);
168+
expect(root.innerHTML).toBe('<div></div>');
169+
});
170+
163171
it('calls injectedMetadata#start()', () => {
164172
startCore();
165173
const [mockInstance] = MockInjectedMetadataService.mock.instances;
@@ -173,14 +181,29 @@ describe('#start()', () => {
173181
expect(mockInstance.start).toHaveBeenCalledTimes(1);
174182
expect(mockInstance.start).toHaveBeenCalledWith();
175183
});
184+
});
176185

177-
it('calls legacyPlatform#start()', () => {
178-
startCore();
179-
const [mockInstance] = MockLegacyPlatformService.mock.instances;
180-
expect(mockInstance.start).toHaveBeenCalledTimes(1);
181-
expect(mockInstance.start).toHaveBeenCalledWith({
182-
injectedMetadata: mockInjectedMetadataStartContract,
183-
fatalErrors: mockFatalErrorsStartContract,
186+
describe('LegacyPlatform targetDomElement', () => {
187+
it('only mounts the element when started, before starting the legacyPlatformService', () => {
188+
const rootDomElement = document.createElement('div');
189+
const core = new CoreSystem({
190+
...defaultCoreSystemParams,
191+
rootDomElement,
192+
});
193+
194+
const [legacyPlatform] = MockLegacyPlatformService.mock.instances;
195+
196+
let targetDomElementParentInStart: HTMLElement;
197+
(legacyPlatform as any).start.mockImplementation(() => {
198+
targetDomElementParentInStart = targetDomElement.parentElement;
184199
});
200+
201+
// targetDomElement should not have a parent element when the LegacyPlatformService is constructed
202+
const [[{ targetDomElement }]] = MockLegacyPlatformService.mock.calls;
203+
expect(targetDomElement).toHaveProperty('parentElement', null);
204+
205+
// starting the core system should mount the targetDomElement as a child of the rootDomElement
206+
core.start();
207+
expect(targetDomElementParentInStart!).toBe(rootDomElement);
185208
});
186209
});

src/core/public/core_system.ts

+18-5
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@
1717
* under the License.
1818
*/
1919

20+
import './core.css';
2021
import { FatalErrorsService } from './fatal_errors';
2122
import { InjectedMetadataParams, InjectedMetadataService } from './injected_metadata';
2223
import { LegacyPlatformParams, LegacyPlatformService } from './legacy_platform';
2324

2425
interface Params {
26+
rootDomElement: HTMLElement;
2527
injectedMetadata: InjectedMetadataParams['injectedMetadata'];
26-
rootDomElement: LegacyPlatformParams['rootDomElement'];
2728
requireLegacyFiles: LegacyPlatformParams['requireLegacyFiles'];
2829
useLegacyTestHarness?: LegacyPlatformParams['useLegacyTestHarness'];
2930
}
@@ -35,13 +36,18 @@ interface Params {
3536
* platform the CoreSystem will get many more Services.
3637
*/
3738
export class CoreSystem {
38-
private fatalErrors: FatalErrorsService;
39-
private injectedMetadata: InjectedMetadataService;
40-
private legacyPlatform: LegacyPlatformService;
39+
private readonly fatalErrors: FatalErrorsService;
40+
private readonly injectedMetadata: InjectedMetadataService;
41+
private readonly legacyPlatform: LegacyPlatformService;
42+
43+
private readonly rootDomElement: HTMLElement;
44+
private readonly legacyPlatformTargetDomElement: HTMLDivElement;
4145

4246
constructor(params: Params) {
4347
const { rootDomElement, injectedMetadata, requireLegacyFiles, useLegacyTestHarness } = params;
4448

49+
this.rootDomElement = rootDomElement;
50+
4551
this.injectedMetadata = new InjectedMetadataService({
4652
injectedMetadata,
4753
});
@@ -54,15 +60,21 @@ export class CoreSystem {
5460
},
5561
});
5662

63+
this.legacyPlatformTargetDomElement = document.createElement('div');
5764
this.legacyPlatform = new LegacyPlatformService({
58-
rootDomElement,
65+
targetDomElement: this.legacyPlatformTargetDomElement,
5966
requireLegacyFiles,
6067
useLegacyTestHarness,
6168
});
6269
}
6370

6471
public start() {
6572
try {
73+
// ensure the rootDomElement is empty
74+
this.rootDomElement.textContent = '';
75+
this.rootDomElement.classList.add('coreSystemRootDomElement');
76+
this.rootDomElement.appendChild(this.legacyPlatformTargetDomElement);
77+
6678
const injectedMetadata = this.injectedMetadata.start();
6779
const fatalErrors = this.fatalErrors.start();
6880
this.legacyPlatform.start({ injectedMetadata, fatalErrors });
@@ -73,5 +85,6 @@ export class CoreSystem {
7385

7486
public stop() {
7587
this.legacyPlatform.stop();
88+
this.rootDomElement.textContent = '';
7689
}
7790
}
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`#stop() destroys the angular scope and empties the rootDomElement if angular is bootstraped to rootDomElement 1`] = `
3+
exports[`#stop() destroys the angular scope and empties the targetDomElement if angular is bootstraped to targetDomElement 1`] = `
44
<div
55
class="ng-scope"
66
/>
77
`;
88

9-
exports[`#stop() does nothing if angular was not bootstrapped to rootDomElement 1`] = `
9+
exports[`#stop() does nothing if angular was not bootstrapped to targetDomElement 1`] = `
1010
<div>
1111
1212
1313
<h1>
14-
foo
14+
this should not be removed
1515
</h1>
1616
17-
18-
<h2>
19-
bar
20-
</h2>
21-
2217
2318
</div>
2419
`;

src/core/public/legacy_platform/legacy_platform_service.test.ts

+17-18
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ const injectedMetadataStartContract = {
6262
};
6363

6464
const defaultParams = {
65-
rootDomElement: { someDomElement: true } as any,
65+
targetDomElement: document.createElement('div'),
6666
requireLegacyFiles: jest.fn(() => {
6767
mockLoadOrder.push('legacy files');
6868
}),
@@ -109,7 +109,7 @@ describe('#start()', () => {
109109
});
110110

111111
describe('useLegacyTestHarness = false', () => {
112-
it('passes the rootDomElement to ui/chrome', () => {
112+
it('passes the targetDomElement to ui/chrome', () => {
113113
const legacyPlatform = new LegacyPlatformService({
114114
...defaultParams,
115115
});
@@ -121,11 +121,11 @@ describe('#start()', () => {
121121

122122
expect(mockUiTestHarnessBootstrap).not.toHaveBeenCalled();
123123
expect(mockUiChromeBootstrap).toHaveBeenCalledTimes(1);
124-
expect(mockUiChromeBootstrap).toHaveBeenCalledWith(defaultParams.rootDomElement);
124+
expect(mockUiChromeBootstrap).toHaveBeenCalledWith(defaultParams.targetDomElement);
125125
});
126126
});
127127
describe('useLegacyTestHarness = true', () => {
128-
it('passes the rootDomElement to ui/test_harness', () => {
128+
it('passes the targetDomElement to ui/test_harness', () => {
129129
const legacyPlatform = new LegacyPlatformService({
130130
...defaultParams,
131131
useLegacyTestHarness: true,
@@ -138,7 +138,7 @@ describe('#start()', () => {
138138

139139
expect(mockUiChromeBootstrap).not.toHaveBeenCalled();
140140
expect(mockUiTestHarnessBootstrap).toHaveBeenCalledTimes(1);
141-
expect(mockUiTestHarnessBootstrap).toHaveBeenCalledWith(defaultParams.rootDomElement);
141+
expect(mockUiTestHarnessBootstrap).toHaveBeenCalledWith(defaultParams.targetDomElement);
142142
});
143143
});
144144
});
@@ -192,29 +192,28 @@ describe('#start()', () => {
192192
});
193193

194194
describe('#stop()', () => {
195-
it('does nothing if angular was not bootstrapped to rootDomElement', () => {
196-
const rootDomElement = document.createElement('div');
197-
rootDomElement.innerHTML = `
198-
<h1>foo</h1>
199-
<h2>bar</h2>
195+
it('does nothing if angular was not bootstrapped to targetDomElement', () => {
196+
const targetDomElement = document.createElement('div');
197+
targetDomElement.innerHTML = `
198+
<h1>this should not be removed</h1>
200199
`;
201200

202201
const legacyPlatform = new LegacyPlatformService({
203202
...defaultParams,
204-
rootDomElement,
203+
targetDomElement,
205204
});
206205

207206
legacyPlatform.stop();
208-
expect(rootDomElement).toMatchSnapshot();
207+
expect(targetDomElement).toMatchSnapshot();
209208
});
210209

211-
it('destroys the angular scope and empties the rootDomElement if angular is bootstraped to rootDomElement', () => {
212-
const rootDomElement = document.createElement('div');
210+
it('destroys the angular scope and empties the targetDomElement if angular is bootstraped to targetDomElement', () => {
211+
const targetDomElement = document.createElement('div');
213212
const scopeDestroySpy = jest.fn();
214213

215214
const legacyPlatform = new LegacyPlatformService({
216215
...defaultParams,
217-
rootDomElement,
216+
targetDomElement,
218217
});
219218

220219
// simulate bootstraping with a module "foo"
@@ -225,15 +224,15 @@ describe('#stop()', () => {
225224
},
226225
}));
227226

228-
rootDomElement.innerHTML = `
227+
targetDomElement.innerHTML = `
229228
<bar></bar>
230229
`;
231230

232-
angular.bootstrap(rootDomElement, ['foo']);
231+
angular.bootstrap(targetDomElement, ['foo']);
233232

234233
legacyPlatform.stop();
235234

236-
expect(rootDomElement).toMatchSnapshot();
235+
expect(targetDomElement).toMatchSnapshot();
237236
expect(scopeDestroySpy).toHaveBeenCalledTimes(1);
238237
});
239238
});

src/core/public/legacy_platform/legacy_platform_service.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ interface Deps {
2727
}
2828

2929
export interface LegacyPlatformParams {
30-
rootDomElement: HTMLElement;
30+
targetDomElement: HTMLElement;
3131
requireLegacyFiles: () => void;
3232
useLegacyTestHarness?: boolean;
3333
}
@@ -55,11 +55,11 @@ export class LegacyPlatformService {
5555
// require the files that will tie into the legacy platform
5656
this.params.requireLegacyFiles();
5757

58-
bootstrapModule.bootstrap(this.params.rootDomElement);
58+
bootstrapModule.bootstrap(this.params.targetDomElement);
5959
}
6060

6161
public stop() {
62-
const angularRoot = angular.element(this.params.rootDomElement);
62+
const angularRoot = angular.element(this.params.targetDomElement);
6363
const injector$ = angularRoot.injector();
6464

6565
// if we haven't gotten to the point of bootstraping
@@ -72,11 +72,11 @@ export class LegacyPlatformService {
7272
injector$.get('$rootScope').$destroy();
7373

7474
// clear the inner html of the root angular element
75-
this.params.rootDomElement.textContent = '';
75+
this.params.targetDomElement.textContent = '';
7676
}
7777

7878
private loadBootstrapModule(): {
79-
bootstrap: (rootDomElement: HTMLElement) => void;
79+
bootstrap: (targetDomElement: HTMLElement) => void;
8080
} {
8181
if (this.params.useLegacyTestHarness) {
8282
// wrapped in NODE_ENV check so the `ui/test_harness` module

src/core_plugins/tests_bundle/tests_entry_template.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,18 @@ const legacyMetadata = {
7272
}
7373
};
7474
75+
// render the core system in a child of the body as the default children of the body
76+
// in the browser tests are needed for mocha and other test components to work
77+
const rootDomElement = document.createElement('div');
78+
document.body.appendChild(rootDomElement)
79+
7580
new CoreSystem({
7681
injectedMetadata: {
7782
version: legacyMetadata.version,
7883
buildNumber: legacyMetadata.buildNum,
7984
legacyMetadata
8085
},
81-
rootDomElement: document.body,
86+
rootDomElement,
8287
useLegacyTestHarness: true,
8388
requireLegacyFiles: () => {
8489
${bundle.getRequires().join('\n ')}

src/ui/public/chrome/directives/kbn_chrome.less

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
@import (reference) "~ui/styles/mixins";
33
@import (reference) "~ui/styles/variables";
44

5-
body {
5+
/**
6+
* stretch the root element of the Kibana application to set the base-size that
7+
* flexed children should keep. Only works when paired with root styles applied
8+
* by core service from new platform
9+
*/
10+
#kibana-body {
611
overflow-x: hidden;
712
min-height: 100%;
813
}

0 commit comments

Comments
 (0)