[new-platform] Add legacy property to NavLinks registered by… (#41301)

This commit is contained in:
Josh Dover 2019-07-29 17:51:45 -05:00 committed by GitHub
parent c91e94510e
commit eb03fd8c5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 246 additions and 170 deletions

View file

@ -4,8 +4,10 @@
## ApplicationStart.availableApps property
Apps available based on the current capabilities. Should be used to show navigation links and make routing decisions.
<b>Signature:</b>
```typescript
availableApps: CapabilitiesStart['availableApps'];
availableApps: readonly App[];
```

View file

@ -4,8 +4,10 @@
## ApplicationStart.capabilities property
Gets the read-only capabilities.
<b>Signature:</b>
```typescript
capabilities: CapabilitiesStart['capabilities'];
capabilities: RecursiveReadonly<Capabilities>;
```

View file

@ -4,6 +4,7 @@
## ApplicationStart interface
<b>Signature:</b>
```typescript
@ -14,7 +15,6 @@ export interface ApplicationStart
| Property | Type | Description |
| --- | --- | --- |
| [availableApps](./kibana-plugin-public.applicationstart.availableapps.md) | <code>CapabilitiesStart['availableApps']</code> | |
| [capabilities](./kibana-plugin-public.applicationstart.capabilities.md) | <code>CapabilitiesStart['capabilities']</code> | |
| [mount](./kibana-plugin-public.applicationstart.mount.md) | <code>(mountHandler: Function) =&gt; void</code> | |
| [availableApps](./kibana-plugin-public.applicationstart.availableapps.md) | <code>readonly App[]</code> | Apps available based on the current capabilities. Should be used to show navigation links and make routing decisions. |
| [capabilities](./kibana-plugin-public.applicationstart.capabilities.md) | <code>RecursiveReadonly&lt;Capabilities&gt;</code> | Gets the read-only capabilities. |

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [ApplicationStart](./kibana-plugin-public.applicationstart.md) &gt; [mount](./kibana-plugin-public.applicationstart.mount.md)
## ApplicationStart.mount property
<b>Signature:</b>
```typescript
mount: (mountHandler: Function) => void;
```

View file

@ -4,9 +4,11 @@
## ChromeNavLink.active property
Indicates whether or not this app is currently on the screen.
> Warning: This API is now obsolete.
>
>
NOTE: remove this when ApplicationService is implemented and managing apps.
Indicates whether or not this app is currently on the screen.
<b>Signature:</b>

View file

@ -4,9 +4,11 @@
## ChromeNavLink.disabled property
Disables a link from being clickable.
> Warning: This API is now obsolete.
>
>
NOTE: this is only used by the ML and Graph plugins currently. They use this field to disable the nav link when the license is expired.
Disables a link from being clickable.
<b>Signature:</b>

View file

@ -6,8 +6,6 @@
Hides a link from the navigation.
NOTE: remove this when ApplicationService is implemented. Instead, plugins should only register an Application if needed.
<b>Signature:</b>
```typescript

View file

@ -4,9 +4,11 @@
## ChromeNavLink.linkToLastSubUrl property
Whether or not the subUrl feature should be enabled.
> Warning: This API is now obsolete.
>
>
NOTE: only read by legacy platform.
Whether or not the subUrl feature should be enabled.
<b>Signature:</b>

View file

@ -15,17 +15,17 @@ export interface ChromeNavLink
| Property | Type | Description |
| --- | --- | --- |
| [active](./kibana-plugin-public.chromenavlink.active.md) | <code>boolean</code> | Indicates whether or not this app is currently on the screen.<!-- -->NOTE: remove this when ApplicationService is implemented and managing apps. |
| [active](./kibana-plugin-public.chromenavlink.active.md) | <code>boolean</code> | Indicates whether or not this app is currently on the screen. |
| [baseUrl](./kibana-plugin-public.chromenavlink.baseurl.md) | <code>string</code> | The base route used to open the root of an application. |
| [disabled](./kibana-plugin-public.chromenavlink.disabled.md) | <code>boolean</code> | Disables a link from being clickable.<!-- -->NOTE: this is only used by the ML and Graph plugins currently. They use this field to disable the nav link when the license is expired. |
| [disabled](./kibana-plugin-public.chromenavlink.disabled.md) | <code>boolean</code> | Disables a link from being clickable. |
| [euiIconType](./kibana-plugin-public.chromenavlink.euiicontype.md) | <code>string</code> | A EUI iconType that will be used for the app's icon. This icon takes precendence over the <code>icon</code> property. |
| [hidden](./kibana-plugin-public.chromenavlink.hidden.md) | <code>boolean</code> | Hides a link from the navigation.<!-- -->NOTE: remove this when ApplicationService is implemented. Instead, plugins should only register an Application if needed. |
| [hidden](./kibana-plugin-public.chromenavlink.hidden.md) | <code>boolean</code> | Hides a link from the navigation. |
| [icon](./kibana-plugin-public.chromenavlink.icon.md) | <code>string</code> | A URL to an image file used as an icon. Used as a fallback if <code>euiIconType</code> is not provided. |
| [id](./kibana-plugin-public.chromenavlink.id.md) | <code>string</code> | A unique identifier for looking up links. |
| [linkToLastSubUrl](./kibana-plugin-public.chromenavlink.linktolastsuburl.md) | <code>boolean</code> | Whether or not the subUrl feature should be enabled.<!-- -->NOTE: only read by legacy platform. |
| [linkToLastSubUrl](./kibana-plugin-public.chromenavlink.linktolastsuburl.md) | <code>boolean</code> | Whether or not the subUrl feature should be enabled. |
| [order](./kibana-plugin-public.chromenavlink.order.md) | <code>number</code> | An ordinal used to sort nav links relative to one another for display. |
| [subUrlBase](./kibana-plugin-public.chromenavlink.suburlbase.md) | <code>string</code> | A url base that legacy apps can set to match deep URLs to an applcation.<!-- -->NOTE: this should be removed once legacy apps are gone. |
| [subUrlBase](./kibana-plugin-public.chromenavlink.suburlbase.md) | <code>string</code> | A url base that legacy apps can set to match deep URLs to an applcation. |
| [title](./kibana-plugin-public.chromenavlink.title.md) | <code>string</code> | The title of the application. |
| [tooltip](./kibana-plugin-public.chromenavlink.tooltip.md) | <code>string</code> | A tooltip shown when hovering over an app link. |
| [url](./kibana-plugin-public.chromenavlink.url.md) | <code>string</code> | A url that legacy apps can set to deep link into their applications.<!-- -->NOTE: Currently used by the "lastSubUrl" feature legacy/ui/chrome. This should be removed once the ApplicationService is implemented and mounting apps. At that time, each app can handle opening to the previous location when they are mounted. |
| [url](./kibana-plugin-public.chromenavlink.url.md) | <code>string</code> | A url that legacy apps can set to deep link into their applications. |

View file

@ -4,9 +4,11 @@
## ChromeNavLink.subUrlBase property
A url base that legacy apps can set to match deep URLs to an applcation.
> Warning: This API is now obsolete.
>
>
NOTE: this should be removed once legacy apps are gone.
A url base that legacy apps can set to match deep URLs to an applcation.
<b>Signature:</b>

View file

@ -4,9 +4,11 @@
## ChromeNavLink.url property
A url that legacy apps can set to deep link into their applications.
> Warning: This API is now obsolete.
>
>
NOTE: Currently used by the "lastSubUrl" feature legacy/ui/chrome. This should be removed once the ApplicationService is implemented and mounting apps. At that time, each app can handle opening to the previous location when they are mounted.
A url that legacy apps can set to deep link into their applications.
<b>Signature:</b>

View file

@ -16,5 +16,5 @@ export interface OverlayStart
| Property | Type | Description |
| --- | --- | --- |
| [openFlyout](./kibana-plugin-public.overlaystart.openflyout.md) | <code>(flyoutChildren: React.ReactNode, flyoutProps?: {</code><br/><code> closeButtonAriaLabel?: string;</code><br/><code> 'data-test-subj'?: string;</code><br/><code> }) =&gt; OverlayRef</code> | |
| [openModal](./kibana-plugin-public.overlaystart.openmodal.md) | <code>(modalChildren: React.ReactNode, modalProps?: {</code><br/><code> closeButtonAriaLabel?: string;</code><br/><code> 'data-test-subj'?: string;</code><br/><code> }) =&gt; OverlayRef</code> | |
| [openModal](./kibana-plugin-public.overlaystart.openmodal.md) | <code>(modalChildren: React.ReactNode, modalProps?: {</code><br/><code> className?: string;</code><br/><code> closeButtonAriaLabel?: string;</code><br/><code> 'data-test-subj'?: string;</code><br/><code> }) =&gt; OverlayRef</code> | |

View file

@ -8,6 +8,7 @@
```typescript
openModal: (modalChildren: React.ReactNode, modalProps?: {
className?: string;
closeButtonAriaLabel?: string;
'data-test-subj'?: string;
}) => OverlayRef;

View file

@ -28,7 +28,6 @@ const createSetupContractMock = (): jest.Mocked<ApplicationSetup> => ({
});
const createStartContractMock = (): jest.Mocked<ApplicationStart> => ({
mount: jest.fn(),
...capabilitiesServiceMock.createStartContract(),
});

View file

@ -28,11 +28,16 @@ describe('#start()', () => {
setup.registerApp({ id: 'app1' } as any);
setup.registerLegacyApp({ id: 'app2' } as any);
const injectedMetadata = injectedMetadataServiceMock.createStartContract();
expect((await service.start({ injectedMetadata })).availableApps).toMatchInlineSnapshot(`
const startContract = await service.start({ injectedMetadata });
expect(startContract.availableApps).toMatchInlineSnapshot(`
Array [
Object {
"id": "app1",
},
]
`);
expect(startContract.availableLegacyApps).toMatchInlineSnapshot(`
Array [
Object {
"id": "app2",
},
@ -48,6 +53,7 @@ Array [
await service.start({ injectedMetadata });
expect(MockCapabilitiesService.start).toHaveBeenCalledWith({
apps: [{ id: 'app1' }],
legacyApps: [],
injectedMetadata,
});
});
@ -59,7 +65,8 @@ Array [
const injectedMetadata = injectedMetadataServiceMock.createStartContract();
await service.start({ injectedMetadata });
expect(MockCapabilitiesService.start).toHaveBeenCalledWith({
apps: [{ id: 'legacyApp1' }],
apps: [],
legacyApps: [{ id: 'legacyApp1' }],
injectedMetadata,
});
});

View file

@ -18,8 +18,9 @@
*/
import { Observable, BehaviorSubject } from 'rxjs';
import { CapabilitiesStart, CapabilitiesService, Capabilities } from './capabilities';
import { CapabilitiesService, Capabilities } from './capabilities';
import { InjectedMetadataStart } from '../injected_metadata';
import { RecursiveReadonly } from '../../utils';
interface BaseApp {
id: string;
@ -59,11 +60,6 @@ interface BaseApp {
/** @public */
export interface App extends BaseApp {
/**
* The root route to mount this application at.
*/
rootRoute: string;
/**
* A mount function called when the user navigates to this app's `rootRoute`.
* @param targetDomElement An HTMLElement to mount the application onto.
@ -98,10 +94,27 @@ export interface ApplicationSetup {
registerLegacyApp(app: LegacyApp): void;
}
/**
* @public
*/
export interface ApplicationStart {
mount: (mountHandler: Function) => void;
availableApps: CapabilitiesStart['availableApps'];
capabilities: CapabilitiesStart['capabilities'];
/**
* Gets the read-only capabilities.
*/
capabilities: RecursiveReadonly<Capabilities>;
/**
* Apps available based on the current capabilities. Should be used
* to show navigation links and make routing decisions.
*/
availableApps: readonly App[];
/**
* Apps available based on the current capabilities. Should be used
* to show navigation links and make routing decisions.
* @internal
*/
availableLegacyApps: readonly LegacyApp[];
}
interface StartDeps {
@ -132,17 +145,11 @@ export class ApplicationService {
this.apps$.complete();
this.legacyApps$.complete();
const apps = [...this.apps$.value, ...this.legacyApps$.value];
const { capabilities, availableApps } = await this.capabilities.start({
apps,
return this.capabilities.start({
apps: this.apps$.value,
legacyApps: this.legacyApps$.value,
injectedMetadata,
});
return {
mount() {},
capabilities,
availableApps,
};
}
public stop() {}

View file

@ -18,12 +18,14 @@
*/
import { CapabilitiesService, CapabilitiesStart } from './capabilities_service';
import { deepFreeze } from '../../../utils/';
import { MixedApp } from '../application_service';
import { App, LegacyApp } from '../application_service';
const createStartContractMock = (
apps: readonly MixedApp[] = []
apps: readonly App[] = [],
legacyApps: readonly LegacyApp[] = []
): jest.Mocked<CapabilitiesStart> => ({
availableApps: apps,
availableLegacyApps: legacyApps,
capabilities: deepFreeze({
catalogue: {},
management: {},
@ -33,7 +35,9 @@ const createStartContractMock = (
type CapabilitiesServiceContract = PublicMethodsOf<CapabilitiesService>;
const createMock = (): jest.Mocked<CapabilitiesServiceContract> => ({
start: jest.fn().mockImplementation(({ apps }) => createStartContractMock(apps)),
start: jest
.fn()
.mockImplementation(({ apps, legacyApps }) => createStartContractMock(apps, legacyApps)),
});
export const capabilitiesServiceMock = {

View file

@ -30,25 +30,33 @@ describe('#start', () => {
navLinks: {
app1: true,
app2: false,
legacyApp1: true,
legacyApp2: false,
},
foo: { feature: true },
bar: { feature: true },
},
} as any,
}).start();
const apps = [{ id: 'app1' }, { id: 'app2', capabilities: { app2: { feature: true } } }] as any;
const legacyApps = [
{ id: 'legacyApp1' },
{ id: 'legacyApp2', capabilities: { app2: { feature: true } } },
] as any;
it('filters available apps based on returned navLinks', async () => {
const service = new CapabilitiesService();
expect((await service.start({ apps, injectedMetadata })).availableApps).toEqual([
{ id: 'app1' },
]);
const startContract = await service.start({ apps, legacyApps, injectedMetadata });
expect(startContract.availableApps).toEqual([{ id: 'app1' }]);
expect(startContract.availableLegacyApps).toEqual([{ id: 'legacyApp1' }]);
});
it('does not allow Capabilities to be modified', async () => {
const service = new CapabilitiesService();
const { capabilities } = await service.start({
apps,
legacyApps,
injectedMetadata,
});

View file

@ -18,11 +18,12 @@
*/
import { deepFreeze, RecursiveReadonly } from '../../../utils';
import { MixedApp } from '../application_service';
import { LegacyApp, App } from '../application_service';
import { InjectedMetadataStart } from '../../injected_metadata';
interface StartDeps {
apps: readonly MixedApp[];
apps: readonly App[];
legacyApps: readonly LegacyApp[];
injectedMetadata: InjectedMetadataStart;
}
@ -49,35 +50,28 @@ export interface Capabilities {
[key: string]: Record<string, boolean | Record<string, boolean>>;
}
/**
* Capabilities Setup.
* @public
*/
export interface CapabilitiesStart {
/**
* Gets the read-only capabilities.
*/
capabilities: RecursiveReadonly<Capabilities>;
/**
* Apps available based on the current capabilities. Should be used
* to show navigation links and make routing decisions.
*/
availableApps: readonly MixedApp[];
}
/** @internal */
export interface CapabilitiesStart {
capabilities: RecursiveReadonly<Capabilities>;
availableApps: readonly App[];
availableLegacyApps: readonly LegacyApp[];
}
/**
* Service that is responsible for UI Capabilities.
* @internal
*/
export class CapabilitiesService {
public async start({ apps, injectedMetadata }: StartDeps): Promise<CapabilitiesStart> {
public async start({
apps,
legacyApps,
injectedMetadata,
}: StartDeps): Promise<CapabilitiesStart> {
const capabilities = deepFreeze(injectedMetadata.getCapabilities());
const availableApps = apps.filter(app => capabilities.navLinks[app.id]);
return {
availableApps,
availableApps: apps.filter(app => capabilities.navLinks[app.id]),
availableLegacyApps: legacyApps.filter(app => capabilities.navLinks[app.id]),
capabilities,
};
}

View file

@ -17,4 +17,4 @@
* under the License.
*/
export { Capabilities, CapabilitiesService, CapabilitiesStart } from './capabilities_service';
export { Capabilities, CapabilitiesService } from './capabilities_service';

View file

@ -26,7 +26,7 @@ import {
} from './chrome_service';
const createStartContractMock = () => {
const startContract: jest.Mocked<InternalChromeStart> = {
const startContract: DeeplyMockedKeys<InternalChromeStart> = {
getComponent: jest.fn(),
navLinks: {
getNavLinks$: jest.fn(),
@ -66,6 +66,7 @@ const createStartContractMock = () => {
getHelpExtension$: jest.fn(),
setHelpExtension: jest.fn(),
};
startContract.navLinks.getAll.mockReturnValue([]);
startContract.getBrand$.mockReturnValue(new BehaviorSubject({} as ChromeBrand));
startContract.getIsVisible$.mockReturnValue(new BehaviorSubject(false));
startContract.getIsCollapsed$.mockReturnValue(new BehaviorSubject(false));

View file

@ -28,29 +28,6 @@ export interface ChromeNavLink {
*/
readonly id: string;
/**
* Indicates whether or not this app is currently on the screen.
*
* NOTE: remove this when ApplicationService is implemented and managing apps.
*/
readonly active?: boolean;
/**
* Disables a link from being clickable.
*
* NOTE: this is only used by the ML and Graph plugins currently. They use this field
* to disable the nav link when the license is expired.
*/
readonly disabled?: boolean;
/**
* Hides a link from the navigation.
*
* NOTE: remove this when ApplicationService is implemented. Instead, plugins should only
* register an Application if needed.
*/
readonly hidden?: boolean;
/**
* An ordinal used to sort nav links relative to one another for display.
*/
@ -61,16 +38,16 @@ export interface ChromeNavLink {
*/
readonly title: string;
/**
* A tooltip shown when hovering over an app link.
*/
readonly tooltip?: string;
/**
* The base route used to open the root of an application.
*/
readonly baseUrl: string;
/**
* A tooltip shown when hovering over an app link.
*/
readonly tooltip?: string;
/**
* A EUI iconType that will be used for the app's icon. This icon
* takes precendence over the `icon` property.
@ -88,25 +65,70 @@ export interface ChromeNavLink {
/**
* A url base that legacy apps can set to match deep URLs to an applcation.
*
* NOTE: this should be removed once legacy apps are gone.
* @internalRemarks
* This should be removed once legacy apps are gone.
*
* @deprecated
*/
readonly subUrlBase?: string;
/**
* Whether or not the subUrl feature should be enabled.
*
* NOTE: only read by legacy platform.
* @internalRemarks
* Only read by legacy platform.
*
* @deprecated
*/
readonly linkToLastSubUrl?: boolean;
/**
* A url that legacy apps can set to deep link into their applications.
*
* NOTE: Currently used by the "lastSubUrl" feature legacy/ui/chrome. This should
* @internalRemarks
* Currently used by the "lastSubUrl" feature legacy/ui/chrome. This should
* be removed once the ApplicationService is implemented and mounting apps. At that
* time, each app can handle opening to the previous location when they are mounted.
*
* @deprecated
*/
readonly url?: string;
/**
* Indicates whether or not this app is currently on the screen.
*
* @internalRemarks
* Remove this when ApplicationService is implemented and managing apps.
*
* @deprecated
*/
readonly active?: boolean;
/**
* Disables a link from being clickable.
*
* @internalRemarks
* This is only used by the ML and Graph plugins currently. They use this field
* to disable the nav link when the license is expired.
*
* @deprecated
*/
readonly disabled?: boolean;
/**
* Hides a link from the navigation.
*
* @internalRemarks
* Remove this when ApplicationService is implemented. Instead, plugins should only
* register an Application if needed.
*/
readonly hidden?: boolean;
/**
* Used to separate links to legacy applications from NP applications
* @internal
*/
readonly legacy: boolean;
}
/** @public */

View file

@ -21,10 +21,17 @@ import { NavLinksService } from './nav_links_service';
import { take, map, takeLast } from 'rxjs/operators';
const mockAppService = {
availableApps: [
{ id: 'app1', order: 0, title: 'App 1', icon: 'app1', rootRoute: '/app1' },
{ id: 'app2', order: -10, title: 'App 2', euiIconType: 'canvasApp', rootRoute: '/app2' },
{ id: 'legacyApp', order: 20, title: 'Legacy App', appUrl: '/legacy-app' },
availableApps: [],
availableLegacyApps: [
{ id: 'legacyApp1', order: 0, title: 'Legacy App 1', icon: 'legacyApp1', appUrl: '/app1' },
{
id: 'legacyApp2',
order: -10,
title: 'Legacy App 2',
euiIconType: 'canvasApp',
appUrl: '/app2',
},
{ id: 'legacyApp3', order: 20, title: 'Legacy App 3', appUrl: '/app3' },
],
} as any;
@ -53,17 +60,20 @@ describe('NavLinksService', () => {
map(links => links.map(l => l.id))
)
.toPromise()
).toEqual(['app2', 'app1', 'legacyApp']);
).toEqual(['legacyApp2', 'legacyApp1', 'legacyApp3']);
});
it('emits multiple values', async () => {
const navLinkIds$ = start.getNavLinks$().pipe(map(links => links.map(l => l.id)));
const emittedLinks: string[][] = [];
navLinkIds$.subscribe(r => emittedLinks.push(r));
start.update('app1', { active: true });
start.update('legacyApp1', { active: true });
service.stop();
expect(emittedLinks).toEqual([['app2', 'app1', 'legacyApp'], ['app2', 'app1', 'legacyApp']]);
expect(emittedLinks).toEqual([
['legacyApp2', 'legacyApp1', 'legacyApp3'],
['legacyApp2', 'legacyApp1', 'legacyApp3'],
]);
});
it('completes when service is stopped', async () => {
@ -78,7 +88,7 @@ describe('NavLinksService', () => {
describe('#get()', () => {
it('returns link if exists', () => {
expect(start.get('app1')!.title).toEqual('App 1');
expect(start.get('legacyApp1')!.title).toEqual('Legacy App 1');
});
it('returns undefined if it does not exist', () => {
@ -88,13 +98,13 @@ describe('NavLinksService', () => {
describe('#getAll()', () => {
it('returns a sorted array of navlinks', () => {
expect(start.getAll().map(l => l.id)).toEqual(['app2', 'app1', 'legacyApp']);
expect(start.getAll().map(l => l.id)).toEqual(['legacyApp2', 'legacyApp1', 'legacyApp3']);
});
});
describe('#has()', () => {
it('returns true if exists', () => {
expect(start.has('app1')).toBe(true);
expect(start.has('legacyApp1')).toBe(true);
});
it('returns false if it does not exist', () => {
@ -113,11 +123,11 @@ describe('NavLinksService', () => {
map(links => links.map(l => l.id))
)
.toPromise()
).toEqual(['app2', 'app1', 'legacyApp']);
).toEqual(['legacyApp2', 'legacyApp1', 'legacyApp3']);
});
it('removes all other links', async () => {
start.showOnly('app1');
start.showOnly('legacyApp1');
expect(
await start
.getNavLinks$()
@ -126,23 +136,24 @@ describe('NavLinksService', () => {
map(links => links.map(l => l.id))
)
.toPromise()
).toEqual(['app1']);
).toEqual(['legacyApp1']);
});
});
describe('#update()', () => {
it('updates the navlinks and returns the updated link', async () => {
expect(start.update('app1', { hidden: true })).toMatchInlineSnapshot(`
Object {
"baseUrl": "http://localhost/wow/app1",
"hidden": true,
"icon": "app1",
"id": "app1",
"order": 0,
"rootRoute": "/app1",
"title": "App 1",
}
`);
expect(start.update('legacyApp1', { hidden: true })).toMatchInlineSnapshot(`
Object {
"appUrl": "/app1",
"baseUrl": "http://localhost/wow/app1",
"hidden": true,
"icon": "legacyApp1",
"id": "legacyApp1",
"legacy": true,
"order": 0,
"title": "Legacy App 1",
}
`);
const hiddenLinkIds = await start
.getNavLinks$()
.pipe(
@ -150,7 +161,7 @@ Object {
map(links => links.filter(l => l.hidden).map(l => l.id))
)
.toPromise();
expect(hiddenLinkIds).toEqual(['app1']);
expect(hiddenLinkIds).toEqual(['legacyApp1']);
});
it('returns undefined if link does not exist', () => {

View file

@ -99,20 +99,20 @@ export class NavLinksService {
private readonly stop$ = new ReplaySubject(1);
public start({ application, http }: StartDeps): ChromeNavLinks {
const legacyAppLinks = application.availableLegacyApps.map(
app =>
[
app.id,
new NavLinkWrapper({
...app,
legacy: true,
baseUrl: relativeToAbsolute(http.basePath.prepend(app.appUrl)),
}),
] as [string, NavLinkWrapper]
);
const navLinks$ = new BehaviorSubject<ReadonlyMap<string, NavLinkWrapper>>(
new Map(
application.availableApps.map(
app =>
[
app.id,
new NavLinkWrapper({
...app,
// Either rootRoute or appUrl must be defined.
baseUrl: relativeToAbsolute(http.basePath.prepend((app.rootRoute || app.appUrl)!)),
}),
] as [string, NavLinkWrapper]
)
)
new Map(legacyAppLinks)
);
const forceAppSwitcherNavigation$ = new BehaviorSubject(false);

View file

@ -20,18 +20,12 @@ export interface ApplicationSetup {
registerLegacyApp(app: LegacyApp): void;
}
// Warning: (ae-missing-release-tag) "ApplicationStart" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface ApplicationStart {
// Warning: (ae-forgotten-export) The symbol "CapabilitiesStart" needs to be exported by the entry point index.d.ts
//
// (undocumented)
availableApps: CapabilitiesStart['availableApps'];
// (undocumented)
capabilities: CapabilitiesStart['capabilities'];
// (undocumented)
mount: (mountHandler: Function) => void;
availableApps: readonly App[];
// @internal
availableLegacyApps: readonly LegacyApp[];
capabilities: RecursiveReadonly<Capabilities>;
}
// @public
@ -95,18 +89,25 @@ export interface ChromeNavControls {
// @public (undocumented)
export interface ChromeNavLink {
// @deprecated
readonly active?: boolean;
readonly baseUrl: string;
// @deprecated
readonly disabled?: boolean;
readonly euiIconType?: string;
readonly hidden?: boolean;
readonly icon?: string;
readonly id: string;
// @internal
readonly legacy: boolean;
// @deprecated
readonly linkToLastSubUrl?: boolean;
readonly order: number;
// @deprecated
readonly subUrlBase?: string;
readonly title: string;
readonly tooltip?: string;
// @deprecated
readonly url?: string;
}

View file

@ -83,6 +83,7 @@ describe('chrome nav apis', function () {
url: `${appUrl}?id=${deletedId}`,
baseUrl: appUrl,
linkToLastSubUrl: true,
legacy: true,
}];
const { chrome } = init({ appUrlStore });
@ -98,7 +99,8 @@ describe('chrome nav apis', function () {
title: 'Discover',
url: lastUrl,
baseUrl: appUrl,
linkToLastSubUrl: true
linkToLastSubUrl: true,
legacy: true,
}];
const { chrome } = init({ appUrlStore });
@ -114,17 +116,20 @@ describe('chrome nav apis', function () {
{
id: 'kibana:discover',
baseUrl: `${baseUrl}/app/kibana#discover`,
subUrlBase: '/app/kibana#discover'
subUrlBase: '/app/kibana#discover',
legacy: true,
},
{
id: 'kibana:visualize',
baseUrl: `${baseUrl}/app/kibana#visualize`,
subUrlBase: '/app/kibana#visualize'
subUrlBase: '/app/kibana#visualize',
legacy: true,
},
{
id: 'kibana:dashboard',
baseUrl: `${baseUrl}/app/kibana#dashboards`,
subUrlBase: '/app/kibana#dashboard'
subUrlBase: '/app/kibana#dashboard',
legacy: true,
},
];
@ -150,6 +155,7 @@ describe('chrome nav apis', function () {
baseUrl: `${baseUrl}/app/kibana#visualize`,
url: `${baseUrl}/app/kibana#visualize`,
subUrlBase: '/app/kibana#visualize',
legacy: true,
}];
const { chrome } = init({ appUrlStore });

