Move client-side application service to packages (#139502)

* deletes unused utils file

* just some fix while I see it

* creating empty packages

* moving all the things

* package build success

* start fixing usages

* fix the scoped history type issue

* export internal utils

* add default for mock

* fix test import

* fix external import

* start fixing external usages

* more usages

* more usages

* more usages

* More usages

* [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix'

* fix integration test imports

* fix more test types

* remove public/utils from the core bundle

* trying to import from the package

* updating README's

* remove unused test types from mock package

* cleanup test types

* use import type

* add author to packages

* more import type

* remove dead path from some config

* remove src/core/utils/index.ts (and pray)

* update tsdoc

* fix new file usage

* fix paths

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Pierre Gayvallet 2022-08-30 19:08:44 +02:00 committed by GitHub
parent 6c4c78aaea
commit 383d8fab58
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
128 changed files with 2317 additions and 1081 deletions

View file

@ -33,7 +33,7 @@ import { catchError, map, tap } from 'rxjs/operators';
import { lastValueFrom, of } from 'rxjs';
import { CoreStart } from '@kbn/core/public';
import { mountReactNode } from '@kbn/core/public/utils';
import { mountReactNode } from '@kbn/core-mount-utils-browser-internal';
import type { TimeRange } from '@kbn/es-query';
import { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public';

View file

@ -153,12 +153,18 @@
"@kbn/core-analytics-server": "link:bazel-bin/packages/core/analytics/core-analytics-server",
"@kbn/core-analytics-server-internal": "link:bazel-bin/packages/core/analytics/core-analytics-server-internal",
"@kbn/core-analytics-server-mocks": "link:bazel-bin/packages/core/analytics/core-analytics-server-mocks",
"@kbn/core-application-browser": "link:bazel-bin/packages/core/application/core-application-browser",
"@kbn/core-application-browser-internal": "link:bazel-bin/packages/core/application/core-application-browser-internal",
"@kbn/core-application-browser-mocks": "link:bazel-bin/packages/core/application/core-application-browser-mocks",
"@kbn/core-application-common": "link:bazel-bin/packages/core/application/core-application-common",
"@kbn/core-base-browser-internal": "link:bazel-bin/packages/core/base/core-base-browser-internal",
"@kbn/core-base-browser-mocks": "link:bazel-bin/packages/core/base/core-base-browser-mocks",
"@kbn/core-base-common": "link:bazel-bin/packages/core/base/core-base-common",
"@kbn/core-base-common-internal": "link:bazel-bin/packages/core/base/core-base-common-internal",
"@kbn/core-base-server-internal": "link:bazel-bin/packages/core/base/core-base-server-internal",
"@kbn/core-base-server-mocks": "link:bazel-bin/packages/core/base/core-base-server-mocks",
"@kbn/core-capabilities-browser-internal": "link:bazel-bin/packages/core/capabilities/core-capabilities-browser-internal",
"@kbn/core-capabilities-browser-mocks": "link:bazel-bin/packages/core/capabilities/core-capabilities-browser-mocks",
"@kbn/core-capabilities-common": "link:bazel-bin/packages/core/capabilities/core-capabilities-common",
"@kbn/core-capabilities-server": "link:bazel-bin/packages/core/capabilities/core-capabilities-server",
"@kbn/core-capabilities-server-internal": "link:bazel-bin/packages/core/capabilities/core-capabilities-server-internal",
@ -819,6 +825,10 @@
"@types/kbn__core-analytics-server": "link:bazel-bin/packages/core/analytics/core-analytics-server/npm_module_types",
"@types/kbn__core-analytics-server-internal": "link:bazel-bin/packages/core/analytics/core-analytics-server-internal/npm_module_types",
"@types/kbn__core-analytics-server-mocks": "link:bazel-bin/packages/core/analytics/core-analytics-server-mocks/npm_module_types",
"@types/kbn__core-application-browser": "link:bazel-bin/packages/core/application/core-application-browser/npm_module_types",
"@types/kbn__core-application-browser-internal": "link:bazel-bin/packages/core/application/core-application-browser-internal/npm_module_types",
"@types/kbn__core-application-browser-mocks": "link:bazel-bin/packages/core/application/core-application-browser-mocks/npm_module_types",
"@types/kbn__core-application-common": "link:bazel-bin/packages/core/application/core-application-common/npm_module_types",
"@types/kbn__core-base-browser": "link:bazel-bin/packages/core/base/core-base-browser/npm_module_types",
"@types/kbn__core-base-browser-internal": "link:bazel-bin/packages/core/base/core-base-browser-internal/npm_module_types",
"@types/kbn__core-base-browser-mocks": "link:bazel-bin/packages/core/base/core-base-browser-mocks/npm_module_types",
@ -827,6 +837,8 @@
"@types/kbn__core-base-server": "link:bazel-bin/packages/core/base/core-base-server/npm_module_types",
"@types/kbn__core-base-server-internal": "link:bazel-bin/packages/core/base/core-base-server-internal/npm_module_types",
"@types/kbn__core-base-server-mocks": "link:bazel-bin/packages/core/base/core-base-server-mocks/npm_module_types",
"@types/kbn__core-capabilities-browser-internal": "link:bazel-bin/packages/core/capabilities/core-capabilities-browser-internal/npm_module_types",
"@types/kbn__core-capabilities-browser-mocks": "link:bazel-bin/packages/core/capabilities/core-capabilities-browser-mocks/npm_module_types",
"@types/kbn__core-capabilities-common": "link:bazel-bin/packages/core/capabilities/core-capabilities-common/npm_module_types",
"@types/kbn__core-capabilities-server": "link:bazel-bin/packages/core/capabilities/core-capabilities-server/npm_module_types",
"@types/kbn__core-capabilities-server-internal": "link:bazel-bin/packages/core/capabilities/core-capabilities-server-internal/npm_module_types",

View file

@ -20,12 +20,18 @@ filegroup(
"//packages/core/analytics/core-analytics-server:build",
"//packages/core/analytics/core-analytics-server-internal:build",
"//packages/core/analytics/core-analytics-server-mocks:build",
"//packages/core/application/core-application-browser:build",
"//packages/core/application/core-application-browser-internal:build",
"//packages/core/application/core-application-browser-mocks:build",
"//packages/core/application/core-application-common:build",
"//packages/core/base/core-base-browser-internal:build",
"//packages/core/base/core-base-browser-mocks:build",
"//packages/core/base/core-base-common:build",
"//packages/core/base/core-base-common-internal:build",
"//packages/core/base/core-base-server-internal:build",
"//packages/core/base/core-base-server-mocks:build",
"//packages/core/capabilities/core-capabilities-browser-internal:build",
"//packages/core/capabilities/core-capabilities-browser-mocks:build",
"//packages/core/capabilities/core-capabilities-common:build",
"//packages/core/capabilities/core-capabilities-server:build",
"//packages/core/capabilities/core-capabilities-server-internal:build",
@ -307,12 +313,18 @@ filegroup(
"//packages/core/analytics/core-analytics-server:build_types",
"//packages/core/analytics/core-analytics-server-internal:build_types",
"//packages/core/analytics/core-analytics-server-mocks:build_types",
"//packages/core/application/core-application-browser:build_types",
"//packages/core/application/core-application-browser-internal:build_types",
"//packages/core/application/core-application-browser-mocks:build_types",
"//packages/core/application/core-application-common:build_types",
"//packages/core/base/core-base-browser-internal:build_types",
"//packages/core/base/core-base-browser-mocks:build_types",
"//packages/core/base/core-base-common:build_types",
"//packages/core/base/core-base-common-internal:build_types",
"//packages/core/base/core-base-server-internal:build_types",
"//packages/core/base/core-base-server-mocks:build_types",
"//packages/core/capabilities/core-capabilities-browser-internal:build_types",
"//packages/core/capabilities/core-capabilities-browser-mocks:build_types",
"//packages/core/capabilities/core-capabilities-common:build_types",
"//packages/core/capabilities/core-capabilities-server:build_types",
"//packages/core/capabilities/core-capabilities-server-internal:build_types",

View file

@ -0,0 +1,150 @@
load("@npm//@bazel/typescript:index.bzl", "ts_config")
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project")
PKG_DIRNAME = "core-application-browser-internal"
PKG_REQUIRE_NAME = "@kbn/core-application-browser-internal"
SOURCE_FILES = glob(
[
"**/*.ts",
"**/*.tsx",
"**/*.scss",
],
exclude = [
"**/*.config.js",
"**/*.mock.*",
"**/*.test.*",
"**/*.stories.*",
"**/__snapshots__",
"**/integration_tests",
"**/mocks",
"**/scripts",
"**/storybook",
"**/test_fixtures",
"**/test_helpers",
],
)
SRCS = SOURCE_FILES
filegroup(
name = "srcs",
srcs = SRCS,
)
NPM_MODULE_EXTRA_FILES = [
"package.json",
]
RUNTIME_DEPS = [
"@npm//react",
"@npm//react-router-dom",
"@npm//react-use",
"@npm//enzyme",
"@npm//rxjs",
"@npm//history",
"@npm//@elastic/eui",
"//packages/kbn-std",
"//packages/kbn-i18n",
"//packages/kbn-i18n-react",
"//packages/core/application/core-application-common",
]
TYPES_DEPS = [
"@npm//@types/node",
"@npm//@types/jest",
"@npm//@types/enzyme",
"@npm//@types/react",
"@npm//@types/react-router-dom",
"@npm//react-use",
"@npm//@types/history",
"@npm//rxjs",
"@npm//@elastic/eui",
"//packages/kbn-utility-types:npm_module_types",
"//packages/kbn-std:npm_module_types",
"//packages/kbn-i18n:npm_module_types",
"//packages/kbn-i18n-react:npm_module_types",
"//packages/core/base/core-base-common:npm_module_types",
"//packages/core/http/core-http-browser:npm_module_types",
"//packages/core/capabilities/core-capabilities-common:npm_module_types",
"//packages/core/theme/core-theme-browser:npm_module_types",
"//packages/core/overlays/core-overlays-browser:npm_module_types",
"//packages/core/mount-utils/core-mount-utils-browser:npm_module_types",
"//packages/core/capabilities/core-capabilities-browser-internal:npm_module_types",
"//packages/core/application/core-application-common:npm_module_types",
"//packages/core/application/core-application-browser:npm_module_types",
]
jsts_transpiler(
name = "target_node",
srcs = SRCS,
build_pkg_name = package_name(),
)
jsts_transpiler(
name = "target_web",
srcs = SRCS,
build_pkg_name = package_name(),
web = True,
additional_args = [
"--copy-files",
"--quiet"
],
)
ts_config(
name = "tsconfig",
src = "tsconfig.json",
deps = [
"//:tsconfig.base.json",
"//:tsconfig.bazel.json",
],
)
ts_project(
name = "tsc_types",
args = ['--pretty'],
srcs = SRCS,
deps = TYPES_DEPS,
declaration = True,
declaration_map = True,
emit_declaration_only = True,
out_dir = "target_types",
root_dir = ".",
tsconfig = ":tsconfig",
)
js_library(
name = PKG_DIRNAME,
srcs = NPM_MODULE_EXTRA_FILES,
deps = RUNTIME_DEPS + [":target_node", ":target_web"],
package_name = PKG_REQUIRE_NAME,
visibility = ["//visibility:public"],
)
pkg_npm(
name = "npm_module",
deps = [":" + PKG_DIRNAME],
)
filegroup(
name = "build",
srcs = [":npm_module"],
visibility = ["//visibility:public"],
)
pkg_npm_types(
name = "npm_module_types",
srcs = SRCS,
deps = [":tsc_types"],
package_name = PKG_REQUIRE_NAME,
tsconfig = ":tsconfig",
visibility = ["//visibility:public"],
)
filegroup(
name = "build_types",
srcs = [":npm_module_types"],
visibility = ["//visibility:public"],
)

View file

@ -0,0 +1,3 @@
# @kbn/core-application-browser-internal
Contains the internal implementation and types of Core's browser-side `application` service.

View file

@ -0,0 +1,23 @@
/*
* 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.
*/
export { ApplicationService } from './src/application_service';
export { CoreScopedHistory } from './src/scoped_history';
export type {
InternalApplicationSetup,
InternalApplicationStart,
Mounter,
ParsedAppUrl,
} from './src/types';
export {
appendAppPath,
getAppInfo,
parseAppUrl,
relativeToAbsolute,
removeSlashes,
} from './src/utils';

View file

@ -13,12 +13,12 @@ import { createMemoryHistory, MemoryHistory } from 'history';
import { httpServiceMock } from '@kbn/core-http-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { createRenderer } from './utils';
import { ApplicationService } from '../application_service';
import type { MockLifecycle } from '../test_types';
import type { AppMountParameters, AppUpdater } from '@kbn/core-application-browser';
import { overlayServiceMock } from '@kbn/core-overlays-browser-mocks';
import type { AppMountParameters, AppUpdater } from '../types';
import type { MountPoint } from '@kbn/core-mount-utils-browser';
import type { MockLifecycle } from '../src/test_helpers/test_types';
import { ApplicationService } from '../src/application_service';
import { createRenderer } from './utils';
const flushPromises = () => new Promise((resolve) => setImmediate(resolve));

View file

@ -11,10 +11,10 @@ import { BehaviorSubject } from 'rxjs';
import { createMemoryHistory, History, createHashHistory } from 'history';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { AppRouter, AppNotFound } from '../ui';
import { MockedMounterMap, MockedMounterTuple } from '../test_types';
import { AppStatus } from '@kbn/core-application-browser';
import { AppRouter, AppNotFound } from '../src/ui';
import { MockedMounterMap, MockedMounterTuple } from '../src/test_helpers/test_types';
import { createRenderer, createAppMounter, getUnmounter } from './utils';
import { AppStatus } from '../types';
describe('AppRouter', () => {
let mounters: MockedMounterMap;

View file

@ -10,9 +10,9 @@ import React, { ReactElement } from 'react';
import { act } from 'react-dom/test-utils';
import { mount } from 'enzyme';
import { I18nProvider } from '@kbn/i18n-react';
import type { AppMountParameters } from '@kbn/core-application-browser';
import { AppMountParameters } from '../types';
import { MockedMounterTuple, Mountable } from '../test_types';
import { MockedMounterTuple, Mountable } from '../src/test_helpers/test_types';
type Dom = ReturnType<typeof mount> | null;
type Renderer = () => Dom | Promise<Dom>;

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.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../../..',
roots: ['<rootDir>/packages/core/application/core-application-browser-internal'],
};

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.
*/
module.exports = {
preset: '@kbn/test/jest_integration',
rootDir: '../../../..',
roots: ['<rootDir>/packages/core/application/core-application-browser-internal'],
};

View file

@ -0,0 +1,9 @@
{
"name": "@kbn/core-application-browser-internal",
"private": true,
"version": "1.0.0",
"main": "./target_node/index.js",
"browser": "./target_web/index.js",
"author": "Kibana Core",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -6,8 +6,8 @@
* Side Public License, v 1.
*/
import { AppLeaveActionType } from '@kbn/core-application-browser';
import { isConfirmAction, getLeaveAction } from './application_leave';
import { AppLeaveActionType } from './types';
describe('isConfirmAction', () => {
it('returns true if action is confirm', () => {

View file

@ -7,12 +7,12 @@
*/
import type { ButtonColor } from '@elastic/eui';
import {
AppLeaveActionFactory,
AppLeaveActionType,
AppLeaveAction,
AppLeaveConfirmAction,
AppLeaveHandler,
} from './types';
type AppLeaveActionFactory,
type AppLeaveAction,
type AppLeaveConfirmAction,
type AppLeaveHandler,
} from '@kbn/core-application-browser';
const appLeaveActionFactory: AppLeaveActionFactory = {
confirm(

View file

@ -6,13 +6,13 @@
* Side Public License, v 1.
*/
import { capabilitiesServiceMock } from './capabilities/capabilities_service.mock';
import { capabilitiesServiceMock } from '@kbn/core-capabilities-browser-mocks';
export const MockCapabilitiesService = capabilitiesServiceMock.create();
export const CapabilitiesServiceConstructor = jest
.fn()
.mockImplementation(() => MockCapabilitiesService);
jest.doMock('./capabilities', () => ({
jest.doMock('@kbn/core-capabilities-browser-internal', () => ({
CapabilitiesService: CapabilitiesServiceConstructor,
}));

View file

@ -20,9 +20,16 @@ import { mount, shallow } from 'enzyme';
import { httpServiceMock } from '@kbn/core-http-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { overlayServiceMock } from '@kbn/core-overlays-browser-mocks';
import { MockLifecycle } from './test_types';
import { MockLifecycle } from './test_helpers/test_types';
import { ApplicationService } from './application_service';
import { App, AppDeepLink, AppNavLinkStatus, AppStatus, AppUpdater, PublicAppInfo } from './types';
import {
App,
AppDeepLink,
AppNavLinkStatus,
AppStatus,
AppUpdater,
PublicAppInfo,
} from '@kbn/core-application-browser';
import { act } from 'react-dom/test-utils';
const createApp = (props: Partial<App>): App => {

View file

@ -17,8 +17,6 @@ import type { HttpSetup, HttpStart } from '@kbn/core-http-browser';
import type { Capabilities } from '@kbn/core-capabilities-common';
import type { MountPoint } from '@kbn/core-mount-utils-browser';
import type { OverlayStart } from '@kbn/core-overlays-browser';
import { AppRouter } from './ui';
import { CapabilitiesService } from './capabilities';
import type {
App,
AppDeepLink,
@ -26,13 +24,14 @@ import type {
AppMount,
AppUpdatableFields,
AppUpdater,
InternalApplicationSetup,
InternalApplicationStart,
Mounter,
NavigateToAppOptions,
NavigateToUrlOptions,
} from './types';
import { AppStatus, AppNavLinkStatus } from './types';
} from '@kbn/core-application-browser';
import { CapabilitiesService } from '@kbn/core-capabilities-browser-internal';
import { AppStatus, AppNavLinkStatus } from '@kbn/core-application-browser';
import { AppRouter } from './ui';
import type { InternalApplicationSetup, InternalApplicationStart, Mounter } from './types';
import { getLeaveAction, isConfirmAction } from './application_leave';
import { getUserConfirmationHandler } from './navigation_confirm';
import { appendAppPath, parseAppUrl, relativeToAbsolute, getAppInfo } from './utils';

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { OverlayStart } from '..';
import type { OverlayStart } from '@kbn/core-overlays-browser';
export type ConfirmHandlerCallback = (result: boolean) => void;
export type ConfirmHandler = (message: string, callback: ConfirmHandlerCallback) => void;

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { ScopedHistory } from './scoped_history';
import { CoreScopedHistory as ScopedHistory } from './scoped_history';
import { createMemoryHistory, History } from 'history';
import type { ConfirmHandler } from './navigation_confirm';

View file

@ -17,22 +17,15 @@ import {
Href,
Action,
} from 'history';
import type { ScopedHistory } from '@kbn/core-application-browser';
/**
* A wrapper around a `History` instance that is scoped to a particular base path of the history stack. Behaves
* similarly to the `basename` option except that this wrapper hides any history stack entries from outside the scope
* of this base path.
* Core's internal implementation of {@link ScopedHistory}
*
* This wrapper also allows Core and Plugins to share a single underlying global `History` instance without exposing
* the history of other applications.
*
* The {@link ScopedHistory.createSubHistory | createSubHistory} method is particularly useful for applications that
* contain any number of "sub-apps" which should not have access to the main application's history or basePath.
*
* @public
* @internal Only exposed publicly for testing purpose.
*/
export class ScopedHistory<HistoryLocationState = unknown>
implements History<HistoryLocationState>
export class CoreScopedHistory<HistoryLocationState = unknown>
implements ScopedHistory<HistoryLocationState>
{
/**
* Tracks whether or not the user has left this history's scope. All methods throw errors if called after scope has
@ -79,7 +72,7 @@ export class ScopedHistory<HistoryLocationState = unknown>
* @param basePath the URL path scope for the sub history
*/
public createSubHistory = (basePath: string) => {
return new ScopedHistory<HistoryLocationState>(this, basePath);
return new CoreScopedHistory<HistoryLocationState>(this, basePath);
};
/**

View file

@ -6,12 +6,10 @@
* Side Public License, v 1.
*/
import type { PublicMethodsOf } from '@kbn/utility-types';
import { AppUnmount, Mounter } from './types';
import { ApplicationService } from './application_service';
import { AppUnmount } from '@kbn/core-application-browser';
import { Mounter } from '../types';
import { ApplicationService } from '../application_service';
/** @internal */
export type ApplicationServiceContract = PublicMethodsOf<ApplicationService>;
/** @internal */
export type MockedUnmount = jest.Mocked<AppUnmount>;

View file

@ -0,0 +1,66 @@
/*
* 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.
*/
import type { Observable } from 'rxjs';
import type { History } from 'history';
import type { MountPoint } from '@kbn/core-mount-utils-browser';
import type { PluginOpaqueId } from '@kbn/core-base-common';
import type {
App,
AppMount,
ApplicationSetup,
ApplicationStart,
} from '@kbn/core-application-browser';
/** @internal */
export interface Mounter {
appRoute: string;
appBasePath: string;
mount: AppMount;
exactRoute: boolean;
unmountBeforeMounting?: boolean;
}
/** @internal */
export interface ParsedAppUrl {
app: string;
path?: string;
}
/** @internal */
export interface InternalApplicationSetup extends Pick<ApplicationSetup, 'registerAppUpdater'> {
/**
* Register an mountable application to the system.
* @param plugin - opaque ID of the plugin that registers this application
* @param app
*/
register<HistoryLocationState = unknown>(
plugin: PluginOpaqueId,
app: App<HistoryLocationState>
): void;
}
/** @internal */
export interface InternalApplicationStart extends ApplicationStart {
// Internal APIs
getComponent(): JSX.Element | null;
/**
* The potential action menu set by the currently mounted app.
* Consumed by the chrome header.
*
* @internal
*/
currentActionMenu$: Observable<MountPoint | undefined>;
/**
* The global history instance, exposed only to Core.
* @internal
*/
history: History<unknown>;
}

View file

@ -11,10 +11,11 @@ import { act } from 'react-dom/test-utils';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { type AppMountParameters, AppStatus } from '@kbn/core-application-browser';
import { AppContainer } from './app_container';
import { Mounter, AppMountParameters, AppStatus } from '../types';
import type { Mounter } from '../types';
import { createMemoryHistory } from 'history';
import { ScopedHistory } from '../scoped_history';
import { CoreScopedHistory as ScopedHistory } from '../scoped_history';
describe('AppContainer', () => {
const appId = 'someApp';

View file

@ -5,6 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import './app_container.scss';
import { Observable } from 'rxjs';
@ -14,10 +15,15 @@ import { EuiLoadingElastic } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import type { CoreTheme } from '@kbn/core-theme-browser';
import type { MountPoint } from '@kbn/core-mount-utils-browser';
import { AppLeaveHandler, AppStatus, AppUnmount, Mounter } from '../types';
import { APP_WRAPPER_CLASS } from '@kbn/core-application-common';
import {
AppStatus,
type AppLeaveHandler,
type AppUnmount,
type ScopedHistory,
} from '@kbn/core-application-browser';
import type { Mounter } from '../types';
import { AppNotFound } from './app_not_found_screen';
import { ScopedHistory } from '../scoped_history';
import { APP_WRAPPER_CLASS } from '../../../utils';
interface Props {
/** Path application is mounted on without the global basePath */

View file

@ -14,9 +14,10 @@ import useObservable from 'react-use/lib/useObservable';
import type { CoreTheme } from '@kbn/core-theme-browser';
import type { MountPoint } from '@kbn/core-mount-utils-browser';
import { AppLeaveHandler, AppStatus, Mounter } from '../types';
import { type AppLeaveHandler, AppStatus } from '@kbn/core-application-browser';
import type { Mounter } from '../types';
import { AppContainer } from './app_container';
import { ScopedHistory } from '../scoped_history';
import { CoreScopedHistory } from '../scoped_history';
interface Props {
mounters: Map<string, Mounter>;
@ -43,7 +44,7 @@ export const AppRouter: FunctionComponent<Props> = ({
}) => {
const appStatuses = useObservable(appStatuses$, new Map());
const createScopedHistory = useMemo(
() => (appPath: string) => new ScopedHistory(history, appPath),
() => (appPath: string) => new CoreScopedHistory(history, appPath),
[history]
);

View file

@ -7,7 +7,7 @@
*/
import { of } from 'rxjs';
import { App, AppDeepLink, AppNavLinkStatus, AppStatus } from '../types';
import { App, AppDeepLink, AppNavLinkStatus, AppStatus } from '@kbn/core-application-browser';
import { getAppInfo } from './get_app_info';
describe('getAppInfo', () => {

View file

@ -7,13 +7,13 @@
*/
import {
App,
AppNavLinkStatus,
AppStatus,
AppDeepLink,
PublicAppInfo,
PublicAppDeepLinkInfo,
} from '../types';
type App,
type AppDeepLink,
type PublicAppInfo,
type PublicAppDeepLinkInfo,
} from '@kbn/core-application-browser';
export function getAppInfo(app: App): PublicAppInfo {
const { updater$, mount, navLinkStatus = AppNavLinkStatus.default, ...infos } = app;

View file

@ -7,7 +7,7 @@
*/
import { BasePath } from '@kbn/core-http-browser-internal';
import type { App } from '../types';
import type { App } from '@kbn/core-application-browser';
import { parseAppUrl } from './parse_app_url';
describe('parseAppUrl', () => {

View file

@ -9,7 +9,8 @@
import { getUrlOrigin } from '@kbn/std';
import { resolve } from 'url';
import type { IBasePath } from '@kbn/core-http-browser';
import type { App, ParsedAppUrl } from '../types';
import type { App } from '@kbn/core-application-browser';
import type { ParsedAppUrl } from '../types';
/**
* Parse given URL and return the associated app id and path if any app matches, or undefined if none do.

View file

@ -0,0 +1,20 @@
{
"extends": "../../../../tsconfig.bazel.json",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"emitDeclarationOnly": true,
"outDir": "target_types",
"rootDir": ".",
"stripInternal": false,
"types": [
"jest",
"node",
"react"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
]
}

View file

@ -0,0 +1,123 @@
load("@npm//@bazel/typescript:index.bzl", "ts_config")
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project")
PKG_DIRNAME = "core-application-browser-mocks"
PKG_REQUIRE_NAME = "@kbn/core-application-browser-mocks"
SOURCE_FILES = glob(
[
"**/*.ts",
"**/*.tsx",
],
exclude = [
"**/*.config.js",
"**/*.test.*",
"**/*.stories.*",
"**/__snapshots__",
"**/integration_tests",
"**/mocks",
"**/scripts",
"**/storybook",
"**/test_fixtures",
"**/test_helpers",
],
)
SRCS = SOURCE_FILES
filegroup(
name = "srcs",
srcs = SRCS,
)
NPM_MODULE_EXTRA_FILES = [
"package.json",
]
RUNTIME_DEPS = [
"//packages/kbn-std",
"//packages/core/capabilities/core-capabilities-browser-mocks"
]
TYPES_DEPS = [
"@npm//@types/node",
"@npm//@types/jest",
"@npm//@types/history",
"@npm//rxjs",
"//packages/kbn-std:npm_module_types",
"//packages/core/mount-utils/core-mount-utils-browser:npm_module_types",
"//packages/core/application/core-application-browser:npm_module_types",
"//packages/core/application/core-application-browser-internal:npm_module_types",
"//packages/core/capabilities/core-capabilities-browser-mocks:npm_module_types",
"//packages/core/theme/core-theme-browser-mocks:npm_module_types",
]
jsts_transpiler(
name = "target_node",
srcs = SRCS,
build_pkg_name = package_name(),
)
jsts_transpiler(
name = "target_web",
srcs = SRCS,
build_pkg_name = package_name(),
web = True,
)
ts_config(
name = "tsconfig",
src = "tsconfig.json",
deps = [
"//:tsconfig.base.json",
"//:tsconfig.bazel.json",
],
)
ts_project(
name = "tsc_types",
args = ['--pretty'],
srcs = SRCS,
deps = TYPES_DEPS,
declaration = True,
declaration_map = True,
emit_declaration_only = True,
out_dir = "target_types",
root_dir = ".",
tsconfig = ":tsconfig",
)
js_library(
name = PKG_DIRNAME,
srcs = NPM_MODULE_EXTRA_FILES,
deps = RUNTIME_DEPS + [":target_node", ":target_web"],
package_name = PKG_REQUIRE_NAME,
visibility = ["//visibility:public"],
)
pkg_npm(
name = "npm_module",
deps = [":" + PKG_DIRNAME],
)
filegroup(
name = "build",
srcs = [":npm_module"],
visibility = ["//visibility:public"],
)
pkg_npm_types(
name = "npm_module_types",
srcs = SRCS,
deps = [":tsc_types"],
package_name = PKG_REQUIRE_NAME,
tsconfig = ":tsconfig",
visibility = ["//visibility:public"],
)
filegroup(
name = "build_types",
srcs = [":npm_module_types"],
visibility = ["//visibility:public"],
)

View file

@ -0,0 +1,5 @@
# @kbn/core-application-browser-mocks
Contains the mocks for Core's browser-side `application` service.
- `applicationServiceMock`
- `scopedHistoryMock`

View file

@ -0,0 +1,11 @@
/*
* 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.
*/
export { applicationServiceMock } from './src/application_service.mock';
export type { ScopedHistoryMock } from './src/scoped_history.mock';
export { scopedHistoryMock } from './src/scoped_history.mock';

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.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../../..',
roots: ['<rootDir>/packages/core/application/core-application-browser-mocks'],
};

View file

@ -0,0 +1,9 @@
{
"name": "@kbn/core-application-browser-mocks",
"private": true,
"version": "1.0.0",
"main": "./target_node/index.js",
"browser": "./target_web/index.js",
"author": "Kibana Core",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -9,19 +9,24 @@
import { History } from 'history';
import { BehaviorSubject, Subject } from 'rxjs';
import type { PublicMethodsOf } from '@kbn/utility-types';
import type { MountPoint } from '@kbn/core-mount-utils-browser';
import { capabilitiesServiceMock } from './capabilities/capabilities_service.mock';
import { capabilitiesServiceMock } from '@kbn/core-capabilities-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { scopedHistoryMock } from './scoped_history.mock';
import {
ApplicationSetup,
InternalApplicationStart,
ApplicationStart,
InternalApplicationSetup,
PublicAppInfo,
AppMountParameters,
} from './types';
import { ApplicationServiceContract } from './test_types';
} from '@kbn/core-application-browser';
import type {
ApplicationService,
InternalApplicationStart,
InternalApplicationSetup,
} from '@kbn/core-application-browser-internal';
type ApplicationServiceContract = PublicMethodsOf<ApplicationService>;
const createSetupContractMock = (): jest.Mocked<ApplicationSetup> => ({
register: jest.fn(),
@ -84,7 +89,7 @@ const createInternalStartContractMock = (): jest.Mocked<InternalApplicationStart
};
};
const createAppMountParametersMock = (parts: Partial<AppMountParameters>) => {
const createAppMountParametersMock = (parts: Partial<AppMountParameters> = {}) => {
const mock: AppMountParameters = {
element: document.createElement('div'),
history: scopedHistoryMock.create(),

View file

@ -6,8 +6,8 @@
* Side Public License, v 1.
*/
import { Location } from 'history';
import { ScopedHistory } from './scoped_history';
import type { Location } from 'history';
import type { ScopedHistory } from '@kbn/core-application-browser';
export type ScopedHistoryMock = jest.Mocked<ScopedHistory>;
@ -18,7 +18,7 @@ const createMock = ({
key,
state,
}: Partial<Location> = {}) => {
const mock: jest.Mocked<Pick<ScopedHistory, keyof ScopedHistory>> = {
const mock: ScopedHistoryMock = {
block: jest.fn(),
createHref: jest.fn(),
createSubHistory: jest.fn(),
@ -39,9 +39,7 @@ const createMock = ({
},
};
// jest.Mocked still expects private methods and properties to be present, even
// if not part of the public contract.
return mock as ScopedHistoryMock;
return mock;
};
export const scopedHistoryMock = {

View file

@ -0,0 +1,20 @@
{
"extends": "../../../../tsconfig.bazel.json",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"emitDeclarationOnly": true,
"outDir": "target_types",
"rootDir": ".",
"stripInternal": false,
"types": [
"jest",
"node",
"react"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
]
}

View file

@ -0,0 +1,122 @@
load("@npm//@bazel/typescript:index.bzl", "ts_config")
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project")
PKG_DIRNAME = "core-application-browser"
PKG_REQUIRE_NAME = "@kbn/core-application-browser"
SOURCE_FILES = glob(
[
"**/*.ts",
"**/*.tsx",
],
exclude = [
"**/*.config.js",
"**/*.mock.*",
"**/*.test.*",
"**/*.stories.*",
"**/__snapshots__",
"**/integration_tests",
"**/mocks",
"**/scripts",
"**/storybook",
"**/test_fixtures",
"**/test_helpers",
],
)
SRCS = SOURCE_FILES
filegroup(
name = "srcs",
srcs = SRCS,
)
NPM_MODULE_EXTRA_FILES = [
"package.json",
]
RUNTIME_DEPS = [
]
TYPES_DEPS = [
"@npm//@types/node",
"@npm//@types/jest",
"@npm//@types/history",
"@npm//@elastic/eui",
"@npm//rxjs",
"//packages/kbn-utility-types:npm_module_types",
"//packages/core/theme/core-theme-browser:npm_module_types",
"//packages/core/mount-utils/core-mount-utils-browser:npm_module_types",
"//packages/core/capabilities/core-capabilities-common:npm_module_types",
"//packages/core/application/core-application-common:npm_module_types",
]
jsts_transpiler(
name = "target_node",
srcs = SRCS,
build_pkg_name = package_name(),
)
jsts_transpiler(
name = "target_web",
srcs = SRCS,
build_pkg_name = package_name(),
web = True,
)
ts_config(
name = "tsconfig",
src = "tsconfig.json",
deps = [
"//:tsconfig.base.json",
"//:tsconfig.bazel.json",
],
)
ts_project(
name = "tsc_types",
args = ['--pretty'],
srcs = SRCS,
deps = TYPES_DEPS,
declaration = True,
declaration_map = True,
emit_declaration_only = True,
out_dir = "target_types",
root_dir = ".",
tsconfig = ":tsconfig",
)
js_library(
name = PKG_DIRNAME,
srcs = NPM_MODULE_EXTRA_FILES,
deps = RUNTIME_DEPS + [":target_node", ":target_web"],
package_name = PKG_REQUIRE_NAME,
visibility = ["//visibility:public"],
)
pkg_npm(
name = "npm_module",
deps = [":" + PKG_DIRNAME],
)
filegroup(
name = "build",
srcs = [":npm_module"],
visibility = ["//visibility:public"],
)
pkg_npm_types(
name = "npm_module_types",
srcs = SRCS,
deps = [":tsc_types"],
package_name = PKG_REQUIRE_NAME,
tsconfig = ":tsconfig",
visibility = ["//visibility:public"],
)
filegroup(
name = "build_types",
srcs = [":npm_module_types"],
visibility = ["//visibility:public"],
)

View file

@ -0,0 +1,3 @@
# @kbn/core-application-browser
Contains the public type for Core's browser-side `application` service.

View file

@ -6,30 +6,29 @@
* Side Public License, v 1.
*/
export { ApplicationService } from './application_service';
export { ScopedHistory } from './scoped_history';
export { AppNavLinkStatus, AppStatus } from './types';
export { AppLeaveActionType } from './src/app_leave';
export type {
AppLeaveAction,
AppLeaveActionFactory,
AppLeaveConfirmAction,
AppLeaveDefaultAction,
AppLeaveHandler,
} from './src/app_leave';
export type { AppMount, AppMountParameters, AppUnmount } from './src/app_mount';
export type {
App,
AppMount,
AppUnmount,
AppMountParameters,
AppUpdatableFields,
AppNavOptions,
AppUpdater,
AppDeepLink,
PublicAppInfo,
AppNavOptions,
PublicAppDeepLinkInfo,
AppUpdater,
AppUpdatableFields,
} from './src/application';
export { AppNavLinkStatus, AppStatus } from './src/application';
export type {
ApplicationSetup,
ApplicationStart,
AppLeaveHandler,
AppLeaveActionType,
AppLeaveAction,
AppLeaveDefaultAction,
AppLeaveConfirmAction,
NavigateToAppOptions,
NavigateToUrlOptions,
PublicAppInfo,
PublicAppDeepLinkInfo,
// Internal types
InternalApplicationSetup,
InternalApplicationStart,
} from './types';
} from './src/contracts';
export type { ScopedHistory } from './src/scoped_history';

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.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../../..',
roots: ['<rootDir>/packages/core/application/core-application-browser'],
};

View file

@ -0,0 +1,9 @@
{
"name": "@kbn/core-application-browser",
"private": true,
"version": "1.0.0",
"main": "./target_node/index.js",
"browser": "./target_web/index.js",
"author": "Kibana Core",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,106 @@
/*
* 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.
*/
import type { ButtonColor } from '@elastic/eui';
/**
* A handler that will be executed before leaving the application, either when
* going to another application or when closing the browser tab or manually changing
* the url.
* Should return `confirm` to prompt a message to the user before leaving the page, or `default`
* to keep the default behavior (doing nothing).
*
* See {@link AppMountParameters} for detailed usage examples.
*
* @public
* @deprecated {@link AppMountParameters.onAppLeave} has been deprecated in favor of {@link ScopedHistory.block}
* @removeBy 8.8.0
*/
export type AppLeaveHandler = (
factory: AppLeaveActionFactory,
nextAppId?: string
) => AppLeaveAction;
/**
* Possible type of actions on application leave.
*
* @public
*/
export enum AppLeaveActionType {
confirm = 'confirm',
default = 'default',
}
/**
* Action to return from a {@link AppLeaveHandler} to execute the default
* behaviour when leaving the application.
*
* See {@link AppLeaveActionFactory}
*
* @public
*/
export interface AppLeaveDefaultAction {
type: AppLeaveActionType.default;
}
/**
* Action to return from a {@link AppLeaveHandler} to show a confirmation
* message when trying to leave an application.
*
* See {@link AppLeaveActionFactory}
*
* @public
*/
export interface AppLeaveConfirmAction {
type: AppLeaveActionType.confirm;
text: string;
title?: string;
confirmButtonText?: string;
buttonColor?: ButtonColor;
callback?: () => void;
}
/**
* Possible actions to return from a {@link AppLeaveHandler}
*
* See {@link AppLeaveConfirmAction} and {@link AppLeaveDefaultAction}
*
* @public
* */
export type AppLeaveAction = AppLeaveDefaultAction | AppLeaveConfirmAction;
/**
* Factory provided when invoking a {@link AppLeaveHandler} to retrieve the {@link AppLeaveAction} to execute.
*/
export interface AppLeaveActionFactory {
/**
* Returns a confirm action, resulting on prompting a message to the user before leaving the
* application, allowing him to choose if he wants to stay on the app or confirm that he
* wants to leave.
*
* @param text The text to display in the confirmation message
* @param title (optional) title to display in the confirmation message
* @param callback (optional) to know that the user want to stay on the page
* @param confirmButtonText (optional) text for the confirmation button
* @param buttonColor (optional) color for the confirmation button
* so we can show to the user the right UX for him to saved his/her/their changes
*/
confirm(
text: string,
title?: string,
callback?: () => void,
confirmButtonText?: string,
buttonColor?: ButtonColor
): AppLeaveConfirmAction;
/**
* Returns a default action, resulting on executing the default behavior when
* the user tries to leave an application
*/
default(): AppLeaveDefaultAction;
}

View file

@ -0,0 +1,226 @@
/*
* 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.
*/
import type { Observable } from 'rxjs';
import type { CoreTheme } from '@kbn/core-theme-browser';
import type { MountPoint } from '@kbn/core-mount-utils-browser';
import type { AppLeaveHandler } from './app_leave';
import type { ScopedHistory } from './scoped_history';
/**
* A mount function called when the user navigates to this app's route.
*
* @param params {@link AppMountParameters}
* @returns An unmounting function that will be called to unmount the application. See {@link AppUnmount}.
*
* @public
*/
export type AppMount<HistoryLocationState = unknown> = (
params: AppMountParameters<HistoryLocationState>
) => AppUnmount | Promise<AppUnmount>;
/**
* A function called when an application should be unmounted from the page. This function should be synchronous.
* @public
*/
export type AppUnmount = () => void;
/** @public */
export interface AppMountParameters<HistoryLocationState = unknown> {
/**
* The container element to render the application into.
*/
element: HTMLElement;
/**
* A scoped history instance for your application. Should be used to wire up
* your applications Router.
*
* @example
* How to configure react-router with a base path:
*
* ```ts
* // inside your plugin's setup function
* export class MyPlugin implements Plugin {
* setup({ application }) {
* application.register({
* id: 'my-app',
* appRoute: '/my-app',
* async mount(params) {
* const { renderApp } = await import('./application');
* return renderApp(params);
* },
* });
* }
* }
* ```
*
* ```ts
* // application.tsx
* import React from 'react';
* import ReactDOM from 'react-dom';
* import { Router, Route } from 'react-router-dom';
*
* import { CoreStart, AppMountParameters } from 'src/core/public';
* import { MyPluginDepsStart } from './plugin';
*
* export renderApp = ({ element, history }: AppMountParameters) => {
* ReactDOM.render(
* <Router history={history}>
* <Route path="/" exact component={HomePage} />
* </Router>,
* element
* );
*
* return () => ReactDOM.unmountComponentAtNode(element);
* }
* ```
*/
history: ScopedHistory<HistoryLocationState>;
/**
* The route path for configuring navigation to the application.
* This string should not include the base path from HTTP.
*
* @deprecated Use {@link AppMountParameters.history} instead.
* @removeBy 8.8.0
*
* @example
*
* How to configure react-router with a base path:
*
* ```ts
* // inside your plugin's setup function
* export class MyPlugin implements Plugin {
* setup({ application }) {
* application.register({
* id: 'my-app',
* appRoute: '/my-app',
* async mount(params) {
* const { renderApp } = await import('./application');
* return renderApp(params);
* },
* });
* }
* }
* ```
*
* ```ts
* // application.tsx
* import React from 'react';
* import ReactDOM from 'react-dom';
* import { BrowserRouter, Route } from 'react-router-dom';
*
* import { CoreStart, AppMountParameters } from 'src/core/public';
* import { MyPluginDepsStart } from './plugin';
*
* export renderApp = ({ appBasePath, element }: AppMountParameters) => {
* ReactDOM.render(
* // pass `appBasePath` to `basename`
* <BrowserRouter basename={appBasePath}>
* <Route path="/" exact component={HomePage} />
* </BrowserRouter>,
* element
* );
*
* return () => ReactDOM.unmountComponentAtNode(element);
* }
* ```
*/
appBasePath: string;
/**
* A function that can be used to register a handler that will be called
* when the user is leaving the current application, allowing to
* prompt a confirmation message before actually changing the page.
*
* This will be called either when the user goes to another application, or when
* trying to close the tab or manually changing the url.
*
* @example
*
* ```ts
* // application.tsx
* import React from 'react';
* import ReactDOM from 'react-dom';
* import { BrowserRouter, Route } from 'react-router-dom';
*
* import { CoreStart, AppMountParameters } from 'src/core/public';
* import { MyPluginDepsStart } from './plugin';
*
* export renderApp = ({ element, history, onAppLeave }: AppMountParameters) => {
* const { renderApp, hasUnsavedChanges } = await import('./application');
* onAppLeave(actions => {
* if(hasUnsavedChanges()) {
* return actions.confirm('Some changes were not saved. Are you sure you want to leave?');
* }
* return actions.default();
* });
* return renderApp({ element, history });
* }
* ```
*
* @deprecated {@link ScopedHistory.block} should be used instead.
* @removeBy 8.8.0
*/
onAppLeave: (handler: AppLeaveHandler) => void;
/**
* A function that can be used to set the mount point used to populate the application action container
* in the chrome header.
*
* Calling the handler multiple time will erase the current content of the action menu with the mount from the latest call.
* Calling the handler with `undefined` will unmount the current mount point.
* Calling the handler after the application has been unmounted will have no effect.
*
* @example
*
* ```ts
* // application.tsx
* import React from 'react';
* import ReactDOM from 'react-dom';
* import { BrowserRouter, Route } from 'react-router-dom';
*
* import { CoreStart, AppMountParameters } from 'src/core/public';
* import { MyPluginDepsStart } from './plugin';
*
* export renderApp = ({ element, history, setHeaderActionMenu }: AppMountParameters) => {
* const { renderApp } = await import('./application');
* const { renderActionMenu } = await import('./action_menu');
* setHeaderActionMenu((element) => {
* return renderActionMenu(element);
* })
* return renderApp({ element, history });
* }
* ```
*/
setHeaderActionMenu: (menuMount: MountPoint | undefined) => void;
/**
* An observable emitting {@link CoreTheme | Core's theme}.
* Should be used when mounting the application to include theme information.
*
* @example
* When mounting a react application:
* ```ts
* // application.tsx
* import React from 'react';
* import ReactDOM from 'react-dom';
*
* import { AppMountParameters } from 'src/core/public';
* import { wrapWithTheme } from 'src/plugins/kibana_react';
* import { MyApp } from './app';
*
* export renderApp = ({ element, theme$ }: AppMountParameters) => {
* ReactDOM.render(wrapWithTheme(<MyApp/>, theme$), element);
* return () => ReactDOM.unmountComponentAtNode(element);
* }
* ```
*/
theme$: Observable<CoreTheme>;
}

View file

@ -0,0 +1,332 @@
/*
* 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.
*/
import type { Observable } from 'rxjs';
import type { Capabilities } from '@kbn/core-capabilities-common';
import type { AppCategory } from '@kbn/core-application-common';
import type { AppMount } from './app_mount';
/**
* Accessibility status of an application.
*
* @public
*/
export enum AppStatus {
/**
* Application is accessible.
*/
accessible = 0,
/**
* Application is not accessible.
*/
inaccessible = 1,
}
/**
* Status of the application's navLink.
*
* @public
*/
export enum AppNavLinkStatus {
/**
* The application navLink will be `visible` if the application's {@link AppStatus} is set to `accessible`
* and `hidden` if the application status is set to `inaccessible`.
*/
default = 0,
/**
* The application navLink is visible and clickable in the navigation bar.
*/
visible = 1,
/**
* The application navLink is visible but inactive and not clickable in the navigation bar.
*/
disabled = 2,
/**
* The application navLink does not appear in the navigation bar.
*/
hidden = 3,
}
/**
* App navigation menu options
* @public
*/
export interface AppNavOptions {
/**
* An ordinal used to sort nav links relative to one another for display.
*/
order?: number;
/**
* A tooltip shown when hovering over app link.
*/
tooltip?: string;
/**
* A EUI iconType that will be used for the app's icon. This icon
* takes precedence over the `icon` property.
*/
euiIconType?: string;
/**
* A URL to an image file used as an icon. Used as a fallback
* if `euiIconType` is not provided.
*/
icon?: string;
}
/**
* Updater for applications.
* see {@link ApplicationSetup}
* @public
*/
export type AppUpdater = (app: App) => Partial<AppUpdatableFields> | undefined;
/**
* Defines the list of fields that can be updated via an {@link AppUpdater}.
* @public
*/
export type AppUpdatableFields = Pick<
App,
'status' | 'navLinkStatus' | 'searchable' | 'tooltip' | 'defaultPath' | 'deepLinks'
>;
/**
* @public
*/
export interface App<HistoryLocationState = unknown> extends AppNavOptions {
/**
* The unique identifier of the application.
*
* Can only be composed of alphanumeric characters, `-`, `:` and `_`
*/
id: string;
/**
* The title of the application.
*/
title: string;
/**
* The category definition of the product
* See {@link AppCategory}
* See DEFAULT_APP_CATEGORIES for more reference
*/
category?: AppCategory;
/**
* The initial status of the application.
* Defaulting to `accessible`
*/
status?: AppStatus;
/**
* The initial status of the application's navLink.
* Defaulting to `visible` if `status` is `accessible` and `hidden` if status is `inaccessible`
* See {@link AppNavLinkStatus}
*/
navLinkStatus?: AppNavLinkStatus;
/**
* The initial flag to determine if the application is searchable in the global search.
* Defaulting to `true` if `navLinkStatus` is `visible` or omitted.
*/
searchable?: boolean;
/**
* Allow to define the default path a user should be directed to when navigating to the app.
* When defined, this value will be used as a default for the `path` option when calling {@link ApplicationStart.navigateToApp | navigateToApp}`,
* and will also be appended to the {@link ChromeNavLink | application navLink} in the navigation bar.
*/
defaultPath?: string;
/**
* An {@link AppUpdater} observable that can be used to update the application {@link AppUpdatableFields} at runtime.
*
* @example
*
* How to update an application navLink at runtime
*
* ```ts
* // inside your plugin's setup function
* export class MyPlugin implements Plugin {
* private appUpdater = new BehaviorSubject<AppUpdater>(() => ({}));
*
* setup({ application }) {
* application.register({
* id: 'my-app',
* title: 'My App',
* updater$: this.appUpdater,
* async mount(params) {
* const { renderApp } = await import('./application');
* return renderApp(params);
* },
* });
* }
*
* start() {
* // later, when the navlink needs to be updated
* appUpdater.next(() => {
* navLinkStatus: AppNavLinkStatus.disabled,
* })
* }
* ```
*/
updater$?: Observable<AppUpdater>;
/**
* Custom capabilities defined by the app.
*/
capabilities?: Partial<Capabilities>;
/**
* Hide the UI chrome when the application is mounted. Defaults to `false`.
* Takes precedence over chrome service visibility settings.
*/
chromeless?: boolean;
/**
* A mount function called when the user navigates to this app's route.
*/
mount: AppMount<HistoryLocationState>;
/**
* Override the application's routing path from `/app/${id}`.
* Must be unique across registered applications. Should not include the
* base path from HTTP.
*/
appRoute?: string;
/**
* If set to true, the application's route will only be checked against an exact match. Defaults to `false`.
*
* @example
* ```ts
* core.application.register({
* id: 'my_app',
* title: 'My App',
* exactRoute: true,
* mount: () => { ... },
* })
*
* // '[basePath]/app/my_app' will be matched
* // '[basePath]/app/my_app/some/path' will not be matched
* ```
*/
exactRoute?: boolean;
/** Optional keywords to match with in deep links search. Omit if this part of the hierarchy does not have a page URL. */
keywords?: string[];
/**
* Input type for registering secondary in-app locations for an application.
*
* Deep links must include at least one of `path` or `deepLinks`. A deep link that does not have a `path`
* represents a topological level in the application's hierarchy, but does not have a destination URL that is
* user-accessible.
*
* @example
* ```ts
* core.application.register({
* id: 'my_app',
* title: 'Translated title',
* keywords: ['translated keyword1', 'translated keyword2'],
* deepLinks: [
* {
* id: 'sub1',
* title: 'Sub1',
* path: '/sub1',
* keywords: ['subpath1'],
* },
* {
* id: 'sub2',
* title: 'Sub2',
* deepLinks: [
* {
* id: 'subsub',
* title: 'SubSub',
* path: '/sub2/sub',
* keywords: ['subpath2'],
* },
* ],
* },
* ],
* mount: () => { ... }
* })
* ```
*/
deepLinks?: AppDeepLink[];
}
/**
* Public information about a registered app's {@link AppDeepLink | deepLinks}
*
* @public
*/
export type PublicAppDeepLinkInfo = Omit<
AppDeepLink,
'deepLinks' | 'keywords' | 'navLinkStatus' | 'searchable'
> & {
deepLinks: PublicAppDeepLinkInfo[];
keywords: string[];
navLinkStatus: AppNavLinkStatus;
searchable: boolean;
};
/**
* Input type for registering secondary in-app locations for an application.
*
* Deep links must include at least one of `path` or `deepLinks`. A deep link that does not have a `path`
* represents a topological level in the application's hierarchy, but does not have a destination URL that is
* user-accessible.
* @public
*/
export type AppDeepLink = {
/** Identifier to represent this sublink, should be unique for this application */
id: string;
/** Title to label represent this deep link */
title: string;
/** Optional keywords to match with in deep links search. Omit if this part of the hierarchy does not have a page URL. */
keywords?: string[];
/** Optional status of the chrome navigation, defaults to `hidden` */
navLinkStatus?: AppNavLinkStatus;
/** Optional flag to determine if the link is searchable in the global search. Defaulting to `true` if `navLinkStatus` is `visible` or omitted */
searchable?: boolean;
} & AppNavOptions &
(
| {
/** URL path to access this link, relative to the application's appRoute. */
path: string;
/** Optional array of links that are 'underneath' this section in the hierarchy */
deepLinks?: AppDeepLink[];
}
| {
/** Optional path to access this section. Omit if this part of the hierarchy does not have a page URL. */
path?: string;
/** Array links that are 'underneath' this section in this hierarchy. */
deepLinks: AppDeepLink[];
}
);
/**
* Public information about a registered {@link App | application}
*
* @public
*/
export type PublicAppInfo = Omit<
App,
'mount' | 'updater$' | 'keywords' | 'deepLinks' | 'searchable'
> & {
// remove optional on fields populated with default values
status: AppStatus;
navLinkStatus: AppNavLinkStatus;
appRoute: string;
keywords: string[];
deepLinks: PublicAppDeepLinkInfo[];
searchable: boolean;
};

View file

@ -0,0 +1,191 @@
/*
* 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.
*/
import type { Observable } from 'rxjs';
import type { RecursiveReadonly } from '@kbn/utility-types';
import type { Capabilities } from '@kbn/core-capabilities-common';
import type { App, AppUpdater, PublicAppInfo } from './application';
/** @public */
export interface ApplicationSetup {
/**
* Register an mountable application to the system.
* @param app - an {@link App}
* @typeParam HistoryLocationState - shape of the `History` state on {@link AppMountParameters.history}, defaults to `unknown`.
*/
register<HistoryLocationState = unknown>(app: App<HistoryLocationState>): void;
/**
* Register an application updater that can be used to change the {@link AppUpdatableFields} fields
* of all applications at runtime.
*
* This is meant to be used by plugins that needs to updates the whole list of applications.
* To only updates a specific application, use the `updater$` property of the registered application instead.
*
* @example
*
* How to register an application updater that disables some applications:
*
* ```ts
* // inside your plugin's setup function
* export class MyPlugin implements Plugin {
* setup({ application }) {
* application.registerAppUpdater(
* new BehaviorSubject<AppUpdater>(app => {
* if (myPluginApi.shouldDisable(app))
* return {
* status: AppStatus.inaccessible,
* };
* })
* );
* }
* }
* ```
*/
registerAppUpdater(appUpdater$: Observable<AppUpdater>): void;
}
/** @public */
export interface ApplicationStart {
/**
* Gets the read-only capabilities.
*/
capabilities: RecursiveReadonly<Capabilities>;
/**
* Observable emitting the list of currently registered apps and their associated status.
*
* @remarks
* Applications disabled by {@link Capabilities} will not be present in the map. Applications manually disabled from
* the client-side using an {@link AppUpdater | application updater} are present, with their status properly set as `inaccessible`.
*/
applications$: Observable<ReadonlyMap<string, PublicAppInfo>>;
/**
* Navigate to a given app
*
* @param appId
* @param options - navigation options
*/
navigateToApp(appId: string, options?: NavigateToAppOptions): Promise<void>;
/**
* Navigate to given URL in a SPA friendly way when possible (when the URL will redirect to a valid application
* within the current basePath).
*
* The method resolves pathnames the same way browsers do when resolving a `<a href>` value. The provided `url` can be:
* - an absolute URL
* - an absolute path
* - a path relative to the current URL (window.location.href)
*
* If all these criteria are true for the given URL:
* - (only for absolute URLs) The origin of the URL matches the origin of the browser's current location
* - The resolved pathname of the provided URL/path starts with the current basePath (eg. /mybasepath/s/my-space)
* - The pathname segment after the basePath matches any known application route (eg. /app/<id>/ or any application's `appRoute` configuration)
*
* Then a SPA navigation will be performed using `navigateToApp` using the corresponding application and path.
* Otherwise, fallback to a full page reload to navigate to the url using `window.location.assign`.
*
* @example
* ```ts
* // current url: `https://kibana:8080/base-path/s/my-space/app/dashboard`
*
* // will call `application.navigateToApp('discover', { path: '/some-path?foo=bar'})`
* application.navigateToUrl('https://kibana:8080/base-path/s/my-space/app/discover/some-path?foo=bar')
* application.navigateToUrl('/base-path/s/my-space/app/discover/some-path?foo=bar')
* application.navigateToUrl('./discover/some-path?foo=bar')
*
* // will perform a full page reload using `window.location.assign`
* application.navigateToUrl('https://elsewhere:8080/base-path/s/my-space/app/discover/some-path') // origin does not match
* application.navigateToUrl('/app/discover/some-path') // does not include the current basePath
* application.navigateToUrl('/base-path/s/my-space/app/unknown-app/some-path') // unknown application
* application.navigateToUrl('../discover') // resolve to `/base-path/s/my-space/discover` which is not a path of a known app.
* application.navigateToUrl('../../other-space/discover') // resolve to `/base-path/s/other-space/discover` which is not within the current basePath.
* ```
*
* @param url - an absolute URL, an absolute path or a relative path, to navigate to.
* @param options - navigation options
*/
navigateToUrl(url: string, options?: NavigateToUrlOptions): Promise<void>;
/**
* Returns the absolute path (or URL) to a given app, including the global base path.
*
* By default, it returns the absolute path of the application (e.g `/basePath/app/my-app`).
* Use the `absolute` option to generate an absolute url instead (e.g `http://host:port/basePath/app/my-app`)
*
* Note that when generating absolute urls, the origin (protocol, host and port) are determined from the browser's current location.
*
* @param appId
* @param options.path - optional path inside application to deep link to
* @param options.absolute - if true, will returns an absolute url instead of a relative one
*/
getUrlForApp(
appId: string,
options?: { path?: string; absolute?: boolean; deepLinkId?: string }
): string;
/**
* An observable that emits the current application id and each subsequent id update.
*/
currentAppId$: Observable<string | undefined>;
}
/**
* Options for the {@link ApplicationStart.navigateToApp | navigateToApp API}
* @public
*/
export interface NavigateToAppOptions {
/**
* optional {@link App.deepLinks | deep link} id inside the application to navigate to.
* If an additional {@link NavigateToAppOptions.path | path} is defined it will be appended to the deep link path.
*/
deepLinkId?: string;
/**
* optional path inside application to deep link to.
* If undefined, will use {@link App.defaultPath | the app's default path} as default.
*/
path?: string;
/**
* optional state to forward to the application
*/
state?: unknown;
/**
* if true, will not create a new history entry when navigating (using `replace` instead of `push`)
*/
replace?: boolean;
/**
* if true, will open the app in new tab, will share session information via window.open if base
*/
openInNewTab?: boolean;
/**
* if true, will bypass the default onAppLeave behavior
*/
skipAppLeave?: boolean;
}
/**
* Options for the {@link ApplicationStart.navigateToUrl | navigateToUrl API}
* @public
*/
export interface NavigateToUrlOptions {
/**
* if true, will bypass the default onAppLeave behavior
*/
skipAppLeave?: boolean;
/**
* if true will force a full page reload/refresh/assign, overriding the outcome of other url checks against current the location (effectively using `window.location.assign` instead of `push`)
*/
forceRedirect?: boolean;
/**
* optional state to forward to the application
*/
state?: unknown;
}

View file

@ -0,0 +1,45 @@
/*
* 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.
*/
import type { History, LocationDescriptorObject, Href } from 'history';
/**
* A wrapper around a `History` instance that is scoped to a particular base path of the history stack. Behaves
* similarly to the `basename` option except that this wrapper hides any history stack entries from outside the scope
* of this base path.
*
* This wrapper also allows Core and Plugins to share a single underlying global `History` instance without exposing
* the history of other applications.
*
* The {@link ScopedHistory.createSubHistory | createSubHistory} method is particularly useful for applications that
* contain any number of "sub-apps" which should not have access to the main application's history or basePath.
*
* @public
*/
export interface ScopedHistory<HistoryLocationState = unknown>
extends History<HistoryLocationState> {
/**
* Creates a `ScopedHistory` for a subpath of this `ScopedHistory`. Useful for applications that may have sub-apps
* that do not need access to the containing application's history.
*
* @param basePath the URL path scope for the sub history
*/
createSubHistory(basePath: string): ScopedHistory;
/**
* Creates an href (string) to the location.
* If `prependBasePath` is true (default), it will prepend the location's path with the scoped history basePath.
*
* @param location
* @param options.prependBasePath
*/
createHref(
location: LocationDescriptorObject<HistoryLocationState>,
options?: { prependBasePath?: boolean }
): Href;
}

View file

@ -0,0 +1,19 @@
{
"extends": "../../../../tsconfig.bazel.json",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"emitDeclarationOnly": true,
"outDir": "target_types",
"rootDir": ".",
"stripInternal": false,
"types": [
"jest",
"node"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
]
}

View file

@ -0,0 +1,117 @@
load("@npm//@bazel/typescript:index.bzl", "ts_config")
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project")
PKG_DIRNAME = "core-application-common"
PKG_REQUIRE_NAME = "@kbn/core-application-common"
SOURCE_FILES = glob(
[
"**/*.ts",
"**/*.tsx",
],
exclude = [
"**/*.config.js",
"**/*.mock.*",
"**/*.test.*",
"**/*.stories.*",
"**/__snapshots__",
"**/integration_tests",
"**/mocks",
"**/scripts",
"**/storybook",
"**/test_fixtures",
"**/test_helpers",
],
)
SRCS = SOURCE_FILES
filegroup(
name = "srcs",
srcs = SRCS,
)
NPM_MODULE_EXTRA_FILES = [
"package.json",
]
RUNTIME_DEPS = [
"@npm//react",
"//packages/kbn-i18n",
]
TYPES_DEPS = [
"@npm//@types/node",
"@npm//@types/jest",
"//packages/kbn-i18n:npm_module_types",
]
jsts_transpiler(
name = "target_node",
srcs = SRCS,
build_pkg_name = package_name(),
)
jsts_transpiler(
name = "target_web",
srcs = SRCS,
build_pkg_name = package_name(),
web = True,
)
ts_config(
name = "tsconfig",
src = "tsconfig.json",
deps = [
"//:tsconfig.base.json",
"//:tsconfig.bazel.json",
],
)
ts_project(
name = "tsc_types",
args = ['--pretty'],
srcs = SRCS,
deps = TYPES_DEPS,
declaration = True,
declaration_map = True,
emit_declaration_only = True,
out_dir = "target_types",
root_dir = ".",
tsconfig = ":tsconfig",
)
js_library(
name = PKG_DIRNAME,
srcs = NPM_MODULE_EXTRA_FILES,
deps = RUNTIME_DEPS + [":target_node", ":target_web"],
package_name = PKG_REQUIRE_NAME,
visibility = ["//visibility:public"],
)
pkg_npm(
name = "npm_module",
deps = [":" + PKG_DIRNAME],
)
filegroup(
name = "build",
srcs = [":npm_module"],
visibility = ["//visibility:public"],
)
pkg_npm_types(
name = "npm_module_types",
srcs = SRCS,
deps = [":tsc_types"],
package_name = PKG_REQUIRE_NAME,
tsconfig = ":tsconfig",
visibility = ["//visibility:public"],
)
filegroup(
name = "build_types",
srcs = [":npm_module_types"],
visibility = ["//visibility:public"],
)

View file

@ -0,0 +1,3 @@
# @kbn/core-application-common
Contains public types and constants for Core's browser-side `application` service.

View file

@ -0,0 +1,11 @@
/*
* 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.
*/
export type { AppCategory } from './src/app_category';
export { APP_WRAPPER_CLASS } from './src/app_wrapper_class';
export { DEFAULT_APP_CATEGORIES } from './src/default_app_categories';

View file

@ -6,5 +6,8 @@
* Side Public License, v 1.
*/
export { DEFAULT_APP_CATEGORIES } from './default_app_categories';
export { APP_WRAPPER_CLASS } from './app_wrapper_class';
module.exports = {
preset: '@kbn/test',
rootDir: '../../../..',
roots: ['<rootDir>/packages/core/application/core-application-common'],
};

View file

@ -0,0 +1,9 @@
{
"name": "@kbn/core-application-common",
"private": true,
"version": "1.0.0",
"main": "./target_node/index.js",
"browser": "./target_web/index.js",
"author": "Kibana Core",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -6,8 +6,6 @@
* Side Public License, v 1.
*/
/** @public */
/**
* A category definition for nav links to know where to sort them in the left hand nav
* @public

View file

@ -7,9 +7,9 @@
*/
import { i18n } from '@kbn/i18n';
import { AppCategory } from '../types';
import type { AppCategory } from './app_category';
/** @internal */
/** @public */
export const DEFAULT_APP_CATEGORIES: Record<string, AppCategory> = Object.freeze({
kibana: {
id: 'kibana',

View file

@ -0,0 +1,19 @@
{
"extends": "../../../../tsconfig.bazel.json",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"emitDeclarationOnly": true,
"outDir": "target_types",
"rootDir": ".",
"stripInternal": false,
"types": [
"jest",
"node"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
]
}

View file

@ -0,0 +1,121 @@
load("@npm//@bazel/typescript:index.bzl", "ts_config")
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project")
PKG_DIRNAME = "core-capabilities-browser-internal"
PKG_REQUIRE_NAME = "@kbn/core-capabilities-browser-internal"
SOURCE_FILES = glob(
[
"**/*.ts",
"**/*.tsx",
],
exclude = [
"**/*.config.js",
"**/*.mock.*",
"**/*.test.*",
"**/*.stories.*",
"**/__snapshots__",
"**/integration_tests",
"**/mocks",
"**/scripts",
"**/storybook",
"**/test_fixtures",
"**/test_helpers",
],
)
SRCS = SOURCE_FILES
filegroup(
name = "srcs",
srcs = SRCS,
)
NPM_MODULE_EXTRA_FILES = [
"package.json",
]
RUNTIME_DEPS = [
"//packages/kbn-std",
### test dependencies
"//packages/core/http/core-http-browser-mocks"
]
TYPES_DEPS = [
"@npm//@types/node",
"@npm//@types/jest",
"//packages/kbn-utility-types:npm_module_types",
"//packages/kbn-std:npm_module_types",
"//packages/core/http/core-http-browser:npm_module_types",
"//packages/core/capabilities/core-capabilities-common:npm_module_types",
]
jsts_transpiler(
name = "target_node",
srcs = SRCS,
build_pkg_name = package_name(),
)
jsts_transpiler(
name = "target_web",
srcs = SRCS,
build_pkg_name = package_name(),
web = True,
)
ts_config(
name = "tsconfig",
src = "tsconfig.json",
deps = [
"//:tsconfig.base.json",
"//:tsconfig.bazel.json",
],
)
ts_project(
name = "tsc_types",
args = ['--pretty'],
srcs = SRCS,
deps = TYPES_DEPS,
declaration = True,
declaration_map = True,
emit_declaration_only = True,
out_dir = "target_types",
root_dir = ".",
tsconfig = ":tsconfig",
)
js_library(
name = PKG_DIRNAME,
srcs = NPM_MODULE_EXTRA_FILES,
deps = RUNTIME_DEPS + [":target_node", ":target_web"],
package_name = PKG_REQUIRE_NAME,
visibility = ["//visibility:public"],
)
pkg_npm(
name = "npm_module",
deps = [":" + PKG_DIRNAME],
)
filegroup(
name = "build",
srcs = [":npm_module"],
visibility = ["//visibility:public"],
)
pkg_npm_types(
name = "npm_module_types",
srcs = SRCS,
deps = [":tsc_types"],
package_name = PKG_REQUIRE_NAME,
tsconfig = ":tsconfig",
visibility = ["//visibility:public"],
)
filegroup(
name = "build_types",
srcs = [":npm_module_types"],
visibility = ["//visibility:public"],
)

View file

@ -0,0 +1,3 @@
# @kbn/core-capabilities-browser-internal
Contains the implementation of Core's internal `capabilities` browser-side service.

View file

@ -6,8 +6,5 @@
* Side Public License, v 1.
*/
export {
MountWrapper,
mountReactNode,
KBN_LOAD_MARKS,
} from '@kbn/core-mount-utils-browser-internal';
export { CapabilitiesService } from './src/capabilities_service';
export type { CapabilitiesStart } from './src/capabilities_service';

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.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../../..',
roots: ['<rootDir>/packages/core/capabilities/core-capabilities-browser-internal'],
};

View file

@ -0,0 +1,9 @@
{
"name": "@kbn/core-capabilities-browser-internal",
"private": true,
"version": "1.0.0",
"main": "./target_node/index.js",
"browser": "./target_web/index.js",
"author": "Kibana Core",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,19 @@
{
"extends": "../../../../tsconfig.bazel.json",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"emitDeclarationOnly": true,
"outDir": "target_types",
"rootDir": ".",
"stripInternal": false,
"types": [
"jest",
"node"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
]
}

View file

@ -0,0 +1,117 @@
load("@npm//@bazel/typescript:index.bzl", "ts_config")
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project")
PKG_DIRNAME = "core-capabilities-browser-mocks"
PKG_REQUIRE_NAME = "@kbn/core-capabilities-browser-mocks"
SOURCE_FILES = glob(
[
"**/*.ts",
"**/*.tsx",
],
exclude = [
"**/*.config.js",
"**/*.test.*",
"**/*.stories.*",
"**/__snapshots__",
"**/integration_tests",
"**/mocks",
"**/scripts",
"**/storybook",
"**/test_fixtures",
"**/test_helpers",
],
)
SRCS = SOURCE_FILES
filegroup(
name = "srcs",
srcs = SRCS,
)
NPM_MODULE_EXTRA_FILES = [
"package.json",
]
RUNTIME_DEPS = [
"//packages/kbn-std",
]
TYPES_DEPS = [
"@npm//@types/node",
"@npm//@types/jest",
"//packages/kbn-std:npm_module_types",
"//packages/kbn-utility-types:npm_module_types",
"//packages/core/capabilities/core-capabilities-browser-internal:npm_module_types",
]
jsts_transpiler(
name = "target_node",
srcs = SRCS,
build_pkg_name = package_name(),
)
jsts_transpiler(
name = "target_web",
srcs = SRCS,
build_pkg_name = package_name(),
web = True,
)
ts_config(
name = "tsconfig",
src = "tsconfig.json",
deps = [
"//:tsconfig.base.json",
"//:tsconfig.bazel.json",
],
)
ts_project(
name = "tsc_types",
args = ['--pretty'],
srcs = SRCS,
deps = TYPES_DEPS,
declaration = True,
declaration_map = True,
emit_declaration_only = True,
out_dir = "target_types",
root_dir = ".",
tsconfig = ":tsconfig",
)
js_library(
name = PKG_DIRNAME,
srcs = NPM_MODULE_EXTRA_FILES,
deps = RUNTIME_DEPS + [":target_node", ":target_web"],
package_name = PKG_REQUIRE_NAME,
visibility = ["//visibility:public"],
)
pkg_npm(
name = "npm_module",
deps = [":" + PKG_DIRNAME],
)
filegroup(
name = "build",
srcs = [":npm_module"],
visibility = ["//visibility:public"],
)
pkg_npm_types(
name = "npm_module_types",
srcs = SRCS,
deps = [":tsc_types"],
package_name = PKG_REQUIRE_NAME,
tsconfig = ":tsconfig",
visibility = ["//visibility:public"],
)
filegroup(
name = "build_types",
srcs = [":npm_module_types"],
visibility = ["//visibility:public"],
)

View file

@ -0,0 +1,4 @@
# @kbn/core-capabilities-browser-mocks
Contains the mocks for Core's internal `capabilities` browser-side service:
- `capabilitiesServiceMock`

View file

@ -6,4 +6,4 @@
* Side Public License, v 1.
*/
export { CapabilitiesService } from './capabilities_service';
export { capabilitiesServiceMock } from './src/capabilities_service.mock';

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.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../../..',
roots: ['<rootDir>/packages/core/capabilities/core-capabilities-browser-mocks'],
};

View file

@ -0,0 +1,9 @@
{
"name": "@kbn/core-capabilities-browser-mocks",
"private": true,
"version": "1.0.0",
"main": "./target_node/index.js",
"browser": "./target_web/index.js",
"author": "Kibana Core",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -8,7 +8,10 @@
import { deepFreeze } from '@kbn/std';
import type { PublicMethodsOf } from '@kbn/utility-types';
import { CapabilitiesService, CapabilitiesStart } from './capabilities_service';
import type {
CapabilitiesStart,
CapabilitiesService,
} from '@kbn/core-capabilities-browser-internal';
const createStartContractMock = (): jest.Mocked<CapabilitiesStart> => ({
capabilities: deepFreeze({

View file

@ -0,0 +1,19 @@
{
"extends": "../../../../tsconfig.bazel.json",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"emitDeclarationOnly": true,
"outDir": "target_types",
"rootDir": ".",
"stripInternal": false,
"types": [
"jest",
"node"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
]
}

View file

@ -25,6 +25,7 @@ type MountWrapperComponent = React.FunctionComponent<MountWrapperComponentProps>
/**
* MountWrapper is a react component to mount a {@link MountPoint} inside a react tree.
* @internal
*/
export const MountWrapper: MountWrapperComponent = ({ mount, className = defaultWrapperClass }) => {
const element = useRef(null);
@ -36,6 +37,7 @@ export const MountWrapper: MountWrapperComponent = ({ mount, className = default
* Mount converter for react node.
*
* @param node to get a mount for
* @internal
*/
export const mountReactNode =
(node: React.ReactNode): MountPoint =>

View file

@ -219,7 +219,7 @@ export class OptimizerConfig {
new Bundle({
type: 'entry',
id: 'core',
publicDirNames: ['public', 'public/utils'],
publicDirNames: ['public'],
sourceRoot: options.repoRoot,
contextDir: Path.resolve(options.repoRoot, 'src/core'),
outputDir: Path.resolve(options.outputRoot, 'src/core/target/public'),

View file

@ -22,7 +22,6 @@ const transpileKbnPaths = [
'x-pack/examples',
// TODO: should should probably remove this link back to the source
'x-pack/plugins/task_manager/server/config.ts',
'src/core/utils/default_app_categories.ts',
'src/plugins/field_formats/common',
].map((path) => Path.resolve(BASE_REPO_ROOT, path));

View file

@ -7,13 +7,13 @@
*/
jest.mock('@elastic/apm-rum');
import type { DeeplyMockedKeys, MockedKeys } from '@kbn/utility-types-jest';
import { executionContextServiceMock } from '@kbn/core-execution-context-browser-mocks';
import { init, apm } from '@elastic/apm-rum';
import type { Transaction } from '@elastic/apm-rum';
import { ApmSystem } from './apm_system';
import { Subject } from 'rxjs';
import { InternalApplicationStart } from './application/types';
import type { DeeplyMockedKeys, MockedKeys } from '@kbn/utility-types-jest';
import { init, apm, type Transaction } from '@elastic/apm-rum';
import { executionContextServiceMock } from '@kbn/core-execution-context-browser-mocks';
import type { InternalApplicationStart } from '@kbn/core-application-browser-internal';
import { ApmSystem } from './apm_system';
const initMock = init as jest.Mocked<typeof init>;
const apmMock = apm as DeeplyMockedKeys<typeof apm>;

View file

@ -9,8 +9,8 @@
import type { ApmBase, AgentConfigOptions, Transaction } from '@elastic/apm-rum';
import { modifyUrl } from '@kbn/std';
import type { ExecutionContextStart } from '@kbn/core-execution-context-browser';
import type { InternalApplicationStart } from '@kbn/core-application-browser-internal';
import { CachedResourceObserver } from './apm_resource_counter';
import type { InternalApplicationStart } from './application';
/** "GET protocol://hostname:port/pathname" */
const HTTP_REQUEST_TRANSACTION_NAME_REGEX =

View file

@ -1,876 +0,0 @@
/*
* 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.
*/
import type { ButtonColor } from '@elastic/eui';
import { Observable } from 'rxjs';
import { History } from 'history';
import { RecursiveReadonly } from '@kbn/utility-types';
import type { CoreTheme } from '@kbn/core-theme-browser';
import type { Capabilities } from '@kbn/core-capabilities-common';
import type { MountPoint } from '@kbn/core-mount-utils-browser';
import { PluginOpaqueId } from '../plugins';
import { AppCategory } from '../../types';
import { ScopedHistory } from './scoped_history';
/**
* Accessibility status of an application.
*
* @public
*/
export enum AppStatus {
/**
* Application is accessible.
*/
accessible = 0,
/**
* Application is not accessible.
*/
inaccessible = 1,
}
/**
* Status of the application's navLink.
*
* @public
*/
export enum AppNavLinkStatus {
/**
* The application navLink will be `visible` if the application's {@link AppStatus} is set to `accessible`
* and `hidden` if the application status is set to `inaccessible`.
*/
default = 0,
/**
* The application navLink is visible and clickable in the navigation bar.
*/
visible = 1,
/**
* The application navLink is visible but inactive and not clickable in the navigation bar.
*/
disabled = 2,
/**
* The application navLink does not appear in the navigation bar.
*/
hidden = 3,
}
/**
* Defines the list of fields that can be updated via an {@link AppUpdater}.
* @public
*/
export type AppUpdatableFields = Pick<
App,
'status' | 'navLinkStatus' | 'searchable' | 'tooltip' | 'defaultPath' | 'deepLinks'
>;
/**
* App navigation menu options
* @public
*/
export interface AppNavOptions {
/**
* An ordinal used to sort nav links relative to one another for display.
*/
order?: number;
/**
* A tooltip shown when hovering over app link.
*/
tooltip?: string;
/**
* A EUI iconType that will be used for the app's icon. This icon
* takes precedence over the `icon` property.
*/
euiIconType?: string;
/**
* A URL to an image file used as an icon. Used as a fallback
* if `euiIconType` is not provided.
*/
icon?: string;
}
/**
* Updater for applications.
* see {@link ApplicationSetup}
* @public
*/
export type AppUpdater = (app: App) => Partial<AppUpdatableFields> | undefined;
/**
* @public
*/
export interface App<HistoryLocationState = unknown> extends AppNavOptions {
/**
* The unique identifier of the application.
*
* Can only be composed of alphanumeric characters, `-`, `:` and `_`
*/
id: string;
/**
* The title of the application.
*/
title: string;
/**
* The category definition of the product
* See {@link AppCategory}
* See DEFAULT_APP_CATEGORIES for more reference
*/
category?: AppCategory;
/**
* The initial status of the application.
* Defaulting to `accessible`
*/
status?: AppStatus;
/**
* The initial status of the application's navLink.
* Defaulting to `visible` if `status` is `accessible` and `hidden` if status is `inaccessible`
* See {@link AppNavLinkStatus}
*/
navLinkStatus?: AppNavLinkStatus;
/**
* The initial flag to determine if the application is searchable in the global search.
* Defaulting to `true` if `navLinkStatus` is `visible` or omitted.
*/
searchable?: boolean;
/**
* Allow to define the default path a user should be directed to when navigating to the app.
* When defined, this value will be used as a default for the `path` option when calling {@link ApplicationStart.navigateToApp | navigateToApp}`,
* and will also be appended to the {@link ChromeNavLink | application navLink} in the navigation bar.
*/
defaultPath?: string;
/**
* An {@link AppUpdater} observable that can be used to update the application {@link AppUpdatableFields} at runtime.
*
* @example
*
* How to update an application navLink at runtime
*
* ```ts
* // inside your plugin's setup function
* export class MyPlugin implements Plugin {
* private appUpdater = new BehaviorSubject<AppUpdater>(() => ({}));
*
* setup({ application }) {
* application.register({
* id: 'my-app',
* title: 'My App',
* updater$: this.appUpdater,
* async mount(params) {
* const { renderApp } = await import('./application');
* return renderApp(params);
* },
* });
* }
*
* start() {
* // later, when the navlink needs to be updated
* appUpdater.next(() => {
* navLinkStatus: AppNavLinkStatus.disabled,
* })
* }
* ```
*/
updater$?: Observable<AppUpdater>;
/**
* Custom capabilities defined by the app.
*/
capabilities?: Partial<Capabilities>;
/**
* Hide the UI chrome when the application is mounted. Defaults to `false`.
* Takes precedence over chrome service visibility settings.
*/
chromeless?: boolean;
/**
* A mount function called when the user navigates to this app's route.
*/
mount: AppMount<HistoryLocationState>;
/**
* Override the application's routing path from `/app/${id}`.
* Must be unique across registered applications. Should not include the
* base path from HTTP.
*/
appRoute?: string;
/**
* If set to true, the application's route will only be checked against an exact match. Defaults to `false`.
*
* @example
* ```ts
* core.application.register({
* id: 'my_app',
* title: 'My App',
* exactRoute: true,
* mount: () => { ... },
* })
*
* // '[basePath]/app/my_app' will be matched
* // '[basePath]/app/my_app/some/path' will not be matched
* ```
*/
exactRoute?: boolean;
/** Optional keywords to match with in deep links search. Omit if this part of the hierarchy does not have a page URL. */
keywords?: string[];
/**
* Input type for registering secondary in-app locations for an application.
*
* Deep links must include at least one of `path` or `deepLinks`. A deep link that does not have a `path`
* represents a topological level in the application's hierarchy, but does not have a destination URL that is
* user-accessible.
*
* @example
* ```ts
* core.application.register({
* id: 'my_app',
* title: 'Translated title',
* keywords: ['translated keyword1', 'translated keyword2'],
* deepLinks: [
* {
* id: 'sub1',
* title: 'Sub1',
* path: '/sub1',
* keywords: ['subpath1'],
* },
* {
* id: 'sub2',
* title: 'Sub2',
* deepLinks: [
* {
* id: 'subsub',
* title: 'SubSub',
* path: '/sub2/sub',
* keywords: ['subpath2'],
* },
* ],
* },
* ],
* mount: () => { ... }
* })
* ```
*/
deepLinks?: AppDeepLink[];
}
/**
* Public information about a registered app's {@link AppDeepLink | deepLinks}
*
* @public
*/
export type PublicAppDeepLinkInfo = Omit<
AppDeepLink,
'deepLinks' | 'keywords' | 'navLinkStatus' | 'searchable'
> & {
deepLinks: PublicAppDeepLinkInfo[];
keywords: string[];
navLinkStatus: AppNavLinkStatus;
searchable: boolean;
};
/**
* Input type for registering secondary in-app locations for an application.
*
* Deep links must include at least one of `path` or `deepLinks`. A deep link that does not have a `path`
* represents a topological level in the application's hierarchy, but does not have a destination URL that is
* user-accessible.
* @public
*/
export type AppDeepLink = {
/** Identifier to represent this sublink, should be unique for this application */
id: string;
/** Title to label represent this deep link */
title: string;
/** Optional keywords to match with in deep links search. Omit if this part of the hierarchy does not have a page URL. */
keywords?: string[];
/** Optional status of the chrome navigation, defaults to `hidden` */
navLinkStatus?: AppNavLinkStatus;
/** Optional flag to determine if the link is searchable in the global search. Defaulting to `true` if `navLinkStatus` is `visible` or omitted */
searchable?: boolean;
} & AppNavOptions &
(
| {
/** URL path to access this link, relative to the application's appRoute. */
path: string;
/** Optional array of links that are 'underneath' this section in the hierarchy */
deepLinks?: AppDeepLink[];
}
| {
/** Optional path to access this section. Omit if this part of the hierarchy does not have a page URL. */
path?: string;
/** Array links that are 'underneath' this section in this hierarchy. */
deepLinks: AppDeepLink[];
}
);
/**
* Public information about a registered {@link App | application}
*
* @public
*/
export type PublicAppInfo = Omit<
App,
'mount' | 'updater$' | 'keywords' | 'deepLinks' | 'searchable'
> & {
// remove optional on fields populated with default values
status: AppStatus;
navLinkStatus: AppNavLinkStatus;
appRoute: string;
keywords: string[];
deepLinks: PublicAppDeepLinkInfo[];
searchable: boolean;
};
/**
* A mount function called when the user navigates to this app's route.
*
* @param params {@link AppMountParameters}
* @returns An unmounting function that will be called to unmount the application. See {@link AppUnmount}.
*
* @public
*/
export type AppMount<HistoryLocationState = unknown> = (
params: AppMountParameters<HistoryLocationState>
) => AppUnmount | Promise<AppUnmount>;
/**
* A function called when an application should be unmounted from the page. This function should be synchronous.
* @public
*/
export type AppUnmount = () => void;
/** @public */
export interface AppMountParameters<HistoryLocationState = unknown> {
/**
* The container element to render the application into.
*/
element: HTMLElement;
/**
* A scoped history instance for your application. Should be used to wire up
* your applications Router.
*
* @example
* How to configure react-router with a base path:
*
* ```ts
* // inside your plugin's setup function
* export class MyPlugin implements Plugin {
* setup({ application }) {
* application.register({
* id: 'my-app',
* appRoute: '/my-app',
* async mount(params) {
* const { renderApp } = await import('./application');
* return renderApp(params);
* },
* });
* }
* }
* ```
*
* ```ts
* // application.tsx
* import React from 'react';
* import ReactDOM from 'react-dom';
* import { Router, Route } from 'react-router-dom';
*
* import { CoreStart, AppMountParameters } from 'src/core/public';
* import { MyPluginDepsStart } from './plugin';
*
* export renderApp = ({ element, history }: AppMountParameters) => {
* ReactDOM.render(
* <Router history={history}>
* <Route path="/" exact component={HomePage} />
* </Router>,
* element
* );
*
* return () => ReactDOM.unmountComponentAtNode(element);
* }
* ```
*/
history: ScopedHistory<HistoryLocationState>;
/**
* The route path for configuring navigation to the application.
* This string should not include the base path from HTTP.
*
* @deprecated Use {@link AppMountParameters.history} instead.
* @removeBy 8.8.0
*
* @example
*
* How to configure react-router with a base path:
*
* ```ts
* // inside your plugin's setup function
* export class MyPlugin implements Plugin {
* setup({ application }) {
* application.register({
* id: 'my-app',
* appRoute: '/my-app',
* async mount(params) {
* const { renderApp } = await import('./application');
* return renderApp(params);
* },
* });
* }
* }
* ```
*
* ```ts
* // application.tsx
* import React from 'react';
* import ReactDOM from 'react-dom';
* import { BrowserRouter, Route } from 'react-router-dom';
*
* import { CoreStart, AppMountParameters } from 'src/core/public';
* import { MyPluginDepsStart } from './plugin';
*
* export renderApp = ({ appBasePath, element }: AppMountParameters) => {
* ReactDOM.render(
* // pass `appBasePath` to `basename`
* <BrowserRouter basename={appBasePath}>
* <Route path="/" exact component={HomePage} />
* </BrowserRouter>,
* element
* );
*
* return () => ReactDOM.unmountComponentAtNode(element);
* }
* ```
*/
appBasePath: string;
/**
* A function that can be used to register a handler that will be called
* when the user is leaving the current application, allowing to
* prompt a confirmation message before actually changing the page.
*
* This will be called either when the user goes to another application, or when
* trying to close the tab or manually changing the url.
*
* @example
*
* ```ts
* // application.tsx
* import React from 'react';
* import ReactDOM from 'react-dom';
* import { BrowserRouter, Route } from 'react-router-dom';
*
* import { CoreStart, AppMountParameters } from 'src/core/public';
* import { MyPluginDepsStart } from './plugin';
*
* export renderApp = ({ element, history, onAppLeave }: AppMountParameters) => {
* const { renderApp, hasUnsavedChanges } = await import('./application');
* onAppLeave(actions => {
* if(hasUnsavedChanges()) {
* return actions.confirm('Some changes were not saved. Are you sure you want to leave?');
* }
* return actions.default();
* });
* return renderApp({ element, history });
* }
* ```
*
* @deprecated {@link ScopedHistory.block} should be used instead.
* @removeBy 8.8.0
*/
onAppLeave: (handler: AppLeaveHandler) => void;
/**
* A function that can be used to set the mount point used to populate the application action container
* in the chrome header.
*
* Calling the handler multiple time will erase the current content of the action menu with the mount from the latest call.
* Calling the handler with `undefined` will unmount the current mount point.
* Calling the handler after the application has been unmounted will have no effect.
*
* @example
*
* ```ts
* // application.tsx
* import React from 'react';
* import ReactDOM from 'react-dom';
* import { BrowserRouter, Route } from 'react-router-dom';
*
* import { CoreStart, AppMountParameters } from 'src/core/public';
* import { MyPluginDepsStart } from './plugin';
*
* export renderApp = ({ element, history, setHeaderActionMenu }: AppMountParameters) => {
* const { renderApp } = await import('./application');
* const { renderActionMenu } = await import('./action_menu');
* setHeaderActionMenu((element) => {
* return renderActionMenu(element);
* })
* return renderApp({ element, history });
* }
* ```
*/
setHeaderActionMenu: (menuMount: MountPoint | undefined) => void;
/**
* An observable emitting {@link CoreTheme | Core's theme}.
* Should be used when mounting the application to include theme information.
*
* @example
* When mounting a react application:
* ```ts
* // application.tsx
* import React from 'react';
* import ReactDOM from 'react-dom';
*
* import { AppMountParameters } from 'src/core/public';
* import { wrapWithTheme } from 'src/plugins/kibana_react';
* import { MyApp } from './app';
*
* export renderApp = ({ element, theme$ }: AppMountParameters) => {
* ReactDOM.render(wrapWithTheme(<MyApp/>, theme$), element);
* return () => ReactDOM.unmountComponentAtNode(element);
* }
* ```
*/
theme$: Observable<CoreTheme>;
}
/**
* A handler that will be executed before leaving the application, either when
* going to another application or when closing the browser tab or manually changing
* the url.
* Should return `confirm` to prompt a message to the user before leaving the page, or `default`
* to keep the default behavior (doing nothing).
*
* See {@link AppMountParameters} for detailed usage examples.
*
* @public
* @deprecated {@link AppMountParameters.onAppLeave} has been deprecated in favor of {@link ScopedHistory.block}
* @removeBy 8.8.0
*/
export type AppLeaveHandler = (
factory: AppLeaveActionFactory,
nextAppId?: string
) => AppLeaveAction;
/**
* Possible type of actions on application leave.
*
* @public
*/
export enum AppLeaveActionType {
confirm = 'confirm',
default = 'default',
}
/**
* Action to return from a {@link AppLeaveHandler} to execute the default
* behaviour when leaving the application.
*
* See {@link AppLeaveActionFactory}
*
* @public
*/
export interface AppLeaveDefaultAction {
type: AppLeaveActionType.default;
}
/**
* Action to return from a {@link AppLeaveHandler} to show a confirmation
* message when trying to leave an application.
*
* See {@link AppLeaveActionFactory}
*
* @public
*/
export interface AppLeaveConfirmAction {
type: AppLeaveActionType.confirm;
text: string;
title?: string;
confirmButtonText?: string;
buttonColor?: ButtonColor;
callback?: () => void;
}
/**
* Possible actions to return from a {@link AppLeaveHandler}
*
* See {@link AppLeaveConfirmAction} and {@link AppLeaveDefaultAction}
*
* @public
* */
export type AppLeaveAction = AppLeaveDefaultAction | AppLeaveConfirmAction;
/**
* Factory provided when invoking a {@link AppLeaveHandler} to retrieve the {@link AppLeaveAction} to execute.
*/
export interface AppLeaveActionFactory {
/**
* Returns a confirm action, resulting on prompting a message to the user before leaving the
* application, allowing him to choose if he wants to stay on the app or confirm that he
* wants to leave.
*
* @param text The text to display in the confirmation message
* @param title (optional) title to display in the confirmation message
* @param callback (optional) to know that the user want to stay on the page
* @param confirmButtonText (optional) text for the confirmation button
* @param buttonColor (optional) color for the confirmation button
* so we can show to the user the right UX for him to saved his/her/their changes
*/
confirm(
text: string,
title?: string,
callback?: () => void,
confirmButtonText?: string,
buttonColor?: ButtonColor
): AppLeaveConfirmAction;
/**
* Returns a default action, resulting on executing the default behavior when
* the user tries to leave an application
*/
default(): AppLeaveDefaultAction;
}
/** @internal */
export interface Mounter {
appRoute: string;
appBasePath: string;
mount: AppMount;
exactRoute: boolean;
unmountBeforeMounting?: boolean;
}
/** @internal */
export interface ParsedAppUrl {
app: string;
path?: string;
}
/** @public */
export interface ApplicationSetup {
/**
* Register an mountable application to the system.
* @param app - an {@link App}
* @typeParam HistoryLocationState - shape of the `History` state on {@link AppMountParameters.history}, defaults to `unknown`.
*/
register<HistoryLocationState = unknown>(app: App<HistoryLocationState>): void;
/**
* Register an application updater that can be used to change the {@link AppUpdatableFields} fields
* of all applications at runtime.
*
* This is meant to be used by plugins that needs to updates the whole list of applications.
* To only updates a specific application, use the `updater$` property of the registered application instead.
*
* @example
*
* How to register an application updater that disables some applications:
*
* ```ts
* // inside your plugin's setup function
* export class MyPlugin implements Plugin {
* setup({ application }) {
* application.registerAppUpdater(
* new BehaviorSubject<AppUpdater>(app => {
* if (myPluginApi.shouldDisable(app))
* return {
* status: AppStatus.inaccessible,
* };
* })
* );
* }
* }
* ```
*/
registerAppUpdater(appUpdater$: Observable<AppUpdater>): void;
}
/** @internal */
export interface InternalApplicationSetup extends Pick<ApplicationSetup, 'registerAppUpdater'> {
/**
* Register an mountable application to the system.
* @param plugin - opaque ID of the plugin that registers this application
* @param app
*/
register<HistoryLocationState = unknown>(
plugin: PluginOpaqueId,
app: App<HistoryLocationState>
): void;
}
/**
* Options for the {@link ApplicationStart.navigateToApp | navigateToApp API}
* @public
*/
export interface NavigateToAppOptions {
/**
* optional {@link App.deepLinks | deep link} id inside the application to navigate to.
* If an additional {@link NavigateToAppOptions.path | path} is defined it will be appended to the deep link path.
*/
deepLinkId?: string;
/**
* optional path inside application to deep link to.
* If undefined, will use {@link App.defaultPath | the app's default path} as default.
*/
path?: string;
/**
* optional state to forward to the application
*/
state?: unknown;
/**
* if true, will not create a new history entry when navigating (using `replace` instead of `push`)
*/
replace?: boolean;
/**
* if true, will open the app in new tab, will share session information via window.open if base
*/
openInNewTab?: boolean;
/**
* if true, will bypass the default onAppLeave behavior
*/
skipAppLeave?: boolean;
}
/**
* Options for the {@link ApplicationStart.navigateToUrl | navigateToUrl API}
* @public
*/
export interface NavigateToUrlOptions {
/**
* if true, will bypass the default onAppLeave behavior
*/
skipAppLeave?: boolean;
/**
* if true will force a full page reload/refresh/assign, overriding the outcome of other url checks against current the location (effectively using `window.location.assign` instead of `push`)
*/
forceRedirect?: boolean;
/**
* optional state to forward to the application
*/
state?: unknown;
}
/** @public */
export interface ApplicationStart {
/**
* Gets the read-only capabilities.
*/
capabilities: RecursiveReadonly<Capabilities>;
/**
* Observable emitting the list of currently registered apps and their associated status.
*
* @remarks
* Applications disabled by {@link Capabilities} will not be present in the map. Applications manually disabled from
* the client-side using an {@link AppUpdater | application updater} are present, with their status properly set as `inaccessible`.
*/
applications$: Observable<ReadonlyMap<string, PublicAppInfo>>;
/**
* Navigate to a given app
*
* @param appId
* @param options - navigation options
*/
navigateToApp(appId: string, options?: NavigateToAppOptions): Promise<void>;
/**
* Navigate to given URL in a SPA friendly way when possible (when the URL will redirect to a valid application
* within the current basePath).
*
* The method resolves pathnames the same way browsers do when resolving a `<a href>` value. The provided `url` can be:
* - an absolute URL
* - an absolute path
* - a path relative to the current URL (window.location.href)
*
* If all these criteria are true for the given URL:
* - (only for absolute URLs) The origin of the URL matches the origin of the browser's current location
* - The resolved pathname of the provided URL/path starts with the current basePath (eg. /mybasepath/s/my-space)
* - The pathname segment after the basePath matches any known application route (eg. /app/<id>/ or any application's `appRoute` configuration)
*
* Then a SPA navigation will be performed using `navigateToApp` using the corresponding application and path.
* Otherwise, fallback to a full page reload to navigate to the url using `window.location.assign`.
*
* @example
* ```ts
* // current url: `https://kibana:8080/base-path/s/my-space/app/dashboard`
*
* // will call `application.navigateToApp('discover', { path: '/some-path?foo=bar'})`
* application.navigateToUrl('https://kibana:8080/base-path/s/my-space/app/discover/some-path?foo=bar')
* application.navigateToUrl('/base-path/s/my-space/app/discover/some-path?foo=bar')
* application.navigateToUrl('./discover/some-path?foo=bar')
*
* // will perform a full page reload using `window.location.assign`
* application.navigateToUrl('https://elsewhere:8080/base-path/s/my-space/app/discover/some-path') // origin does not match
* application.navigateToUrl('/app/discover/some-path') // does not include the current basePath
* application.navigateToUrl('/base-path/s/my-space/app/unknown-app/some-path') // unknown application
* application.navigateToUrl('../discover') // resolve to `/base-path/s/my-space/discover` which is not a path of a known app.
* application.navigateToUrl('../../other-space/discover') // resolve to `/base-path/s/other-space/discover` which is not within the current basePath.
* ```
*
* @param url - an absolute URL, an absolute path or a relative path, to navigate to.
* @param options - navigation options
*/
navigateToUrl(url: string, options?: NavigateToUrlOptions): Promise<void>;
/**
* Returns the absolute path (or URL) to a given app, including the global base path.
*
* By default, it returns the absolute path of the application (e.g `/basePath/app/my-app`).
* Use the `absolute` option to generate an absolute url instead (e.g `http://host:port/basePath/app/my-app`)
*
* Note that when generating absolute urls, the origin (protocol, host and port) are determined from the browser's current location.
*
* @param appId
* @param options.path - optional path inside application to deep link to
* @param options.absolute - if true, will returns an absolute url instead of a relative one
*/
getUrlForApp(
appId: string,
options?: { path?: string; absolute?: boolean; deepLinkId?: string }
): string;
/**
* An observable that emits the current application id and each subsequent id update.
*/
currentAppId$: Observable<string | undefined>;
}
/** @internal */
export interface InternalApplicationStart extends ApplicationStart {
// Internal APIs
getComponent(): JSX.Element | null;
/**
* The potential action menu set by the currently mounted app.
* Consumed by the chrome header.
*
* @internal
*/
currentActionMenu$: Observable<MountPoint | undefined>;
/**
* The global history instance, exposed only to Core.
* @internal
*/
history: History<unknown>;
}

View file

@ -13,12 +13,12 @@ import { toArray } from 'rxjs/operators';
import { injectedMetadataServiceMock } from '@kbn/core-injected-metadata-browser-mocks';
import { docLinksServiceMock } from '@kbn/core-doc-links-browser-mocks';
import { httpServiceMock } from '@kbn/core-http-browser-mocks';
import { App, PublicAppInfo } from '../application';
import { applicationServiceMock } from '../application/application_service.mock';
import type { App, PublicAppInfo } from '@kbn/core-application-browser';
import { applicationServiceMock } from '@kbn/core-application-browser-mocks';
import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks';
import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks';
import { getAppInfo } from '@kbn/core-application-browser-internal';
import { ChromeService } from './chrome_service';
import { getAppInfo } from '../application/utils';
class FakeApp implements App {
public title: string;

View file

@ -17,7 +17,7 @@ import type { DocLinksStart } from '@kbn/core-doc-links-browser';
import type { HttpStart } from '@kbn/core-http-browser';
import { mountReactNode } from '@kbn/core-mount-utils-browser-internal';
import type { NotificationsStart } from '@kbn/core-notifications-browser';
import type { InternalApplicationStart } from '../application';
import type { InternalApplicationStart } from '@kbn/core-application-browser-internal';
import { KIBANA_ASK_ELASTIC_LINK } from './constants';
import { type ChromeDocTitle, DocTitleService } from './doc_title';
import { type ChromeNavControls, NavControlsService } from './nav_controls';

View file

@ -6,10 +6,10 @@
* Side Public License, v 1.
*/
import { NavLinksService } from './nav_links_service';
import { take, map, takeLast } from 'rxjs/operators';
import { App } from '../../application';
import { BehaviorSubject } from 'rxjs';
import { take, map, takeLast } from 'rxjs/operators';
import type { App } from '@kbn/core-application-browser';
import { NavLinksService } from './nav_links_service';
const availableApps = new Map([
['app1', { id: 'app1', order: 0, title: 'App 1', icon: 'app1' }],

View file

@ -10,12 +10,9 @@ import { sortBy } from 'lodash';
import { BehaviorSubject, type Observable, ReplaySubject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import type { HttpStart, IBasePath } from '@kbn/core-http-browser';
import type { PublicAppDeepLinkInfo, PublicAppInfo } from '@kbn/core-application-browser';
import type { InternalApplicationStart } from '@kbn/core-application-browser-internal';
import type {
InternalApplicationStart,
PublicAppDeepLinkInfo,
PublicAppInfo,
} from '../../application';
import type { ChromeNavLink, NavLinkWrapper } from './nav_link';
import { toNavLink } from './to_nav_link';

View file

@ -7,11 +7,11 @@
*/
import {
PublicAppInfo,
AppNavLinkStatus,
AppStatus,
PublicAppDeepLinkInfo,
} from '../../application';
type PublicAppInfo,
type PublicAppDeepLinkInfo,
} from '@kbn/core-application-browser';
import { toNavLink } from './to_nav_link';
import { httpServiceMock } from '../../mocks';

View file

@ -8,14 +8,13 @@
import type { IBasePath } from '@kbn/core-http-browser';
import {
type PublicAppInfo,
AppNavLinkStatus,
AppStatus,
type PublicAppInfo,
type PublicAppDeepLinkInfo,
} from '../../application';
} from '@kbn/core-application-browser';
import { appendAppPath } from '@kbn/core-application-browser-internal';
import { NavLinkWrapper } from './nav_link';
import { appendAppPath } from '../../application/utils';
export function toNavLink(
app: PublicAppInfo,

View file

@ -23,10 +23,10 @@ import React, { Fragment, useMemo } from 'react';
import useObservable from 'react-use/lib/useObservable';
import * as Rx from 'rxjs';
import type { HttpStart } from '@kbn/core-http-browser';
import type { InternalApplicationStart } from '@kbn/core-application-browser-internal';
import type { AppCategory } from '@kbn/core-application-common';
import type { ChromeNavLink, ChromeRecentlyAccessedHistoryItem } from '../..';
import type { AppCategory } from '../../../../types';
import type { InternalApplicationStart } from '../../../application/types';
import type { OnIsLockedUpdate } from '.';
import {
createEuiListItem,

View file

@ -23,6 +23,7 @@ import React, { createRef, useState } from 'react';
import useObservable from 'react-use/lib/useObservable';
import type { Observable } from 'rxjs';
import type { HttpStart } from '@kbn/core-http-browser';
import type { InternalApplicationStart } from '@kbn/core-application-browser-internal';
import { LoadingIndicator } from '..';
import type {
@ -32,7 +33,6 @@ import type {
ChromeNavLink,
ChromeRecentlyAccessedHistoryItem,
} from '../..';
import type { InternalApplicationStart } from '../../../application/types';
import type {
ChromeBreadcrumbsAppendExtension,
ChromeHelpExtension,

View file

@ -24,7 +24,7 @@ import {
EuiHorizontalRule,
} from '@elastic/eui';
import { InternalApplicationStart } from '../../../application';
import type { InternalApplicationStart } from '@kbn/core-application-browser-internal';
import { GITHUB_CREATE_ISSUE_LINK, KIBANA_FEEDBACK_LINK } from '../../constants';
import { ChromeHelpExtension } from '../../types';
import { HeaderExtension } from './header_extension';

Some files were not shown because too many files have changed in this diff Show more