View file

@ -78,7 +78,7 @@ export function initChromeNavApi(chrome: any, internals: NavInternals) {
coreNavLinks
.getAll()
// Filter only legacy links
.filter(link => link.subUrlBase)
.filter(link => link.legacy)
.forEach(link => {
const active = url.startsWith(link.subUrlBase!);
link = coreNavLinks.update(link.id, { active })!;

7
typings/index.d.ts vendored
View file

@ -30,3 +30,10 @@ type MethodKeysOf<T> = {
type PublicMethodsOf<T> = Pick<T, MethodKeysOf<T>>;
type MockedKeys<T> = { [P in keyof T]: jest.Mocked<T[P]> };
type DeeplyMockedKeys<T> = {
[P in keyof T]: T[P] extends (...args: any[]) => any
? jest.MockInstance<ReturnType<T[P]>, Parameters<T[P]>>
: DeeplyMockedKeys<T[P]>;
} &
T;

View file

@ -25,6 +25,13 @@ declare module 'axios/lib/adapters/xhr';
type MockedKeys<T> = { [P in keyof T]: jest.Mocked<T[P]> };
type DeeplyMockedKeys<T> = {
[P in keyof T]: T[P] extends (...args: any[]) => any
? jest.MockInstance<ReturnType<T[P]>, Parameters<T[P]>>
: DeeplyMockedKeys<T[P]>;
} &
T;
// allow JSON files to be imported directly without lint errors
// see: https://github.com/palantir/tslint/issues/1264#issuecomment-228433367
// and: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#arbitrary-expressions-are-forbidden-in-export-assignments-in-ambient-contexts