diff --git a/docs/user/dashboard/drilldowns.asciidoc b/docs/user/dashboard/drilldowns.asciidoc index e3582c20b6312..cbe47f23fcbaf 100644 --- a/docs/user/dashboard/drilldowns.asciidoc +++ b/docs/user/dashboard/drilldowns.asciidoc @@ -36,7 +36,7 @@ that shows a single data center or server. [[url-drilldowns]] ==== URL drilldowns -beta[] URL drilldowns enable you to navigate from a dashboard to internal or external URLs. +URL drilldowns enable you to navigate from a dashboard to internal or external URLs. Destination URLs can be dynamic, depending on the dashboard context or user interaction with a panel. For example, if you have a dashboard that shows data from a Github repository, you can create a URL drilldown that opens Github from the dashboard. @@ -226,7 +226,7 @@ and date range are carried over. [[create-a-url-drilldown]] === Try it: Create a URL drilldown -beta[] To create URL drilldowns, you add <> to a URL template, which configures the bahavior of the drilldown. +To create URL drilldowns, you add <> to a URL template, which configures the behavior of the drilldown. image:images/url_drilldown_go_to_github.gif[Drilldown on pie chart that navigates to Github] diff --git a/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx b/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx index c5ae2127ac030..2e629940ea4e5 100644 --- a/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx +++ b/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx @@ -13,9 +13,12 @@ import { Query } from '@elastic/eui'; import { parse } from 'query-string'; import { i18n } from '@kbn/i18n'; import { CoreStart, ChromeBreadcrumb } from 'src/core/public'; +import type { + SpacesAvailableStartContract, + SpacesContextProps, +} from 'src/plugins/spaces_oss/public'; import { DataPublicPluginStart } from '../../../data/public'; import { SavedObjectsTaggingApi } from '../../../saved_objects_tagging_oss/public'; -import type { SpacesAvailableStartContract } from '../../../spaces_oss/public'; import { ISavedObjectsManagementServiceRegistry, SavedObjectsManagementActionServiceStart, @@ -23,7 +26,7 @@ import { } from '../services'; import { SavedObjectsTable } from './objects_table'; -const EmptyFunctionComponent: React.FC = ({ children }) => <>{children}; +const getEmptyFunctionComponent: React.FC = ({ children }) => <>{children}; const SavedObjectsTablePage = ({ coreStart, @@ -71,7 +74,8 @@ const SavedObjectsTablePage = ({ }, [setBreadcrumbs]); const ContextWrapper = useMemo( - () => spacesApi?.ui.components.SpacesContext || EmptyFunctionComponent, + () => + spacesApi ? spacesApi.ui.components.getSpacesContextProvider : getEmptyFunctionComponent, [spacesApi] ); diff --git a/src/plugins/spaces_oss/public/api.mock.ts b/src/plugins/spaces_oss/public/api.mock.ts index c4a410c76e796..1f0899ca1df60 100644 --- a/src/plugins/spaces_oss/public/api.mock.ts +++ b/src/plugins/spaces_oss/public/api.mock.ts @@ -32,10 +32,11 @@ type SpacesApiUiComponentMock = jest.Mocked; const createApiUiComponentsMock = () => { const mock: SpacesApiUiComponentMock = { - SpacesContext: jest.fn(), - ShareToSpaceFlyout: jest.fn(), - SpaceList: jest.fn(), - LegacyUrlConflict: jest.fn(), + getSpacesContextProvider: jest.fn(), + getShareToSpaceFlyout: jest.fn(), + getSpaceList: jest.fn(), + getLegacyUrlConflict: jest.fn(), + getSpaceAvatar: jest.fn(), }; return mock; diff --git a/src/plugins/spaces_oss/public/api.ts b/src/plugins/spaces_oss/public/api.ts index 2d5e144158d78..3c10995b75003 100644 --- a/src/plugins/spaces_oss/public/api.ts +++ b/src/plugins/spaces_oss/public/api.ts @@ -7,7 +7,7 @@ */ import { Observable } from 'rxjs'; -import type { FunctionComponent } from 'react'; +import type { ReactElement } from 'react'; import { Space } from '../common'; /** @@ -22,12 +22,19 @@ export interface SpacesApi { ui: SpacesApiUi; } +/** + * Function that returns a promise for a lazy-loadable component. + * + * @public + */ +export type LazyComponentFn = (props: T) => ReactElement; + /** * @public */ export interface SpacesApiUi { /** - * {@link SpacesApiUiComponent | React components} to support the spaces feature. + * Lazy-loadable {@link SpacesApiUiComponent | React components} to support the spaces feature. */ components: SpacesApiUiComponent; /** @@ -62,13 +69,13 @@ export interface SpacesApiUiComponent { /** * Provides a context that is required to render some Spaces components. */ - SpacesContext: FunctionComponent; + getSpacesContextProvider: LazyComponentFn; /** * Displays a flyout to edit the spaces that an object is shared to. * * Note: must be rendered inside of a SpacesContext. */ - ShareToSpaceFlyout: FunctionComponent; + getShareToSpaceFlyout: LazyComponentFn; /** * Displays a corresponding list of spaces for a given list of saved object namespaces. It shows up to five spaces (and an indicator for * any number of spaces that the user is not authorized to see) by default. If more than five named spaces would be displayed, the extras @@ -77,7 +84,7 @@ export interface SpacesApiUiComponent { * * Note: must be rendered inside of a SpacesContext. */ - SpaceList: FunctionComponent; + getSpaceList: LazyComponentFn; /** * Displays a callout that needs to be used if a call to `SavedObjectsClient.resolve()` results in an `"conflict"` outcome, which * indicates that the user has loaded the page which is associated directly with one object (A), *and* with a legacy URL that points to a @@ -95,7 +102,11 @@ export interface SpacesApiUiComponent { * * New URL path: `#/workpad/workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e/page/1` */ - LegacyUrlConflict: FunctionComponent; + getLegacyUrlConflict: LazyComponentFn; + /** + * Displays an avatar for the given space. + */ + getSpaceAvatar: LazyComponentFn; } /** @@ -251,3 +262,18 @@ export interface LegacyUrlConflictProps { */ otherObjectPath: string; } + +/** + * @public + */ +export interface SpaceAvatarProps { + space: Partial; + size?: 's' | 'm' | 'l' | 'xl'; + className?: string; + /** + * When enabled, allows EUI to provide an aria-label for this component, which is announced on screen readers. + * + * Default value is true. + */ + announceSpaceName?: boolean; +} diff --git a/src/plugins/spaces_oss/public/index.ts b/src/plugins/spaces_oss/public/index.ts index be42bd9a899b1..828ce6f4dec63 100644 --- a/src/plugins/spaces_oss/public/index.ts +++ b/src/plugins/spaces_oss/public/index.ts @@ -16,6 +16,7 @@ export { } from './types'; export { + LazyComponentFn, SpacesApi, SpacesApiUi, SpacesApiUiComponent, @@ -24,6 +25,7 @@ export { ShareToSpaceSavedObjectTarget, SpaceListProps, LegacyUrlConflictProps, + SpaceAvatarProps, } from './api'; export const plugin = () => new SpacesOssPlugin(); diff --git a/src/plugins/vis_default_editor/public/components/options/index.ts b/src/plugins/vis_default_editor/public/components/options/index.ts index ccde0248a73e2..31b09977f5c99 100644 --- a/src/plugins/vis_default_editor/public/components/options/index.ts +++ b/src/plugins/vis_default_editor/public/components/options/index.ts @@ -15,3 +15,4 @@ export { NumberInputOption } from './number_input'; export { RangeOption } from './range'; export { RequiredNumberInputOption } from './required_number_input'; export { TextInputOption } from './text_input'; +export { PercentageModeOption } from './percentage_mode'; diff --git a/src/plugins/vis_default_editor/public/components/options/percentage_mode.test.tsx b/src/plugins/vis_default_editor/public/components/options/percentage_mode.test.tsx new file mode 100644 index 0000000000000..05d321a7b465c --- /dev/null +++ b/src/plugins/vis_default_editor/public/components/options/percentage_mode.test.tsx @@ -0,0 +1,48 @@ +/* + * 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 React from 'react'; +import { mountWithIntl } from '@kbn/test/jest'; +import { PercentageModeOption, PercentageModeOptionProps } from './percentage_mode'; +import { EuiFieldText } from '@elastic/eui'; + +describe('PercentageModeOption', () => { + let props: PercentageModeOptionProps; + let component; + beforeAll(() => { + props = { + percentageMode: true, + setValue: jest.fn(), + }; + }); + + it('renders the EuiFieldText', () => { + component = mountWithIntl(); + expect(component.find(EuiFieldText).length).toBe(1); + }); + + it('should call setValue when value was putted in fieldText', () => { + component = mountWithIntl(); + const fieldText = component.find(EuiFieldText); + fieldText.props().onChange!({ + target: { + value: '0.0%', + }, + } as React.ChangeEvent); + + expect(props.setValue).toHaveBeenCalledWith('percentageFormatPattern', '0.0%'); + }); + + it('fieldText should be disabled when percentageMode is false', () => { + props.percentageMode = false; + component = mountWithIntl(); + const fieldText = component.find(EuiFieldText); + + expect(fieldText.props().disabled).toBeTruthy(); + }); +}); diff --git a/src/plugins/vis_default_editor/public/components/options/percentage_mode.tsx b/src/plugins/vis_default_editor/public/components/options/percentage_mode.tsx new file mode 100644 index 0000000000000..542055d185ec5 --- /dev/null +++ b/src/plugins/vis_default_editor/public/components/options/percentage_mode.tsx @@ -0,0 +1,80 @@ +/* + * 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 React from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFieldText, EuiFormRow, EuiLink } from '@elastic/eui'; +import { SwitchOption } from './switch'; +import { useKibana } from '../../../../kibana_react/public'; +import { UI_SETTINGS } from '../../../../data/public'; + +export interface PercentageModeOptionProps { + setValue: ( + paramName: 'percentageMode' | 'percentageFormatPattern', + value: boolean | string | undefined + ) => void; + percentageMode: boolean; + formatPattern?: string; + 'data-test-subj'?: string; +} + +function PercentageModeOption({ + 'data-test-subj': dataTestSubj, + setValue, + percentageMode, + formatPattern, +}: PercentageModeOptionProps) { + const { services } = useKibana(); + const defaultPattern = services.uiSettings?.get(UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN); + + return ( + <> + + + } + helpText={ + + + + } + > + { + setValue('percentageFormatPattern', e.target.value ? e.target.value : undefined); + }} + disabled={!percentageMode} + /> + + + ); +} + +export { PercentageModeOption }; diff --git a/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx b/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx index 8faab4b8f1dba..5c6c4bf95b4f2 100644 --- a/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx +++ b/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx @@ -26,6 +26,7 @@ import { SetColorSchemaOptionsValue, ColorSchemaOptions, RangeOption, + PercentageModeOption, } from '../../../vis_default_editor/public'; import { ColorMode, colorSchemas } from '../../../charts/public'; import { MetricVisParam, VisParams } from '../types'; @@ -113,12 +114,10 @@ function MetricVisOptions({ - diff --git a/src/plugins/vis_type_metric/public/to_ast.ts b/src/plugins/vis_type_metric/public/to_ast.ts index 54692c0c107bf..ec9c2b3b0157e 100644 --- a/src/plugins/vis_type_metric/public/to_ast.ts +++ b/src/plugins/vis_type_metric/public/to_ast.ts @@ -43,6 +43,7 @@ export const toExpressionAst: VisToExpressionAst = (vis, params) => { const { percentageMode, + percentageFormatPattern, useRanges, colorSchema, metricColorMode, @@ -55,7 +56,10 @@ export const toExpressionAst: VisToExpressionAst = (vis, params) => { // fix formatter for percentage mode if (get(vis.params, 'metric.percentageMode') === true) { schemas.metric.forEach((metric: SchemaConfig) => { - metric.format = { id: 'percent' }; + metric.format = { + id: 'percent', + params: { pattern: percentageFormatPattern }, + }; }); } diff --git a/src/plugins/vis_type_metric/public/types.ts b/src/plugins/vis_type_metric/public/types.ts index 5840e8c12d3d4..45b8e17425891 100644 --- a/src/plugins/vis_type_metric/public/types.ts +++ b/src/plugins/vis_type_metric/public/types.ts @@ -19,6 +19,7 @@ export interface DimensionsVisParam { export interface MetricVisParam { percentageMode: boolean; + percentageFormatPattern?: string; useRanges: boolean; colorSchema: ColorSchemas; metricColorMode: ColorMode; diff --git a/src/plugins/vis_type_vislib/public/editor/components/gauge/ranges_panel.tsx b/src/plugins/vis_type_vislib/public/editor/components/gauge/ranges_panel.tsx index 3e37348bf25e1..5091c29c28752 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/gauge/ranges_panel.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/gauge/ranges_panel.tsx @@ -15,6 +15,7 @@ import { SetColorRangeValue, SwitchOption, ColorSchemaOptions, + PercentageModeOption, } from '../../../../../vis_default_editor/public'; import { ColorSchemaParams, ColorSchemas, colorSchemas } from '../../../../../charts/public'; import { GaugeOptionsInternalProps } from '../gauge'; @@ -77,13 +78,10 @@ function RangesPanel({ setValue={setGaugeValue} /> - diff --git a/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx b/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx index 20fa1efc3b6e7..bdabded67a74a 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx @@ -23,6 +23,7 @@ import { SetColorSchemaOptionsValue, ColorSchemaOptions, NumberInputOption, + PercentageModeOption, } from '../../../../../vis_default_editor/public'; import { HeatmapVisParams } from '../../../heatmap'; @@ -125,13 +126,10 @@ function HeatmapOptions(props: VisEditorOptionsProps) { setValue={setValueAxisScale} /> - diff --git a/src/plugins/vis_type_vislib/public/fixtures/mocks.js b/src/plugins/vis_type_vislib/public/fixtures/mocks.js index 7eca949863638..b81210bd965bd 100644 --- a/src/plugins/vis_type_vislib/public/fixtures/mocks.js +++ b/src/plugins/vis_type_vislib/public/fixtures/mocks.js @@ -12,6 +12,9 @@ setFormatService({ deserialize: () => ({ convert: (v) => v, }), + getInstance: () => ({ + convert: (v) => v, + }), }); export const getMockUiState = () => { diff --git a/src/plugins/vis_type_vislib/public/gauge.ts b/src/plugins/vis_type_vislib/public/gauge.ts index 315c4388a5cd3..172ce83b4f7c2 100644 --- a/src/plugins/vis_type_vislib/public/gauge.ts +++ b/src/plugins/vis_type_vislib/public/gauge.ts @@ -28,6 +28,7 @@ export interface Gauge extends ColorSchemaParams { gaugeType: GaugeType; labels: Labels; percentageMode: boolean; + percentageFormatPattern?: string; outline?: boolean; scale: { show: boolean; diff --git a/src/plugins/vis_type_vislib/public/heatmap.ts b/src/plugins/vis_type_vislib/public/heatmap.ts index f804a78cbe453..6f6160f3756fd 100644 --- a/src/plugins/vis_type_vislib/public/heatmap.ts +++ b/src/plugins/vis_type_vislib/public/heatmap.ts @@ -29,6 +29,7 @@ export interface HeatmapVisParams extends CommonVislibParams, ColorSchemaParams valueAxes: ValueAxis[]; setColorRange: boolean; percentageMode: boolean; + percentageFormatPattern?: string; times: TimeMarker[]; } diff --git a/src/plugins/vis_type_vislib/public/to_ast.ts b/src/plugins/vis_type_vislib/public/to_ast.ts index bed39591df61b..5894b4be964e0 100644 --- a/src/plugins/vis_type_vislib/public/to_ast.ts +++ b/src/plugins/vis_type_vislib/public/to_ast.ts @@ -78,7 +78,10 @@ export const toExpressionAst = async ( } } if (visConfig?.gauge?.percentageMode === true) { - yDimension.format = { id: 'percent' }; + yDimension.format = { + id: 'percent', + params: { pattern: visConfig?.gauge?.percentageFormatPattern }, + }; } }); diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js index 8765f6266692e..cb8a8f72c5172 100644 --- a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js +++ b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js @@ -6,14 +6,33 @@ * Side Public License, v 1. */ +import { last } from 'lodash'; import React from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; +import { UI_SETTINGS } from '../../../../../../plugins/data/public'; +import { getValueForPercentageMode } from '../../percentage_mode_transform'; + +function getMax(handler, config, isGauge) { + let max; + if (handler.pointSeries) { + const series = handler.pointSeries.getSeries(); + const scale = series.getValueAxis().getScale(); + max = scale.domain()[1]; + } else { + max = last(config.get(isGauge ? 'gauge.colorsRange' : 'colorsRange', [{}])).to; + } + + return max; +} export function pointSeriesTooltipFormatter() { - return function tooltipFormatter({ datum, data }) { + return function tooltipFormatter({ datum, data, config, handler }, uiSettings) { if (!datum) return ''; const details = []; + const isGauge = config.get('gauge', false); + const isPercentageMode = config.get(isGauge ? 'gauge.percentageMode' : 'percentageMode', false); + const isSetColorRange = config.get('setColorRange', false); const currentSeries = data.series && data.series.find((serie) => serie.rawId === datum.seriesId); @@ -30,8 +49,20 @@ export function pointSeriesTooltipFormatter() { } if (datum.y !== null && datum.y !== undefined) { - const value = datum.yScale ? datum.yScale * datum.y : datum.y; - addDetail(currentSeries.label, currentSeries.yAxisFormatter(value)); + let value = datum.yScale ? datum.yScale * datum.y : datum.y; + if (isPercentageMode && !isSetColorRange) { + const percentageFormatPattern = config.get( + isGauge ? 'gauge.percentageFormatPattern' : 'percentageFormatPattern', + uiSettings.get(UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN) + ); + value = getValueForPercentageMode( + value / getMax(handler, config, isGauge), + percentageFormatPattern + ); + addDetail(currentSeries.label, value); + } else { + addDetail(currentSeries.label, currentSeries.yAxisFormatter(value)); + } } if (datum.z !== null && datum.z !== undefined) { diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.test.js b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.test.js index 98c95de41dae4..5c0548ea399b7 100644 --- a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.test.js +++ b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.test.js @@ -43,11 +43,36 @@ describe('tooltipFormatter', function () { extraMetrics: [], seriesId: '1', }, + config: { + get: (name) => { + const config = { + setColorRange: false, + gauge: false, + percentageMode: false, + }; + return config[name]; + }, + }, + handler: { + pointSeries: { + getSeries: () => ({ + getValueAxis: () => ({ + getScale: () => ({ + domain: () => [0, 10], + }), + }), + }), + }, + }, + }; + + const uiSettings = { + get: () => '', }; it('returns html based on the mouse event', function () { const event = _.cloneDeep(baseEvent); - const $el = $(tooltipFormatter(event)); + const $el = $(tooltipFormatter(event, uiSettings)); const $rows = $el.find('tr'); expect($rows.length).toBe(3); @@ -67,7 +92,7 @@ describe('tooltipFormatter', function () { it('renders correctly on missing extraMetrics in datum', function () { const event = _.cloneDeep(baseEvent); delete event.datum.extraMetrics; - const $el = $(tooltipFormatter(event)); + const $el = $(tooltipFormatter(event, uiSettings)); const $rows = $el.find('tr'); expect($rows.length).toBe(3); }); diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/tooltip.js b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/tooltip.js index b1e8ef5862b8d..e2decb86c9032 100644 --- a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/tooltip.js +++ b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/tooltip.js @@ -29,9 +29,9 @@ const tooltipMaxWidth = parseInt(theme.euiSizeXL || 0, 10) * 10; * @param formatter {Function} Tooltip formatter * @param events {Constructor} Allows tooltip to return event response data */ -export function Tooltip(id, el, formatter, events) { +export function Tooltip(id, el, formatter, events, uiSettings) { if (!(this instanceof Tooltip)) { - return new Tooltip(id, el, formatter, events); + return new Tooltip(id, el, formatter, events, uiSettings); } this.id = id; // unique id for this tooltip type @@ -39,6 +39,7 @@ export function Tooltip(id, el, formatter, events) { this.order = 100; // higher ordered contents are rendered below the others this.formatter = formatter; this.events = events; + this.uiSettings = uiSettings; this.containerClass = 'visWrapper'; this.tooltipClass = 'visTooltip'; this.tooltipSizerClass = 'visTooltip__sizingClone'; @@ -223,7 +224,7 @@ Tooltip.prototype.render = function () { } const events = self.events ? self.events.eventResponse(d, i) : d; - return render(self.formatter(events)); + return render(self.formatter(events, self.uiSettings)); }); self.binder.fakeD3Bind(this, 'mouseleave', function () { diff --git a/src/plugins/vis_type_vislib/public/vislib/percentage_mode_transform.ts b/src/plugins/vis_type_vislib/public/vislib/percentage_mode_transform.ts new file mode 100644 index 0000000000000..2aa33dc4a5459 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/vislib/percentage_mode_transform.ts @@ -0,0 +1,20 @@ +/* + * 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. + */ + +// @ts-ignore +import numeral from '@elastic/numeral'; +import { getFormatService } from '../services'; + +export function getValueForPercentageMode(value: string | number, percentageFormatPattern: string) { + const formatServices = getFormatService(); + const percentFormatter = formatServices.getInstance('percent', { + pattern: percentageFormatPattern, + }); + + return percentFormatter.convert(value); +} diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/_chart.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/_chart.js index b88500c9985cd..281415503349f 100644 --- a/src/plugins/vis_type_vislib/public/vislib/visualizations/_chart.js +++ b/src/plugins/vis_type_vislib/public/vislib/visualizations/_chart.js @@ -49,7 +49,7 @@ export class Chart { const element = this.handler.el; // Add tooltip - this.tooltip = new Tooltip('chart', element, tooltipFormatter, events); + this.tooltip = new Tooltip('chart', element, tooltipFormatter, events, uiSettings); this.tooltips.push(this.tooltip); } diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/gauge_chart.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/gauge_chart.js index 25c3e0bd7e903..c31307fe100ae 100644 --- a/src/plugins/vis_type_vislib/public/vislib/visualizations/gauge_chart.js +++ b/src/plugins/vis_type_vislib/public/vislib/visualizations/gauge_chart.js @@ -14,7 +14,7 @@ export class GaugeChart extends Chart { constructor(handler, chartEl, chartData, uiSettings) { super(handler, chartEl, chartData, uiSettings); this.gaugeConfig = handler.visConfig.get('gauge', {}); - this.gauge = new gaugeTypes[this.gaugeConfig.type](this); + this.gauge = new gaugeTypes[this.gaugeConfig.type](this, uiSettings); } addEvents(element) { diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js index 0c627245a2110..b7f5b966a08c6 100644 --- a/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js +++ b/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js @@ -10,6 +10,8 @@ import d3 from 'd3'; import _ from 'lodash'; import { getHeatmapColors } from '../../../../../charts/public'; +import { UI_SETTINGS } from '../../../../../data/public'; +import { getValueForPercentageMode } from '../../percentage_mode_transform'; const arcAngles = { angleFactor: 0.75, @@ -47,9 +49,10 @@ const defaultConfig = { }; export class MeterGauge { - constructor(gaugeChart) { + constructor(gaugeChart, uiSettings) { this.gaugeChart = gaugeChart; this.gaugeConfig = gaugeChart.gaugeConfig; + this.uiSettings = uiSettings; this.gaugeConfig = _.defaultsDeep(this.gaugeConfig, defaultConfig); this.gaugeChart.handler.visConfig.set('legend', { @@ -68,12 +71,19 @@ export class MeterGauge { getLabels() { const isPercentageMode = this.gaugeConfig.percentageMode; + const percentageFormatPattern = + this.gaugeConfig.percentageFormatPattern || + this.uiSettings.get(UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN); const colorsRange = this.gaugeConfig.colorsRange; const max = _.last(colorsRange).to; const labels = []; colorsRange.forEach((range) => { - const from = isPercentageMode ? Math.round((100 * range.from) / max) : range.from; - const to = isPercentageMode ? Math.round((100 * range.to) / max) : range.to; + const from = isPercentageMode + ? getValueForPercentageMode(range.from / max, percentageFormatPattern) + : range.from; + const to = isPercentageMode + ? getValueForPercentageMode(range.to / max, percentageFormatPattern) + : range.to; labels.push(`${from} - ${to}`); }); diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/area_chart.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/area_chart.js index 43bae92bf0aac..2e2ce79247c3d 100644 --- a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/area_chart.js +++ b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/area_chart.js @@ -32,8 +32,8 @@ const defaults = { * chart */ export class AreaChart extends PointSeries { - constructor(handler, chartEl, chartData, seriesConfigArgs, core) { - super(handler, chartEl, chartData, seriesConfigArgs, core); + constructor(handler, chartEl, chartData, seriesConfigArgs, uiSettings) { + super(handler, chartEl, chartData, seriesConfigArgs, uiSettings); this.seriesConfig = _.defaults(seriesConfigArgs || {}, defaults); this.isOverlapping = this.seriesConfig.mode !== 'stacked'; diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/column_chart.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/column_chart.js index 8982213b5c5ee..1c543d06e9be9 100644 --- a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/column_chart.js +++ b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/column_chart.js @@ -46,8 +46,8 @@ function datumWidth(defaultWidth, datum, nextDatum, scale, gutterWidth, groupCou * @param chartData {Object} Elasticsearch query results for this specific chart */ export class ColumnChart extends PointSeries { - constructor(handler, chartEl, chartData, seriesConfigArgs, core) { - super(handler, chartEl, chartData, seriesConfigArgs, core); + constructor(handler, chartEl, chartData, seriesConfigArgs, uiSettings) { + super(handler, chartEl, chartData, seriesConfigArgs, uiSettings); this.seriesConfig = _.defaults(seriesConfigArgs || {}, defaults); this.labelOptions = _.defaults(handler.visConfig.get('labels', {}), defaults.showLabel); } diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js index 4911e781180cf..0dc1e18270f78 100644 --- a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js +++ b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js @@ -13,6 +13,8 @@ import { isColorDark } from '@elastic/eui'; import { PointSeries } from './_point_series'; import { getHeatmapColors } from '../../../../../../plugins/charts/public'; +import { UI_SETTINGS } from '../../../../../../plugins/data/public'; +import { getValueForPercentageMode } from '../../percentage_mode_transform'; const defaults = { color: undefined, // todo @@ -29,8 +31,10 @@ const defaults = { * @param chartData {Object} Elasticsearch query results for this specific chart */ export class HeatmapChart extends PointSeries { - constructor(handler, chartEl, chartData, seriesConfigArgs, core) { - super(handler, chartEl, chartData, seriesConfigArgs, core); + constructor(handler, chartEl, chartData, seriesConfigArgs, uiSettings) { + super(handler, chartEl, chartData, seriesConfigArgs, uiSettings); + + this.uiSettings = uiSettings; this.seriesConfig = _.defaults(seriesConfigArgs || {}, defaults); @@ -48,6 +52,10 @@ export class HeatmapChart extends PointSeries { getHeatmapLabels(cfg) { const percentageMode = cfg.get('percentageMode'); + const percentageFormatPattern = cfg.get( + 'percentageFormatPattern', + this.uiSettings.get(UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN) + ); const colorsNumber = cfg.get('colorsNumber'); const colorsRange = cfg.get('colorsRange'); const zAxisConfig = this.getValueAxis().axisConfig; @@ -71,9 +79,9 @@ export class HeatmapChart extends PointSeries { let val = i / colorsNumber; let nextVal = (i + 1) / colorsNumber; if (percentageMode) { - val = Math.ceil(val * 100); - nextVal = Math.ceil(nextVal * 100); - label = `${val}% - ${nextVal}%`; + val = getValueForPercentageMode(val, percentageFormatPattern); + nextVal = getValueForPercentageMode(nextVal, percentageFormatPattern); + label = `${val} - ${nextVal}`; } else { val = val * (max - min) + min; nextVal = nextVal * (max - min) + min; diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.test.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.test.js index 5903b1ce6c3e5..d9bac77acf5ad 100644 --- a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.test.js +++ b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.test.js @@ -68,6 +68,7 @@ describe('Vislib Heatmap Chart Test Suite', function () { colorSchema: 'Greens', setColorRange: false, percentageMode: true, + percentageFormatPattern: '0.0%', invertColors: false, colorsRange: [], }; diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/line_chart.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/line_chart.js index fa7a9e3f6ff24..4476574c940bc 100644 --- a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/line_chart.js +++ b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/line_chart.js @@ -31,8 +31,8 @@ const defaults = { * @param chartData {Object} Elasticsearch query results for this specific chart */ export class LineChart extends PointSeries { - constructor(handler, chartEl, chartData, seriesConfigArgs, core) { - super(handler, chartEl, chartData, seriesConfigArgs, core); + constructor(handler, chartEl, chartData, seriesConfigArgs, uiSettings) { + super(handler, chartEl, chartData, seriesConfigArgs, uiSettings); this.seriesConfig = _.defaults(seriesConfigArgs || {}, defaults); } diff --git a/test/functional/apps/visualize/_gauge_chart.ts b/test/functional/apps/visualize/_gauge_chart.ts index f346f21f69455..0153e022e71b3 100644 --- a/test/functional/apps/visualize/_gauge_chart.ts +++ b/test/functional/apps/visualize/_gauge_chart.ts @@ -51,11 +51,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visEditor.clickOptionsTab(); await testSubjects.setValue('gaugeColorRange2__to', '10000'); await testSubjects.click('gaugePercentageMode'); + await testSubjects.setValue('gaugePercentageModeFormatPattern', '0.0%'); await PageObjects.visChart.waitForVisualizationRenderingStabilized(); await PageObjects.visEditor.clickGo(); await retry.try(async function tryingForTime() { - const expectedTexts = ['57.273%', 'Average bytes']; + const expectedTexts = ['57.3%', 'Average bytes']; const metricValue = await PageObjects.visChart.getGaugeValue(); expect(expectedTexts).to.eql(metricValue); }); diff --git a/x-pack/plugins/apm/common/utils/as_mutable_array.ts b/x-pack/plugins/apm/common/utils/as_mutable_array.ts new file mode 100644 index 0000000000000..ce1d7e607ec4c --- /dev/null +++ b/x-pack/plugins/apm/common/utils/as_mutable_array.ts @@ -0,0 +1,41 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// Sometimes we use `as const` to have a more specific type, +// because TypeScript by default will widen the value type of an +// array literal. Consider the following example: +// +// const filter = [ +// { term: { 'agent.name': 'nodejs' } }, +// { range: { '@timestamp': { gte: 'now-15m ' }} +// ]; + +// The result value type will be: + +// const filter: ({ +// term: { +// 'agent.name'?: string +// }; +// range?: undefined +// } | { +// term?: undefined; +// range: { +// '@timestamp': { +// gte: string +// } +// } +// })[]; + +// This can sometimes leads to issues. In those cases, we can +// use `as const`. However, the Readonly type is not compatible +// with Array. This function returns a mutable version of a type. + +export function asMutableArray>( + arr: T +): T extends Readonly<[...infer U]> ? U : unknown[] { + return arr as any; +} diff --git a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap index dec5be8da32f4..7e7e073c0d2f6 100644 --- a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap @@ -118,7 +118,6 @@ Array [ "environments": Object { "terms": Object { "field": "service.environment", - "missing": "", }, }, "outcomes": Object { @@ -197,6 +196,56 @@ Array [ "size": 0, }, }, + Object { + "apm": Object { + "events": Array [ + "metric", + ], + }, + "body": Object { + "aggs": Object { + "services": Object { + "aggs": Object { + "environments": Object { + "terms": Object { + "field": "service.environment", + }, + }, + "latest": Object { + "top_metrics": Object { + "metrics": Object { + "field": "agent.name", + }, + "sort": Object { + "@timestamp": "desc", + }, + }, + }, + }, + "terms": Object { + "field": "service.name", + "size": 500, + }, + }, + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "range": Object { + "@timestamp": Object { + "format": "epoch_millis", + "gte": 1528113600000, + "lte": 1528977600000, + }, + }, + }, + ], + }, + }, + "size": 0, + }, + }, ] `; diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts index 5f0302035462c..10c7420d0f3b0 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts @@ -40,15 +40,15 @@ interface AggregationParams { kuery?: string; setup: ServicesItemsSetup; searchAggregatedTransactions: boolean; + maxNumServices: number; } -const MAX_NUMBER_OF_SERVICES = 500; - export async function getServiceTransactionStats({ environment, kuery, setup, searchAggregatedTransactions, + maxNumServices, }: AggregationParams) { return withApmSpan('get_service_transaction_stats', async () => { const { apmEventClient, start, end } = setup; @@ -92,7 +92,7 @@ export async function getServiceTransactionStats({ services: { terms: { field: SERVICE_NAME, - size: MAX_NUMBER_OF_SERVICES, + size: maxNumServices, }, aggs: { transactionType: { @@ -104,7 +104,6 @@ export async function getServiceTransactionStats({ environments: { terms: { field: SERVICE_ENVIRONMENT, - missing: '', }, }, sample: { @@ -147,9 +146,9 @@ export async function getServiceTransactionStats({ return { serviceName: bucket.key as string, transactionType: topTransactionTypeBucket.key as string, - environments: topTransactionTypeBucket.environments.buckets - .map((environmentBucket) => environmentBucket.key as string) - .filter(Boolean), + environments: topTransactionTypeBucket.environments.buckets.map( + (environmentBucket) => environmentBucket.key as string + ), agentName: topTransactionTypeBucket.sample.top[0].metrics[ AGENT_NAME ] as AgentName, diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_from_metric_documents.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_from_metric_documents.ts new file mode 100644 index 0000000000000..cabd44c1e6907 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_from_metric_documents.ts @@ -0,0 +1,84 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent'; +import { + AGENT_NAME, + SERVICE_ENVIRONMENT, + SERVICE_NAME, +} from '../../../../common/elasticsearch_fieldnames'; +import { environmentQuery, kqlQuery, rangeQuery } from '../../../utils/queries'; +import { ProcessorEvent } from '../../../../common/processor_event'; +import { Setup, SetupTimeRange } from '../../helpers/setup_request'; +import { withApmSpan } from '../../../utils/with_apm_span'; + +export function getServicesFromMetricDocuments({ + environment, + setup, + maxNumServices, + kuery, +}: { + setup: Setup & SetupTimeRange; + environment?: string; + maxNumServices: number; + kuery?: string; +}) { + return withApmSpan('get_services_from_metric_documents', async () => { + const { apmEventClient, start, end } = setup; + + const response = await apmEventClient.search({ + apm: { + events: [ProcessorEvent.metric], + }, + body: { + size: 0, + query: { + bool: { + filter: [ + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ], + }, + }, + aggs: { + services: { + terms: { + field: SERVICE_NAME, + size: maxNumServices, + }, + aggs: { + environments: { + terms: { + field: SERVICE_ENVIRONMENT, + }, + }, + latest: { + top_metrics: { + metrics: { field: AGENT_NAME } as const, + sort: { '@timestamp': 'desc' }, + }, + }, + }, + }, + }, + }, + }); + + return ( + response.aggregations?.services.buckets.map((bucket) => { + return { + serviceName: bucket.key as string, + environments: bucket.environments.buckets.map( + (envBucket) => envBucket.key as string + ), + agentName: bucket.latest.top[0].metrics[AGENT_NAME] as AgentName, + }; + }) ?? [] + ); + }); +} diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts index 1ddc7a6583c81..3b9792519d261 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts @@ -6,15 +6,18 @@ */ import { Logger } from '@kbn/logging'; +import { asMutableArray } from '../../../../common/utils/as_mutable_array'; import { joinByKey } from '../../../../common/utils/join_by_key'; -import { getServicesProjection } from '../../../projections/services'; import { withApmSpan } from '../../../utils/with_apm_span'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { getHealthStatuses } from './get_health_statuses'; +import { getServicesFromMetricDocuments } from './get_services_from_metric_documents'; import { getServiceTransactionStats } from './get_service_transaction_stats'; export type ServicesItemsSetup = Setup & SetupTimeRange; +const MAX_NUMBER_OF_SERVICES = 500; + export async function getServicesItems({ environment, kuery, @@ -32,33 +35,49 @@ export async function getServicesItems({ const params = { environment, kuery, - projection: getServicesProjection({ - kuery, - setup, - searchAggregatedTransactions, - }), setup, searchAggregatedTransactions, + maxNumServices: MAX_NUMBER_OF_SERVICES, }; - const [transactionStats, healthStatuses] = await Promise.all([ + const [ + transactionStats, + servicesFromMetricDocuments, + healthStatuses, + ] = await Promise.all([ getServiceTransactionStats(params), + getServicesFromMetricDocuments(params), getHealthStatuses(params).catch((err) => { logger.error(err); return []; }), ]); - const apmServices = transactionStats.map(({ serviceName }) => serviceName); + const foundServiceNames = transactionStats.map( + ({ serviceName }) => serviceName + ); + + const servicesWithOnlyMetricDocuments = servicesFromMetricDocuments.filter( + ({ serviceName }) => !foundServiceNames.includes(serviceName) + ); + + const allServiceNames = foundServiceNames.concat( + servicesWithOnlyMetricDocuments.map(({ serviceName }) => serviceName) + ); // make sure to exclude health statuses from services // that are not found in APM data const matchedHealthStatuses = healthStatuses.filter(({ serviceName }) => - apmServices.includes(serviceName) + allServiceNames.includes(serviceName) ); - const allMetrics = [...transactionStats, ...matchedHealthStatuses]; - - return joinByKey(allMetrics, 'serviceName'); + return joinByKey( + asMutableArray([ + ...transactionStats, + ...servicesWithOnlyMetricDocuments, + ...matchedHealthStatuses, + ] as const), + 'serviceName' + ); }); } diff --git a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx index bf2ed8c2a45d1..617f5a25ebbc5 100644 --- a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx +++ b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx @@ -79,7 +79,6 @@ export class UrlDrilldown implements Drilldown txtUrlDrilldownDisplayName; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx index 64190f5557707..b2cca858cec4b 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx @@ -26,16 +26,12 @@ import { Aggregators, METRIC_THRESHOLD_ALERT_TYPE_ID, } from '../../../../common/alerting/metrics'; -import { - ForLastExpression, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../triggers_actions_ui/public/common'; +import { ForLastExpression } from '../../../../../triggers_actions_ui/public'; import { IErrorObject, AlertTypeParams, AlertTypeParamsExpressionProps, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../triggers_actions_ui/public/types'; +} from '../../../../../triggers_actions_ui/public'; import { MetricsExplorerKueryBar } from '../../../pages/metrics/metrics_explorer/components/kuery_bar'; import { MetricsExplorerOptions } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; import { MetricsExplorerGroupBy } from '../../../pages/metrics/metrics_explorer/components/group_by'; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.test.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.test.tsx index 50c3623faae2e..54477a39c2626 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.test.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.test.tsx @@ -63,7 +63,8 @@ describe('ExpressionRow', () => { timeUnit: 'm', aggType: 'avg', }; - const { wrapper } = await setup(expression as MetricExpression); + const { wrapper, update } = await setup(expression as MetricExpression); + await update(); const [valueMatch] = wrapper.html().match('50') ?? []; expect(valueMatch).toBeTruthy(); }); @@ -95,7 +96,7 @@ describe('ExpressionRow', () => { const { wrapper } = await setup(expression as MetricExpression); - const helpText = wrapper.find('[data-test-subj="ofExpression"]').prop('helpText'); + const helpText = wrapper.find('[data-test-subj="ofExpression"]').at(0).prop('helpText'); expect(helpText).toMatchSnapshot(); }); diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx index 476cb8d2cd039..f6618603388d0 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx @@ -24,18 +24,15 @@ import { WhenExpression, OfExpression, ThresholdExpression, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../triggers_actions_ui/public/common'; +} from '../../../../../triggers_actions_ui/public'; import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { IErrorObject } from '../../../../../triggers_actions_ui/public/types'; +import { IErrorObject } from '../../../../../triggers_actions_ui/public'; import { MetricExpression, AGGREGATION_TYPES } from '../types'; import { Comparator, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../server/lib/alerting/metric_threshold/types'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { builtInComparators } from '../../../../../triggers_actions_ui/public/common/constants'; +import { builtInComparators } from '../../../../../triggers_actions_ui/public'; const customComparators = { ...builtInComparators, diff --git a/x-pack/plugins/infra/public/components/log_stream/log_stream_embeddable.tsx b/x-pack/plugins/infra/public/components/log_stream/log_stream_embeddable.tsx index e1427bc96e7e0..c69c7ebff2b9e 100644 --- a/x-pack/plugins/infra/public/components/log_stream/log_stream_embeddable.tsx +++ b/x-pack/plugins/infra/public/components/log_stream/log_stream_embeddable.tsx @@ -66,7 +66,7 @@ export class LogStreamEmbeddable extends Embeddable { } const startTimestamp = datemathToEpochMillis(this.input.timeRange.from); - const endTimestamp = datemathToEpochMillis(this.input.timeRange.to); + const endTimestamp = datemathToEpochMillis(this.input.timeRange.to, 'up'); if (!startTimestamp || !endTimestamp) { return; diff --git a/x-pack/plugins/ml/public/application/components/job_spaces_list/job_spaces_list.tsx b/x-pack/plugins/ml/public/application/components/job_spaces_list/job_spaces_list.tsx index 6e0715de12fb9..912dfe99aa3ed 100644 --- a/x-pack/plugins/ml/public/application/components/job_spaces_list/job_spaces_list.tsx +++ b/x-pack/plugins/ml/public/application/components/job_spaces_list/job_spaces_list.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { FC, useState } from 'react'; +import React, { FC, useCallback, useState } from 'react'; import { EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -66,7 +66,11 @@ export const JobSpacesList: FC = ({ spacesApi, spaceIds, jobId, jobType, }); } - const { SpaceList, ShareToSpaceFlyout } = spacesApi.ui.components; + const LazySpaceList = useCallback(spacesApi.ui.components.getSpaceList, [spacesApi]); + const LazyShareToSpaceFlyout = useCallback(spacesApi.ui.components.getShareToSpaceFlyout, [ + spacesApi, + ]); + const shareToSpaceFlyoutProps: ShareToSpaceFlyoutProps = { savedObjectTarget: { type: ML_SAVED_OBJECT_TYPE, @@ -83,9 +87,9 @@ export const JobSpacesList: FC = ({ spacesApi, spaceIds, jobId, jobType, return ( <> setShowFlyout(true)} style={{ height: 'auto' }}> - + - {showFlyout && } + {showFlyout && } ); }; diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx index b61a28aff732a..2dc46bcf8fb41 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx +++ b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx @@ -23,6 +23,7 @@ import { EuiTabbedContentTab, } from '@elastic/eui'; +import type { SpacesContextProps } from 'src/plugins/spaces_oss/public'; import { PLUGIN_ID } from '../../../../../../common/constants/app'; import { ManagementAppMountParams } from '../../../../../../../../../src/plugins/management/public/'; @@ -67,7 +68,7 @@ function usePageState( return [pageState, updateState]; } -const EmptyFunctionComponent: React.FC = ({ children }) => <>{children}; +const getEmptyFunctionComponent: React.FC = ({ children }) => <>{children}; function useTabs(isMlEnabledInSpace: boolean, spacesApi: SpacesPluginStart | undefined): Tab[] { const [adPageState, updateAdPageState] = usePageState(getDefaultAnomalyDetectionJobsListState()); @@ -147,6 +148,11 @@ export const JobsListPage: FC<{ check(); }, []); + const ContextWrapper = useCallback( + spacesApi ? spacesApi.ui.components.getSpacesContextProvider : getEmptyFunctionComponent, + [spacesApi] + ); + if (initialized === false) { return null; } @@ -185,8 +191,6 @@ export const JobsListPage: FC<{ return ; } - const ContextWrapper = spacesApi?.ui.components.SpacesContext || EmptyFunctionComponent; - return ( diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx index e898caf12e2e9..340cf4c72362d 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx @@ -40,6 +40,7 @@ import { NotificationsStart, } from 'src/core/public'; import type { DocLinksStart, ScopedHistory } from 'kibana/public'; +import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; import { FeaturesPluginStart } from '../../../../../features/public'; import { KibanaFeature } from '../../../../../features/common'; import { IndexPatternsContract } from '../../../../../../../src/plugins/data/public'; @@ -84,6 +85,7 @@ interface Props { notifications: NotificationsStart; fatalErrors: FatalErrorsSetup; history: ScopedHistory; + spacesApiUi?: SpacesApiUi; } function useRunAsUsers( @@ -289,6 +291,7 @@ export const EditRolePage: FunctionComponent = ({ uiCapabilities, notifications, history, + spacesApiUi, }) => { const backToRoleList = useCallback(() => history.push('/'), [history]); @@ -447,6 +450,7 @@ export const EditRolePage: FunctionComponent = ({ role={role} onChange={onRoleChange} validator={validator} + spacesApiUi={spacesApiUi} /> ); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx index a6429eb7825d9..308fe673c1cdb 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/kibana_privileges_region.tsx @@ -7,6 +7,7 @@ import React, { Component } from 'react'; import { Capabilities } from 'src/core/public'; +import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; import { Space } from '../../../../../../../spaces/public'; import { Role } from '../../../../../../common/model'; import { RoleValidator } from '../../validate_role'; @@ -26,6 +27,7 @@ interface Props { kibanaPrivileges: KibanaPrivileges; onChange: (role: Role) => void; validator: RoleValidator; + spacesApiUi?: SpacesApiUi; } export class KibanaPrivilegesRegion extends Component { @@ -48,6 +50,7 @@ export class KibanaPrivilegesRegion extends Component { onChange, editable, validator, + spacesApiUi, } = this.props; if (role._transform_error && role._transform_error.includes('kibana')) { @@ -65,6 +68,7 @@ export class KibanaPrivilegesRegion extends Component { editable={editable} canCustomizeSubFeaturePrivileges={canCustomizeSubFeaturePrivileges} validator={validator} + spacesApiUi={spacesApiUi!} /> ); } else { diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.test.tsx index e4614cc7a1544..e4ff169ed20df 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.test.tsx @@ -7,12 +7,15 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; +import { spacesManagerMock } from '../../../../../../../../spaces/public/spaces_manager/mocks'; +import { getUiApi } from '../../../../../../../../spaces/public/ui_api'; import { createKibanaPrivileges } from '../../../../__fixtures__/kibana_privileges'; import { kibanaFeatures } from '../../../../__fixtures__/kibana_features'; import { RoleKibanaPrivilege } from '../../../../../../../common/model'; import { PrivilegeSummary } from '.'; import { findTestSubject } from '@kbn/test/jest'; import { PrivilegeSummaryTable } from './privilege_summary_table'; +import { coreMock } from 'src/core/public/mocks'; const createRole = (roleKibanaPrivileges: RoleKibanaPrivilege[]) => ({ name: 'some-role', @@ -31,6 +34,9 @@ const spaces = [ disabledFeatures: [], }, ]; +const spacesManager = spacesManagerMock.create(); +const { getStartServices } = coreMock.createSetup(); +const spacesApiUi = getUiApi({ spacesManager, getStartServices }); describe('PrivilegeSummary', () => { it('initially renders a button', () => { @@ -50,6 +56,7 @@ describe('PrivilegeSummary', () => { kibanaPrivileges={kibanaPrivileges} role={role} canCustomizeSubFeaturePrivileges={true} + spacesApiUi={spacesApiUi} /> ); @@ -74,6 +81,7 @@ describe('PrivilegeSummary', () => { kibanaPrivileges={kibanaPrivileges} role={role} canCustomizeSubFeaturePrivileges={true} + spacesApiUi={spacesApiUi} /> ); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx index 0fb1f424ac6af..ebe2dc6a164c2 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx @@ -10,6 +10,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButtonEmpty, EuiOverlayMask, EuiButton } from '@elastic/eui'; import { EuiFlyout } from '@elastic/eui'; import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiFlyoutFooter } from '@elastic/eui'; +import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; import { Space } from '../../../../../../../../spaces/public'; import { Role } from '../../../../../../../common/model'; import { PrivilegeSummaryTable } from './privilege_summary_table'; @@ -20,6 +21,7 @@ interface Props { spaces: Space[]; kibanaPrivileges: KibanaPrivileges; canCustomizeSubFeaturePrivileges: boolean; + spacesApiUi: SpacesApiUi; } export const PrivilegeSummary = (props: Props) => { const [isOpen, setIsOpen] = useState(false); @@ -54,6 +56,7 @@ export const PrivilegeSummary = (props: Props) => { spaces={props.spaces} kibanaPrivileges={props.kibanaPrivileges} canCustomizeSubFeaturePrivileges={props.canCustomizeSubFeaturePrivileges} + spacesApiUi={props.spacesApiUi} /> diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.test.tsx index ba7bcdb6ff9c3..1042eea3b0525 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.test.tsx @@ -5,13 +5,17 @@ * 2.0. */ +import { act } from '@testing-library/react'; import React from 'react'; +import { spacesManagerMock } from '../../../../../../../../spaces/public/spaces_manager/mocks'; +import { getUiApi } from '../../../../../../../../spaces/public/ui_api'; import { createKibanaPrivileges } from '../../../../__fixtures__/kibana_privileges'; import { kibanaFeatures } from '../../../../__fixtures__/kibana_features'; import { mountWithIntl } from '@kbn/test/jest'; -import { PrivilegeSummaryTable } from './privilege_summary_table'; +import { PrivilegeSummaryTable, PrivilegeSummaryTableProps } from './privilege_summary_table'; import { RoleKibanaPrivilege } from '../../../../../../../common/model'; import { getDisplayedFeaturePrivileges } from './__fixtures__'; +import { coreMock } from 'src/core/public/mocks'; const createRole = (roleKibanaPrivileges: RoleKibanaPrivilege[]) => ({ name: 'some-role', @@ -40,6 +44,9 @@ const spaces = [ disabledFeatures: [], }, ]; +const spacesManager = spacesManagerMock.create(); +const { getStartServices } = coreMock.createSetup(); +const spacesApiUi = getUiApi({ spacesManager, getStartServices }); const maybeExpectSubFeaturePrivileges = (expect: boolean, subFeaturesPrivileges: unknown) => { return expect ? { subFeaturesPrivileges } : {}; @@ -83,12 +90,23 @@ const expectNoPrivileges = (displayedPrivileges: any, expectSubFeatures: boolean }); }; +const setup = async (props: PrivilegeSummaryTableProps) => { + const wrapper = mountWithIntl(); + + // lazy-load SpaceAvatar + await act(async () => { + wrapper.update(); + }); + + return wrapper; +}; + describe('PrivilegeSummaryTable', () => { [true, false].forEach((allowSubFeaturePrivileges) => { describe(`when sub feature privileges are ${ allowSubFeaturePrivileges ? 'allowed' : 'disallowed' }`, () => { - it('ignores unknown base privileges', () => { + it('ignores unknown base privileges', async () => { const kibanaPrivileges = createKibanaPrivileges(kibanaFeatures, { allowSubFeaturePrivileges, }); @@ -101,21 +119,20 @@ describe('PrivilegeSummaryTable', () => { }, ]); - const wrapper = mountWithIntl( - - ); + const wrapper = await setup({ + spaces, + kibanaPrivileges, + role, + canCustomizeSubFeaturePrivileges: allowSubFeaturePrivileges, + spacesApiUi, + }); const displayedPrivileges = getDisplayedFeaturePrivileges(wrapper, role); expectNoPrivileges(displayedPrivileges, allowSubFeaturePrivileges); }); - it('ignores unknown feature privileges', () => { + it('ignores unknown feature privileges', async () => { const kibanaPrivileges = createKibanaPrivileges(kibanaFeatures, { allowSubFeaturePrivileges, }); @@ -130,21 +147,20 @@ describe('PrivilegeSummaryTable', () => { }, ]); - const wrapper = mountWithIntl( - - ); + const wrapper = await setup({ + spaces, + kibanaPrivileges, + role, + canCustomizeSubFeaturePrivileges: allowSubFeaturePrivileges, + spacesApiUi, + }); const displayedPrivileges = getDisplayedFeaturePrivileges(wrapper, role); expectNoPrivileges(displayedPrivileges, allowSubFeaturePrivileges); }); - it('ignores unknown features', () => { + it('ignores unknown features', async () => { const kibanaPrivileges = createKibanaPrivileges(kibanaFeatures, { allowSubFeaturePrivileges, }); @@ -159,21 +175,20 @@ describe('PrivilegeSummaryTable', () => { }, ]); - const wrapper = mountWithIntl( - - ); + const wrapper = await setup({ + spaces, + kibanaPrivileges, + role, + canCustomizeSubFeaturePrivileges: allowSubFeaturePrivileges, + spacesApiUi, + }); const displayedPrivileges = getDisplayedFeaturePrivileges(wrapper, role); expectNoPrivileges(displayedPrivileges, allowSubFeaturePrivileges); }); - it('renders effective privileges for the global base privilege', () => { + it('renders effective privileges for the global base privilege', async () => { const kibanaPrivileges = createKibanaPrivileges(kibanaFeatures, { allowSubFeaturePrivileges, }); @@ -186,14 +201,13 @@ describe('PrivilegeSummaryTable', () => { }, ]); - const wrapper = mountWithIntl( - - ); + const wrapper = await setup({ + spaces, + kibanaPrivileges, + role, + canCustomizeSubFeaturePrivileges: allowSubFeaturePrivileges, + spacesApiUi, + }); const displayedPrivileges = getDisplayedFeaturePrivileges(wrapper, role); @@ -234,7 +248,7 @@ describe('PrivilegeSummaryTable', () => { }); }); - it('renders effective privileges for a global feature privilege', () => { + it('renders effective privileges for a global feature privilege', async () => { const kibanaPrivileges = createKibanaPrivileges(kibanaFeatures, { allowSubFeaturePrivileges, }); @@ -249,14 +263,13 @@ describe('PrivilegeSummaryTable', () => { }, ]); - const wrapper = mountWithIntl( - - ); + const wrapper = await setup({ + spaces, + kibanaPrivileges, + role, + canCustomizeSubFeaturePrivileges: allowSubFeaturePrivileges, + spacesApiUi, + }); const displayedPrivileges = getDisplayedFeaturePrivileges(wrapper, role); @@ -297,7 +310,7 @@ describe('PrivilegeSummaryTable', () => { }); }); - it('renders effective privileges for the space base privilege', () => { + it('renders effective privileges for the space base privilege', async () => { const kibanaPrivileges = createKibanaPrivileges(kibanaFeatures, { allowSubFeaturePrivileges, }); @@ -310,14 +323,13 @@ describe('PrivilegeSummaryTable', () => { }, ]); - const wrapper = mountWithIntl( - - ); + const wrapper = await setup({ + spaces, + kibanaPrivileges, + role, + canCustomizeSubFeaturePrivileges: allowSubFeaturePrivileges, + spacesApiUi, + }); const displayedPrivileges = getDisplayedFeaturePrivileges(wrapper, role); @@ -358,7 +370,7 @@ describe('PrivilegeSummaryTable', () => { }); }); - it('renders effective privileges for a space feature privilege', () => { + it('renders effective privileges for a space feature privilege', async () => { const kibanaPrivileges = createKibanaPrivileges(kibanaFeatures, { allowSubFeaturePrivileges, }); @@ -373,14 +385,13 @@ describe('PrivilegeSummaryTable', () => { }, ]); - const wrapper = mountWithIntl( - - ); + const wrapper = await setup({ + spaces, + kibanaPrivileges, + role, + canCustomizeSubFeaturePrivileges: allowSubFeaturePrivileges, + spacesApiUi, + }); const displayedPrivileges = getDisplayedFeaturePrivileges(wrapper, role); @@ -421,7 +432,7 @@ describe('PrivilegeSummaryTable', () => { }); }); - it('renders effective privileges for global base + space base privileges', () => { + it('renders effective privileges for global base + space base privileges', async () => { const kibanaPrivileges = createKibanaPrivileges(kibanaFeatures, { allowSubFeaturePrivileges, }); @@ -439,14 +450,13 @@ describe('PrivilegeSummaryTable', () => { }, ]); - const wrapper = mountWithIntl( - - ); + const wrapper = await setup({ + spaces, + kibanaPrivileges, + role, + canCustomizeSubFeaturePrivileges: allowSubFeaturePrivileges, + spacesApiUi, + }); const displayedPrivileges = getDisplayedFeaturePrivileges(wrapper, role); @@ -512,7 +522,7 @@ describe('PrivilegeSummaryTable', () => { }); }); - it('renders effective privileges for global base + space feature privileges', () => { + it('renders effective privileges for global base + space feature privileges', async () => { const kibanaPrivileges = createKibanaPrivileges(kibanaFeatures, { allowSubFeaturePrivileges, }); @@ -532,14 +542,13 @@ describe('PrivilegeSummaryTable', () => { }, ]); - const wrapper = mountWithIntl( - - ); + const wrapper = await setup({ + spaces, + kibanaPrivileges, + role, + canCustomizeSubFeaturePrivileges: allowSubFeaturePrivileges, + spacesApiUi, + }); const displayedPrivileges = getDisplayedFeaturePrivileges(wrapper, role); @@ -605,7 +614,7 @@ describe('PrivilegeSummaryTable', () => { }); }); - it('renders effective privileges for global feature + space base privileges', () => { + it('renders effective privileges for global feature + space base privileges', async () => { const kibanaPrivileges = createKibanaPrivileges(kibanaFeatures, { allowSubFeaturePrivileges, }); @@ -625,14 +634,13 @@ describe('PrivilegeSummaryTable', () => { }, ]); - const wrapper = mountWithIntl( - - ); + const wrapper = await setup({ + spaces, + kibanaPrivileges, + role, + canCustomizeSubFeaturePrivileges: allowSubFeaturePrivileges, + spacesApiUi, + }); const displayedPrivileges = getDisplayedFeaturePrivileges(wrapper, role); @@ -698,7 +706,7 @@ describe('PrivilegeSummaryTable', () => { }); }); - it('renders effective privileges for global feature + space feature privileges', () => { + it('renders effective privileges for global feature + space feature privileges', async () => { const kibanaPrivileges = createKibanaPrivileges(kibanaFeatures, { allowSubFeaturePrivileges, }); @@ -720,14 +728,13 @@ describe('PrivilegeSummaryTable', () => { }, ]); - const wrapper = mountWithIntl( - - ); + const wrapper = await setup({ + spaces, + kibanaPrivileges, + role, + canCustomizeSubFeaturePrivileges: allowSubFeaturePrivileges, + spacesApiUi, + }); const displayedPrivileges = getDisplayedFeaturePrivileges(wrapper, role); @@ -793,7 +800,7 @@ describe('PrivilegeSummaryTable', () => { }); }); - it('renders effective privileges for a complex setup', () => { + it('renders effective privileges for a complex setup', async () => { const kibanaPrivileges = createKibanaPrivileges(kibanaFeatures, { allowSubFeaturePrivileges, }); @@ -821,14 +828,13 @@ describe('PrivilegeSummaryTable', () => { }, ]); - const wrapper = mountWithIntl( - - ); + const wrapper = await setup({ + spaces, + kibanaPrivileges, + role, + canCustomizeSubFeaturePrivileges: allowSubFeaturePrivileges, + spacesApiUi, + }); const displayedPrivileges = getDisplayedFeaturePrivileges(wrapper, role); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx index f5a1b8cf7c526..693343889476a 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx @@ -19,6 +19,7 @@ import { EuiAccordion, EuiTitle, } from '@elastic/eui'; +import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; import { Space } from '../../../../../../../../spaces/public'; import { Role, RoleKibanaPrivilege } from '../../../../../../../common/model'; import { isGlobalPrivilegeDefinition } from '../../../privilege_utils'; @@ -31,18 +32,19 @@ import { EffectiveFeaturePrivileges, } from './privilege_summary_calculator'; -interface Props { +export interface PrivilegeSummaryTableProps { role: Role; spaces: Space[]; kibanaPrivileges: KibanaPrivileges; canCustomizeSubFeaturePrivileges: boolean; + spacesApiUi: SpacesApiUi; } function getColumnKey(entry: RoleKibanaPrivilege) { return `privilege_entry_${entry.spaces.join('|')}`; } -export const PrivilegeSummaryTable = (props: Props) => { +export const PrivilegeSummaryTable = (props: PrivilegeSummaryTableProps) => { const [expandedFeatures, setExpandedFeatures] = useState([]); const featureCategories = useMemo(() => { @@ -113,7 +115,9 @@ export const PrivilegeSummaryTable = (props: Props) => { const privilegeColumns = rawKibanaPrivileges.map((entry) => { const key = getColumnKey(entry); return { - name: , + name: ( + + ), field: key, render: (kibanaPrivilege: EffectiveFeaturePrivileges, record: { featureId: string }) => { const { primary, hasCustomizedSubFeaturePrivileges } = kibanaPrivilege[record.featureId]; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx index 599642f40c26d..12eac1852c212 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx @@ -5,11 +5,16 @@ * 2.0. */ +import { act } from '@testing-library/react'; import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; +import { spacesManagerMock } from '../../../../../../../../spaces/public/spaces_manager/mocks'; +import { getUiApi } from '../../../../../../../../spaces/public/ui_api'; +import { SpaceAvatarInternal } from '../../../../../../../../spaces/public/space_avatar/space_avatar_internal'; +import type { RoleKibanaPrivilege } from '../../../../../../../common/model'; import { SpaceColumnHeader } from './space_column_header'; import { SpacesPopoverList } from '../../../spaces_popover_list'; -import { SpaceAvatar } from '../../../../../../../../spaces/public'; +import { coreMock } from 'src/core/public/mocks'; const spaces = [ { @@ -43,80 +48,78 @@ const spaces = [ disabledFeatures: [], }, ]; +const spacesManager = spacesManagerMock.create(); +const { getStartServices } = coreMock.createSetup(); +const spacesApiUi = getUiApi({ spacesManager, getStartServices }); describe('SpaceColumnHeader', () => { - it('renders the Global privilege definition with a special label', () => { + async function setup(entry: RoleKibanaPrivilege) { const wrapper = mountWithIntl( - + ); + await act(async () => {}); + + // lazy-load SpaceAvatar + await act(async () => { + wrapper.update(); + }); + + return wrapper; + } + + it('renders the Global privilege definition with a special label', async () => { + const wrapper = await setup({ + base: [], + feature: {}, + spaces: ['*'], + }); + // Snapshot includes space avatar (The first "G"), followed by the "Global" label, // followed by the (all spaces) text as part of the SpacesPopoverList expect(wrapper.text()).toMatchInlineSnapshot(`"G All Spaces"`); }); - it('renders a placeholder space when the requested space no longer exists', () => { - const wrapper = mountWithIntl( - - ); + it('renders a placeholder space when the requested space no longer exists', async () => { + const wrapper = await setup({ + base: [], + feature: {}, + spaces: ['space-1', 'missing-space', 'space-3'], + }); expect(wrapper.find(SpacesPopoverList)).toHaveLength(0); - const avatars = wrapper.find(SpaceAvatar); + const avatars = wrapper.find(SpaceAvatarInternal); expect(avatars).toHaveLength(3); expect(wrapper.text()).toMatchInlineSnapshot(`"S1 m S3 "`); }); - it('renders a space privilege definition with an avatar for each space in the group', () => { - const wrapper = mountWithIntl( - - ); + it('renders a space privilege definition with an avatar for each space in the group', async () => { + const wrapper = await setup({ + base: [], + feature: {}, + spaces: ['space-1', 'space-2', 'space-3', 'space-4'], + }); expect(wrapper.find(SpacesPopoverList)).toHaveLength(0); - const avatars = wrapper.find(SpaceAvatar); + const avatars = wrapper.find(SpaceAvatarInternal); expect(avatars).toHaveLength(4); expect(wrapper.text()).toMatchInlineSnapshot(`"S1 S2 S3 S4 "`); }); - it('renders a space privilege definition with an avatar for the first 4 spaces in the group, with the popover control showing the rest', () => { - const wrapper = mountWithIntl( - - ); + it('renders a space privilege definition with an avatar for the first 4 spaces in the group, with the popover control showing the rest', async () => { + const wrapper = await setup({ + base: [], + feature: {}, + spaces: ['space-1', 'space-2', 'space-3', 'space-4', 'space-5'], + }); expect(wrapper.find(SpacesPopoverList)).toHaveLength(1); - const avatars = wrapper.find(SpaceAvatar); + const avatars = wrapper.find(SpaceAvatarInternal); expect(avatars).toHaveLength(4); expect(wrapper.text()).toMatchInlineSnapshot(`"S1 S2 S3 S4 +1 more"`); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx index 208028fd28921..3c200fedf6ce0 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx @@ -5,22 +5,25 @@ * 2.0. */ -import React, { Fragment } from 'react'; +import React, { Fragment, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { Space, SpaceAvatar } from '../../../../../../../../spaces/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; +import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; import { RoleKibanaPrivilege } from '../../../../../../../common/model'; import { isGlobalPrivilegeDefinition } from '../../../privilege_utils'; import { SpacesPopoverList } from '../../../spaces_popover_list'; -interface Props { +export interface SpaceColumnHeaderProps { spaces: Space[]; entry: RoleKibanaPrivilege; + spacesApiUi: SpacesApiUi; } const SPACES_DISPLAY_COUNT = 4; -export const SpaceColumnHeader = (props: Props) => { +export const SpaceColumnHeader = (props: SpaceColumnHeaderProps) => { + const { spacesApiUi } = props; const isGlobal = isGlobalPrivilegeDefinition(props.entry); const entrySpaces = props.entry.spaces.map((spaceId) => { return ( @@ -31,12 +34,14 @@ export const SpaceColumnHeader = (props: Props) => { } ); }); + const LazySpaceAvatar = useMemo(() => spacesApiUi.components.getSpaceAvatar, [spacesApiUi]); + return (
{entrySpaces.slice(0, SPACES_DISPLAY_COUNT).map((space) => { return ( - {' '} + {' '} {isGlobal && ( { }, } )} + spacesApiUi={spacesApiUi} /> )} diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx index 99dbf4c7e2f7c..ae0a6a503c73a 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx @@ -19,6 +19,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import _ from 'lodash'; import React, { Component, Fragment } from 'react'; import { Capabilities } from 'src/core/public'; +import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; import { Space } from '../../../../../../../../spaces/public'; import { Role, isRoleReserved } from '../../../../../../../common/model'; import { RoleValidator } from '../../../validate_role'; @@ -37,6 +38,7 @@ interface Props { canCustomizeSubFeaturePrivileges: boolean; validator: RoleValidator; uiCapabilities: Capabilities; + spacesApiUi: SpacesApiUi; } interface State { @@ -215,6 +217,7 @@ export class SpaceAwarePrivilegeSection extends Component { spaces={this.getDisplaySpaces()} kibanaPrivileges={this.props.kibanaPrivileges} canCustomizeSubFeaturePrivileges={this.props.canCustomizeSubFeaturePrivileges} + spacesApiUi={this.props.spacesApiUi} /> ); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.test.tsx index 6e06863792896..a9b49eb2b137c 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.test.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import { act } from '@testing-library/react'; import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; import { SpacesPopoverList } from '.'; @@ -15,9 +16,13 @@ import { EuiFieldSearch, EuiPopover, } from '@elastic/eui'; -import { SpaceAvatar } from '../../../../../../spaces/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; +import { spacesManagerMock } from '../../../../../../spaces/public/spaces_manager/mocks'; +import { getUiApi } from '../../../../../../spaces/public/ui_api'; +import { SpaceAvatarInternal } from '../../../../../../spaces/public/space_avatar/space_avatar_internal'; +import { coreMock } from 'src/core/public/mocks'; -const spaces = [ +const mockSpaces = [ { id: 'default', name: 'Default Space', @@ -35,44 +40,62 @@ const spaces = [ disabledFeatures: [], }, ]; +const spacesManager = spacesManagerMock.create(); +const { getStartServices } = coreMock.createSetup(); +const spacesApiUi = getUiApi({ spacesManager, getStartServices }); describe('SpacesPopoverList', () => { - it('renders a button with the provided text', () => { - const wrapper = mountWithIntl(); + async function setup(spaces: Space[]) { + const wrapper = mountWithIntl( + + ); + + // lazy-load SpaceAvatar + await act(async () => { + wrapper.update(); + }); + + return wrapper; + } + + it('renders a button with the provided text', async () => { + const wrapper = await setup(mockSpaces); expect(wrapper.find(EuiButtonEmpty).text()).toEqual('hello world'); expect(wrapper.find(EuiContextMenuPanel)).toHaveLength(0); }); - it('clicking the button renders a context menu with the provided spaces', () => { - const wrapper = mountWithIntl(); - wrapper.find(EuiButtonEmpty).simulate('click'); + it('clicking the button renders a context menu with the provided spaces', async () => { + const wrapper = await setup(mockSpaces); + await act(async () => { + wrapper.find(EuiButtonEmpty).simulate('click'); + }); wrapper.update(); const menu = wrapper.find(EuiContextMenuPanel); expect(menu).toHaveLength(1); const items = menu.find(EuiContextMenuItem); - expect(items).toHaveLength(spaces.length); + expect(items).toHaveLength(mockSpaces.length); - spaces.forEach((space, index) => { - const spaceAvatar = items.at(index).find(SpaceAvatar); + mockSpaces.forEach((space, index) => { + const spaceAvatar = items.at(index).find(SpaceAvatarInternal); expect(spaceAvatar.props().space).toEqual(space); }); expect(wrapper.find(EuiFieldSearch)).toHaveLength(0); }); - it('renders a search box when there are 8 or more spaces', () => { + it('renders a search box when there are 8 or more spaces', async () => { const lotsOfSpaces = [1, 2, 3, 4, 5, 6, 7, 8].map((num) => ({ id: `space-${num}`, name: `Space ${num}`, disabledFeatures: [], })); - const wrapper = mountWithIntl( - - ); - wrapper.find(EuiButtonEmpty).simulate('click'); + const wrapper = await setup(lotsOfSpaces); + await act(async () => { + wrapper.find(EuiButtonEmpty).simulate('click'); + }); wrapper.update(); const menu = wrapper.find(EuiContextMenuPanel).first(); @@ -83,20 +106,23 @@ describe('SpacesPopoverList', () => { expect(searchField).toHaveLength(1); searchField.props().onSearch!('Space 6'); + await act(async () => {}); wrapper.update(); - expect(wrapper.find(SpaceAvatar)).toHaveLength(1); + expect(wrapper.find(SpaceAvatarInternal)).toHaveLength(1); searchField.props().onSearch!('this does not match'); wrapper.update(); - expect(wrapper.find(SpaceAvatar)).toHaveLength(0); + expect(wrapper.find(SpaceAvatarInternal)).toHaveLength(0); const updatedMenu = wrapper.find(EuiContextMenuPanel).first(); expect(updatedMenu.text()).toMatchInlineSnapshot(`"Spaces no spaces found "`); }); - it('can close its popover', () => { - const wrapper = mountWithIntl(); - wrapper.find(EuiButtonEmpty).simulate('click'); + it('can close its popover', async () => { + const wrapper = await setup(mockSpaces); + await act(async () => { + wrapper.find(EuiButtonEmpty).simulate('click'); + }); wrapper.update(); expect(wrapper.find(EuiPopover).props().isOpen).toEqual(true); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx index cd54630bdf8ba..783fbf03f766d 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx @@ -17,13 +17,15 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import React, { Component } from 'react'; -import { Space, SpaceAvatar } from '../../../../../../spaces/public'; +import React, { Component, memo } from 'react'; +import type { Space } from 'src/plugins/spaces_oss/common'; +import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../../../../spaces/common'; interface Props { spaces: Space[]; buttonText: string; + spacesApiUi: SpacesApiUi; } interface State { @@ -191,7 +193,8 @@ export class SpacesPopoverList extends Component { }; private renderSpaceMenuItem = (space: Space): JSX.Element => { - const icon = ; + const LazySpaceAvatar = memo(this.props.spacesApiUi.components.getSpaceAvatar); + const icon = ; // wrapped in a Suspense above return ( ); }; diff --git a/x-pack/plugins/security/public/nav_control/index.ts b/x-pack/plugins/security/public/nav_control/index.ts index d9d6f4f27f0d5..5ec306fa97170 100644 --- a/x-pack/plugins/security/public/nav_control/index.ts +++ b/x-pack/plugins/security/public/nav_control/index.ts @@ -6,4 +6,4 @@ */ export { SecurityNavControlService, SecurityNavControlServiceStart } from './nav_control_service'; -export { UserMenuLink } from './nav_control_component'; +export type { UserMenuLink } from './nav_control_component'; diff --git a/x-pack/plugins/security/public/plugin.tsx b/x-pack/plugins/security/public/plugin.tsx index e2247b8e49341..8da7e71241b81 100644 --- a/x-pack/plugins/security/public/plugin.tsx +++ b/x-pack/plugins/security/public/plugin.tsx @@ -14,6 +14,7 @@ import { PluginInitializerContext, } from '../../../../src/core/public'; import { FeaturesPluginStart } from '../../features/public'; +import type { SpacesPluginStart } from '../../spaces/public'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; import { FeatureCatalogueCategory, @@ -48,6 +49,7 @@ export interface PluginStartDependencies { features: FeaturesPluginStart; securityOss: SecurityOssPluginStart; management?: ManagementStart; + spaces?: SpacesPluginStart; } export class SecurityPlugin diff --git a/x-pack/plugins/spaces/.eslintrc.json b/x-pack/plugins/spaces/.eslintrc.json new file mode 100644 index 0000000000000..2b63a9259d220 --- /dev/null +++ b/x-pack/plugins/spaces/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "rules": { + "@typescript-eslint/consistent-type-imports": 1 + } +} diff --git a/x-pack/plugins/spaces/common/is_reserved_space.test.ts b/x-pack/plugins/spaces/common/is_reserved_space.test.ts index 3483c106032e3..0128a7483f166 100644 --- a/x-pack/plugins/spaces/common/is_reserved_space.test.ts +++ b/x-pack/plugins/spaces/common/is_reserved_space.test.ts @@ -5,8 +5,9 @@ * 2.0. */ +import type { Space } from 'src/plugins/spaces_oss/common'; + import { isReservedSpace } from './is_reserved_space'; -import { Space } from '../../../../src/plugins/spaces_oss/common'; test('it returns true for reserved spaces', () => { const space: Space = { diff --git a/x-pack/plugins/spaces/common/is_reserved_space.ts b/x-pack/plugins/spaces/common/is_reserved_space.ts index df3b21c049a93..f78fe7bbdac1b 100644 --- a/x-pack/plugins/spaces/common/is_reserved_space.ts +++ b/x-pack/plugins/spaces/common/is_reserved_space.ts @@ -6,7 +6,8 @@ */ import { get } from 'lodash'; -import { Space } from '../../../../src/plugins/spaces_oss/common'; + +import type { Space } from 'src/plugins/spaces_oss/common'; /** * Returns whether the given Space is reserved or not. diff --git a/x-pack/plugins/spaces/common/licensing/index.mock.ts b/x-pack/plugins/spaces/common/licensing/index.mock.ts index af66a64c4c4bc..c8039f83f464d 100644 --- a/x-pack/plugins/spaces/common/licensing/index.mock.ts +++ b/x-pack/plugins/spaces/common/licensing/index.mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SpacesLicense } from '.'; +import type { SpacesLicense } from './license_service'; export const licenseMock = { create: (): jest.Mocked => ({ diff --git a/x-pack/plugins/spaces/common/licensing/license_service.test.ts b/x-pack/plugins/spaces/common/licensing/license_service.test.ts index 6405e3f3e6d6e..3d87588bc7497 100644 --- a/x-pack/plugins/spaces/common/licensing/license_service.test.ts +++ b/x-pack/plugins/spaces/common/licensing/license_service.test.ts @@ -6,9 +6,11 @@ */ import { of } from 'rxjs'; + import { licenseMock } from '../../../licensing/common/licensing.mock'; +import type { LicenseType } from '../../../licensing/common/types'; +import { LICENSE_TYPE } from '../../../licensing/common/types'; import { SpacesLicenseService } from './license_service'; -import { LICENSE_TYPE, LicenseType } from '../../../licensing/common/types'; describe('license#isEnabled', function () { it('should indicate that Spaces is disabled when there is no license information', () => { diff --git a/x-pack/plugins/spaces/common/licensing/license_service.ts b/x-pack/plugins/spaces/common/licensing/license_service.ts index 42954b06f3d80..0836c87613db7 100644 --- a/x-pack/plugins/spaces/common/licensing/license_service.ts +++ b/x-pack/plugins/spaces/common/licensing/license_service.ts @@ -5,8 +5,9 @@ * 2.0. */ -import { Observable, Subscription } from 'rxjs'; -import { ILicense } from '../../../licensing/common/types'; +import type { Observable, Subscription } from 'rxjs'; + +import type { ILicense } from '../../../licensing/common/types'; export interface SpacesLicense { isEnabled(): boolean; diff --git a/x-pack/plugins/spaces/common/types.ts b/x-pack/plugins/spaces/common/types.ts index 974cf2284048b..46455c1f601d4 100644 --- a/x-pack/plugins/spaces/common/types.ts +++ b/x-pack/plugins/spaces/common/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { Space } from '../../../../src/plugins/spaces_oss/common'; +import type { Space } from 'src/plugins/spaces_oss/common'; export interface GetAllSpacesOptions { purpose?: GetAllSpacesPurpose; diff --git a/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.test.tsx b/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.test.tsx index 11df5440547fe..28b630efe0b44 100644 --- a/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.test.tsx +++ b/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.test.tsx @@ -5,8 +5,9 @@ * 2.0. */ +import { advancedSettingsMock } from 'src/plugins/advanced_settings/public/mocks'; + import { AdvancedSettingsService } from './advanced_settings_service'; -import { advancedSettingsMock } from '../../../../../src/plugins/advanced_settings/public/mocks'; const componentRegistryMock = advancedSettingsMock.createSetupContract(); diff --git a/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx b/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx index 991fe6b22a6a9..5658f95b62854 100644 --- a/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx +++ b/x-pack/plugins/spaces/public/advanced_settings/advanced_settings_service.tsx @@ -6,9 +6,11 @@ */ import React from 'react'; -import { AdvancedSettingsSetup } from 'src/plugins/advanced_settings/public'; -import { Space } from '../../../../../src/plugins/spaces_oss/common'; -import { AdvancedSettingsTitle, AdvancedSettingsSubtitle } from './components'; + +import type { AdvancedSettingsSetup } from 'src/plugins/advanced_settings/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import { AdvancedSettingsSubtitle, AdvancedSettingsTitle } from './components'; interface SetupDeps { getActiveSpace: () => Promise; diff --git a/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx index ad4f4289de39a..6e215b20e4f2a 100644 --- a/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx +++ b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.test.tsx @@ -5,11 +5,13 @@ * 2.0. */ +import { EuiCallOut } from '@elastic/eui'; +import { act } from '@testing-library/react'; import React from 'react'; + import { mountWithIntl, nextTick } from '@kbn/test/jest'; + import { AdvancedSettingsSubtitle } from './advanced_settings_subtitle'; -import { EuiCallOut } from '@elastic/eui'; -import { act } from '@testing-library/react'; describe('AdvancedSettingsSubtitle', () => { it('renders as expected', async () => { diff --git a/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx index ae152640eed63..75a27a3738e61 100644 --- a/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx +++ b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_subtitle/advanced_settings_subtitle.tsx @@ -6,9 +6,10 @@ */ import { EuiCallOut, EuiSpacer } from '@elastic/eui'; +import React, { Fragment, useEffect, useState } from 'react'; + import { FormattedMessage } from '@kbn/i18n/react'; -import React, { Fragment, useState, useEffect } from 'react'; -import { Space } from '../../../../../../../src/plugins/spaces_oss/common'; +import type { Space } from 'src/plugins/spaces_oss/common'; interface Props { getActiveSpace: () => Promise; diff --git a/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.test.tsx b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.test.tsx index ade7a4bed39cc..95dd62a6680a6 100644 --- a/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.test.tsx +++ b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.test.tsx @@ -5,11 +5,13 @@ * 2.0. */ +import { act } from '@testing-library/react'; import React from 'react'; -import { mountWithIntl, nextTick } from '@kbn/test/jest'; + +import { mountWithIntl } from '@kbn/test/jest'; + +import { SpaceAvatarInternal } from '../../../space_avatar/space_avatar_internal'; import { AdvancedSettingsTitle } from './advanced_settings_title'; -import { SpaceAvatar } from '../../../space_avatar'; -import { act } from '@testing-library/react'; describe('AdvancedSettingsTitle', () => { it('renders without crashing', async () => { @@ -23,11 +25,12 @@ describe('AdvancedSettingsTitle', () => { Promise.resolve(space)} /> ); - await act(async () => { - await nextTick(); - wrapper.update(); - }); + await act(async () => {}); + + // wait for SpaceAvatar to lazy-load + await act(async () => {}); + wrapper.update(); - expect(wrapper.find(SpaceAvatar)).toHaveLength(1); + expect(wrapper.find(SpaceAvatarInternal)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx index 78fa07e853b05..9bec9e32ca736 100644 --- a/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx +++ b/x-pack/plugins/spaces/public/advanced_settings/components/advanced_settings_title/advanced_settings_title.tsx @@ -5,11 +5,18 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiTitle } from '@elastic/eui'; +import React, { lazy, Suspense, useEffect, useState } from 'react'; + import { FormattedMessage } from '@kbn/i18n/react'; -import React, { useState, useEffect } from 'react'; -import { Space } from '../../../../../../../src/plugins/spaces_oss/common'; -import { SpaceAvatar } from '../../../space_avatar'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import { getSpaceAvatarComponent } from '../../../space_avatar'; + +// No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana. +const LazySpaceAvatar = lazy(() => + getSpaceAvatarComponent().then((component) => ({ default: component })) +); interface Props { getActiveSpace: () => Promise; @@ -27,7 +34,9 @@ export const AdvancedSettingsTitle = (props: Props) => { return ( - + }> + + diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_mode_control.test.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_mode_control.test.tsx index 1e0bcbcbfec2e..9c69b172751cf 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_mode_control.test.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_mode_control.test.tsx @@ -5,10 +5,13 @@ * 2.0. */ +import type { ReactWrapper } from 'enzyme'; import React from 'react'; -import { ReactWrapper } from 'enzyme'; + import { mountWithIntl } from '@kbn/test/jest'; -import { CopyModeControl, CopyModeControlProps } from './copy_mode_control'; + +import type { CopyModeControlProps } from './copy_mode_control'; +import { CopyModeControl } from './copy_mode_control'; describe('CopyModeControl', () => { const initialValues = { createNewCopies: true, overwrite: true }; // some test cases below make assumptions based on these initial values diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_mode_control.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_mode_control.tsx index 2fc78fee79556..223a21f248f7d 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_mode_control.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_mode_control.tsx @@ -5,18 +5,19 @@ * 2.0. */ -import React, { useState } from 'react'; import { - EuiFormFieldset, - EuiTitle, EuiCheckableCard, - EuiRadioGroup, - EuiText, - EuiSpacer, EuiFlexGroup, EuiFlexItem, + EuiFormFieldset, EuiIconTip, + EuiRadioGroup, + EuiSpacer, + EuiText, + EuiTitle, } from '@elastic/eui'; +import React, { useState } from 'react'; + import { i18n } from '@kbn/i18n'; export interface CopyModeControlProps { diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_indicator.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_indicator.tsx index 471ebc5c6667f..caad5a0fafd9c 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_indicator.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_indicator.tsx @@ -5,11 +5,13 @@ * 2.0. */ +import { EuiIconTip, EuiLoadingSpinner } from '@elastic/eui'; import React, { Fragment } from 'react'; -import { EuiLoadingSpinner, EuiIconTip } from '@elastic/eui'; + import { FormattedMessage } from '@kbn/i18n/react'; -import { ImportRetry } from '../types'; -import { SummarizedCopyToSpaceResult, SummarizedSavedObjectResult } from '..'; + +import type { SummarizedCopyToSpaceResult, SummarizedSavedObjectResult } from '../lib'; +import type { ImportRetry } from '../types'; interface Props { summarizedCopyResult: SummarizedCopyToSpaceResult; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_summary_indicator.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_summary_indicator.tsx index 3454d09fc439c..b04450ae4febd 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_summary_indicator.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_status_summary_indicator.tsx @@ -6,13 +6,16 @@ */ import './copy_status_summary_indicator.scss'; + +import { EuiBadge, EuiIconTip, EuiLoadingSpinner } from '@elastic/eui'; import React, { Fragment } from 'react'; -import { EuiLoadingSpinner, EuiIconTip, EuiBadge } from '@elastic/eui'; + import { FormattedMessage } from '@kbn/i18n/react'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; -import { ImportRetry } from '../types'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { SummarizedCopyToSpaceResult } from '../lib'; +import type { ImportRetry } from '../types'; import { ResolveAllConflicts } from './resolve_all_conflicts'; -import { SummarizedCopyToSpaceResult } from '..'; interface Props { space: Space; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx index c86a7c92993a2..3fee2fdfa975d 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx @@ -5,302 +5,15 @@ * 2.0. */ -import React, { useState, useEffect, useMemo } from 'react'; -import { - EuiFlyout, - EuiIcon, - EuiFlyoutHeader, - EuiTitle, - EuiText, - EuiFlyoutBody, - EuiFlyoutFooter, - EuiLoadingSpinner, - EuiFlexGroup, - EuiFlexItem, - EuiHorizontalRule, - EuiEmptyPrompt, -} from '@elastic/eui'; -import { mapValues } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { ToastsStart } from 'src/core/public'; -import { - ProcessedImportResponse, - processImportResponse, -} from '../../../../../../src/plugins/saved_objects_management/public'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; -import { SpacesManager } from '../../spaces_manager'; -import { ProcessingCopyToSpace } from './processing_copy_to_space'; -import { CopyToSpaceFlyoutFooter } from './copy_to_space_flyout_footer'; -import { CopyToSpaceForm } from './copy_to_space_form'; -import { CopyOptions, ImportRetry, SavedObjectTarget } from '../types'; +import React from 'react'; -interface Props { - onClose: () => void; - savedObjectTarget: SavedObjectTarget; - spacesManager: SpacesManager; - toastNotifications: ToastsStart; -} +import type { CopyToSpaceFlyoutProps } from './copy_to_space_flyout_internal'; -const INCLUDE_RELATED_DEFAULT = true; -const CREATE_NEW_COPIES_DEFAULT = true; -const OVERWRITE_ALL_DEFAULT = true; - -export const CopySavedObjectsToSpaceFlyout = (props: Props) => { - const { onClose, savedObjectTarget: object, spacesManager, toastNotifications } = props; - const savedObjectTarget = useMemo( - () => ({ - type: object.type, - id: object.id, - namespaces: object.namespaces, - icon: object.icon || 'apps', - title: object.title || `${object.type} [id=${object.id}]`, - }), - [object] - ); - const [copyOptions, setCopyOptions] = useState({ - includeRelated: INCLUDE_RELATED_DEFAULT, - createNewCopies: CREATE_NEW_COPIES_DEFAULT, - overwrite: OVERWRITE_ALL_DEFAULT, - selectedSpaceIds: [], - }); - - const [{ isLoading, spaces }, setSpacesState] = useState<{ isLoading: boolean; spaces: Space[] }>( - { - isLoading: true, - spaces: [], - } - ); - useEffect(() => { - const getSpaces = spacesManager.getSpaces({ includeAuthorizedPurposes: true }); - const getActiveSpace = spacesManager.getActiveSpace(); - Promise.all([getSpaces, getActiveSpace]) - .then(([allSpaces, activeSpace]) => { - setSpacesState({ - isLoading: false, - spaces: allSpaces.filter( - ({ id, authorizedPurposes }) => - id !== activeSpace.id && authorizedPurposes?.copySavedObjectsIntoSpace !== false - ), - }); - }) - .catch((e) => { - toastNotifications.addError(e, { - title: i18n.translate('xpack.spaces.management.copyToSpace.spacesLoadErrorTitle', { - defaultMessage: 'Error loading available spaces', - }), - }); - }); - }, [spacesManager, toastNotifications]); - - const [copyInProgress, setCopyInProgress] = useState(false); - const [conflictResolutionInProgress, setConflictResolutionInProgress] = useState(false); - const [copyResult, setCopyResult] = useState>({}); - const [retries, setRetries] = useState>({}); - - const initialCopyFinished = Object.values(copyResult).length > 0; - - const onRetriesChange = (updatedRetries: Record) => { - setRetries(updatedRetries); - }; - - async function startCopy() { - setCopyInProgress(true); - setCopyResult({}); - try { - const copySavedObjectsResult = await spacesManager.copySavedObjects( - [{ type: savedObjectTarget.type, id: savedObjectTarget.id }], - copyOptions.selectedSpaceIds, - copyOptions.includeRelated, - copyOptions.createNewCopies, - copyOptions.overwrite - ); - const processedResult = mapValues(copySavedObjectsResult, processImportResponse); - setCopyResult(processedResult); - - // retry all successful imports - const getAutomaticRetries = (response: ProcessedImportResponse): ImportRetry[] => { - const { failedImports, successfulImports } = response; - if (!failedImports.length) { - // if no imports failed for this space, return an empty array - return []; - } - - // get missing references failures that do not also have a conflict - const nonMissingReferencesFailures = failedImports - .filter(({ error }) => error.type !== 'missing_references') - .reduce((acc, { obj: { type, id } }) => acc.add(`${type}:${id}`), new Set()); - const missingReferencesToRetry = failedImports.filter( - ({ obj: { type, id }, error }) => - error.type === 'missing_references' && - !nonMissingReferencesFailures.has(`${type}:${id}`) - ); - - // otherwise, some imports failed for this space, so retry any successful imports (if any) - return [ - ...successfulImports.map(({ type, id, overwrite, destinationId, createNewCopy }) => { - return { type, id, overwrite: overwrite === true, destinationId, createNewCopy }; - }), - ...missingReferencesToRetry.map(({ obj: { type, id } }) => ({ - type, - id, - overwrite: false, - ignoreMissingReferences: true, - })), - ]; - }; - const automaticRetries = mapValues(processedResult, getAutomaticRetries); - setRetries(automaticRetries); - } catch (e) { - setCopyInProgress(false); - toastNotifications.addError(e, { - title: i18n.translate('xpack.spaces.management.copyToSpace.copyErrorTitle', { - defaultMessage: 'Error copying saved object', - }), - }); - } - } - - async function finishCopy() { - // if any retries are present, attempt to resolve errors again - const needsErrorResolution = Object.values(retries).some((spaceRetry) => spaceRetry.length); - - if (needsErrorResolution) { - setConflictResolutionInProgress(true); - try { - await spacesManager.resolveCopySavedObjectsErrors( - [{ type: savedObjectTarget.type, id: savedObjectTarget.id }], - retries, - copyOptions.includeRelated, - copyOptions.createNewCopies - ); - - toastNotifications.addSuccess( - i18n.translate('xpack.spaces.management.copyToSpace.resolveCopySuccessTitle', { - defaultMessage: 'Copy successful', - }) - ); - - onClose(); - } catch (e) { - setCopyInProgress(false); - toastNotifications.addError(e, { - title: i18n.translate('xpack.spaces.management.copyToSpace.resolveCopyErrorTitle', { - defaultMessage: 'Error resolving saved object conflicts', - }), - }); - } - } else { - onClose(); - } - } - - const getFlyoutBody = () => { - // Step 1: loading assets for main form - if (isLoading) { - return ; - } - - // Step 1a: assets loaded, but no spaces are available for copy. - if (spaces.length === 0) { - return ( - - -

- } - title={ -

- -

- } - /> - ); - } - - // Step 2: Copy has not been initiated yet; User must fill out form to continue. - if (!copyInProgress) { - return ( - - ); - } - - // Step3: Copy operation is in progress - return ( - - ); +export const getCopyToSpaceFlyoutComponent = async (): Promise< + React.FC +> => { + const { CopyToSpaceFlyoutInternal } = await import('./copy_to_space_flyout_internal'); + return (props: CopyToSpaceFlyoutProps) => { + return ; }; - - return ( - - - - - - - - -

- -

-
-
-
-
- - - - - - - -

{savedObjectTarget.title}

-
-
-
- - - - {getFlyoutBody()} -
- - - - -
- ); }; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx index 1005f03988a86..c021d8bdf69a1 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx @@ -5,19 +5,24 @@ * 2.0. */ -import React, { Fragment } from 'react'; import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, - EuiStat, EuiHorizontalRule, + EuiStat, } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; +import React, { Fragment } from 'react'; + import { i18n } from '@kbn/i18n'; -import { ProcessedImportResponse, FailedImport } from 'src/plugins/saved_objects_management/public'; -import { ImportRetry } from '../types'; +import { FormattedMessage } from '@kbn/i18n/react'; +import type { + FailedImport, + ProcessedImportResponse, +} from 'src/plugins/saved_objects_management/public'; + +import type { ImportRetry } from '../types'; interface Props { copyInProgress: boolean; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_internal.test.tsx similarity index 91% rename from x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_internal.test.tsx index c880e3144fac0..e0326a6c9ff11 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_internal.test.tsx @@ -5,22 +5,23 @@ * 2.0. */ -import React from 'react'; +import { EuiEmptyPrompt, EuiLoadingSpinner } from '@elastic/eui'; import Boom from '@hapi/boom'; -import { mountWithIntl, nextTick } from '@kbn/test/jest'; -import { CopySavedObjectsToSpaceFlyout } from './copy_to_space_flyout'; -import { CopyToSpaceForm } from './copy_to_space_form'; -import { EuiLoadingSpinner, EuiEmptyPrompt } from '@elastic/eui'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; -import { findTestSubject } from '@kbn/test/jest'; -import { SelectableSpacesControl } from './selectable_spaces_control'; -import { CopyModeControl } from './copy_mode_control'; import { act } from '@testing-library/react'; -import { ProcessingCopyToSpace } from './processing_copy_to_space'; +import React from 'react'; + +import { findTestSubject, mountWithIntl, nextTick } from '@kbn/test/jest'; +import { coreMock } from 'src/core/public/mocks'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import { getSpacesContextProviderWrapper } from '../../spaces_context'; import { spacesManagerMock } from '../../spaces_manager/mocks'; -import { SpacesManager } from '../../spaces_manager'; -import { ToastsApi } from 'src/core/public'; -import { SavedObjectTarget } from '../types'; +import type { SavedObjectTarget } from '../types'; +import { CopyModeControl } from './copy_mode_control'; +import { getCopyToSpaceFlyoutComponent } from './copy_to_space_flyout'; +import { CopyToSpaceForm } from './copy_to_space_form'; +import { ProcessingCopyToSpace } from './processing_copy_to_space'; +import { SelectableSpacesControl } from './selectable_spaces_control'; interface SetupOpts { mockSpaces?: Space[]; @@ -32,13 +33,14 @@ const setup = async (opts: SetupOpts = {}) => { const mockSpacesManager = spacesManagerMock.create(); - mockSpacesManager.getActiveSpace.mockResolvedValue({ + const getActiveSpace = Promise.resolve({ id: 'my-active-space', name: 'my active space', disabledFeatures: [], }); + mockSpacesManager.getActiveSpace.mockReturnValue(getActiveSpace); - mockSpacesManager.getSpaces.mockResolvedValue( + const getSpaces = Promise.resolve( opts.mockSpaces || [ { id: 'space-1', @@ -62,11 +64,18 @@ const setup = async (opts: SetupOpts = {}) => { }, ] ); + mockSpacesManager.getSpaces.mockReturnValue(getSpaces); - const mockToastNotifications = { - addError: jest.fn(), - addSuccess: jest.fn(), + const { getStartServices } = coreMock.createSetup(); + const startServices = coreMock.createStart(); + startServices.application.capabilities = { + ...startServices.application.capabilities, + spaces: { manage: true }, }; + const mockToastNotifications = startServices.notifications.toasts; + const getStartServicesPromise = Promise.resolve([startServices, , ,]); + getStartServices.mockReturnValue(getStartServicesPromise); + const savedObjectToCopy = { type: 'dashboard', id: 'my-dash', @@ -75,21 +84,29 @@ const setup = async (opts: SetupOpts = {}) => { title: 'foo', } as SavedObjectTarget; + const SpacesContext = await getSpacesContextProviderWrapper({ + getStartServices, + spacesManager: mockSpacesManager, + }); + const CopyToSpaceFlyout = await getCopyToSpaceFlyoutComponent(); + const wrapper = mountWithIntl( - + + + ); + // wait for context wrapper to rerender + await act(async () => { + await getStartServicesPromise; + wrapper.update(); + }); + + await getActiveSpace; + await getSpaces; if (!opts.returnBeforeSpacesLoad) { - // Wait for spaces manager to complete and flyout to rerender - await act(async () => { - await nextTick(); - wrapper.update(); - }); + // rerender after spaces manager API calls are completed + wrapper.update(); } return { wrapper, onClose, mockSpacesManager, mockToastNotifications, savedObjectToCopy }; @@ -103,10 +120,7 @@ describe('CopyToSpaceFlyout', () => { expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(0); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(1); - await act(async () => { - await nextTick(); - wrapper.update(); - }); + wrapper.update(); expect(wrapper.find(CopyToSpaceForm)).toHaveLength(1); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_internal.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_internal.tsx new file mode 100644 index 0000000000000..9dbeb70d46cfb --- /dev/null +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_internal.tsx @@ -0,0 +1,307 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiHorizontalRule, + EuiIcon, + EuiLoadingSpinner, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { mapValues } from 'lodash'; +import React, { useEffect, useMemo, useState } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import type { ProcessedImportResponse } from 'src/plugins/saved_objects_management/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import { processImportResponse } from '../../../../../../src/plugins/saved_objects_management/public'; +import type { CopyOptions, ImportRetry, SavedObjectTarget } from '../types'; +import { CopyToSpaceFlyoutFooter } from './copy_to_space_flyout_footer'; +import { CopyToSpaceForm } from './copy_to_space_form'; +import { ProcessingCopyToSpace } from './processing_copy_to_space'; +import { useSpaces } from '../../spaces_context'; + +export interface CopyToSpaceFlyoutProps { + onClose: () => void; + savedObjectTarget: SavedObjectTarget; +} + +const INCLUDE_RELATED_DEFAULT = true; +const CREATE_NEW_COPIES_DEFAULT = true; +const OVERWRITE_ALL_DEFAULT = true; + +export const CopyToSpaceFlyoutInternal = (props: CopyToSpaceFlyoutProps) => { + const { spacesManager, services } = useSpaces(); + const { notifications } = services; + const toastNotifications = notifications!.toasts; + + const { onClose, savedObjectTarget: object } = props; + const savedObjectTarget = useMemo( + () => ({ + type: object.type, + id: object.id, + namespaces: object.namespaces, + icon: object.icon || 'apps', + title: object.title || `${object.type} [id=${object.id}]`, + }), + [object] + ); + const [copyOptions, setCopyOptions] = useState({ + includeRelated: INCLUDE_RELATED_DEFAULT, + createNewCopies: CREATE_NEW_COPIES_DEFAULT, + overwrite: OVERWRITE_ALL_DEFAULT, + selectedSpaceIds: [], + }); + + const [{ isLoading, spaces }, setSpacesState] = useState<{ isLoading: boolean; spaces: Space[] }>( + { + isLoading: true, + spaces: [], + } + ); + useEffect(() => { + const getSpaces = spacesManager.getSpaces({ includeAuthorizedPurposes: true }); + const getActiveSpace = spacesManager.getActiveSpace(); + Promise.all([getSpaces, getActiveSpace]) + .then(([allSpaces, activeSpace]) => { + setSpacesState({ + isLoading: false, + spaces: allSpaces.filter( + ({ id, authorizedPurposes }) => + id !== activeSpace.id && authorizedPurposes?.copySavedObjectsIntoSpace !== false + ), + }); + }) + .catch((e) => { + toastNotifications.addError(e, { + title: i18n.translate('xpack.spaces.management.copyToSpace.spacesLoadErrorTitle', { + defaultMessage: 'Error loading available spaces', + }), + }); + }); + }, [spacesManager, toastNotifications]); + + const [copyInProgress, setCopyInProgress] = useState(false); + const [conflictResolutionInProgress, setConflictResolutionInProgress] = useState(false); + const [copyResult, setCopyResult] = useState>({}); + const [retries, setRetries] = useState>({}); + + const initialCopyFinished = Object.values(copyResult).length > 0; + + const onRetriesChange = (updatedRetries: Record) => { + setRetries(updatedRetries); + }; + + async function startCopy() { + setCopyInProgress(true); + setCopyResult({}); + try { + const copySavedObjectsResult = await spacesManager.copySavedObjects( + [{ type: savedObjectTarget.type, id: savedObjectTarget.id }], + copyOptions.selectedSpaceIds, + copyOptions.includeRelated, + copyOptions.createNewCopies, + copyOptions.overwrite + ); + const processedResult = mapValues(copySavedObjectsResult, processImportResponse); + setCopyResult(processedResult); + + // retry all successful imports + const getAutomaticRetries = (response: ProcessedImportResponse): ImportRetry[] => { + const { failedImports, successfulImports } = response; + if (!failedImports.length) { + // if no imports failed for this space, return an empty array + return []; + } + + // get missing references failures that do not also have a conflict + const nonMissingReferencesFailures = failedImports + .filter(({ error }) => error.type !== 'missing_references') + .reduce((acc, { obj: { type, id } }) => acc.add(`${type}:${id}`), new Set()); + const missingReferencesToRetry = failedImports.filter( + ({ obj: { type, id }, error }) => + error.type === 'missing_references' && + !nonMissingReferencesFailures.has(`${type}:${id}`) + ); + + // otherwise, some imports failed for this space, so retry any successful imports (if any) + return [ + ...successfulImports.map(({ type, id, overwrite, destinationId, createNewCopy }) => { + return { type, id, overwrite: overwrite === true, destinationId, createNewCopy }; + }), + ...missingReferencesToRetry.map(({ obj: { type, id } }) => ({ + type, + id, + overwrite: false, + ignoreMissingReferences: true, + })), + ]; + }; + const automaticRetries = mapValues(processedResult, getAutomaticRetries); + setRetries(automaticRetries); + } catch (e) { + setCopyInProgress(false); + toastNotifications.addError(e, { + title: i18n.translate('xpack.spaces.management.copyToSpace.copyErrorTitle', { + defaultMessage: 'Error copying saved object', + }), + }); + } + } + + async function finishCopy() { + // if any retries are present, attempt to resolve errors again + const needsErrorResolution = Object.values(retries).some((spaceRetry) => spaceRetry.length); + + if (needsErrorResolution) { + setConflictResolutionInProgress(true); + try { + await spacesManager.resolveCopySavedObjectsErrors( + [{ type: savedObjectTarget.type, id: savedObjectTarget.id }], + retries, + copyOptions.includeRelated, + copyOptions.createNewCopies + ); + + toastNotifications.addSuccess( + i18n.translate('xpack.spaces.management.copyToSpace.resolveCopySuccessTitle', { + defaultMessage: 'Copy successful', + }) + ); + + onClose(); + } catch (e) { + setCopyInProgress(false); + toastNotifications.addError(e, { + title: i18n.translate('xpack.spaces.management.copyToSpace.resolveCopyErrorTitle', { + defaultMessage: 'Error resolving saved object conflicts', + }), + }); + } + } else { + onClose(); + } + } + + const getFlyoutBody = () => { + // Step 1: loading assets for main form + if (isLoading) { + return ; + } + + // Step 1a: assets loaded, but no spaces are available for copy. + if (spaces.length === 0) { + return ( + + +

+ } + title={ +

+ +

+ } + /> + ); + } + + // Step 2: Copy has not been initiated yet; User must fill out form to continue. + if (!copyInProgress) { + return ( + + ); + } + + // Step3: Copy operation is in progress + return ( + + ); + }; + + return ( + + + + + + + + +

+ +

+
+
+
+
+ + + + + + + +

{savedObjectTarget.title}

+
+
+
+ + + + {getFlyoutBody()} +
+ + + + +
+ ); +}; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_form.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_form.tsx index 6c0ab695d94d8..e28e95ed42d25 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_form.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_form.tsx @@ -5,13 +5,16 @@ * 2.0. */ +import { EuiFormRow, EuiSpacer, EuiTitle } from '@elastic/eui'; import React from 'react'; -import { EuiSpacer, EuiTitle, EuiFormRow } from '@elastic/eui'; + import { FormattedMessage } from '@kbn/i18n/react'; -import { CopyOptions, SavedObjectTarget } from '../types'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { CopyOptions, SavedObjectTarget } from '../types'; +import type { CopyMode } from './copy_mode_control'; +import { CopyModeControl } from './copy_mode_control'; import { SelectableSpacesControl } from './selectable_spaces_control'; -import { CopyModeControl, CopyMode } from './copy_mode_control'; interface Props { savedObjectTarget: Required; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/index.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/index.ts index a37cb079945ec..f3173e14aa794 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/index.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/index.ts @@ -5,4 +5,5 @@ * 2.0. */ -export { CopySavedObjectsToSpaceFlyout } from './copy_to_space_flyout'; +export { getCopyToSpaceFlyoutComponent } from './copy_to_space_flyout'; +export type { CopyToSpaceFlyoutProps } from './copy_to_space_flyout_internal'; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx index 08c72b595a61d..1fba249e5410a 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx @@ -5,20 +5,22 @@ * 2.0. */ -import React, { Fragment } from 'react'; import { - EuiSpacer, - EuiText, + EuiHorizontalRule, EuiListGroup, EuiListGroupItem, - EuiHorizontalRule, + EuiSpacer, + EuiText, } from '@elastic/eui'; +import React, { Fragment } from 'react'; + import { FormattedMessage } from '@kbn/i18n/react'; -import { ProcessedImportResponse } from 'src/plugins/saved_objects_management/public'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; -import { CopyOptions, ImportRetry, SavedObjectTarget } from '../types'; +import type { ProcessedImportResponse } from 'src/plugins/saved_objects_management/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import { summarizeCopyResult } from '../lib'; +import type { CopyOptions, ImportRetry, SavedObjectTarget } from '../types'; import { SpaceResult, SpaceResultProcessing } from './space_result'; -import { summarizeCopyResult } from '..'; interface Props { savedObjectTarget: Required; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/resolve_all_conflicts.test.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/resolve_all_conflicts.test.tsx index a2c9487f7a781..e6e526fc83615 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/resolve_all_conflicts.test.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/resolve_all_conflicts.test.tsx @@ -5,14 +5,17 @@ * 2.0. */ -import React from 'react'; -import { ReactWrapper } from 'enzyme'; import { act } from '@testing-library/react'; -import { shallowWithIntl, mountWithIntl, nextTick } from '@kbn/test/jest'; -import { findTestSubject } from '@kbn/test/jest'; -import { ResolveAllConflicts, ResolveAllConflictsProps } from './resolve_all_conflicts'; -import { SummarizedCopyToSpaceResult } from '..'; -import { ImportRetry } from '../types'; +import type { ReactWrapper } from 'enzyme'; +import React from 'react'; + +import { findTestSubject, mountWithIntl, nextTick, shallowWithIntl } from '@kbn/test/jest'; + +import type { SummarizedCopyToSpaceResult } from '../lib'; +import type { ImportRetry } from '../types'; +import type { ResolveAllConflictsProps } from './resolve_all_conflicts'; +import { ResolveAllConflicts } from './resolve_all_conflicts'; + describe('ResolveAllConflicts', () => { const summarizedCopyResult = ({ objects: [ diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/resolve_all_conflicts.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/resolve_all_conflicts.tsx index b98adc8a31986..4e9afb12e1a76 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/resolve_all_conflicts.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/resolve_all_conflicts.tsx @@ -8,11 +8,13 @@ import './resolve_all_conflicts.scss'; import { EuiContextMenuItem, EuiContextMenuPanel, EuiLink, EuiPopover } from '@elastic/eui'; +import React, { Component } from 'react'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import React, { Component } from 'react'; -import { ImportRetry } from '../types'; -import { SummarizedCopyToSpaceResult } from '..'; + +import type { SummarizedCopyToSpaceResult } from '../lib'; +import type { ImportRetry } from '../types'; export interface ResolveAllConflictsProps { summarizedCopyResult: SummarizedCopyToSpaceResult; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/selectable_spaces_control.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/selectable_spaces_control.tsx index 059a8cd669a16..e1ecc06935791 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/selectable_spaces_control.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/selectable_spaces_control.tsx @@ -6,11 +6,20 @@ */ import './selectable_spaces_control.scss'; -import React, { Fragment } from 'react'; + +import type { EuiSelectableOption } from '@elastic/eui'; +import { EuiIconTip, EuiLoadingSpinner, EuiSelectable } from '@elastic/eui'; +import React, { lazy, Suspense } from 'react'; + import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiSelectable, EuiSelectableOption, EuiLoadingSpinner, EuiIconTip } from '@elastic/eui'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; -import { SpaceAvatar } from '../../space_avatar'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import { getSpaceAvatarComponent } from '../../space_avatar'; + +// No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana. +const LazySpaceAvatar = lazy(() => + getSpaceAvatarComponent().then((component) => ({ default: component })) +); interface Props { spaces: Space[]; @@ -44,7 +53,7 @@ export const SelectableSpacesControl = (props: Props) => { const disabled = props.disabledSpaceIds.has(space.id); return { label: space.name, - prepend: , + prepend: , // wrapped in a Suspense below append: disabled ? disabledIndicator : null, checked: props.selectedSpaceIds.includes(space.id) ? 'on' : undefined, disabled, @@ -64,25 +73,27 @@ export const SelectableSpacesControl = (props: Props) => { } return ( - updateSelectedSpaces(newOptions as SpaceOption[])} - listProps={{ - bordered: true, - rowHeight: 40, - className: 'spcCopyToSpace__spacesList', - 'data-test-subj': 'cts-form-space-selector', - }} - searchable={options.length > 6} - > - {(list, search) => { - return ( - - {search} - {list} - - ); - }} - + }> + updateSelectedSpaces(newOptions as SpaceOption[])} + listProps={{ + bordered: true, + rowHeight: 40, + className: 'spcCopyToSpace__spacesList', + 'data-test-subj': 'cts-form-space-selector', + }} + searchable={options.length > 6} + > + {(list, search) => { + return ( + <> + {search} + {list} + + ); + }} + + ); }; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx index 6febb62371cc7..6d14584ac21a9 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result.tsx @@ -6,21 +6,29 @@ */ import './space_result.scss'; -import React, { useState } from 'react'; + import { EuiAccordion, EuiFlexGroup, EuiFlexItem, - EuiText, - EuiSpacer, EuiLoadingSpinner, + EuiSpacer, + EuiText, } from '@elastic/eui'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; -import { SummarizedCopyToSpaceResult } from '../index'; -import { SpaceAvatar } from '../../space_avatar'; +import React, { lazy, Suspense, useState } from 'react'; + +import type { Space } from 'src/plugins/spaces_oss/common'; + +import { getSpaceAvatarComponent } from '../../space_avatar'; +import type { SummarizedCopyToSpaceResult } from '../lib'; +import type { ImportRetry } from '../types'; import { CopyStatusSummaryIndicator } from './copy_status_summary_indicator'; import { SpaceCopyResultDetails } from './space_result_details'; -import { ImportRetry } from '../types'; + +// No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana. +const LazySpaceAvatar = lazy(() => + getSpaceAvatarComponent().then((component) => ({ default: component })) +); interface Props { space: Space; @@ -40,6 +48,7 @@ const getInitialDestinationMap = (objects: SummarizedCopyToSpaceResult['objects' export const SpaceResultProcessing = (props: Pick) => { const { space } = props; + return ( ) => { buttonContent={ - + }> + + {space.name} @@ -86,7 +97,9 @@ export const SpaceResult = (props: Props) => { buttonContent={ - + }> + + {space.name} diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx index 154c55fd82ad8..e2db8e7fb7480 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/space_result_details.tsx @@ -6,27 +6,30 @@ */ import './space_result_details.scss'; -import React, { Fragment } from 'react'; + +import type { EuiSwitchEvent } from '@elastic/eui'; import { - EuiText, EuiFlexGroup, EuiFlexItem, + EuiIcon, + EuiSuperSelect, EuiSwitch, - EuiSwitchEvent, + EuiText, EuiToolTip, - EuiIcon, } from '@elastic/eui'; +import moment from 'moment'; +import React, { Fragment } from 'react'; + import { i18n } from '@kbn/i18n'; -import { - SavedObjectsImportConflictError, +import type { SavedObjectsImportAmbiguousConflictError, -} from 'kibana/public'; -import { EuiSuperSelect } from '@elastic/eui'; -import moment from 'moment'; -import { SummarizedCopyToSpaceResult } from '../index'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; + SavedObjectsImportConflictError, +} from 'src/core/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { SummarizedCopyToSpaceResult } from '../lib'; +import type { ImportRetry } from '../types'; import { CopyStatusIndicator } from './copy_status_indicator'; -import { ImportRetry } from '../types'; interface Props { summarizedCopyResult: SummarizedCopyToSpaceResult; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx index ce4edd4b51958..57ea66f35ba0c 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_action.tsx @@ -5,15 +5,42 @@ * 2.0. */ -import React from 'react'; +import React, { lazy } from 'react'; +import useAsync from 'react-use/lib/useAsync'; + import { i18n } from '@kbn/i18n'; -import { NotificationsStart } from 'src/core/public'; -import { - SavedObjectsManagementAction, - SavedObjectsManagementRecord, -} from '../../../../../src/plugins/saved_objects_management/public'; -import { CopySavedObjectsToSpaceFlyout } from './components'; -import { SpacesManager } from '../spaces_manager'; +import type { StartServicesAccessor } from 'src/core/public'; +import type { SavedObjectsManagementRecord } from 'src/plugins/saved_objects_management/public'; + +import { SavedObjectsManagementAction } from '../../../../../src/plugins/saved_objects_management/public'; +import type { PluginsStart } from '../plugin'; +import { SuspenseErrorBoundary } from '../suspense_error_boundary'; +import type { CopyToSpaceFlyoutProps } from './components'; +import { getCopyToSpaceFlyoutComponent } from './components'; + +const LazyCopyToSpaceFlyout = lazy(() => + getCopyToSpaceFlyoutComponent().then((component) => ({ default: component })) +); + +interface WrapperProps { + getStartServices: StartServicesAccessor; + props: CopyToSpaceFlyoutProps; +} + +const Wrapper = ({ getStartServices, props }: WrapperProps) => { + const { value: startServices = [{ notifications: undefined }] } = useAsync(getStartServices); + const [{ notifications }] = startServices; + + if (!notifications) { + return null; + } + + return ( + + + + ); +}; export class CopyToSpaceSavedObjectsManagementAction extends SavedObjectsManagementAction { public id: string = 'copy_saved_objects_to_space'; @@ -35,10 +62,7 @@ export class CopyToSpaceSavedObjectsManagementAction extends SavedObjectsManagem }, }; - constructor( - private readonly spacesManager: SpacesManager, - private readonly notifications: NotificationsStart - ) { + constructor(private getStartServices: StartServicesAccessor) { super(); } @@ -47,22 +71,18 @@ export class CopyToSpaceSavedObjectsManagementAction extends SavedObjectsManagem throw new Error('No record available! `render()` was likely called before `start()`.'); } - const savedObjectTarget = { - type: this.record.type, - id: this.record.id, - namespaces: this.record.namespaces ?? [], - title: this.record.meta.title, - icon: this.record.meta.icon, + const props: CopyToSpaceFlyoutProps = { + onClose: this.onClose, + savedObjectTarget: { + type: this.record.type, + id: this.record.id, + namespaces: this.record.namespaces ?? [], + title: this.record.meta.title, + icon: this.record.meta.icon, + }, }; - return ( - - ); + return ; }; private onClose = () => { diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts index 15f66464b6da7..f55a7d8054608 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.test.ts @@ -5,19 +5,19 @@ * 2.0. */ +import { coreMock } from 'src/core/public/mocks'; +import { savedObjectsManagementPluginMock } from 'src/plugins/saved_objects_management/public/mocks'; + import { CopyToSpaceSavedObjectsManagementAction } from './copy_saved_objects_to_space_action'; -import { spacesManagerMock } from '../spaces_manager/mocks'; -import { CopySavedObjectsToSpaceService } from '.'; -import { notificationServiceMock } from 'src/core/public/mocks'; -import { savedObjectsManagementPluginMock } from '../../../../../src/plugins/saved_objects_management/public/mocks'; +import { CopySavedObjectsToSpaceService } from './copy_saved_objects_to_space_service'; describe('CopySavedObjectsToSpaceService', () => { describe('#setup', () => { it('registers the CopyToSpaceSavedObjectsManagementAction', () => { + const { getStartServices } = coreMock.createSetup(); const deps = { - spacesManager: spacesManagerMock.create(), - notificationsSetup: notificationServiceMock.createSetupContract(), savedObjectsManagementSetup: savedObjectsManagementPluginMock.createSetupContract(), + getStartServices, }; const service = new CopySavedObjectsToSpaceService(); diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts index f5ecfb2d374c8..17bb26cbf7f11 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/copy_saved_objects_to_space_service.ts @@ -5,20 +5,20 @@ * 2.0. */ -import { NotificationsSetup } from 'src/core/public'; -import { SavedObjectsManagementPluginSetup } from 'src/plugins/saved_objects_management/public'; +import type { StartServicesAccessor } from 'src/core/public'; +import type { SavedObjectsManagementPluginSetup } from 'src/plugins/saved_objects_management/public'; + +import type { PluginsStart } from '../plugin'; import { CopyToSpaceSavedObjectsManagementAction } from './copy_saved_objects_to_space_action'; -import { SpacesManager } from '../spaces_manager'; interface SetupDeps { - spacesManager: SpacesManager; savedObjectsManagementSetup: SavedObjectsManagementPluginSetup; - notificationsSetup: NotificationsSetup; + getStartServices: StartServicesAccessor; } export class CopySavedObjectsToSpaceService { - public setup({ spacesManager, savedObjectsManagementSetup, notificationsSetup }: SetupDeps) { - const action = new CopyToSpaceSavedObjectsManagementAction(spacesManager, notificationsSetup); + public setup({ savedObjectsManagementSetup, getStartServices }: SetupDeps) { + const action = new CopyToSpaceSavedObjectsManagementAction(getStartServices); savedObjectsManagementSetup.actions.register(action); } } diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/index.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/index.ts index 114e4410c3853..abbbc8ba1368f 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/index.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/index.ts @@ -5,5 +5,5 @@ * 2.0. */ -export * from './summarize_copy_result'; +export { getCopyToSpaceFlyoutComponent } from './components'; export { CopySavedObjectsToSpaceService } from './copy_saved_objects_to_space_service'; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/index.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/index.ts new file mode 100644 index 0000000000000..882ee234ca5dc --- /dev/null +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/index.ts @@ -0,0 +1,12 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { + SummarizedCopyToSpaceResult, + SummarizedSavedObjectResult, +} from './summarize_copy_result'; +export { summarizeCopyResult } from './summarize_copy_result'; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/summarize_copy_result.test.ts similarity index 99% rename from x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/summarize_copy_result.test.ts index 525efc4158f72..43435b322d6b0 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/summarize_copy_result.test.ts @@ -5,13 +5,14 @@ * 2.0. */ -import { summarizeCopyResult } from './summarize_copy_result'; -import { - ProcessedImportResponse, +import type { FailedImport, + ProcessedImportResponse, SavedObjectsManagementRecord, } from 'src/plugins/saved_objects_management/public'; -import { SavedObjectTarget } from './types'; + +import { summarizeCopyResult } from './summarize_copy_result'; +import type { SavedObjectTarget } from '../types'; // Sample data references: // diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/summarize_copy_result.ts similarity index 96% rename from x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts rename to x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/summarize_copy_result.ts index 0986f5723a6de..68baba12d8b98 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/lib/summarize_copy_result.ts @@ -5,12 +5,16 @@ * 2.0. */ -import { ProcessedImportResponse, FailedImport } from 'src/plugins/saved_objects_management/public'; -import { - SavedObjectsImportConflictError, +import type { SavedObjectsImportAmbiguousConflictError, -} from 'kibana/public'; -import { SavedObjectTarget } from './types'; + SavedObjectsImportConflictError, +} from 'src/core/public'; +import type { + FailedImport, + ProcessedImportResponse, +} from 'src/plugins/saved_objects_management/public'; + +import type { SavedObjectTarget } from '../types'; export interface SummarizedSavedObjectResult { type: string; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/types.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/types.ts index 676b8ee460751..60a4e176f40bb 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/types.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObjectsImportRetry, SavedObjectsImportResponse } from 'src/core/public'; +import type { SavedObjectsImportResponse, SavedObjectsImportRetry } from 'src/core/public'; export interface CopyOptions { includeRelated: boolean; diff --git a/x-pack/plugins/spaces/public/create_feature_catalogue_entry.ts b/x-pack/plugins/spaces/public/create_feature_catalogue_entry.ts index d2a1f5b98284c..acaa361bca9fa 100644 --- a/x-pack/plugins/spaces/public/create_feature_catalogue_entry.ts +++ b/x-pack/plugins/spaces/public/create_feature_catalogue_entry.ts @@ -6,10 +6,10 @@ */ import { i18n } from '@kbn/i18n'; -import { - FeatureCatalogueEntry, - FeatureCatalogueCategory, -} from '../../../../src/plugins/home/public'; + +import type { FeatureCatalogueEntry } from 'src/plugins/home/public'; + +import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public'; import { getSpacesFeatureDescription } from './constants'; export const createSpacesFeatureCatalogueEntry = (): FeatureCatalogueEntry => { diff --git a/x-pack/plugins/spaces/public/index.ts b/x-pack/plugins/spaces/public/index.ts index 3620ae757052d..08dda35a6eb10 100644 --- a/x-pack/plugins/spaces/public/index.ts +++ b/x-pack/plugins/spaces/public/index.ts @@ -7,14 +7,14 @@ import { SpacesPlugin } from './plugin'; -export { SpaceAvatar, getSpaceColor, getSpaceImageUrl, getSpaceInitials } from './space_avatar'; +export { getSpaceColor, getSpaceImageUrl, getSpaceInitials } from './space_avatar'; export { SpacesPluginSetup, SpacesPluginStart } from './plugin'; export type { GetAllSpacesPurpose, GetSpaceResult } from '../common'; // re-export types from oss definition -export type { Space } from '../../../../src/plugins/spaces_oss/common'; +export type { Space } from 'src/plugins/spaces_oss/common'; export const plugin = () => { return new SpacesPlugin(); diff --git a/x-pack/plugins/spaces/public/lib/documentation_links.test.ts b/x-pack/plugins/spaces/public/lib/documentation_links.test.ts index f94d2e0f5fb13..5ebaf0ebadaf6 100644 --- a/x-pack/plugins/spaces/public/lib/documentation_links.test.ts +++ b/x-pack/plugins/spaces/public/lib/documentation_links.test.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { docLinksServiceMock } from '../../../../../src/core/public/mocks'; +import { docLinksServiceMock } from 'src/core/public/mocks'; + import { DocumentationLinksService } from './documentation_links'; describe('DocumentationLinksService', () => { diff --git a/x-pack/plugins/spaces/public/lib/documentation_links.ts b/x-pack/plugins/spaces/public/lib/documentation_links.ts index a9e911aebfa95..108be17fe84ba 100644 --- a/x-pack/plugins/spaces/public/lib/documentation_links.ts +++ b/x-pack/plugins/spaces/public/lib/documentation_links.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { DocLinksStart } from 'src/core/public'; +import type { DocLinksStart } from 'src/core/public'; export class DocumentationLinksService { private readonly kbnPrivileges: string; diff --git a/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.test.tsx b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.test.tsx index a36e17047ba75..36d282a1cd653 100644 --- a/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.test.tsx +++ b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.test.tsx @@ -6,10 +6,12 @@ */ import React from 'react'; + import { mountWithIntl, shallowWithIntl } from '@kbn/test/jest'; -import { ConfirmDeleteModal } from './confirm_delete_modal'; + +import type { SpacesManager } from '../../../spaces_manager'; import { spacesManagerMock } from '../../../spaces_manager/mocks'; -import { SpacesManager } from '../../../spaces_manager'; +import { ConfirmDeleteModal } from './confirm_delete_modal'; describe('ConfirmDeleteModal', () => { it('renders as expected', () => { diff --git a/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx index 94a5c082834ad..100b5b6493e30 100644 --- a/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx +++ b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx @@ -7,8 +7,8 @@ import './confirm_delete_modal.scss'; +import type { CommonProps, EuiModalProps } from '@elastic/eui'; import { - CommonProps, EuiButton, EuiButtonEmpty, EuiCallOut, @@ -19,14 +19,17 @@ import { EuiModalFooter, EuiModalHeader, EuiModalHeaderTitle, - EuiModalProps, EuiSpacer, EuiText, } from '@elastic/eui'; -import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; -import React, { ChangeEvent, Component } from 'react'; -import { Space } from '../../../../../../../src/plugins/spaces_oss/common'; -import { SpacesManager } from '../../../spaces_manager'; +import type { ChangeEvent } from 'react'; +import React, { Component } from 'react'; + +import type { InjectedIntl } from '@kbn/i18n/react'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { SpacesManager } from '../../../spaces_manager'; interface Props { space: Space; diff --git a/x-pack/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.test.tsx b/x-pack/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.test.tsx index 4610b72d30a8f..0f26c69cac2be 100644 --- a/x-pack/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.test.tsx +++ b/x-pack/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.test.tsx @@ -6,7 +6,9 @@ */ import React from 'react'; + import { shallowWithIntl } from '@kbn/test/jest'; + import { UnauthorizedPrompt } from './unauthorized_prompt'; describe('UnauthorizedPrompt', () => { diff --git a/x-pack/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.tsx b/x-pack/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.tsx index 166d1750582f3..095e6cd311d84 100644 --- a/x-pack/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.tsx +++ b/x-pack/plugins/spaces/public/management/components/unauthorized_prompt/unauthorized_prompt.tsx @@ -6,9 +6,10 @@ */ import { EuiEmptyPrompt } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; + export const UnauthorizedPrompt = () => ( { diff --git a/x-pack/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx b/x-pack/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx index c95bb7250a23e..7e6f394a773bb 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/confirm_alter_active_space_modal/confirm_alter_active_space_modal.tsx @@ -6,9 +6,11 @@ */ import { EuiConfirmModal } from '@elastic/eui'; -import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React from 'react'; +import type { InjectedIntl } from '@kbn/i18n/react'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; + interface Props { onCancel: () => void; onConfirm: () => void; diff --git a/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx index edfb2a0200673..4bbad58b5d139 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx @@ -5,27 +5,37 @@ * 2.0. */ +import type { EuiPopoverProps } from '@elastic/eui'; import { EuiDescribedFormGroup, EuiFieldText, EuiFormRow, + EuiLoadingSpinner, EuiPopover, - EuiPopoverProps, EuiSpacer, EuiTextArea, EuiTitle, } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; +import type { ChangeEvent } from 'react'; +import React, { Component, Fragment, lazy, Suspense } from 'react'; + import { i18n } from '@kbn/i18n'; -import React, { ChangeEvent, Component, Fragment } from 'react'; -import { Space } from '../../../../../../../src/plugins/spaces_oss/common'; +import { FormattedMessage } from '@kbn/i18n/react'; +import type { Space } from 'src/plugins/spaces_oss/common'; + import { isReservedSpace } from '../../../../common'; -import { SpaceAvatar } from '../../../space_avatar'; -import { SpaceValidator, toSpaceIdentifier } from '../../lib'; +import { getSpaceAvatarComponent } from '../../../space_avatar'; +import type { SpaceValidator } from '../../lib'; +import { toSpaceIdentifier } from '../../lib'; import { SectionPanel } from '../section_panel'; import { CustomizeSpaceAvatar } from './customize_space_avatar'; import { SpaceIdentifier } from './space_identifier'; +// No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana. +const LazySpaceAvatar = lazy(() => + getSpaceAvatarComponent().then((component) => ({ default: component })) +); + interface Props { validator: SpaceValidator; space: Partial; @@ -153,7 +163,9 @@ export class CustomizeSpace extends Component { )} onClick={this.togglePopover} > - + }> + + } closePopover={this.closePopover} diff --git a/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.test.tsx index 6b658e9d04da4..babd89f69c784 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.test.tsx @@ -5,10 +5,11 @@ * 2.0. */ -// @ts-ignore import { EuiColorPicker, EuiFieldText, EuiLink } from '@elastic/eui'; import React from 'react'; + import { mountWithIntl, shallowWithIntl } from '@kbn/test/jest'; + import { CustomizeSpaceAvatar } from './customize_space_avatar'; const space = { diff --git a/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.tsx b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.tsx index e0cf04d9c7e48..96cd094c14645 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space_avatar.tsx @@ -5,23 +5,26 @@ * 2.0. */ -import React, { ChangeEvent, Component } from 'react'; import { + EuiButton, EuiColorPicker, EuiFieldText, + EuiFilePicker, EuiFlexItem, EuiFormRow, - // @ts-ignore (elastic/eui#1262) EuiFilePicker is not exported yet - EuiFilePicker, - EuiButton, EuiSpacer, isValidHex, } from '@elastic/eui'; +import type { ChangeEvent } from 'react'; +import React, { Component } from 'react'; + import { i18n } from '@kbn/i18n'; -import { Space } from '../../../../../../../src/plugins/spaces_oss/common'; -import { imageTypes, encode } from '../../../../common/lib/dataurl'; -import { getSpaceColor, getSpaceInitials } from '../../../space_avatar'; +import type { Space } from 'src/plugins/spaces_oss/common'; + import { MAX_SPACE_INITIALS } from '../../../../common'; +import { encode, imageTypes } from '../../../../common/lib/dataurl'; +import { getSpaceColor, getSpaceInitials } from '../../../space_avatar'; + interface Props { space: Partial; onChange: (space: Partial) => void; diff --git a/x-pack/plugins/spaces/public/management/edit_space/customize_space/space_identifier.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/customize_space/space_identifier.test.tsx index 9a16ec1985a0f..f28e17d0e1f03 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/customize_space/space_identifier.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/customize_space/space_identifier.test.tsx @@ -6,7 +6,9 @@ */ import React from 'react'; + import { shallowWithIntl } from '@kbn/test/jest'; + import { SpaceValidator } from '../../lib'; import { SpaceIdentifier } from './space_identifier'; diff --git a/x-pack/plugins/spaces/public/management/edit_space/customize_space/space_identifier.tsx b/x-pack/plugins/spaces/public/management/edit_space/customize_space/space_identifier.tsx index f549b81d28a09..bc863e4add2cc 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/customize_space/space_identifier.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/customize_space/space_identifier.tsx @@ -6,10 +6,15 @@ */ import { EuiFieldText, EuiFormRow, EuiLink } from '@elastic/eui'; -import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; -import React, { ChangeEvent, Component, Fragment } from 'react'; -import { Space } from '../../../../../../../src/plugins/spaces_oss/common'; -import { SpaceValidator, toSpaceIdentifier } from '../../lib'; +import type { ChangeEvent } from 'react'; +import React, { Component, Fragment } from 'react'; + +import type { InjectedIntl } from '@kbn/i18n/react'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { SpaceValidator } from '../../lib'; +import { toSpaceIdentifier } from '../../lib'; interface Props { space: Partial; diff --git a/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.test.tsx index 3f89abba378fe..fbfcfbe12b737 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.test.tsx @@ -6,12 +6,14 @@ */ import React from 'react'; + import { shallowWithIntl } from '@kbn/test/jest'; -import { DeleteSpacesButton } from './delete_spaces_button'; -import { spacesManagerMock } from '../../spaces_manager/mocks'; -import { SpacesManager } from '../../spaces_manager'; import { notificationServiceMock } from 'src/core/public/mocks'; +import type { SpacesManager } from '../../spaces_manager'; +import { spacesManagerMock } from '../../spaces_manager/mocks'; +import { DeleteSpacesButton } from './delete_spaces_button'; + const space = { id: 'my-space', name: 'My Space', diff --git a/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx b/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx index 74d354827f125..d03b878cb19ab 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx @@ -5,13 +5,16 @@ * 2.0. */ -import { EuiButton, EuiButtonIcon, EuiButtonIconProps } from '@elastic/eui'; +import type { EuiButtonIconProps } from '@elastic/eui'; +import { EuiButton, EuiButtonIcon } from '@elastic/eui'; +import React, { Component, Fragment } from 'react'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import React, { Component, Fragment } from 'react'; -import { NotificationsStart } from 'src/core/public'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; -import { SpacesManager } from '../../spaces_manager'; +import type { NotificationsStart } from 'src/core/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { SpacesManager } from '../../spaces_manager'; import { ConfirmDeleteModal } from '../components/confirm_delete_modal'; interface Props { diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx index 83288406623ae..7f1ea57a6b89c 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx @@ -5,13 +5,14 @@ * 2.0. */ +import type { EuiCheckboxProps } from '@elastic/eui'; import React from 'react'; -import { mountWithIntl, nextTick, shallowWithIntl } from '@kbn/test/jest'; + +import { findTestSubject, mountWithIntl, nextTick, shallowWithIntl } from '@kbn/test/jest'; +import { DEFAULT_APP_CATEGORIES } from 'src/core/public'; + +import type { KibanaFeatureConfig } from '../../../../../features/public'; import { EnabledFeatures } from './enabled_features'; -import { KibanaFeatureConfig } from '../../../../../features/public'; -import { DEFAULT_APP_CATEGORIES } from '../../../../../../../src/core/public'; -import { findTestSubject } from '@kbn/test/jest'; -import { EuiCheckboxProps } from '@elastic/eui'; const features: KibanaFeatureConfig[] = [ { diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx index 2c65e80a39902..667878df3254a 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx @@ -6,12 +6,15 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; +import type { ReactNode } from 'react'; +import React, { Component, Fragment } from 'react'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import React, { Component, Fragment, ReactNode } from 'react'; -import { ApplicationStart } from 'kibana/public'; -import { Space } from '../../../../../../../src/plugins/spaces_oss/common'; -import { KibanaFeatureConfig } from '../../../../../../plugins/features/public'; +import type { ApplicationStart } from 'src/core/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { KibanaFeatureConfig } from '../../../../../features/public'; import { getEnabledFeatures } from '../../lib/feature_utils'; import { SectionPanel } from '../section_panel'; import { FeatureTable } from './feature_table'; diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.tsx b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.tsx index df4536cf18a4c..2c9eaf4563d05 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.tsx @@ -5,12 +5,13 @@ * 2.0. */ -import { EuiCallOut } from '@elastic/eui'; +import './feature_table.scss'; +import type { EuiCheckboxProps } from '@elastic/eui'; import { EuiAccordion, + EuiCallOut, EuiCheckbox, - EuiCheckboxProps, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, @@ -20,14 +21,16 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { AppCategory } from 'kibana/public'; import _ from 'lodash'; -import React, { ChangeEvent, Component, ReactElement } from 'react'; -import { Space } from '../../../../../../../src/plugins/spaces_oss/common'; -import { KibanaFeatureConfig } from '../../../../../../plugins/features/public'; +import type { ChangeEvent, ReactElement } from 'react'; +import React, { Component } from 'react'; + +import { i18n } from '@kbn/i18n'; +import type { AppCategory } from 'src/core/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { KibanaFeatureConfig } from '../../../../../features/public'; import { getEnabledFeatures } from '../../lib/feature_utils'; -import './feature_table.scss'; interface Props { space: Partial; diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/toggle_all_features.tsx b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/toggle_all_features.tsx index a5f32d4894645..644faf24a33e1 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/toggle_all_features.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/toggle_all_features.tsx @@ -8,9 +8,11 @@ import './toggle_all_features.scss'; import { EuiContextMenuItem, EuiContextMenuPanel, EuiLink, EuiPopover } from '@elastic/eui'; +import React, { Component } from 'react'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import React, { Component } from 'react'; + interface Props { onChange: (visible: boolean) => void; disabled?: boolean; diff --git a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx index 63e1a80437c5c..79daeba2c8ec3 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx @@ -5,20 +5,22 @@ * 2.0. */ -import { EuiButton, EuiCheckboxProps } from '@elastic/eui'; -import { ReactWrapper } from 'enzyme'; -import React from 'react'; +import type { EuiCheckboxProps } from '@elastic/eui'; +import { EuiButton } from '@elastic/eui'; import { waitFor } from '@testing-library/react'; +import type { ReactWrapper } from 'enzyme'; +import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; -import { ConfirmAlterActiveSpaceModal } from './confirm_alter_active_space_modal'; -import { ManageSpacePage } from './manage_space_page'; -import { spacesManagerMock } from '../../spaces_manager/mocks'; -import { SpacesManager } from '../../spaces_manager'; +import { DEFAULT_APP_CATEGORIES } from 'src/core/public'; import { notificationServiceMock, scopedHistoryMock } from 'src/core/public/mocks'; -import { featuresPluginMock } from '../../../../features/public/mocks'; + import { KibanaFeature } from '../../../../features/public'; -import { DEFAULT_APP_CATEGORIES } from '../../../../../../src/core/public'; +import { featuresPluginMock } from '../../../../features/public/mocks'; +import type { SpacesManager } from '../../spaces_manager'; +import { spacesManagerMock } from '../../spaces_manager/mocks'; +import { ConfirmAlterActiveSpaceModal } from './confirm_alter_active_space_modal'; +import { ManageSpacePage } from './manage_space_page'; // To be resolved by EUI team. // https://github.com/elastic/eui/issues/3712 @@ -46,6 +48,13 @@ featuresStart.getFeatures.mockResolvedValue([ ]); describe('ManageSpacePage', () => { + beforeAll(() => { + Object.defineProperty(window, 'location', { + value: { reload: jest.fn() }, + writable: true, + }); + }); + const getUrlForApp = (appId: string) => appId; const history = scopedHistoryMock.create(); diff --git a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx index 37c61f8162e57..d00ee0bb26a3e 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx @@ -16,15 +16,22 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; import _ from 'lodash'; import React, { Component, Fragment } from 'react'; -import { ApplicationStart, Capabilities, NotificationsStart, ScopedHistory } from 'src/core/public'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; -import { KibanaFeature, FeaturesPluginStart } from '../../../../features/public'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import type { + ApplicationStart, + Capabilities, + NotificationsStart, + ScopedHistory, +} from 'src/core/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { FeaturesPluginStart, KibanaFeature } from '../../../../features/public'; import { isReservedSpace } from '../../../common'; -import { SpacesManager } from '../../spaces_manager'; +import type { SpacesManager } from '../../spaces_manager'; import { UnauthorizedPrompt } from '../components'; import { toSpaceIdentifier } from '../lib'; import { SpaceValidator } from '../lib/validate_space'; diff --git a/x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.test.tsx index 18cb0e7bd9765..d8fb026058dd0 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.test.tsx @@ -7,7 +7,9 @@ import { EuiBadge } from '@elastic/eui'; import React from 'react'; + import { shallowWithIntl } from '@kbn/test/jest'; + import { ReservedSpaceBadge } from './reserved_space_badge'; const reservedSpace = { diff --git a/x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.tsx b/x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.tsx index e9bcfadcc5b7a..da32ea79724ad 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/reserved_space_badge.tsx @@ -5,11 +5,12 @@ * 2.0. */ +import { EuiBadge, EuiToolTip } from '@elastic/eui'; import React from 'react'; -import { EuiBadge, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; +import type { Space } from 'src/plugins/spaces_oss/common'; + import { isReservedSpace } from '../../../common'; interface Props { diff --git a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx index b33b5a5381453..c3c5d03dff002 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx @@ -6,7 +6,9 @@ */ import React from 'react'; + import { mountWithIntl, shallowWithIntl } from '@kbn/test/jest'; + import { SectionPanel } from './section_panel'; test('it renders without blowing up', () => { diff --git a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx index 9c74ff6254aa2..68de71bda83b3 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx @@ -5,16 +5,10 @@ * 2.0. */ -import { - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiPanel, - EuiSpacer, - EuiTitle, - IconType, -} from '@elastic/eui'; -import React, { Component, Fragment, ReactNode } from 'react'; +import type { IconType } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; +import type { ReactNode } from 'react'; +import React, { Component, Fragment } from 'react'; interface Props { iconType?: IconType; diff --git a/x-pack/plugins/spaces/public/management/lib/feature_utils.test.ts b/x-pack/plugins/spaces/public/management/lib/feature_utils.test.ts index 05bc850113f91..83a06c8a4b733 100644 --- a/x-pack/plugins/spaces/public/management/lib/feature_utils.test.ts +++ b/x-pack/plugins/spaces/public/management/lib/feature_utils.test.ts @@ -5,8 +5,8 @@ * 2.0. */ +import type { KibanaFeatureConfig } from '../../../../features/public'; import { getEnabledFeatures } from './feature_utils'; -import { KibanaFeatureConfig } from '../../../../features/public'; const buildFeatures = () => [ diff --git a/x-pack/plugins/spaces/public/management/lib/feature_utils.ts b/x-pack/plugins/spaces/public/management/lib/feature_utils.ts index 1b5697f656854..d332b3e34c0a6 100644 --- a/x-pack/plugins/spaces/public/management/lib/feature_utils.ts +++ b/x-pack/plugins/spaces/public/management/lib/feature_utils.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { KibanaFeatureConfig } from '../../../../features/common'; +import type { Space } from 'src/plugins/spaces_oss/common'; -import { Space } from '../..'; +import type { KibanaFeatureConfig } from '../../../../features/common'; export function getEnabledFeatures(features: KibanaFeatureConfig[], space: Partial) { return features.filter((feature) => !(space.disabledFeatures || []).includes(feature.id)); diff --git a/x-pack/plugins/spaces/public/management/lib/validate_space.ts b/x-pack/plugins/spaces/public/management/lib/validate_space.ts index dac093e29ba09..157e990ef66ca 100644 --- a/x-pack/plugins/spaces/public/management/lib/validate_space.ts +++ b/x-pack/plugins/spaces/public/management/lib/validate_space.ts @@ -6,7 +6,8 @@ */ import { i18n } from '@kbn/i18n'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; +import type { Space } from 'src/plugins/spaces_oss/common'; + import { isReservedSpace } from '../../../common/is_reserved_space'; import { isValidSpaceIdentifier } from './space_identifier_utils'; diff --git a/x-pack/plugins/spaces/public/management/management_service.test.ts b/x-pack/plugins/spaces/public/management/management_service.test.ts index 1af0793ec8a34..8369bfe1f02c8 100644 --- a/x-pack/plugins/spaces/public/management/management_service.test.ts +++ b/x-pack/plugins/spaces/public/management/management_service.test.ts @@ -5,13 +5,14 @@ * 2.0. */ -import { ManagementService } from '.'; +import type { CoreSetup } from 'src/core/public'; import { coreMock } from 'src/core/public/mocks'; +import type { ManagementSection } from 'src/plugins/management/public'; +import { managementPluginMock } from 'src/plugins/management/public/mocks'; + +import type { PluginsStart } from '../plugin'; import { spacesManagerMock } from '../spaces_manager/mocks'; -import { managementPluginMock } from '../../../../../src/plugins/management/public/mocks'; -import { ManagementSection } from 'src/plugins/management/public'; -import { PluginsStart } from '../plugin'; -import { CoreSetup } from 'src/core/public'; +import { ManagementService } from './management_service'; describe('ManagementService', () => { describe('#setup', () => { diff --git a/x-pack/plugins/spaces/public/management/management_service.tsx b/x-pack/plugins/spaces/public/management/management_service.tsx index cff0682db5fd0..e7446a01076d1 100644 --- a/x-pack/plugins/spaces/public/management/management_service.tsx +++ b/x-pack/plugins/spaces/public/management/management_service.tsx @@ -5,10 +5,11 @@ * 2.0. */ -import { StartServicesAccessor } from 'src/core/public'; -import { ManagementSetup, ManagementApp } from '../../../../../src/plugins/management/public'; -import { SpacesManager } from '../spaces_manager'; -import { PluginsStart } from '../plugin'; +import type { StartServicesAccessor } from 'src/core/public'; +import type { ManagementApp, ManagementSetup } from 'src/plugins/management/public'; + +import type { PluginsStart } from '../plugin'; +import type { SpacesManager } from '../spaces_manager'; import { spacesManagementApp } from './spaces_management_app'; interface SetupDeps { diff --git a/x-pack/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap b/x-pack/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap index 3b67cceb1c869..0735a87dce039 100644 --- a/x-pack/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap +++ b/x-pack/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap @@ -130,7 +130,7 @@ exports[`SpacesGridPage renders as expected 1`] = ` exports[`SpacesGridPage renders the list of spaces 1`] = ` Array [ -
- , - , + - , - , + - , + , ] `; diff --git a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx index d5e2ff7d090ee..ac57a566e2a00 100644 --- a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx @@ -5,8 +5,6 @@ * 2.0. */ -import React, { Component, Fragment } from 'react'; - import { EuiButton, EuiButtonIcon, @@ -14,24 +12,38 @@ import { EuiFlexItem, EuiInMemoryTable, EuiLink, + EuiLoadingSpinner, EuiPageContent, EuiSpacer, EuiText, EuiTitle, } from '@elastic/eui'; +import React, { Component, Fragment, lazy, Suspense } from 'react'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ApplicationStart, Capabilities, NotificationsStart, ScopedHistory } from 'src/core/public'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; -import { KibanaFeature, FeaturesPluginStart } from '../../../../features/public'; +import type { + ApplicationStart, + Capabilities, + NotificationsStart, + ScopedHistory, +} from 'src/core/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import { reactRouterNavigate } from '../../../../../../src/plugins/kibana_react/public'; +import type { FeaturesPluginStart, KibanaFeature } from '../../../../features/public'; import { isReservedSpace } from '../../../common'; import { DEFAULT_SPACE_ID } from '../../../common/constants'; -import { SpaceAvatar } from '../../space_avatar'; import { getSpacesFeatureDescription } from '../../constants'; -import { SpacesManager } from '../../spaces_manager'; +import { getSpaceAvatarComponent } from '../../space_avatar'; +import type { SpacesManager } from '../../spaces_manager'; import { ConfirmDeleteModal, UnauthorizedPrompt } from '../components'; import { getEnabledFeatures } from '../lib/feature_utils'; -import { reactRouterNavigate } from '../../../../../../src/plugins/kibana_react/public'; + +// No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana. +const LazySpaceAvatar = lazy(() => + getSpaceAvatarComponent().then((component) => ({ default: component })) +); interface Props { spacesManager: SpacesManager; @@ -251,11 +263,15 @@ export class SpacesGridPage extends Component { field: 'initials', name: '', width: '50px', - render: (value: string, record: Space) => ( - - - - ), + render: (value: string, record: Space) => { + return ( + }> + + + + + ); + }, }, { field: 'name', diff --git a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx index bfef7b89d3c03..74301bd169de4 100644 --- a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx @@ -5,16 +5,18 @@ * 2.0. */ +import { act } from '@testing-library/react'; import React from 'react'; -import { mountWithIntl, shallowWithIntl, nextTick } from '@kbn/test/jest'; -import { SpaceAvatar } from '../../space_avatar'; + +import { mountWithIntl, shallowWithIntl } from '@kbn/test/jest'; +import { httpServiceMock, notificationServiceMock, scopedHistoryMock } from 'src/core/public/mocks'; + +import { KibanaFeature } from '../../../../features/public'; +import { featuresPluginMock } from '../../../../features/public/mocks'; +import { SpaceAvatarInternal } from '../../space_avatar/space_avatar_internal'; +import type { SpacesManager } from '../../spaces_manager'; import { spacesManagerMock } from '../../spaces_manager/mocks'; -import { SpacesManager } from '../../spaces_manager'; import { SpacesGridPage } from './spaces_grid_page'; -import { httpServiceMock, scopedHistoryMock } from 'src/core/public/mocks'; -import { notificationServiceMock } from 'src/core/public/mocks'; -import { featuresPluginMock } from '../../../../features/public/mocks'; -import { KibanaFeature } from '../../../../features/public'; const spaces = [ { @@ -99,12 +101,12 @@ describe('SpacesGridPage', () => { /> ); - // allow spacesManager to load spaces - await nextTick(); + // allow spacesManager to load spaces and lazy-load SpaceAvatar + await act(async () => {}); wrapper.update(); - expect(wrapper.find(SpaceAvatar)).toHaveLength(spaces.length); - expect(wrapper.find(SpaceAvatar)).toMatchSnapshot(); + expect(wrapper.find(SpaceAvatarInternal)).toHaveLength(spaces.length); + expect(wrapper.find(SpaceAvatarInternal)).toMatchSnapshot(); }); it('notifies when spaces fail to load', async () => { @@ -132,11 +134,11 @@ describe('SpacesGridPage', () => { /> ); - // allow spacesManager to load spaces - await nextTick(); + // allow spacesManager to load spaces and lazy-load SpaceAvatar + await act(async () => {}); wrapper.update(); - expect(wrapper.find(SpaceAvatar)).toHaveLength(0); + expect(wrapper.find(SpaceAvatarInternal)).toHaveLength(0); expect(notifications.toasts.addError).toHaveBeenCalledWith(error, { title: 'Error loading spaces', }); @@ -166,11 +168,11 @@ describe('SpacesGridPage', () => { /> ); - // allow spacesManager to load spaces - await nextTick(); + // allow spacesManager to load spaces and lazy-load SpaceAvatar + await act(async () => {}); wrapper.update(); - expect(wrapper.find(SpaceAvatar)).toHaveLength(0); + expect(wrapper.find(SpaceAvatarInternal)).toHaveLength(0); // For end-users, the effect is that spaces won't load, even though this was a request to retrieve features. expect(notifications.toasts.addError).toHaveBeenCalledWith(error, { title: 'Error loading spaces', diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx index cd6befac300c9..76467bd838a10 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx @@ -18,12 +18,12 @@ jest.mock('./edit_space', () => ({ }, })); -import { spacesManagementApp } from './spaces_management_app'; +import { coreMock, scopedHistoryMock } from 'src/core/public/mocks'; -import { coreMock, scopedHistoryMock } from '../../../../../src/core/public/mocks'; -import { spacesManagerMock } from '../spaces_manager/mocks'; import { featuresPluginMock } from '../../../features/public/mocks'; -import { PluginsStart } from '../plugin'; +import type { PluginsStart } from '../plugin'; +import { spacesManagerMock } from '../spaces_manager/mocks'; +import { spacesManagementApp } from './spaces_management_app'; async function mountApp(basePath: string, pathname: string, spaceId?: string) { const container = document.createElement('div'); diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx index c94baface8058..da0f9157f310d 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx @@ -7,16 +7,16 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { Router, Route, Switch, useParams } from 'react-router-dom'; +import { Route, Router, Switch, useParams } from 'react-router-dom'; + import { i18n } from '@kbn/i18n'; -import { StartServicesAccessor } from 'src/core/public'; +import type { StartServicesAccessor } from 'src/core/public'; +import type { RegisterManagementAppArgs } from 'src/plugins/management/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; + import { RedirectAppLinks } from '../../../../../src/plugins/kibana_react/public'; -import { RegisterManagementAppArgs } from '../../../../../src/plugins/management/public'; -import { PluginsStart } from '../plugin'; -import { SpacesManager } from '../spaces_manager'; -import { SpacesGridPage } from './spaces_grid'; -import { ManageSpacePage } from './edit_space'; -import { Space } from '..'; +import type { PluginsStart } from '../plugin'; +import type { SpacesManager } from '../spaces_manager'; interface CreateParams { getStartServices: StartServicesAccessor; @@ -36,10 +36,16 @@ export const spacesManagementApp = Object.freeze({ title, async mount({ element, setBreadcrumbs, history }) { + const [startServices, { SpacesGridPage }, { ManageSpacePage }] = await Promise.all([ + getStartServices(), + import('./spaces_grid'), + import('./edit_space'), + ]); + const [ { notifications, i18n: i18nStart, application, chrome }, { features }, - ] = await getStartServices(); + ] = startServices; const spacesBreadcrumbs = [ { text: title, diff --git a/x-pack/plugins/spaces/public/nav_control/components/manage_spaces_button.test.tsx b/x-pack/plugins/spaces/public/nav_control/components/manage_spaces_button.test.tsx index 63d35aa33ae65..3e1d77d0a45f1 100644 --- a/x-pack/plugins/spaces/public/nav_control/components/manage_spaces_button.test.tsx +++ b/x-pack/plugins/spaces/public/nav_control/components/manage_spaces_button.test.tsx @@ -6,7 +6,9 @@ */ import React from 'react'; + import { shallowWithIntl } from '@kbn/test/jest'; + import { ManageSpacesButton } from './manage_spaces_button'; describe('ManageSpacesButton', () => { diff --git a/x-pack/plugins/spaces/public/nav_control/components/manage_spaces_button.tsx b/x-pack/plugins/spaces/public/nav_control/components/manage_spaces_button.tsx index ffe4f40246292..11d77b347c15b 100644 --- a/x-pack/plugins/spaces/public/nav_control/components/manage_spaces_button.tsx +++ b/x-pack/plugins/spaces/public/nav_control/components/manage_spaces_button.tsx @@ -6,9 +6,11 @@ */ import { EuiButton } from '@elastic/eui'; +import type { CSSProperties } from 'react'; +import React, { Component } from 'react'; + import { FormattedMessage } from '@kbn/i18n/react'; -import React, { Component, CSSProperties } from 'react'; -import { Capabilities, ApplicationStart } from 'src/core/public'; +import type { ApplicationStart, Capabilities } from 'src/core/public'; interface Props { isDisabled?: boolean; diff --git a/x-pack/plugins/spaces/public/nav_control/components/spaces_description.tsx b/x-pack/plugins/spaces/public/nav_control/components/spaces_description.tsx index 4a9f5afa09183..4f7e47b6b7bbe 100644 --- a/x-pack/plugins/spaces/public/nav_control/components/spaces_description.tsx +++ b/x-pack/plugins/spaces/public/nav_control/components/spaces_description.tsx @@ -6,11 +6,15 @@ */ import './spaces_description.scss'; + import { EuiContextMenuPanel, EuiText } from '@elastic/eui'; -import React, { FC } from 'react'; -import { Capabilities, ApplicationStart } from 'src/core/public'; -import { ManageSpacesButton } from './manage_spaces_button'; +import type { FC } from 'react'; +import React from 'react'; + +import type { ApplicationStart, Capabilities } from 'src/core/public'; + import { getSpacesFeatureDescription } from '../../constants'; +import { ManageSpacesButton } from './manage_spaces_button'; interface Props { id: string; diff --git a/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx index cad61d4eeefe0..fda79bd93e39a 100644 --- a/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx +++ b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx @@ -6,20 +6,31 @@ */ import './spaces_menu.scss'; + import { EuiContextMenuItem, EuiContextMenuPanel, EuiFieldSearch, - EuiText, EuiLoadingContent, + EuiLoadingSpinner, + EuiText, } from '@elastic/eui'; -import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; -import React, { Component, ReactElement } from 'react'; -import { Capabilities, ApplicationStart } from 'src/core/public'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; -import { addSpaceIdToPath, SPACE_SEARCH_COUNT_THRESHOLD, ENTER_SPACE_PATH } from '../../../common'; +import type { ReactElement } from 'react'; +import React, { Component, lazy, Suspense } from 'react'; + +import type { InjectedIntl } from '@kbn/i18n/react'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; +import type { ApplicationStart, Capabilities } from 'src/core/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import { addSpaceIdToPath, ENTER_SPACE_PATH, SPACE_SEARCH_COUNT_THRESHOLD } from '../../../common'; +import { getSpaceAvatarComponent } from '../../space_avatar'; import { ManageSpacesButton } from './manage_spaces_button'; -import { SpaceAvatar } from '../../space_avatar'; + +// No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana. +const LazySpaceAvatar = lazy(() => + getSpaceAvatarComponent().then((component) => ({ default: component })) +); interface Props { id: string; @@ -181,7 +192,11 @@ class SpacesMenuUI extends Component { }; private renderSpaceMenuItem = (space: Space): JSX.Element => { - const icon = ; + const icon = ( + }> + + + ); return ( null; } + const LazyNavControlPopover = lazy(() => + import('./nav_control_popover').then(({ NavControlPopover }) => ({ + default: NavControlPopover, + })) + ); + ReactDOM.render( - + }> + + , targetDomElement ); diff --git a/x-pack/plugins/spaces/public/nav_control/nav_control_popover.test.tsx b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.test.tsx index f78ecfd247d09..241f8c72ef9ff 100644 --- a/x-pack/plugins/spaces/public/nav_control/nav_control_popover.test.tsx +++ b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.test.tsx @@ -5,16 +5,18 @@ * 2.0. */ -import * as Rx from 'rxjs'; +import { EuiHeaderSectionItemButton } from '@elastic/eui'; +import { waitFor } from '@testing-library/react'; import { shallow } from 'enzyme'; import React from 'react'; -import { SpaceAvatar } from '../space_avatar'; +import * as Rx from 'rxjs'; + +import { mountWithIntl } from '@kbn/test/jest'; + +import { SpaceAvatarInternal } from '../space_avatar/space_avatar_internal'; +import type { SpacesManager } from '../spaces_manager'; import { spacesManagerMock } from '../spaces_manager/mocks'; -import { SpacesManager } from '../spaces_manager'; import { NavControlPopover } from './nav_control_popover'; -import { EuiHeaderSectionItemButton } from '@elastic/eui'; -import { mountWithIntl } from '@kbn/test/jest'; -import { waitFor } from '@testing-library/react'; describe('NavControlPopover', () => { it('renders without crashing', () => { @@ -68,7 +70,7 @@ describe('NavControlPopover', () => { // Wait for `getSpaces` promise to resolve await waitFor(() => { wrapper.update(); - expect(wrapper.find(SpaceAvatar)).toHaveLength(3); + expect(wrapper.find(SpaceAvatarInternal)).toHaveLength(3); }); }); }); diff --git a/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx index a7049e53fb515..392219d480e67 100644 --- a/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx +++ b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx @@ -5,21 +5,24 @@ * 2.0. */ -import { - EuiPopover, - PopoverAnchorPosition, - EuiLoadingSpinner, - EuiHeaderSectionItemButton, -} from '@elastic/eui'; -import React, { Component } from 'react'; -import { Capabilities, ApplicationStart } from 'src/core/public'; -import { Subscription } from 'rxjs'; -import { Space } from '../../../../../src/plugins/spaces_oss/common'; -import { SpaceAvatar } from '../space_avatar'; -import { SpacesManager } from '../spaces_manager'; +import type { PopoverAnchorPosition } from '@elastic/eui'; +import { EuiHeaderSectionItemButton, EuiLoadingSpinner, EuiPopover } from '@elastic/eui'; +import React, { Component, lazy, Suspense } from 'react'; +import type { Subscription } from 'rxjs'; + +import type { ApplicationStart, Capabilities } from 'src/core/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import { getSpaceAvatarComponent } from '../space_avatar'; +import type { SpacesManager } from '../spaces_manager'; import { SpacesDescription } from './components/spaces_description'; import { SpacesMenu } from './components/spaces_menu'; +// No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana. +const LazySpaceAvatar = lazy(() => + getSpaceAvatarComponent().then((component) => ({ default: component })) +); + interface Props { spacesManager: SpacesManager; anchorPosition: PopoverAnchorPosition; @@ -140,7 +143,9 @@ export class NavControlPopover extends Component { } return this.getButton( - , + }> + + , (activeSpace as Space).name ); }; diff --git a/x-pack/plugins/spaces/public/plugin.test.ts b/x-pack/plugins/spaces/public/plugin.test.ts index 20e52895a900f..39478ca2fd9be 100644 --- a/x-pack/plugins/spaces/public/plugin.test.ts +++ b/x-pack/plugins/spaces/public/plugin.test.ts @@ -6,14 +6,15 @@ */ import { coreMock } from 'src/core/public/mocks'; -import { SpacesPlugin } from './plugin'; -import { spacesOssPluginMock } from '../../../../src/plugins/spaces_oss/public/mocks'; -import { homePluginMock } from '../../../../src/plugins/home/public/mocks'; +import { advancedSettingsMock } from 'src/plugins/advanced_settings/public/mocks'; +import { homePluginMock } from 'src/plugins/home/public/mocks'; import { - managementPluginMock, createManagementSectionMock, -} from '../../../../src/plugins/management/public/mocks'; -import { advancedSettingsMock } from '../../../../src/plugins/advanced_settings/public/mocks'; + managementPluginMock, +} from 'src/plugins/management/public/mocks'; +import { spacesOssPluginMock } from 'src/plugins/spaces_oss/public/mocks'; + +import { SpacesPlugin } from './plugin'; describe('Spaces plugin', () => { describe('#setup', () => { diff --git a/x-pack/plugins/spaces/public/plugin.tsx b/x-pack/plugins/spaces/public/plugin.tsx index 2d02d4a3b98d8..062a534c91c84 100644 --- a/x-pack/plugins/spaces/public/plugin.tsx +++ b/x-pack/plugins/spaces/public/plugin.tsx @@ -5,21 +5,22 @@ * 2.0. */ -import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; -import { SpacesOssPluginSetup, SpacesApi } from 'src/plugins/spaces_oss/public'; -import { HomePublicPluginSetup } from 'src/plugins/home/public'; -import { SavedObjectsManagementPluginSetup } from 'src/plugins/saved_objects_management/public'; -import { ManagementStart, ManagementSetup } from 'src/plugins/management/public'; -import { AdvancedSettingsSetup } from 'src/plugins/advanced_settings/public'; -import { FeaturesPluginStart } from '../../features/public'; -import { SpacesManager } from './spaces_manager'; -import { initSpacesNavControl } from './nav_control'; -import { createSpacesFeatureCatalogueEntry } from './create_feature_catalogue_entry'; -import { CopySavedObjectsToSpaceService } from './copy_saved_objects_to_space'; -import { ShareSavedObjectsToSpaceService } from './share_saved_objects_to_space'; +import type { CoreSetup, CoreStart, Plugin } from 'src/core/public'; +import type { AdvancedSettingsSetup } from 'src/plugins/advanced_settings/public'; +import type { HomePublicPluginSetup } from 'src/plugins/home/public'; +import type { ManagementSetup, ManagementStart } from 'src/plugins/management/public'; +import type { SavedObjectsManagementPluginSetup } from 'src/plugins/saved_objects_management/public'; +import type { SpacesApi, SpacesOssPluginSetup } from 'src/plugins/spaces_oss/public'; + +import type { FeaturesPluginStart } from '../../features/public'; import { AdvancedSettingsService } from './advanced_settings'; +import { CopySavedObjectsToSpaceService } from './copy_saved_objects_to_space'; +import { createSpacesFeatureCatalogueEntry } from './create_feature_catalogue_entry'; import { ManagementService } from './management'; +import { initSpacesNavControl } from './nav_control'; +import { ShareSavedObjectsToSpaceService } from './share_saved_objects_to_space'; import { spaceSelectorApp } from './space_selector'; +import { SpacesManager } from './spaces_manager'; import { getUiApi } from './ui_api'; export interface PluginsSetup { @@ -84,9 +85,8 @@ export class SpacesPlugin implements Plugin => { +): Promise> => { + const { LegacyUrlConflictInternal } = await import('./legacy_url_conflict_internal'); return (props: LegacyUrlConflictProps) => { return ; }; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.test.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.test.tsx index 1b897e8afa7d2..b999ae961d0f1 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.test.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.test.tsx @@ -5,12 +5,14 @@ * 2.0. */ -import React from 'react'; -import { BehaviorSubject } from 'rxjs'; import { EuiCallOut } from '@elastic/eui'; -import { mountWithIntl, findTestSubject } from '@kbn/test/jest'; import { act } from '@testing-library/react'; -import { coreMock } from '../../../../../../src/core/public/mocks'; +import React from 'react'; +import { BehaviorSubject } from 'rxjs'; + +import { findTestSubject, mountWithIntl } from '@kbn/test/jest'; +import { coreMock } from 'src/core/public/mocks'; + import { LegacyUrlConflictInternal } from './legacy_url_conflict_internal'; const APP_ID = 'testAppId'; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.tsx index 1157725c69ee2..1ebde52a734c6 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.tsx @@ -13,11 +13,13 @@ import { EuiFlexItem, EuiSpacer, } from '@elastic/eui'; +import React, { useEffect, useState } from 'react'; +import { first } from 'rxjs/operators'; + import { FormattedMessage } from '@kbn/i18n/react'; -import { firstValueFrom } from '@kbn/std'; -import React, { useState, useEffect } from 'react'; import type { ApplicationStart, StartServicesAccessor } from 'src/core/public'; import type { LegacyUrlConflictProps } from 'src/plugins/spaces_oss/public'; + import type { PluginsStart } from '../../plugin'; import { DEFAULT_OBJECT_NOUN } from './constants'; @@ -41,7 +43,7 @@ export const LegacyUrlConflictInternal = (props: InternalProps & LegacyUrlConfli useEffect(() => { async function setup() { const [{ application }] = await getStartServices(); - const appIdValue = await firstValueFrom(application.currentAppId$); // retrieve the most recent value from the BehaviorSubject + const appIdValue = await application.currentAppId$.pipe(first()).toPromise(); // retrieve the most recent value from the BehaviorSubject setApplicationStart(application); setAppId(appIdValue); } diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/no_spaces_available.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/no_spaces_available.tsx index 46610a2cc9a7c..7dcb50e6cb1db 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/no_spaces_available.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/no_spaces_available.tsx @@ -5,10 +5,11 @@ * 2.0. */ -import React from 'react'; import { EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import React from 'react'; + import { FormattedMessage } from '@kbn/i18n/react'; -import { ApplicationStart } from 'src/core/public'; +import type { ApplicationStart } from 'src/core/public'; interface Props { application: ApplicationStart; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx index 1b5870b8b540d..876c8d027b2b4 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx @@ -6,7 +6,8 @@ */ import './selectable_spaces_control.scss'; -import React from 'react'; + +import type { EuiSelectableOption } from '@elastic/eui'; import { EuiBadge, EuiFlexGroup, @@ -14,20 +15,28 @@ import { EuiFormRow, EuiIconTip, EuiLink, + EuiLoadingSpinner, EuiSelectable, - EuiSelectableOption, EuiSpacer, EuiText, } from '@elastic/eui'; +import React, { lazy, Suspense } from 'react'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { NoSpacesAvailable } from './no_spaces_available'; + import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../../common/constants'; import { DocumentationLinksService } from '../../lib'; -import { SpaceAvatar } from '../../space_avatar'; -import { ShareToSpaceTarget } from '../../types'; +import { getSpaceAvatarComponent } from '../../space_avatar'; import { useSpaces } from '../../spaces_context'; -import { ShareOptions } from '../types'; +import type { ShareToSpaceTarget } from '../../types'; +import type { ShareOptions } from '../types'; +import { NoSpacesAvailable } from './no_spaces_available'; + +// No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana. +const LazySpaceAvatar = lazy(() => + getSpaceAvatarComponent().then((component) => ({ default: component })) +); interface Props { spaces: ShareToSpaceTarget[]; @@ -100,7 +109,7 @@ export const SelectableSpacesControl = (props: Props) => { const additionalProps = getAdditionalProps(space, activeSpaceId, checked); return { label: space.name, - prepend: , + prepend: , // wrapped in a Suspense below checked: checked ? 'on' : undefined, ['data-space-id']: space.id, ['data-test-subj']: `sts-space-selector-row-${space.id}`, @@ -192,27 +201,29 @@ export const SelectableSpacesControl = (props: Props) => { fullWidth > <> - updateSelectedSpaces(newOptions as SpaceOption[])} - listProps={{ - bordered: true, - rowHeight: ROW_HEIGHT, - className: 'spcShareToSpace__spacesList', - 'data-test-subj': 'sts-form-space-selector', - }} - height={ROW_HEIGHT * 3.5} - searchable={options.length > 6} - > - {(list, search) => { - return ( - <> - {search} - {list} - - ); - }} - + }> + updateSelectedSpaces(newOptions as SpaceOption[])} + listProps={{ + bordered: true, + rowHeight: ROW_HEIGHT, + className: 'spcShareToSpace__spacesList', + 'data-test-subj': 'sts-form-space-selector', + }} + height={ROW_HEIGHT * 3.5} + searchable={options.length > 6} + > + {(list, search) => { + return ( + <> + {search} + {list} + + ); + }} + + {getUnknownSpacesLabel()} {getNoSpacesAvailable()} diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.tsx index 23b2dc02ec3cc..df8d72f7a59de 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.tsx @@ -6,7 +6,7 @@ */ import './share_mode_control.scss'; -import React from 'react'; + import { EuiCallOut, EuiCheckableCard, @@ -18,14 +18,17 @@ import { EuiSpacer, EuiText, } from '@elastic/eui'; +import React from 'react'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SelectableSpacesControl } from './selectable_spaces_control'; + import { ALL_SPACES_ID } from '../../../common/constants'; import { DocumentationLinksService } from '../../lib'; import { useSpaces } from '../../spaces_context'; -import { ShareToSpaceTarget } from '../../types'; -import { ShareOptions } from '../types'; +import type { ShareToSpaceTarget } from '../../types'; +import type { ShareOptions } from '../types'; +import { SelectableSpacesControl } from './selectable_spaces_control'; interface Props { spaces: ShareToSpaceTarget[]; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.tsx index 0f9783e3ac8c0..dc2a358a653ab 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.tsx @@ -6,10 +6,13 @@ */ import React from 'react'; -import type { ShareToSpaceFlyoutProps } from '../../../../../../src/plugins/spaces_oss/public'; -import { ShareToSpaceFlyoutInternal } from './share_to_space_flyout_internal'; -export const getShareToSpaceFlyoutComponent = (): React.FC => { +import type { ShareToSpaceFlyoutProps } from 'src/plugins/spaces_oss/public'; + +export const getShareToSpaceFlyoutComponent = async (): Promise< + React.FC +> => { + const { ShareToSpaceFlyoutInternal } = await import('./share_to_space_flyout_internal'); return (props: ShareToSpaceFlyoutProps) => { return ; }; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.test.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.test.tsx index 1b33b42637fe8..b5b7c7c657b1b 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.test.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.test.tsx @@ -5,30 +5,32 @@ * 2.0. */ -import React from 'react'; -import Boom from '@hapi/boom'; -import { mountWithIntl, nextTick, findTestSubject } from '@kbn/test/jest'; -import { ShareToSpaceForm } from './share_to_space_form'; +import type { EuiCheckableCardProps } from '@elastic/eui'; import { EuiCallOut, EuiCheckableCard, - EuiCheckableCardProps, EuiIconTip, EuiLoadingSpinner, EuiSelectable, } from '@elastic/eui'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; -import { SelectableSpacesControl } from './selectable_spaces_control'; +import Boom from '@hapi/boom'; import { act } from '@testing-library/react'; +import type { ReactWrapper } from 'enzyme'; +import React from 'react'; + +import { findTestSubject, mountWithIntl, nextTick } from '@kbn/test/jest'; +import { coreMock } from 'src/core/public/mocks'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import { ALL_SPACES_ID } from '../../../common/constants'; +import { CopyToSpaceFlyoutInternal } from '../../copy_saved_objects_to_space/components/copy_to_space_flyout_internal'; +import { getSpacesContextProviderWrapper } from '../../spaces_context'; import { spacesManagerMock } from '../../spaces_manager/mocks'; -import { coreMock } from '../../../../../../src/core/public/mocks'; -import { CopySavedObjectsToSpaceFlyout } from '../../copy_saved_objects_to_space/components'; import { NoSpacesAvailable } from './no_spaces_available'; -import { getShareToSpaceFlyoutComponent } from './share_to_space_flyout'; +import { SelectableSpacesControl } from './selectable_spaces_control'; import { ShareModeControl } from './share_mode_control'; -import { ReactWrapper } from 'enzyme'; -import { ALL_SPACES_ID } from '../../../common/constants'; -import { getSpacesContextWrapper } from '../../spaces_context'; +import { getShareToSpaceFlyoutComponent } from './share_to_space_flyout'; +import { ShareToSpaceForm } from './share_to_space_form'; interface SetupOpts { mockSpaces?: Space[]; @@ -101,11 +103,11 @@ const setup = async (opts: SetupOpts = {}) => { const mockToastNotifications = startServices.notifications.toasts; getStartServices.mockResolvedValue([startServices, , ,]); - const SpacesContext = getSpacesContextWrapper({ + const SpacesContext = await getSpacesContextProviderWrapper({ getStartServices, spacesManager: mockSpacesManager, }); - const ShareToSpaceFlyout = getShareToSpaceFlyoutComponent(); + const ShareToSpaceFlyout = await getShareToSpaceFlyoutComponent(); // the internal flyout depends upon the Kibana React Context, and it cannot be used without the context wrapper // the context wrapper is only split into a separate component to avoid recreating the context upon every flyout state change // the ShareToSpaceFlyout component renders the internal flyout inside of the context wrapper @@ -195,7 +197,7 @@ describe('ShareToSpaceFlyout', () => { }); expect(wrapper.find(ShareToSpaceForm)).toHaveLength(1); - expect(wrapper.find(CopySavedObjectsToSpaceFlyout)).toHaveLength(0); + expect(wrapper.find(CopyToSpaceFlyoutInternal)).toHaveLength(0); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); expect(onClose).toHaveBeenCalledTimes(0); }); @@ -215,10 +217,10 @@ describe('ShareToSpaceFlyout', () => { await act(async () => { copyButton.simulate('click'); await nextTick(); - wrapper.update(); }); + wrapper.update(); - expect(wrapper.find(CopySavedObjectsToSpaceFlyout)).toHaveLength(1); + expect(wrapper.find(CopyToSpaceFlyoutInternal)).toHaveLength(1); expect(onClose).toHaveBeenCalledTimes(0); }); }); diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.tsx index 8d9875977af18..fc5d42df8af5e 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout_internal.tsx @@ -5,37 +5,45 @@ * 2.0. */ -import React, { useState, useEffect, useMemo } from 'react'; import { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, EuiFlyout, - EuiIcon, - EuiFlyoutHeader, - EuiTitle, - EuiText, EuiFlyoutBody, EuiFlyoutFooter, - EuiLoadingSpinner, - EuiFlexGroup, - EuiFlexItem, + EuiFlyoutHeader, EuiHorizontalRule, - EuiButton, - EuiButtonEmpty, + EuiIcon, + EuiLoadingSpinner, + EuiText, + EuiTitle, } from '@elastic/eui'; +import React, { lazy, Suspense, useEffect, useMemo, useState } from 'react'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ToastsStart } from 'src/core/public'; +import type { ToastsStart } from 'src/core/public'; import type { ShareToSpaceFlyoutProps, ShareToSpaceSavedObjectTarget, } from 'src/plugins/spaces_oss/public'; + import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../../common/constants'; -import { SpacesManager } from '../../spaces_manager'; -import { ShareToSpaceTarget } from '../../types'; -import { ShareToSpaceForm } from './share_to_space_form'; -import { ShareOptions } from '../types'; -import { CopySavedObjectsToSpaceFlyout } from '../../copy_saved_objects_to_space/components'; +import { getCopyToSpaceFlyoutComponent } from '../../copy_saved_objects_to_space'; import { useSpaces } from '../../spaces_context'; +import type { SpacesManager } from '../../spaces_manager'; +import type { ShareToSpaceTarget } from '../../types'; +import type { ShareOptions } from '../types'; import { DEFAULT_OBJECT_NOUN } from './constants'; +import { ShareToSpaceForm } from './share_to_space_form'; + +// No need to wrap LazyCopyToSpaceFlyout in an error boundary, because the ShareToSpaceFlyoutInternal component itself is only ever used in +// a lazy-loaded fashion with an error boundary. +const LazyCopyToSpaceFlyout = lazy(() => + getCopyToSpaceFlyoutComponent().then((component) => ({ default: component })) +); const ALL_SPACES_TARGET = i18n.translate('xpack.spaces.shareToSpace.allSpacesTarget', { defaultMessage: 'all', @@ -269,12 +277,9 @@ export const ShareToSpaceFlyoutInternal = (props: ShareToSpaceFlyoutProps) => { if (showMakeCopy) { return ( - + }> + + ); } diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_form.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_form.tsx index 49c581b07004b..65ed0139d0dd8 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_form.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_form.tsx @@ -6,11 +6,14 @@ */ import './share_to_space_form.scss'; + +import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; import React, { Fragment } from 'react'; -import { EuiSpacer, EuiCallOut, EuiLink } from '@elastic/eui'; + import { FormattedMessage } from '@kbn/i18n/react'; -import { ShareToSpaceTarget } from '../../types'; -import { ShareOptions } from '../types'; + +import type { ShareToSpaceTarget } from '../../types'; +import type { ShareOptions } from '../types'; import { ShareModeControl } from './share_mode_control'; interface Props { diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.test.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.test.tsx index a8d503d306ee8..9c3a56aac20ad 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.test.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.test.tsx @@ -5,7 +5,8 @@ * 2.0. */ -import { SavedObjectsManagementRecord } from '../../../../../src/plugins/saved_objects_management/public'; +import type { SavedObjectsManagementRecord } from 'src/plugins/saved_objects_management/public'; + import { uiApiMock } from '../ui_api/mocks'; import { ShareToSpaceSavedObjectsManagementAction } from './share_saved_objects_to_space_action'; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.tsx index feb073745c616..4e07f6799e9c6 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.tsx @@ -5,13 +5,24 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; + import { i18n } from '@kbn/i18n'; -import { - SavedObjectsManagementAction, - SavedObjectsManagementRecord, -} from '../../../../../src/plugins/saved_objects_management/public'; -import type { SpacesApiUi } from '../../../../../src/plugins/spaces_oss/public'; +import type { SavedObjectsManagementRecord } from 'src/plugins/saved_objects_management/public'; +import type { SpacesApiUi, ShareToSpaceFlyoutProps } from 'src/plugins/spaces_oss/public'; + +import { SavedObjectsManagementAction } from '../../../../../src/plugins/saved_objects_management/public'; + +interface WrapperProps { + spacesApiUi: SpacesApiUi; + props: ShareToSpaceFlyoutProps; +} + +const Wrapper = ({ spacesApiUi, props }: WrapperProps) => { + const LazyComponent = useMemo(() => spacesApiUi.components.getShareToSpaceFlyout, [spacesApiUi]); + + return ; +}; export class ShareToSpaceSavedObjectsManagementAction extends SavedObjectsManagementAction { public id: string = 'share_saved_objects_to_space'; @@ -49,25 +60,22 @@ export class ShareToSpaceSavedObjectsManagementAction extends SavedObjectsManage throw new Error('No record available! `render()` was likely called before `start()`.'); } - const savedObjectTarget = { - type: this.record.type, - id: this.record.id, - namespaces: this.record.namespaces ?? [], - title: this.record.meta.title, - icon: this.record.meta.icon, + const props: ShareToSpaceFlyoutProps = { + savedObjectTarget: { + type: this.record.type, + id: this.record.id, + namespaces: this.record.namespaces ?? [], + title: this.record.meta.title, + icon: this.record.meta.icon, + }, + flyoutIcon: 'share', + onUpdate: () => (this.isDataChanged = true), + onClose: this.onClose, + enableCreateCopyCallout: true, + enableCreateNewSpaceLink: true, }; - const { ShareToSpaceFlyout } = this.spacesApiUi.components; - return ( - (this.isDataChanged = true)} - onClose={this.onClose} - enableCreateCopyCallout={true} - enableCreateNewSpaceLink={true} - /> - ); + return ; }; private onClose = () => { diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_column.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_column.tsx index 05e0976da0710..c9909af16e387 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_column.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_column.tsx @@ -5,10 +5,22 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; + import { i18n } from '@kbn/i18n'; -import { SavedObjectsManagementColumn } from '../../../../../src/plugins/saved_objects_management/public'; -import type { SpacesApiUi } from '../../../../../src/plugins/spaces_oss/public'; +import type { SavedObjectsManagementColumn } from 'src/plugins/saved_objects_management/public'; +import type { SpacesApiUi, SpaceListProps } from 'src/plugins/spaces_oss/public'; + +interface WrapperProps { + spacesApiUi: SpacesApiUi; + props: SpaceListProps; +} + +const Wrapper = ({ spacesApiUi, props }: WrapperProps) => { + const LazyComponent = useMemo(() => spacesApiUi.components.getSpaceList, [spacesApiUi]); + + return ; +}; export class ShareToSpaceSavedObjectsManagementColumn implements SavedObjectsManagementColumn { @@ -26,7 +38,12 @@ export class ShareToSpaceSavedObjectsManagementColumn if (!namespaces) { return null; } - return ; + + const props: SpaceListProps = { + namespaces, + }; + + return ; }, }; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.test.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.test.ts index 6e74fa31ec4b8..eb973a48ef879 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.test.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.test.ts @@ -5,11 +5,12 @@ * 2.0. */ +import { savedObjectsManagementPluginMock } from 'src/plugins/saved_objects_management/public/mocks'; + +import { uiApiMock } from '../ui_api/mocks'; import { ShareToSpaceSavedObjectsManagementAction } from './share_saved_objects_to_space_action'; // import { ShareToSpaceSavedObjectsManagementColumn } from './share_saved_objects_to_space_column'; -import { ShareSavedObjectsToSpaceService } from '.'; -import { savedObjectsManagementPluginMock } from '../../../../../src/plugins/saved_objects_management/public/mocks'; -import { uiApiMock } from '../ui_api/mocks'; +import { ShareSavedObjectsToSpaceService } from './share_saved_objects_to_space_service'; describe('ShareSavedObjectsToSpaceService', () => { describe('#setup', () => { diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.ts index 86b9c07bebe92..bc70347760465 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.ts @@ -5,8 +5,9 @@ * 2.0. */ -import { SavedObjectsManagementPluginSetup } from 'src/plugins/saved_objects_management/public'; -import type { SpacesApiUi } from '../../../../../src/plugins/spaces_oss/public'; +import type { SavedObjectsManagementPluginSetup } from 'src/plugins/saved_objects_management/public'; +import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; + import { ShareToSpaceSavedObjectsManagementAction } from './share_saved_objects_to_space_action'; // import { ShareToSpaceSavedObjectsManagementColumn } from './share_saved_objects_to_space_column'; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts index fda561d8c4af1..be8165e822736 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObjectsImportRetry, SavedObjectsImportResponse } from 'src/core/public'; +import type { SavedObjectsImportResponse, SavedObjectsImportRetry } from 'src/core/public'; export interface ShareOptions { selectedSpaceIds: string[]; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.test.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.test.ts index 84d2958092a65..aaf92098f0b09 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.test.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.test.ts @@ -6,7 +6,9 @@ */ import { BehaviorSubject } from 'rxjs'; -import { coreMock } from '../../../../../../src/core/public/mocks'; + +import { coreMock } from 'src/core/public/mocks'; + import { createRedirectLegacyUrl } from './redirect_legacy_url'; const APP_ID = 'testAppId'; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.ts index 694465e34049c..338b2e8c94e0d 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.ts @@ -5,10 +5,12 @@ * 2.0. */ +import { first } from 'rxjs/operators'; + import { i18n } from '@kbn/i18n'; -import { firstValueFrom } from '@kbn/std'; import type { StartServicesAccessor } from 'src/core/public'; import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; + import type { PluginsStart } from '../../plugin'; import { DEFAULT_OBJECT_NOUN } from '../components/constants'; @@ -18,7 +20,7 @@ export function createRedirectLegacyUrl( return async function (path: string, objectNoun: string = DEFAULT_OBJECT_NOUN) { const [{ notifications, application }] = await getStartServices(); const { currentAppId$, navigateToApp } = application; - const appId = await firstValueFrom(currentAppId$); // retrieve the most recent value from the BehaviorSubject + const appId = await currentAppId$.pipe(first()).toPromise(); // retrieve the most recent value from the BehaviorSubject const title = i18n.translate('xpack.spaces.shareToSpace.redirectLegacyUrlToast.title', { defaultMessage: `We redirected you to a new URL`, diff --git a/x-pack/plugins/spaces/public/space_avatar/__snapshots__/space_avatar.test.tsx.snap b/x-pack/plugins/spaces/public/space_avatar/__snapshots__/space_avatar_internal.test.tsx.snap similarity index 96% rename from x-pack/plugins/spaces/public/space_avatar/__snapshots__/space_avatar.test.tsx.snap rename to x-pack/plugins/spaces/public/space_avatar/__snapshots__/space_avatar_internal.test.tsx.snap index 9261a8fec1470..9a63e14cb38ef 100644 --- a/x-pack/plugins/spaces/public/space_avatar/__snapshots__/space_avatar.test.tsx.snap +++ b/x-pack/plugins/spaces/public/space_avatar/__snapshots__/space_avatar_internal.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`removes aria-label when instructed not to announce the space name 1`] = ` - - + `; exports[`renders with a space name entirely made of whitespace 1`] = ` diff --git a/x-pack/plugins/spaces/public/space_avatar/index.ts b/x-pack/plugins/spaces/public/space_avatar/index.ts index 7ce0cbd11e294..86d94738f2c79 100644 --- a/x-pack/plugins/spaces/public/space_avatar/index.ts +++ b/x-pack/plugins/spaces/public/space_avatar/index.ts @@ -5,5 +5,5 @@ * 2.0. */ -export { SpaceAvatar } from './space_avatar'; +export { getSpaceAvatarComponent } from './space_avatar'; export * from './space_attributes'; diff --git a/x-pack/plugins/spaces/public/space_avatar/space_attributes.ts b/x-pack/plugins/spaces/public/space_avatar/space_attributes.ts index 29b4743fb3940..e37d7eed322e1 100644 --- a/x-pack/plugins/spaces/public/space_avatar/space_attributes.ts +++ b/x-pack/plugins/spaces/public/space_avatar/space_attributes.ts @@ -6,7 +6,9 @@ */ import { VISUALIZATION_COLORS } from '@elastic/eui'; -import { Space } from '../../../../../src/plugins/spaces_oss/common'; + +import type { Space } from 'src/plugins/spaces_oss/common'; + import { MAX_SPACE_INITIALS } from '../../common'; // code point for lowercase "a" diff --git a/x-pack/plugins/spaces/public/space_avatar/space_avatar.tsx b/x-pack/plugins/spaces/public/space_avatar/space_avatar.tsx index d112d7bfd497c..a2d58a12c5eaa 100644 --- a/x-pack/plugins/spaces/public/space_avatar/space_avatar.tsx +++ b/x-pack/plugins/spaces/public/space_avatar/space_avatar.tsx @@ -5,46 +5,13 @@ * 2.0. */ -import { EuiAvatar, isValidHex } from '@elastic/eui'; -import React, { FC } from 'react'; -import { Space } from '../../../../../src/plugins/spaces_oss/common'; -import { MAX_SPACE_INITIALS } from '../../common'; -import { getSpaceColor, getSpaceInitials, getSpaceImageUrl } from './space_attributes'; +import React from 'react'; -interface Props { - space: Partial; - size?: 's' | 'm' | 'l' | 'xl'; - className?: string; - announceSpaceName?: boolean; -} +import type { SpaceAvatarProps } from 'src/plugins/spaces_oss/public'; -export const SpaceAvatar: FC = (props: Props) => { - const { space, size, announceSpaceName, ...rest } = props; - - const spaceName = space.name ? space.name.trim() : ''; - - const spaceColor = getSpaceColor(space); - - return ( - - ); -}; - -SpaceAvatar.defaultProps = { - announceSpaceName: true, +export const getSpaceAvatarComponent = async (): Promise> => { + const { SpaceAvatarInternal } = await import('./space_avatar_internal'); + return (props: SpaceAvatarProps) => { + return ; + }; }; diff --git a/x-pack/plugins/spaces/public/space_avatar/space_avatar.test.tsx b/x-pack/plugins/spaces/public/space_avatar/space_avatar_internal.test.tsx similarity index 65% rename from x-pack/plugins/spaces/public/space_avatar/space_avatar.test.tsx rename to x-pack/plugins/spaces/public/space_avatar/space_avatar_internal.test.tsx index 37646583efb46..e5db20091bf24 100644 --- a/x-pack/plugins/spaces/public/space_avatar/space_avatar.test.tsx +++ b/x-pack/plugins/spaces/public/space_avatar/space_avatar_internal.test.tsx @@ -7,19 +7,22 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; -import { SpaceAvatar } from './space_avatar'; + +import { SpaceAvatarInternal } from './space_avatar_internal'; test('renders without crashing', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); test('renders with a space name entirely made of whitespace', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); test('removes aria-label when instructed not to announce the space name', () => { - const wrapper = mount(); + const wrapper = mount( + + ); expect(wrapper).toMatchSnapshot(); }); diff --git a/x-pack/plugins/spaces/public/space_avatar/space_avatar_internal.tsx b/x-pack/plugins/spaces/public/space_avatar/space_avatar_internal.tsx new file mode 100644 index 0000000000000..dab670c07d562 --- /dev/null +++ b/x-pack/plugins/spaces/public/space_avatar/space_avatar_internal.tsx @@ -0,0 +1,53 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiAvatar, isValidHex } from '@elastic/eui'; +import type { FC } from 'react'; +import React from 'react'; + +import type { Space } from 'src/plugins/spaces_oss/common'; + +import { MAX_SPACE_INITIALS } from '../../common'; +import { getSpaceColor, getSpaceImageUrl, getSpaceInitials } from './space_attributes'; + +interface Props { + space: Partial; + size?: 's' | 'm' | 'l' | 'xl'; + className?: string; + announceSpaceName?: boolean; +} + +export const SpaceAvatarInternal: FC = (props: Props) => { + const { space, size, announceSpaceName, ...rest } = props; + + const spaceName = space.name ? space.name.trim() : ''; + + const spaceColor = getSpaceColor(space); + + return ( + + ); +}; + +SpaceAvatarInternal.defaultProps = { + announceSpaceName: true, +}; diff --git a/x-pack/plugins/spaces/public/space_list/space_list.tsx b/x-pack/plugins/spaces/public/space_list/space_list.tsx index d8bd47b66b5c6..efd8b367bcd45 100644 --- a/x-pack/plugins/spaces/public/space_list/space_list.tsx +++ b/x-pack/plugins/spaces/public/space_list/space_list.tsx @@ -6,10 +6,11 @@ */ import React from 'react'; -import type { SpaceListProps } from '../../../../../src/plugins/spaces_oss/public'; -import { SpaceListInternal } from './space_list_internal'; -export const getSpaceListComponent = (): React.FC => { +import type { SpaceListProps } from 'src/plugins/spaces_oss/public'; + +export const getSpaceListComponent = async (): Promise> => { + const { SpaceListInternal } = await import('./space_list_internal'); return (props: SpaceListProps) => { return ; }; diff --git a/x-pack/plugins/spaces/public/space_list/space_list_internal.test.tsx b/x-pack/plugins/spaces/public/space_list/space_list_internal.test.tsx index e0e8cc2337379..8109444fc1271 100644 --- a/x-pack/plugins/spaces/public/space_list/space_list_internal.test.tsx +++ b/x-pack/plugins/spaces/public/space_list/space_list_internal.test.tsx @@ -5,15 +5,17 @@ * 2.0. */ +import { act } from '@testing-library/react'; +import type { ReactWrapper } from 'enzyme'; import React from 'react'; + import { mountWithIntl } from '@kbn/test/jest'; -import { act } from '@testing-library/react'; import { coreMock } from 'src/core/public/mocks'; import type { Space } from 'src/plugins/spaces_oss/common'; -import type { SpaceListProps } from '../../../../../src/plugins/spaces_oss/public'; -import { getSpacesContextWrapper } from '../spaces_context'; +import type { SpaceListProps } from 'src/plugins/spaces_oss/public'; + +import { getSpacesContextProviderWrapper } from '../spaces_context'; import { spacesManagerMock } from '../spaces_manager/mocks'; -import { ReactWrapper } from 'enzyme'; import { SpaceListInternal } from './space_list_internal'; const ACTIVE_SPACE: Space = { @@ -55,7 +57,10 @@ describe('SpaceListInternal', () => { spacesManager.getActiveSpace.mockResolvedValue(ACTIVE_SPACE); spacesManager.getSpaces.mockResolvedValue(spaces); - const SpacesContext = getSpacesContextWrapper({ getStartServices, spacesManager }); + const SpacesContext = await getSpacesContextProviderWrapper({ + getStartServices, + spacesManager, + }); const wrapper = mountWithIntl( @@ -149,7 +154,10 @@ describe('SpaceListInternal', () => { const button = getButton(wrapper); expect(button.text()).toEqual('+1 more'); - button.simulate('click'); + await act(async () => { + button.simulate('click'); + }); + wrapper.update(); const badgeText = getListText(wrapper); expect(badgeText).toEqual(['A', 'B', 'C', 'D', 'E', 'F']); expect(button.text()).toEqual('show less'); @@ -167,7 +175,10 @@ describe('SpaceListInternal', () => { const button = getButton(wrapper); expect(button.text()).toEqual('+2 more'); - button.simulate('click'); + await act(async () => { + button.simulate('click'); + }); + wrapper.update(); const badgeText = getListText(wrapper); expect(badgeText).toEqual(['A', 'B', 'C', 'D', 'E', 'F', '+1']); expect(button.text()).toEqual('show less'); @@ -185,7 +196,10 @@ describe('SpaceListInternal', () => { const button = getButton(wrapper); expect(button.text()).toEqual('+3 more'); - button.simulate('click'); + await act(async () => { + button.simulate('click'); + }); + wrapper.update(); const badgeText = getListText(wrapper); expect(badgeText).toEqual(['A', 'B', 'C', 'D', 'E', 'F', '+2']); expect(button.text()).toEqual('show less'); @@ -236,7 +250,10 @@ describe('SpaceListInternal', () => { const button = getButton(wrapper); expect(button.text()).toEqual('+8 more'); - button.simulate('click'); + await act(async () => { + button.simulate('click'); + }); + wrapper.update(); const badgeText = getListText(wrapper); expect(badgeText).toEqual(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', '+1']); expect(button.text()).toEqual('show less'); @@ -250,7 +267,10 @@ describe('SpaceListInternal', () => { const button = getButton(wrapper); expect(button.text()).toEqual('+2 more'); - button.simulate('click'); + await act(async () => { + button.simulate('click'); + }); + wrapper.update(); const badgeText = getListText(wrapper); expect(badgeText).toEqual(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', '+1']); expect(button.text()).toEqual('show less'); @@ -275,7 +295,10 @@ describe('SpaceListInternal', () => { const button = getButton(wrapper); expect(button.text()).toEqual('+5 more'); - button.simulate('click'); + await act(async () => { + button.simulate('click'); + }); + wrapper.update(); const badgeText = getListText(wrapper); expect(badgeText).toEqual(['D!', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', '+1']); expect(button.text()).toEqual('show less'); @@ -300,7 +323,10 @@ describe('SpaceListInternal', () => { const button = getButton(wrapper); expect(button.text()).toEqual('+4 more'); - button.simulate('click'); + await act(async () => { + button.simulate('click'); + }); + wrapper.update(); const badgeText = getListText(wrapper); expect(badgeText).toEqual(['A', 'C', 'D', 'E', 'F', 'G', 'H', 'B', '+1']); expect(button.text()).toEqual('show less'); diff --git a/x-pack/plugins/spaces/public/space_list/space_list_internal.tsx b/x-pack/plugins/spaces/public/space_list/space_list_internal.tsx index b0250105885d2..1a512fb2d31f4 100644 --- a/x-pack/plugins/spaces/public/space_list/space_list_internal.tsx +++ b/x-pack/plugins/spaces/public/space_list/space_list_internal.tsx @@ -5,18 +5,30 @@ * 2.0. */ -import React, { useState, ReactNode, useEffect } from 'react'; +import { + EuiBadge, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiLoadingSpinner, + EuiToolTip, +} from '@elastic/eui'; +import type { ReactNode } from 'react'; +import React, { lazy, Suspense, useEffect, useState } from 'react'; + import { i18n } from '@kbn/i18n'; -import { EuiBadge } from '@elastic/eui'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { EuiToolTip } from '@elastic/eui'; -import { EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { SpaceListProps } from '../../../../../src/plugins/spaces_oss/public'; -import { ShareToSpacesData, ShareToSpaceTarget } from '../types'; +import type { SpaceListProps } from 'src/plugins/spaces_oss/public'; + import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../common/constants'; +import { getSpaceAvatarComponent } from '../space_avatar'; import { useSpaces } from '../spaces_context'; -import { SpaceAvatar } from '../space_avatar'; +import type { ShareToSpacesData, ShareToSpaceTarget } from '../types'; + +// No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana. +const LazySpaceAvatar = lazy(() => + getSpaceAvatarComponent().then((component) => ({ default: component })) +); const DEFAULT_DISPLAY_LIMIT = 5; @@ -127,18 +139,20 @@ export const SpaceListInternal = ({ ) : null; return ( - - {displayedSpaces.map((space) => { - // color may be undefined, which is intentional; SpacesAvatar calls the getSpaceColor function before rendering - const color = space.isFeatureDisabled ? 'hollow' : space.color; - return ( - - - - ); - })} - {unauthorizedSpacesCountBadge} - {button} - + }> + + {displayedSpaces.map((space) => { + // color may be undefined, which is intentional; SpacesAvatar calls the getSpaceColor function before rendering + const color = space.isFeatureDisabled ? 'hollow' : space.color; + return ( + + + + ); + })} + {unauthorizedSpacesCountBadge} + {button} + + ); }; diff --git a/x-pack/plugins/spaces/public/space_selector/components/space_card.test.tsx b/x-pack/plugins/spaces/public/space_selector/components/space_card.test.tsx index dc5af4b7dc7f2..43d09dababe42 100644 --- a/x-pack/plugins/spaces/public/space_selector/components/space_card.test.tsx +++ b/x-pack/plugins/spaces/public/space_selector/components/space_card.test.tsx @@ -5,10 +5,11 @@ * 2.0. */ +import { EuiCard } from '@elastic/eui'; import { mount, shallow } from 'enzyme'; import React from 'react'; + import { SpaceCard } from './space_card'; -import { EuiCard } from '@elastic/eui'; test('it renders without crashing', () => { const space = { diff --git a/x-pack/plugins/spaces/public/space_selector/components/space_card.tsx b/x-pack/plugins/spaces/public/space_selector/components/space_card.tsx index 7a18cbb8cc370..0628f79990af6 100644 --- a/x-pack/plugins/spaces/public/space_selector/components/space_card.tsx +++ b/x-pack/plugins/spaces/public/space_selector/components/space_card.tsx @@ -6,11 +6,19 @@ */ import './space_card.scss'; -import { EuiCard } from '@elastic/eui'; -import React from 'react'; + +import { EuiCard, EuiLoadingSpinner } from '@elastic/eui'; +import React, { lazy, Suspense } from 'react'; + +import type { Space } from 'src/plugins/spaces_oss/common'; + import { addSpaceIdToPath, ENTER_SPACE_PATH } from '../../../common'; -import { SpaceAvatar } from '../../space_avatar'; -import { Space } from '../..'; +import { getSpaceAvatarComponent } from '../../space_avatar'; + +// No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana. +const LazySpaceAvatar = lazy(() => + getSpaceAvatarComponent().then((component) => ({ default: component })) +); interface Props { space: Space; @@ -34,7 +42,11 @@ export const SpaceCard = (props: Props) => { function renderSpaceAvatar(space: Space) { // not announcing space name here because the title of the EuiCard that the SpaceAvatar lives in is already // announcing it. See https://github.com/elastic/kibana/issues/27748 - return ; + return ( + }> + + + ); } function renderSpaceDescription(space: Space) { diff --git a/x-pack/plugins/spaces/public/space_selector/components/space_cards.test.tsx b/x-pack/plugins/spaces/public/space_selector/components/space_cards.test.tsx index cd59f2b8468dc..04566fa8a7192 100644 --- a/x-pack/plugins/spaces/public/space_selector/components/space_cards.test.tsx +++ b/x-pack/plugins/spaces/public/space_selector/components/space_cards.test.tsx @@ -7,6 +7,7 @@ import { shallow } from 'enzyme'; import React from 'react'; + import { SpaceCards } from './space_cards'; test('it renders without crashing', () => { diff --git a/x-pack/plugins/spaces/public/space_selector/components/space_cards.tsx b/x-pack/plugins/spaces/public/space_selector/components/space_cards.tsx index 93606cf6597df..e7bef5f646036 100644 --- a/x-pack/plugins/spaces/public/space_selector/components/space_cards.tsx +++ b/x-pack/plugins/spaces/public/space_selector/components/space_cards.tsx @@ -6,9 +6,12 @@ */ import './space_cards.scss'; + import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { Component } from 'react'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; + +import type { Space } from 'src/plugins/spaces_oss/common'; + import { SpaceCard } from './space_card'; interface Props { diff --git a/x-pack/plugins/spaces/public/space_selector/space_selector.test.tsx b/x-pack/plugins/spaces/public/space_selector/space_selector.test.tsx index c0c7307d24ad1..b4417d98bcace 100644 --- a/x-pack/plugins/spaces/public/space_selector/space_selector.test.tsx +++ b/x-pack/plugins/spaces/public/space_selector/space_selector.test.tsx @@ -6,10 +6,12 @@ */ import React from 'react'; + import { shallowWithIntl } from '@kbn/test/jest'; -import { Space } from '../../../../../src/plugins/spaces_oss/common'; -import { SpaceSelector } from './space_selector'; +import type { Space } from 'src/plugins/spaces_oss/common'; + import { spacesManagerMock } from '../spaces_manager/mocks'; +import { SpaceSelector } from './space_selector'; function getSpacesManager(spaces: Space[] = []) { const manager = spacesManagerMock.create(); diff --git a/x-pack/plugins/spaces/public/space_selector/space_selector.tsx b/x-pack/plugins/spaces/public/space_selector/space_selector.tsx index d3330bb382ecc..cee304408495d 100644 --- a/x-pack/plugins/spaces/public/space_selector/space_selector.tsx +++ b/x-pack/plugins/spaces/public/space_selector/space_selector.tsx @@ -12,6 +12,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, + EuiLoadingSpinner, EuiPage, EuiPageBody, EuiPageContent, @@ -20,17 +21,18 @@ import { EuiSpacer, EuiText, EuiTitle, - EuiLoadingSpinner, } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component, Fragment } from 'react'; import ReactDOM from 'react-dom'; -import { CoreStart } from 'src/core/public'; -import { Space } from '../../../../../src/plugins/spaces_oss/common'; -import { SpaceCards } from './components'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import type { CoreStart } from 'src/core/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; + import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../common/constants'; -import { SpacesManager } from '../spaces_manager'; +import type { SpacesManager } from '../spaces_manager'; +import { SpaceCards } from './components'; interface Props { spacesManager: SpacesManager; diff --git a/x-pack/plugins/spaces/public/space_selector/space_selector_app.tsx b/x-pack/plugins/spaces/public/space_selector/space_selector_app.tsx index b848eab68a694..542a4be50d57c 100644 --- a/x-pack/plugins/spaces/public/space_selector/space_selector_app.tsx +++ b/x-pack/plugins/spaces/public/space_selector/space_selector_app.tsx @@ -5,9 +5,10 @@ * 2.0. */ -import { StartServicesAccessor, ApplicationSetup, AppMountParameters } from 'src/core/public'; import { i18n } from '@kbn/i18n'; -import { SpacesManager } from '../spaces_manager'; +import type { ApplicationSetup, AppMountParameters, StartServicesAccessor } from 'src/core/public'; + +import type { SpacesManager } from '../spaces_manager'; interface CreateDeps { application: ApplicationSetup; diff --git a/x-pack/plugins/spaces/public/spaces_context/context.tsx b/x-pack/plugins/spaces/public/spaces_context/context.tsx index 548b2158558c5..e38a2f17151a9 100644 --- a/x-pack/plugins/spaces/public/spaces_context/context.tsx +++ b/x-pack/plugins/spaces/public/spaces_context/context.tsx @@ -6,9 +6,10 @@ */ import * as React from 'react'; -import { SpacesManager } from '../spaces_manager'; -import { ShareToSpacesData } from '../types'; -import { SpacesReactContext, SpacesReactContextValue, KibanaServices } from './types'; + +import type { SpacesManager } from '../spaces_manager'; +import type { ShareToSpacesData } from '../types'; +import type { KibanaServices, SpacesReactContext, SpacesReactContextValue } from './types'; const { useContext, createElement, createContext } = React; diff --git a/x-pack/plugins/spaces/public/spaces_context/index.ts b/x-pack/plugins/spaces/public/spaces_context/index.ts index fdf28ad5957cf..0187131b02b93 100644 --- a/x-pack/plugins/spaces/public/spaces_context/index.ts +++ b/x-pack/plugins/spaces/public/spaces_context/index.ts @@ -6,4 +6,4 @@ */ export { useSpaces } from './context'; -export { getSpacesContextWrapper } from './wrapper'; +export { getSpacesContextProviderWrapper } from './wrapper'; diff --git a/x-pack/plugins/spaces/public/spaces_context/types.ts b/x-pack/plugins/spaces/public/spaces_context/types.ts index c2f7db69add09..e73da7cb26b68 100644 --- a/x-pack/plugins/spaces/public/spaces_context/types.ts +++ b/x-pack/plugins/spaces/public/spaces_context/types.ts @@ -5,10 +5,13 @@ * 2.0. */ -import * as React from 'react'; -import { CoreStart } from 'src/core/public'; -import { ShareToSpacesData } from '../types'; -import { SpacesManager } from '../spaces_manager'; +import type * as React from 'react'; + +import type { CoreStart, StartServicesAccessor } from 'src/core/public'; + +import type { PluginsStart } from '../plugin'; +import type { SpacesManager } from '../spaces_manager'; +import type { ShareToSpacesData } from '../types'; export type KibanaServices = Partial; @@ -23,3 +26,8 @@ export interface SpacesReactContext { Provider: React.FC; Consumer: React.Consumer>; } + +export interface InternalProps { + spacesManager: SpacesManager; + getStartServices: StartServicesAccessor; +} diff --git a/x-pack/plugins/spaces/public/spaces_context/wrapper.tsx b/x-pack/plugins/spaces/public/spaces_context/wrapper.tsx index 18112945ea738..6de14290abb74 100644 --- a/x-pack/plugins/spaces/public/spaces_context/wrapper.tsx +++ b/x-pack/plugins/spaces/public/spaces_context/wrapper.tsx @@ -5,85 +5,18 @@ * 2.0. */ -import React, { useState, useEffect, PropsWithChildren, useMemo } from 'react'; -import { - StartServicesAccessor, - DocLinksStart, - ApplicationStart, - NotificationsStart, -} from 'src/core/public'; -import type { SpacesContextProps } from '../../../../../src/plugins/spaces_oss/public'; -import { createSpacesReactContext } from './context'; -import { PluginsStart } from '../plugin'; -import { SpacesManager } from '../spaces_manager'; -import { ShareToSpacesData, ShareToSpaceTarget } from '../types'; -import { SpacesReactContext } from './types'; +import type { PropsWithChildren } from 'react'; +import React from 'react'; -interface InternalProps { - spacesManager: SpacesManager; - getStartServices: StartServicesAccessor; -} +import type { SpacesContextProps } from 'src/plugins/spaces_oss/public'; -interface Services { - application: ApplicationStart; - docLinks: DocLinksStart; - notifications: NotificationsStart; -} +import type { InternalProps } from './types'; -async function getShareToSpacesData( - spacesManager: SpacesManager, - feature?: string -): Promise { - const spaces = await spacesManager.getSpaces({ includeAuthorizedPurposes: true }); - const activeSpace = await spacesManager.getActiveSpace(); - const spacesMap = spaces - .map(({ authorizedPurposes, disabledFeatures, ...space }) => { - const isActiveSpace = space.id === activeSpace.id; - const cannotShareToSpace = authorizedPurposes?.shareSavedObjectsIntoSpace === false; - const isFeatureDisabled = feature !== undefined && disabledFeatures.includes(feature); - return { - ...space, - ...(isActiveSpace && { isActiveSpace }), - ...(cannotShareToSpace && { cannotShareToSpace }), - ...(isFeatureDisabled && { isFeatureDisabled }), - }; - }) - .reduce((acc, cur) => acc.set(cur.id, cur), new Map()); - - return { - spacesMap, - activeSpaceId: activeSpace.id, - }; -} - -const SpacesContextWrapper = (props: PropsWithChildren) => { - const { spacesManager, getStartServices, feature, children } = props; - - const [context, setContext] = useState | undefined>(); - const shareToSpacesDataPromise = useMemo(() => getShareToSpacesData(spacesManager, feature), [ - spacesManager, - feature, - ]); - - useEffect(() => { - getStartServices().then(([coreStart]) => { - const { application, docLinks, notifications } = coreStart; - const services = { application, docLinks, notifications }; - setContext(createSpacesReactContext(services, spacesManager, shareToSpacesDataPromise)); - }); - }, [getStartServices, shareToSpacesDataPromise, spacesManager]); - - if (!context) { - return null; - } - - return {children}; -}; - -export const getSpacesContextWrapper = ( +export const getSpacesContextProviderWrapper = async ( internalProps: InternalProps -): React.FC => { +): Promise> => { + const { SpacesContextWrapperInternal } = await import('./wrapper_internal'); return ({ children, ...props }: PropsWithChildren) => { - return ; + return ; }; }; diff --git a/x-pack/plugins/spaces/public/spaces_context/wrapper_internal.tsx b/x-pack/plugins/spaces/public/spaces_context/wrapper_internal.tsx new file mode 100644 index 0000000000000..7c33f859a8c3e --- /dev/null +++ b/x-pack/plugins/spaces/public/spaces_context/wrapper_internal.tsx @@ -0,0 +1,75 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { PropsWithChildren } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; + +import type { ApplicationStart, DocLinksStart, NotificationsStart } from 'src/core/public'; +import type { SpacesContextProps } from 'src/plugins/spaces_oss/public'; + +import type { SpacesManager } from '../spaces_manager'; +import type { ShareToSpacesData, ShareToSpaceTarget } from '../types'; +import { createSpacesReactContext } from './context'; +import type { SpacesReactContext, InternalProps } from './types'; + +interface Services { + application: ApplicationStart; + docLinks: DocLinksStart; + notifications: NotificationsStart; +} + +async function getShareToSpacesData( + spacesManager: SpacesManager, + feature?: string +): Promise { + const spaces = await spacesManager.getSpaces({ includeAuthorizedPurposes: true }); + const activeSpace = await spacesManager.getActiveSpace(); + const spacesMap = spaces + .map(({ authorizedPurposes, disabledFeatures, ...space }) => { + const isActiveSpace = space.id === activeSpace.id; + const cannotShareToSpace = authorizedPurposes?.shareSavedObjectsIntoSpace === false; + const isFeatureDisabled = feature !== undefined && disabledFeatures.includes(feature); + return { + ...space, + ...(isActiveSpace && { isActiveSpace }), + ...(cannotShareToSpace && { cannotShareToSpace }), + ...(isFeatureDisabled && { isFeatureDisabled }), + }; + }) + .reduce((acc, cur) => acc.set(cur.id, cur), new Map()); + + return { + spacesMap, + activeSpaceId: activeSpace.id, + }; +} + +export const SpacesContextWrapperInternal = ( + props: PropsWithChildren +) => { + const { spacesManager, getStartServices, feature, children } = props; + + const [context, setContext] = useState | undefined>(); + const shareToSpacesDataPromise = useMemo(() => getShareToSpacesData(spacesManager, feature), [ + spacesManager, + feature, + ]); + + useEffect(() => { + getStartServices().then(([coreStart]) => { + const { application, docLinks, notifications } = coreStart; + const services = { application, docLinks, notifications }; + setContext(createSpacesReactContext(services, spacesManager, shareToSpacesDataPromise)); + }); + }, [getStartServices, shareToSpacesDataPromise, spacesManager]); + + if (!context) { + return null; + } + + return {children}; +}; diff --git a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts index 203f88abb4744..ccb475369104a 100644 --- a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts +++ b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts @@ -5,9 +5,12 @@ * 2.0. */ -import { of, Observable } from 'rxjs'; -import { Space } from '../../../../../src/plugins/spaces_oss/common'; -import { SpacesManager } from './spaces_manager'; +import type { Observable } from 'rxjs'; +import { of } from 'rxjs'; + +import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { SpacesManager } from './spaces_manager'; function createSpacesManagerMock() { return ({ diff --git a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.test.ts b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.test.ts index 6ee6af26e04fa..ff18b2965e8b9 100644 --- a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.test.ts +++ b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.test.ts @@ -5,9 +5,10 @@ * 2.0. */ -import { SpacesManager } from '.'; -import { coreMock } from 'src/core/public/mocks'; import { nextTick } from '@kbn/test/jest'; +import { coreMock } from 'src/core/public/mocks'; + +import { SpacesManager } from './spaces_manager'; describe('SpacesManager', () => { describe('#constructor', () => { diff --git a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts index 0640f88174102..1cae128299197 100644 --- a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts +++ b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts @@ -5,12 +5,15 @@ * 2.0. */ -import { Observable, BehaviorSubject } from 'rxjs'; +import type { Observable } from 'rxjs'; +import { BehaviorSubject } from 'rxjs'; import { skipWhile } from 'rxjs/operators'; -import { HttpSetup } from 'src/core/public'; -import { Space } from '../../../../../src/plugins/spaces_oss/common'; -import { GetAllSpacesOptions, GetSpaceResult } from '../../common'; -import { CopySavedObjectsToSpaceResponse } from '../copy_saved_objects_to_space/types'; + +import type { HttpSetup } from 'src/core/public'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { GetAllSpacesOptions, GetSpaceResult } from '../../common'; +import type { CopySavedObjectsToSpaceResponse } from '../copy_saved_objects_to_space/types'; interface SavedObjectTarget { type: string; diff --git a/x-pack/plugins/spaces/public/suspense_error_boundary/index.ts b/x-pack/plugins/spaces/public/suspense_error_boundary/index.ts new file mode 100644 index 0000000000000..061923c8445c2 --- /dev/null +++ b/x-pack/plugins/spaces/public/suspense_error_boundary/index.ts @@ -0,0 +1,8 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { SuspenseErrorBoundary } from './suspense_error_boundary'; diff --git a/x-pack/plugins/spaces/public/suspense_error_boundary/suspense_error_boundary.tsx b/x-pack/plugins/spaces/public/suspense_error_boundary/suspense_error_boundary.tsx new file mode 100644 index 0000000000000..e90920f3f1e25 --- /dev/null +++ b/x-pack/plugins/spaces/public/suspense_error_boundary/suspense_error_boundary.tsx @@ -0,0 +1,54 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiLoadingSpinner } from '@elastic/eui'; +import type { PropsWithChildren } from 'react'; +import React, { Component, Suspense } from 'react'; + +import { i18n } from '@kbn/i18n'; +import type { NotificationsStart } from 'src/core/public'; + +interface Props { + notifications: NotificationsStart; +} + +interface State { + error: Error | null; +} + +export class SuspenseErrorBoundary extends Component, State> { + state: State = { + error: null, + }; + + static getDerivedStateFromError(error: Error) { + // Update state so next render shows fallback UI. + return { error }; + } + + public componentDidCatch(error: Error) { + const { notifications } = this.props; + if (notifications) { + const title = i18n.translate('xpack.spaces.uiApi.errorBoundaryToastTitle', { + defaultMessage: 'Failed to load Kibana asset', + }); + const toastMessage = i18n.translate('xpack.spaces.uiApi.errorBoundaryToastMessage', { + defaultMessage: 'Reload page to continue.', + }); + notifications.toasts.addError(error, { title, toastMessage }); + } + } + + render() { + const { children, notifications } = this.props; + const { error } = this.state; + if (!notifications || error) { + return null; + } + return }>{children}; + } +} diff --git a/x-pack/plugins/spaces/public/types.ts b/x-pack/plugins/spaces/public/types.ts index a49df82154849..e999e332b9884 100644 --- a/x-pack/plugins/spaces/public/types.ts +++ b/x-pack/plugins/spaces/public/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { GetSpaceResult } from '../common'; +import type { GetSpaceResult } from '../common'; /** * The structure for all of the space data that must be loaded for share-to-space components to function. diff --git a/x-pack/plugins/spaces/public/ui_api/components.ts b/x-pack/plugins/spaces/public/ui_api/components.ts deleted file mode 100644 index 6a8dedb5f5b68..0000000000000 --- a/x-pack/plugins/spaces/public/ui_api/components.ts +++ /dev/null @@ -1,34 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { StartServicesAccessor } from 'src/core/public'; -import type { SpacesApiUiComponent } from '../../../../../src/plugins/spaces_oss/public'; -import { PluginsStart } from '../plugin'; -import { - getShareToSpaceFlyoutComponent, - getLegacyUrlConflict, -} from '../share_saved_objects_to_space'; -import { getSpacesContextWrapper } from '../spaces_context'; -import { SpacesManager } from '../spaces_manager'; -import { getSpaceListComponent } from '../space_list'; - -export interface GetComponentsOptions { - spacesManager: SpacesManager; - getStartServices: StartServicesAccessor; -} - -export const getComponents = ({ - spacesManager, - getStartServices, -}: GetComponentsOptions): SpacesApiUiComponent => { - return { - SpacesContext: getSpacesContextWrapper({ spacesManager, getStartServices }), - ShareToSpaceFlyout: getShareToSpaceFlyoutComponent(), - SpaceList: getSpaceListComponent(), - LegacyUrlConflict: getLegacyUrlConflict({ getStartServices }), - }; -}; diff --git a/x-pack/plugins/spaces/public/ui_api/components.tsx b/x-pack/plugins/spaces/public/ui_api/components.tsx new file mode 100644 index 0000000000000..b564e96be4c41 --- /dev/null +++ b/x-pack/plugins/spaces/public/ui_api/components.tsx @@ -0,0 +1,52 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FC, PropsWithChildren, PropsWithRef } from 'react'; +import React from 'react'; + +import type { StartServicesAccessor } from 'src/core/public'; +import type { SpacesApiUiComponent } from 'src/plugins/spaces_oss/public'; + +import type { PluginsStart } from '../plugin'; +import { + getLegacyUrlConflict, + getShareToSpaceFlyoutComponent, +} from '../share_saved_objects_to_space'; +import { getSpaceAvatarComponent } from '../space_avatar'; +import { getSpaceListComponent } from '../space_list'; +import { getSpacesContextProviderWrapper } from '../spaces_context'; +import type { SpacesManager } from '../spaces_manager'; +import { LazyWrapper } from './lazy_wrapper'; + +export interface GetComponentsOptions { + spacesManager: SpacesManager; + getStartServices: StartServicesAccessor; +} + +export const getComponents = ({ + spacesManager, + getStartServices, +}: GetComponentsOptions): SpacesApiUiComponent => { + /** + * Returns a function that creates a lazy-loading version of a component. + */ + function wrapLazy(fn: () => Promise>) { + return (props: JSX.IntrinsicAttributes & PropsWithRef>) => ( + + ); + } + + return { + getSpacesContextProvider: wrapLazy(() => + getSpacesContextProviderWrapper({ spacesManager, getStartServices }) + ), + getShareToSpaceFlyout: wrapLazy(getShareToSpaceFlyoutComponent), + getSpaceList: wrapLazy(getSpaceListComponent), + getLegacyUrlConflict: wrapLazy(() => getLegacyUrlConflict({ getStartServices })), + getSpaceAvatar: wrapLazy(getSpaceAvatarComponent), + }; +}; diff --git a/x-pack/plugins/spaces/public/ui_api/index.ts b/x-pack/plugins/spaces/public/ui_api/index.ts index e278eb691910f..4bfb482b41407 100644 --- a/x-pack/plugins/spaces/public/ui_api/index.ts +++ b/x-pack/plugins/spaces/public/ui_api/index.ts @@ -6,11 +6,12 @@ */ import type { StartServicesAccessor } from 'src/core/public'; -import type { SpacesApiUi } from '../../../../../src/plugins/spaces_oss/public'; +import type { SpacesApiUi } from 'src/plugins/spaces_oss/public'; + import type { PluginsStart } from '../plugin'; +import { createRedirectLegacyUrl } from '../share_saved_objects_to_space'; import type { SpacesManager } from '../spaces_manager'; import { getComponents } from './components'; -import { createRedirectLegacyUrl } from '../share_saved_objects_to_space'; interface GetUiApiOptions { spacesManager: SpacesManager; diff --git a/x-pack/plugins/spaces/public/ui_api/lazy_wrapper.tsx b/x-pack/plugins/spaces/public/ui_api/lazy_wrapper.tsx new file mode 100644 index 0000000000000..9c3336cfca01d --- /dev/null +++ b/x-pack/plugins/spaces/public/ui_api/lazy_wrapper.tsx @@ -0,0 +1,42 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FC, PropsWithChildren, PropsWithRef, ReactElement } from 'react'; +import React, { lazy, useMemo } from 'react'; +import useAsync from 'react-use/lib/useAsync'; + +import type { StartServicesAccessor } from 'src/core/public'; + +import type { PluginsStart } from '../plugin'; +import { SuspenseErrorBoundary } from '../suspense_error_boundary'; + +interface InternalProps { + fn: () => Promise>; + getStartServices: StartServicesAccessor; + props: JSX.IntrinsicAttributes & PropsWithRef>; +} + +export const LazyWrapper: (props: InternalProps) => ReactElement | null = ({ + fn, + getStartServices, + props, +}) => { + const { value: startServices = [{ notifications: undefined }] } = useAsync(getStartServices); + const [{ notifications }] = startServices; + + const LazyComponent = useMemo(() => lazy(() => fn().then((x) => ({ default: x }))), [fn]); + + if (!notifications) { + return null; + } + + return ( + + + + ); +}; diff --git a/x-pack/plugins/spaces/public/ui_api/mocks.ts b/x-pack/plugins/spaces/public/ui_api/mocks.ts index c9aa2a2b2b52f..fe3826a58fccc 100644 --- a/x-pack/plugins/spaces/public/ui_api/mocks.ts +++ b/x-pack/plugins/spaces/public/ui_api/mocks.ts @@ -5,17 +5,15 @@ * 2.0. */ -import type { - SpacesApiUi, - SpacesApiUiComponent, -} from '../../../../../src/plugins/spaces_oss/public'; +import type { SpacesApiUi, SpacesApiUiComponent } from 'src/plugins/spaces_oss/public'; function createComponentsMock(): jest.Mocked { return { - SpacesContext: jest.fn(), - ShareToSpaceFlyout: jest.fn(), - SpaceList: jest.fn(), - LegacyUrlConflict: jest.fn(), + getSpacesContextProvider: jest.fn(), + getShareToSpaceFlyout: jest.fn(), + getSpaceList: jest.fn(), + getLegacyUrlConflict: jest.fn(), + getSpaceAvatar: jest.fn(), }; } diff --git a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts index 85805d57f2fc0..c3393da770ded 100644 --- a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts +++ b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts @@ -5,14 +5,15 @@ * 2.0. */ -import { Space } from '../../../../../src/plugins/spaces_oss/common'; -import { KibanaFeature } from '../../../../plugins/features/server'; -import { setupCapabilitiesSwitcher } from './capabilities_switcher'; -import { Capabilities, CoreSetup } from 'src/core/server'; +import type { Capabilities, CoreSetup } from 'src/core/server'; import { coreMock, httpServerMock, loggingSystemMock } from 'src/core/server/mocks'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { KibanaFeature } from '../../../features/server'; import { featuresPluginMock } from '../../../features/server/mocks'; +import type { PluginsStart } from '../plugin'; import { spacesServiceMock } from '../spaces_service/spaces_service.mock'; -import { PluginsStart } from '../plugin'; +import { setupCapabilitiesSwitcher } from './capabilities_switcher'; const features = ([ { diff --git a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts index 74d4bb9897803..1f6249d9b3220 100644 --- a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts +++ b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts @@ -6,11 +6,13 @@ */ import _ from 'lodash'; -import { Capabilities, CapabilitiesSwitcher, CoreSetup, Logger } from 'src/core/server'; -import { Space } from '../../../../../src/plugins/spaces_oss/common'; -import { KibanaFeature } from '../../../../plugins/features/server'; -import { SpacesServiceStart } from '../spaces_service'; -import { PluginsStart } from '../plugin'; + +import type { Capabilities, CapabilitiesSwitcher, CoreSetup, Logger } from 'src/core/server'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { KibanaFeature } from '../../../features/server'; +import type { PluginsStart } from '../plugin'; +import type { SpacesServiceStart } from '../spaces_service'; export function setupCapabilitiesSwitcher( core: CoreSetup, diff --git a/x-pack/plugins/spaces/server/capabilities/index.ts b/x-pack/plugins/spaces/server/capabilities/index.ts index 25df7c18e21b0..4b23290bfd0ac 100644 --- a/x-pack/plugins/spaces/server/capabilities/index.ts +++ b/x-pack/plugins/spaces/server/capabilities/index.ts @@ -5,11 +5,12 @@ * 2.0. */ -import { CoreSetup, Logger } from 'src/core/server'; +import type { CoreSetup, Logger } from 'src/core/server'; + +import type { PluginsStart } from '../plugin'; +import type { SpacesServiceStart } from '../spaces_service'; import { capabilitiesProvider } from './capabilities_provider'; import { setupCapabilitiesSwitcher } from './capabilities_switcher'; -import { PluginsStart } from '../plugin'; -import { SpacesServiceStart } from '../spaces_service'; export const setupCapabilities = ( core: CoreSetup, diff --git a/x-pack/plugins/spaces/server/config.test.ts b/x-pack/plugins/spaces/server/config.test.ts index 185849ed0c803..41c4995b5bcf3 100644 --- a/x-pack/plugins/spaces/server/config.test.ts +++ b/x-pack/plugins/spaces/server/config.test.ts @@ -5,8 +5,9 @@ * 2.0. */ -import { configDeprecationFactory, applyDeprecations } from '@kbn/config'; +import { applyDeprecations, configDeprecationFactory } from '@kbn/config'; import { deepFreeze } from '@kbn/std'; + import { spacesConfigDeprecationProvider } from './config'; const applyConfigDeprecations = (settings: Record = {}) => { diff --git a/x-pack/plugins/spaces/server/config.ts b/x-pack/plugins/spaces/server/config.ts index f0899c80f5de5..bc53f43c3e8c4 100644 --- a/x-pack/plugins/spaces/server/config.ts +++ b/x-pack/plugins/spaces/server/config.ts @@ -5,13 +5,15 @@ * 2.0. */ -import { schema, TypeOf } from '@kbn/config-schema'; +import type { Observable } from 'rxjs'; + +import type { TypeOf } from '@kbn/config-schema'; +import { schema } from '@kbn/config-schema'; import type { - PluginInitializerContext, - ConfigDeprecationProvider, ConfigDeprecation, + ConfigDeprecationProvider, + PluginInitializerContext, } from 'src/core/server'; -import { Observable } from 'rxjs'; export const ConfigSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), diff --git a/x-pack/plugins/spaces/server/default_space/create_default_space.test.ts b/x-pack/plugins/spaces/server/default_space/create_default_space.test.ts index 2badb174d94f5..93c0ec3319d5c 100644 --- a/x-pack/plugins/spaces/server/default_space/create_default_space.test.ts +++ b/x-pack/plugins/spaces/server/default_space/create_default_space.test.ts @@ -5,9 +5,10 @@ * 2.0. */ -import { createDefaultSpace } from './create_default_space'; import { SavedObjectsErrorHelpers } from 'src/core/server'; -import { loggingSystemMock } from '../../../../../src/core/server/mocks'; +import { loggingSystemMock } from 'src/core/server/mocks'; + +import { createDefaultSpace } from './create_default_space'; interface MockServerSettings { defaultExists?: boolean; diff --git a/x-pack/plugins/spaces/server/default_space/create_default_space.ts b/x-pack/plugins/spaces/server/default_space/create_default_space.ts index 2b636799e0019..a982feff59692 100644 --- a/x-pack/plugins/spaces/server/default_space/create_default_space.ts +++ b/x-pack/plugins/spaces/server/default_space/create_default_space.ts @@ -6,7 +6,8 @@ */ import { i18n } from '@kbn/i18n'; -import { SavedObjectsServiceStart, SavedObjectsRepository, Logger } from 'src/core/server'; +import type { Logger, SavedObjectsRepository, SavedObjectsServiceStart } from 'src/core/server'; + import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; import { DEFAULT_SPACE_ID } from '../../common/constants'; diff --git a/x-pack/plugins/spaces/server/default_space/default_space_service.test.ts b/x-pack/plugins/spaces/server/default_space/default_space_service.test.ts index e5e357a648a04..5da11e3e27176 100644 --- a/x-pack/plugins/spaces/server/default_space/default_space_service.test.ts +++ b/x-pack/plugins/spaces/server/default_space/default_space_service.test.ts @@ -6,25 +6,22 @@ */ import * as Rx from 'rxjs'; +import { first } from 'rxjs/operators'; + +import { nextTick } from '@kbn/test/jest'; import type { Writable } from '@kbn/utility-types'; +import type { CoreStatus, SavedObjectsRepository, ServiceStatusLevel } from 'src/core/server'; +import { SavedObjectsErrorHelpers, ServiceStatusLevels } from 'src/core/server'; +import { coreMock, loggingSystemMock } from 'src/core/server/mocks'; + +import type { ILicense } from '../../../licensing/server'; +import { licensingMock } from '../../../licensing/server/mocks'; +import { SpacesLicenseService } from '../../common/licensing'; import { DefaultSpaceService, - RETRY_SCALE_DURATION, RETRY_DURATION_MAX, + RETRY_SCALE_DURATION, } from './default_space_service'; -import { - ServiceStatusLevels, - ServiceStatusLevel, - CoreStatus, - SavedObjectsRepository, - SavedObjectsErrorHelpers, -} from '../../../../../src/core/server'; -import { coreMock, loggingSystemMock } from 'src/core/server/mocks'; -import { licensingMock } from '../../../licensing/server/mocks'; -import { SpacesLicenseService } from '../../common/licensing'; -import { ILicense } from '../../../licensing/server'; -import { nextTick } from '@kbn/test/jest'; -import { first } from 'rxjs/operators'; const advanceRetry = async (initializeCount: number) => { await Promise.resolve(); diff --git a/x-pack/plugins/spaces/server/default_space/default_space_service.ts b/x-pack/plugins/spaces/server/default_space/default_space_service.ts index 74bf23c8b6718..f4418c0a5e358 100644 --- a/x-pack/plugins/spaces/server/default_space/default_space_service.ts +++ b/x-pack/plugins/spaces/server/default_space/default_space_service.ts @@ -5,22 +5,15 @@ * 2.0. */ -import { CoreSetup, SavedObjectsServiceStart, Logger, ServiceStatus } from 'src/core/server'; -import { - concat, - of, - timer, - Observable, - ObservableInput, - combineLatest, - defer, - Subscription, - BehaviorSubject, -} from 'rxjs'; -import { mergeMap, switchMap, catchError, tap } from 'rxjs/operators'; +import type { Observable, ObservableInput, Subscription } from 'rxjs'; +import { BehaviorSubject, combineLatest, concat, defer, of, timer } from 'rxjs'; +import { catchError, mergeMap, switchMap, tap } from 'rxjs/operators'; + +import type { CoreSetup, Logger, SavedObjectsServiceStart, ServiceStatus } from 'src/core/server'; + import { ServiceStatusLevels } from '../../../../../src/core/server'; -import { ILicense } from '../../../licensing/server'; -import { SpacesLicense } from '../../common/licensing'; +import type { ILicense } from '../../../licensing/server'; +import type { SpacesLicense } from '../../common/licensing'; import { createDefaultSpace } from './create_default_space'; interface Deps { diff --git a/x-pack/plugins/spaces/server/index.ts b/x-pack/plugins/spaces/server/index.ts index fb6c00c2f6f48..4218d8518720c 100644 --- a/x-pack/plugins/spaces/server/index.ts +++ b/x-pack/plugins/spaces/server/index.ts @@ -5,7 +5,8 @@ * 2.0. */ -import type { PluginConfigDescriptor, PluginInitializerContext } from '../../../../src/core/server'; +import type { PluginConfigDescriptor, PluginInitializerContext } from 'src/core/server'; + import { ConfigSchema, spacesConfigDeprecationProvider } from './config'; import { SpacesPlugin } from './plugin'; @@ -25,7 +26,7 @@ export { ISpacesClient } from './spaces_client'; export { GetAllSpacesOptions, GetAllSpacesPurpose, GetSpaceResult } from '../common'; // re-export types from oss definition -export { Space } from '../../../../src/plugins/spaces_oss/common'; +export type { Space } from 'src/plugins/spaces_oss/common'; export const config: PluginConfigDescriptor = { schema: ConfigSchema, diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts index 560dbe23d6eb6..a8d8ed9b868c8 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts @@ -6,19 +6,21 @@ */ import { Readable } from 'stream'; -import { - SavedObjectsImportResponse, + +import type { + SavedObjectsExportByObjectOptions, SavedObjectsImportOptions, + SavedObjectsImportResponse, SavedObjectsImportSuccess, - SavedObjectsExportByObjectOptions, } from 'src/core/server'; import { coreMock, httpServerMock, - savedObjectsTypeRegistryMock, savedObjectsClientMock, savedObjectsServiceMock, + savedObjectsTypeRegistryMock, } from 'src/core/server/mocks'; + import { copySavedObjectsToSpacesFactory } from './copy_to_spaces'; interface SetupOpts { diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.ts index 85b78566cbb86..29dac92e5fc6d 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.ts @@ -5,15 +5,17 @@ * 2.0. */ -import { SavedObject, KibanaRequest, CoreStart } from 'src/core/server'; -import { Readable } from 'stream'; +import type { Readable } from 'stream'; + +import type { CoreStart, KibanaRequest, SavedObject } from 'src/core/server'; + import { spaceIdToNamespace } from '../utils/namespace'; -import { CopyOptions, CopyResponse } from './types'; -import { createReadableStreamFromArray } from './lib/readable_stream_from_array'; import { createEmptyFailureResponse } from './lib/create_empty_failure_response'; +import { getIneligibleTypes } from './lib/get_ineligible_types'; import { readStreamToCompletion } from './lib/read_stream_to_completion'; +import { createReadableStreamFromArray } from './lib/readable_stream_from_array'; import { COPY_TO_SPACES_SAVED_OBJECTS_CLIENT_OPTS } from './lib/saved_objects_client_opts'; -import { getIneligibleTypes } from './lib/get_ineligible_types'; +import type { CopyOptions, CopyResponse } from './types'; export function copySavedObjectsToSpacesFactory( savedObjects: CoreStart['savedObjects'], diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/create_empty_failure_response.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/create_empty_failure_response.ts index 63f1dc9a8a492..4ef08381f2aee 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/create_empty_failure_response.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/create_empty_failure_response.ts @@ -5,8 +5,10 @@ * 2.0. */ -import Boom, { Payload } from '@hapi/boom'; -import { SavedObjectsImportError } from 'src/core/server'; +import type { Payload } from '@hapi/boom'; +import Boom from '@hapi/boom'; + +import type { SavedObjectsImportError } from 'src/core/server'; export const createEmptyFailureResponse = (errors?: Array) => { const errorMessages: Array = (errors || []).map((error) => { diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/get_ineligible_types.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/get_ineligible_types.ts index 92c6e03b68628..80ff7a6b12ed1 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/get_ineligible_types.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/get_ineligible_types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObjectTypeRegistry } from 'src/core/server'; +import type { SavedObjectTypeRegistry } from 'src/core/server'; /** * This function returns any importable/exportable saved object types that are namespace-agnostic. Even if these are eligible for diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/read_stream_to_completion.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/read_stream_to_completion.ts index 5bb89b8c5d2f0..1e88dc3613e87 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/read_stream_to_completion.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/read_stream_to_completion.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { Readable, pipeline, Writable } from 'stream'; +import type { Readable } from 'stream'; +import { pipeline, Writable } from 'stream'; export const readStreamToCompletion = (stream: Readable) => { return new Promise((resolve, reject) => { diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/saved_objects_client_opts.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/saved_objects_client_opts.ts index 01ba611ee3595..83d10e5f1a27c 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/saved_objects_client_opts.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/lib/saved_objects_client_opts.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObjectsClientProviderOptions } from 'src/core/server'; +import type { SavedObjectsClientProviderOptions } from 'src/core/server'; export const COPY_TO_SPACES_SAVED_OBJECTS_CLIENT_OPTS: SavedObjectsClientProviderOptions = { excludedWrappers: ['spaces'], diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts index 0431846bd659b..581c6a9bbfe4b 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts @@ -6,19 +6,21 @@ */ import { Readable } from 'stream'; -import { - SavedObjectsImportResponse, - SavedObjectsResolveImportErrorsOptions, + +import type { SavedObjectsExportByObjectOptions, + SavedObjectsImportResponse, SavedObjectsImportSuccess, + SavedObjectsResolveImportErrorsOptions, } from 'src/core/server'; import { coreMock, httpServerMock, - savedObjectsTypeRegistryMock, savedObjectsClientMock, savedObjectsServiceMock, + savedObjectsTypeRegistryMock, } from 'src/core/server/mocks'; + import { resolveCopySavedObjectsToSpacesConflictsFactory } from './resolve_copy_conflicts'; interface SetupOpts { diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.ts index 3cae420227735..1ce030ef05d12 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.ts @@ -5,15 +5,22 @@ * 2.0. */ -import { Readable } from 'stream'; -import { SavedObject, CoreStart, KibanaRequest, SavedObjectsImportRetry } from 'src/core/server'; +import type { Readable } from 'stream'; + +import type { + CoreStart, + KibanaRequest, + SavedObject, + SavedObjectsImportRetry, +} from 'src/core/server'; + import { spaceIdToNamespace } from '../utils/namespace'; -import { CopyOptions, ResolveConflictsOptions, CopyResponse } from './types'; import { createEmptyFailureResponse } from './lib/create_empty_failure_response'; +import { getIneligibleTypes } from './lib/get_ineligible_types'; import { readStreamToCompletion } from './lib/read_stream_to_completion'; import { createReadableStreamFromArray } from './lib/readable_stream_from_array'; import { COPY_TO_SPACES_SAVED_OBJECTS_CLIENT_OPTS } from './lib/saved_objects_client_opts'; -import { getIneligibleTypes } from './lib/get_ineligible_types'; +import type { CopyOptions, CopyResponse, ResolveConflictsOptions } from './types'; export function resolveCopySavedObjectsToSpacesConflictsFactory( savedObjects: CoreStart['savedObjects'], diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/types.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/types.ts index ad14937498af9..a6c5517743488 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/types.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/types.ts @@ -5,12 +5,13 @@ * 2.0. */ -import { Payload } from '@hapi/boom'; -import { - SavedObjectsImportSuccess, +import type { Payload } from '@hapi/boom'; + +import type { + SavedObjectsImportError, SavedObjectsImportFailure, SavedObjectsImportRetry, - SavedObjectsImportError, + SavedObjectsImportSuccess, } from 'src/core/server'; export interface CopyOptions { diff --git a/x-pack/plugins/spaces/server/lib/errors.ts b/x-pack/plugins/spaces/server/lib/errors.ts index 81bb0573f1b51..7e20fd72a9517 100644 --- a/x-pack/plugins/spaces/server/lib/errors.ts +++ b/x-pack/plugins/spaces/server/lib/errors.ts @@ -6,7 +6,8 @@ */ import { boomify, isBoom } from '@hapi/boom'; -import { ResponseError, CustomHttpResponseOptions } from 'src/core/server'; + +import type { CustomHttpResponseOptions, ResponseError } from 'src/core/server'; export function wrapError(error: any): CustomHttpResponseOptions { const boom = isBoom(error) ? error : boomify(error); diff --git a/x-pack/plugins/spaces/server/lib/request_interceptors/index.ts b/x-pack/plugins/spaces/server/lib/request_interceptors/index.ts index af3cd0c4fbd15..a9786cca7568c 100644 --- a/x-pack/plugins/spaces/server/lib/request_interceptors/index.ts +++ b/x-pack/plugins/spaces/server/lib/request_interceptors/index.ts @@ -5,11 +5,10 @@ * 2.0. */ -import { initSpacesOnRequestInterceptor, OnRequestInterceptorDeps } from './on_request_interceptor'; -import { - initSpacesOnPostAuthRequestInterceptor, - OnPostAuthInterceptorDeps, -} from './on_post_auth_interceptor'; +import type { OnPostAuthInterceptorDeps } from './on_post_auth_interceptor'; +import { initSpacesOnPostAuthRequestInterceptor } from './on_post_auth_interceptor'; +import type { OnRequestInterceptorDeps } from './on_request_interceptor'; +import { initSpacesOnRequestInterceptor } from './on_request_interceptor'; export type InterceptorDeps = OnRequestInterceptorDeps & OnPostAuthInterceptorDeps; diff --git a/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.test.ts b/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.test.ts index 322e7911db3c1..bc27fdd499368 100644 --- a/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.test.ts +++ b/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.test.ts @@ -6,28 +6,22 @@ */ import Boom from '@hapi/boom'; -import { Legacy } from 'kibana'; + // @ts-ignore import { kibanaTestUser } from '@kbn/test'; -import { initSpacesOnRequestInterceptor } from './on_request_interceptor'; -import { - CoreSetup, - SavedObjectsErrorHelpers, - IBasePath, - IRouter, -} from '../../../../../../src/core/server'; -import { - elasticsearchServiceMock, - loggingSystemMock, - coreMock, -} from '../../../../../../src/core/server/mocks'; -import * as kbnTestServer from '../../../../../../src/core/test_helpers/kbn_server'; -import { SpacesService } from '../../spaces_service'; -import { convertSavedObjectToSpace } from '../../routes/lib'; -import { initSpacesOnPostAuthRequestInterceptor } from './on_post_auth_interceptor'; -import { KibanaFeature } from '../../../../features/server'; +import type { Legacy } from 'kibana'; +import type { CoreSetup, IBasePath, IRouter } from 'src/core/server'; +import { coreMock, elasticsearchServiceMock, loggingSystemMock } from 'src/core/server/mocks'; +import * as kbnTestServer from 'src/core/test_helpers/kbn_server'; + +import { SavedObjectsErrorHelpers } from '../../../../../../src/core/server'; +import type { KibanaFeature } from '../../../../features/server'; import { featuresPluginMock } from '../../../../features/server/mocks'; +import { convertSavedObjectToSpace } from '../../routes/lib'; import { spacesClientServiceMock } from '../../spaces_client/spaces_client_service.mock'; +import { SpacesService } from '../../spaces_service'; +import { initSpacesOnPostAuthRequestInterceptor } from './on_post_auth_interceptor'; +import { initSpacesOnRequestInterceptor } from './on_request_interceptor'; // FLAKY: https://github.com/elastic/kibana/issues/55953 describe.skip('onPostAuthInterceptor', () => { diff --git a/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.ts b/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.ts index 096a6374636a7..ff81960185b62 100644 --- a/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.ts +++ b/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.ts @@ -5,14 +5,15 @@ * 2.0. */ -import { Logger, CoreSetup } from 'src/core/server'; -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; +import type { CoreSetup, Logger } from 'src/core/server'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import { addSpaceIdToPath } from '../../../common'; +import { DEFAULT_SPACE_ID, ENTER_SPACE_PATH } from '../../../common/constants'; +import type { PluginsSetup } from '../../plugin'; +import type { SpacesServiceStart } from '../../spaces_service/spaces_service'; import { wrapError } from '../errors'; -import { SpacesServiceStart } from '../../spaces_service/spaces_service'; -import { PluginsSetup } from '../../plugin'; import { getSpaceSelectorUrl } from '../get_space_selector_url'; -import { DEFAULT_SPACE_ID, ENTER_SPACE_PATH } from '../../../common/constants'; -import { addSpaceIdToPath } from '../../../common'; export interface OnPostAuthInterceptorDeps { http: CoreSetup['http']; diff --git a/x-pack/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.test.ts b/x-pack/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.test.ts index 0978f3afc02db..27164109de74d 100644 --- a/x-pack/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.test.ts +++ b/x-pack/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.test.ts @@ -5,19 +5,19 @@ * 2.0. */ -import { Legacy } from 'kibana'; import { schema } from '@kbn/config-schema'; -import { initSpacesOnRequestInterceptor } from './on_request_interceptor'; -import { - KibanaRequest, - KibanaResponseFactory, +import type { Legacy } from 'kibana'; +import type { CoreSetup, IBasePath, IRouter, -} from '../../../../../../src/core/server'; - -import * as kbnTestServer from '../../../../../../src/core/test_helpers/kbn_server'; + KibanaRequest, + KibanaResponseFactory, +} from 'src/core/server'; import { elasticsearchServiceMock } from 'src/core/server/mocks'; +import * as kbnTestServer from 'src/core/test_helpers/kbn_server'; + +import { initSpacesOnRequestInterceptor } from './on_request_interceptor'; // FAILING: https://github.com/elastic/kibana/issues/58942 describe.skip('onRequestInterceptor', () => { diff --git a/x-pack/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.ts b/x-pack/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.ts index a256b9dedb374..19872e8470319 100644 --- a/x-pack/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.ts +++ b/x-pack/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.ts @@ -5,12 +5,13 @@ * 2.0. */ -import { +import type { + CoreSetup, KibanaRequest, - OnPreRoutingToolkit, LifecycleResponseFactory, - CoreSetup, + OnPreRoutingToolkit, } from 'src/core/server'; + import { getSpaceIdFromPath } from '../../../common'; export interface OnRequestInterceptorDeps { diff --git a/x-pack/plugins/spaces/server/lib/space_schema.ts b/x-pack/plugins/spaces/server/lib/space_schema.ts index 5f3abf7499b8c..b736994c4d13b 100644 --- a/x-pack/plugins/spaces/server/lib/space_schema.ts +++ b/x-pack/plugins/spaces/server/lib/space_schema.ts @@ -6,6 +6,7 @@ */ import { schema } from '@kbn/config-schema'; + import { MAX_SPACE_INITIALS } from '../../common'; export const SPACE_ID_REGEX = /^[a-z0-9_\-]+$/; diff --git a/x-pack/plugins/spaces/server/lib/spaces_tutorial_context_factory.test.ts b/x-pack/plugins/spaces/server/lib/spaces_tutorial_context_factory.test.ts index b248b19e8a916..a7bb34824096f 100644 --- a/x-pack/plugins/spaces/server/lib/spaces_tutorial_context_factory.test.ts +++ b/x-pack/plugins/spaces/server/lib/spaces_tutorial_context_factory.test.ts @@ -5,12 +5,13 @@ * 2.0. */ +import { coreMock, httpServerMock } from 'src/core/server/mocks'; + import { DEFAULT_SPACE_ID } from '../../common/constants'; -import { createSpacesTutorialContextFactory } from './spaces_tutorial_context_factory'; +import { spacesClientServiceMock } from '../spaces_client/spaces_client_service.mock'; import { SpacesService } from '../spaces_service'; -import { coreMock, httpServerMock } from '../../../../../src/core/server/mocks'; import { spacesServiceMock } from '../spaces_service/spaces_service.mock'; -import { spacesClientServiceMock } from '../spaces_client/spaces_client_service.mock'; +import { createSpacesTutorialContextFactory } from './spaces_tutorial_context_factory'; const service = new SpacesService(); diff --git a/x-pack/plugins/spaces/server/lib/spaces_tutorial_context_factory.ts b/x-pack/plugins/spaces/server/lib/spaces_tutorial_context_factory.ts index 13789ee0d40a9..368e1b2cf5a2c 100644 --- a/x-pack/plugins/spaces/server/lib/spaces_tutorial_context_factory.ts +++ b/x-pack/plugins/spaces/server/lib/spaces_tutorial_context_factory.ts @@ -5,8 +5,9 @@ * 2.0. */ -import { KibanaRequest } from 'src/core/server'; -import { SpacesServiceStart } from '../spaces_service/spaces_service'; +import type { KibanaRequest } from 'src/core/server'; + +import type { SpacesServiceStart } from '../spaces_service/spaces_service'; export function createSpacesTutorialContextFactory(getSpacesService: () => SpacesServiceStart) { return function spacesTutorialContextFactory(request: KibanaRequest) { diff --git a/x-pack/plugins/spaces/server/lib/utils/namespace.test.ts b/x-pack/plugins/spaces/server/lib/utils/namespace.test.ts index 5e0af8bc6bb06..103a2229b5267 100644 --- a/x-pack/plugins/spaces/server/lib/utils/namespace.test.ts +++ b/x-pack/plugins/spaces/server/lib/utils/namespace.test.ts @@ -6,7 +6,7 @@ */ import { mockNamespaceIdToString, mockNamespaceStringToId } from './__mocks__'; -import { spaceIdToNamespace, namespaceToSpaceId } from './namespace'; +import { namespaceToSpaceId, spaceIdToNamespace } from './namespace'; beforeEach(() => { jest.clearAllMocks(); diff --git a/x-pack/plugins/spaces/server/lib/utils/url.ts b/x-pack/plugins/spaces/server/lib/utils/url.ts index 7af656dbbcd8e..2e434fe0de6f9 100644 --- a/x-pack/plugins/spaces/server/lib/utils/url.ts +++ b/x-pack/plugins/spaces/server/lib/utils/url.ts @@ -9,8 +9,9 @@ // DIRECT COPY FROM `src/core/utils/url`, since it's not possible to import from there, // nor can I re-export from `src/core/server`... -import { ParsedQuery } from 'query-string'; -import { format as formatUrl, parse as parseUrl, UrlObject } from 'url'; +import type { ParsedQuery } from 'query-string'; +import type { UrlObject } from 'url'; +import { format as formatUrl, parse as parseUrl } from 'url'; export interface URLMeaningfulParts { auth: string | null; diff --git a/x-pack/plugins/spaces/server/plugin.test.ts b/x-pack/plugins/spaces/server/plugin.test.ts index d1bf4d51700ba..94236b6e7f589 100644 --- a/x-pack/plugins/spaces/server/plugin.test.ts +++ b/x-pack/plugins/spaces/server/plugin.test.ts @@ -5,12 +5,14 @@ * 2.0. */ -import { CoreSetup } from 'src/core/server'; +import type { CoreSetup } from 'src/core/server'; import { coreMock } from 'src/core/server/mocks'; +import { usageCollectionPluginMock } from 'src/plugins/usage_collection/server/mocks'; + import { featuresPluginMock } from '../../features/server/mocks'; import { licensingMock } from '../../licensing/server/mocks'; -import { SpacesPlugin, PluginsStart } from './plugin'; -import { usageCollectionPluginMock } from '../../../../src/plugins/usage_collection/server/mocks'; +import type { PluginsStart } from './plugin'; +import { SpacesPlugin } from './plugin'; describe('Spaces Plugin', () => { describe('#setup', () => { diff --git a/x-pack/plugins/spaces/server/plugin.ts b/x-pack/plugins/spaces/server/plugin.ts index 4b26b1016d530..aaa19ee74cb74 100644 --- a/x-pack/plugins/spaces/server/plugin.ts +++ b/x-pack/plugins/spaces/server/plugin.ts @@ -5,40 +5,40 @@ * 2.0. */ -import { Observable } from 'rxjs'; -import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { HomeServerPluginSetup } from 'src/plugins/home/server'; -import { +import type { Observable } from 'rxjs'; + +import type { CoreSetup, CoreStart, Logger, - PluginInitializerContext, Plugin, -} from '../../../../src/core/server'; -import { + PluginInitializerContext, +} from 'src/core/server'; +import type { HomeServerPluginSetup } from 'src/plugins/home/server'; +import type { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; + +import type { PluginSetupContract as FeaturesPluginSetup, PluginStartContract as FeaturesPluginStart, } from '../../features/server'; -import { LicensingPluginSetup } from '../../licensing/server'; -import { createSpacesTutorialContextFactory } from './lib/spaces_tutorial_context_factory'; -import { registerSpacesUsageCollector } from './usage_collection'; -import { SpacesService, SpacesServiceSetup, SpacesServiceStart } from './spaces_service'; -import { UsageStatsService } from './usage_stats'; -import { ConfigType } from './config'; +import type { LicensingPluginSetup } from '../../licensing/server'; +import { SpacesLicenseService } from '../common/licensing'; +import { setupCapabilities } from './capabilities'; +import type { ConfigType } from './config'; +import { DefaultSpaceService } from './default_space'; import { initSpacesRequestInterceptors } from './lib/request_interceptors'; +import { createSpacesTutorialContextFactory } from './lib/spaces_tutorial_context_factory'; import { initExternalSpacesApi } from './routes/api/external'; import { initInternalSpacesApi } from './routes/api/internal'; import { initSpacesViewsRoutes } from './routes/views'; -import { setupCapabilities } from './capabilities'; import { SpacesSavedObjectsService } from './saved_objects'; -import { DefaultSpaceService } from './default_space'; -import { SpacesLicenseService } from '../common/licensing'; -import { - SpacesClientRepositoryFactory, - SpacesClientService, - SpacesClientWrapper, -} from './spaces_client'; +import type { SpacesClientRepositoryFactory, SpacesClientWrapper } from './spaces_client'; +import { SpacesClientService } from './spaces_client'; +import type { SpacesServiceSetup, SpacesServiceStart } from './spaces_service'; +import { SpacesService } from './spaces_service'; import type { SpacesRequestHandlerContext } from './types'; +import { registerSpacesUsageCollector } from './usage_collection'; +import { UsageStatsService } from './usage_stats'; export interface PluginsSetup { features: FeaturesPluginSetup; diff --git a/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_copy_to_space_mocks.ts b/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_copy_to_space_mocks.ts index 40f5f962a54d0..2af455dfa42c0 100644 --- a/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_copy_to_space_mocks.ts +++ b/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_copy_to_space_mocks.ts @@ -6,7 +6,8 @@ */ import { Readable } from 'stream'; -import { createPromiseFromStreams, createConcatStream } from '@kbn/utils'; + +import { createConcatStream, createPromiseFromStreams } from '@kbn/utils'; async function readStreamToCompletion(stream: Readable) { return (await (createPromiseFromStreams([stream, createConcatStream([])]) as unknown)) as any[]; diff --git a/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_mock_so_repository.ts b/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_mock_so_repository.ts index 4844588816151..13398925490fa 100644 --- a/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_mock_so_repository.ts +++ b/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_mock_so_repository.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { ISavedObjectsRepository, SavedObjectsErrorHelpers } from 'src/core/server'; +import type { ISavedObjectsRepository } from 'src/core/server'; +import { SavedObjectsErrorHelpers } from 'src/core/server'; export const createMockSavedObjectsRepository = (spaces: any[] = []) => { const mockSavedObjectsClientContract = ({ diff --git a/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_mock_so_service.ts b/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_mock_so_service.ts index 89448b70587f1..be0b402364ecc 100644 --- a/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_mock_so_service.ts +++ b/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_mock_so_service.ts @@ -5,13 +5,14 @@ * 2.0. */ -import { SavedObject, SavedObjectsUpdateResponse, SavedObjectsErrorHelpers } from 'src/core/server'; +import type { SavedObject, SavedObjectsUpdateResponse } from 'src/core/server'; +import { SavedObjectsErrorHelpers } from 'src/core/server'; import { coreMock, savedObjectsClientMock, - savedObjectsTypeRegistryMock, savedObjectsServiceMock, -} from '../../../../../../../src/core/server/mocks'; + savedObjectsTypeRegistryMock, +} from 'src/core/server/mocks'; export const createMockSavedObjectsService = (spaces: any[] = []) => { const typeRegistry = savedObjectsTypeRegistryMock.create(); diff --git a/x-pack/plugins/spaces/server/routes/api/__fixtures__/route_contexts.ts b/x-pack/plugins/spaces/server/routes/api/__fixtures__/route_contexts.ts index 1016be65b05ae..591ae71b1faf2 100644 --- a/x-pack/plugins/spaces/server/routes/api/__fixtures__/route_contexts.ts +++ b/x-pack/plugins/spaces/server/routes/api/__fixtures__/route_contexts.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { RequestHandlerContext } from 'src/core/server'; +import type { RequestHandlerContext } from 'src/core/server'; export const mockRouteContext = ({ licensing: { diff --git a/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts b/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts index 31b7d0f44e37d..29cad687bf71f 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts @@ -6,31 +6,33 @@ */ import * as Rx from 'rxjs'; + +import type { ObjectType } from '@kbn/config-schema'; +import type { RouteValidatorConfig } from 'src/core/server'; +import { kibanaResponseFactory } from 'src/core/server'; import { - createSpaces, - createMockSavedObjectsRepository, - mockRouteContext, - mockRouteContextWithInvalidLicense, - createExportSavedObjectsToStreamMock, - createImportSavedObjectsFromStreamMock, - createResolveSavedObjectsImportErrorsMock, - createMockSavedObjectsService, -} from '../__fixtures__'; -import { kibanaResponseFactory, RouteValidatorConfig } from 'src/core/server'; -import { - loggingSystemMock, - httpServiceMock, - httpServerMock, coreMock, + httpServerMock, + httpServiceMock, + loggingSystemMock, } from 'src/core/server/mocks'; + +import { spacesConfig } from '../../../lib/__fixtures__'; +import { SpacesClientService } from '../../../spaces_client'; import { SpacesService } from '../../../spaces_service'; import { usageStatsClientMock } from '../../../usage_stats/usage_stats_client.mock'; import { usageStatsServiceMock } from '../../../usage_stats/usage_stats_service.mock'; +import { + createExportSavedObjectsToStreamMock, + createImportSavedObjectsFromStreamMock, + createMockSavedObjectsRepository, + createMockSavedObjectsService, + createResolveSavedObjectsImportErrorsMock, + createSpaces, + mockRouteContext, + mockRouteContextWithInvalidLicense, +} from '../__fixtures__'; import { initCopyToSpacesApi } from './copy_to_space'; -import { spacesConfig } from '../../../lib/__fixtures__'; -import { ObjectType } from '@kbn/config-schema'; - -import { SpacesClientService } from '../../../spaces_client'; describe('copy to space', () => { const spacesSavedObjects = createSpaces(); diff --git a/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.ts b/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.ts index 82d2cdb9955ba..90f8971af006b 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.ts @@ -5,16 +5,18 @@ * 2.0. */ -import { schema } from '@kbn/config-schema'; import _ from 'lodash'; -import { SavedObject } from 'src/core/server'; + +import { schema } from '@kbn/config-schema'; +import type { SavedObject } from 'src/core/server'; + import { copySavedObjectsToSpacesFactory, resolveCopySavedObjectsToSpacesConflictsFactory, } from '../../../lib/copy_to_spaces'; -import { ExternalRouteDeps } from '.'; import { SPACE_ID_REGEX } from '../../../lib/space_schema'; import { createLicensedRouteHandler } from '../../lib'; +import type { ExternalRouteDeps } from './'; type SavedObjectIdentifier = Pick; diff --git a/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts b/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts index 5d6db7e6ce9c2..2ad392d48b2ae 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts @@ -6,29 +6,28 @@ */ import * as Rx from 'rxjs'; + +import type { ObjectType } from '@kbn/config-schema'; +import type { RouteValidatorConfig } from 'src/core/server'; +import { kibanaResponseFactory, SavedObjectsErrorHelpers } from 'src/core/server'; import { - createSpaces, - createMockSavedObjectsRepository, - mockRouteContext, - mockRouteContextWithInvalidLicense, -} from '../__fixtures__'; -import { - kibanaResponseFactory, - RouteValidatorConfig, - SavedObjectsErrorHelpers, -} from 'src/core/server'; -import { - loggingSystemMock, - httpServiceMock, - httpServerMock, coreMock, + httpServerMock, + httpServiceMock, + loggingSystemMock, } from 'src/core/server/mocks'; -import { SpacesService } from '../../../spaces_service'; -import { initDeleteSpacesApi } from './delete'; + import { spacesConfig } from '../../../lib/__fixtures__'; -import { ObjectType } from '@kbn/config-schema'; import { SpacesClientService } from '../../../spaces_client'; +import { SpacesService } from '../../../spaces_service'; import { usageStatsServiceMock } from '../../../usage_stats/usage_stats_service.mock'; +import { + createMockSavedObjectsRepository, + createSpaces, + mockRouteContext, + mockRouteContextWithInvalidLicense, +} from '../__fixtures__'; +import { initDeleteSpacesApi } from './delete'; describe('Spaces Public API', () => { const spacesSavedObjects = createSpaces(); diff --git a/x-pack/plugins/spaces/server/routes/api/external/delete.ts b/x-pack/plugins/spaces/server/routes/api/external/delete.ts index d2bb4f8746089..7d70cd1bb3664 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/delete.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/delete.ts @@ -6,11 +6,13 @@ */ import Boom from '@hapi/boom'; + import { schema } from '@kbn/config-schema'; + import { SavedObjectsErrorHelpers } from '../../../../../../../src/core/server'; import { wrapError } from '../../../lib/errors'; -import { ExternalRouteDeps } from '.'; import { createLicensedRouteHandler } from '../../lib'; +import type { ExternalRouteDeps } from './'; export function initDeleteSpacesApi(deps: ExternalRouteDeps) { const { externalRouter, log, getSpacesService } = deps; diff --git a/x-pack/plugins/spaces/server/routes/api/external/get.test.ts b/x-pack/plugins/spaces/server/routes/api/external/get.test.ts index af839273dacce..5ebe09444934b 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/get.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/get.test.ts @@ -6,24 +6,26 @@ */ import * as Rx from 'rxjs'; -import { - createSpaces, - createMockSavedObjectsRepository, - mockRouteContextWithInvalidLicense, - mockRouteContext, -} from '../__fixtures__'; -import { initGetSpaceApi } from './get'; + import { kibanaResponseFactory } from 'src/core/server'; import { - loggingSystemMock, - httpServiceMock, - httpServerMock, coreMock, + httpServerMock, + httpServiceMock, + loggingSystemMock, } from 'src/core/server/mocks'; -import { SpacesService } from '../../../spaces_service'; + import { spacesConfig } from '../../../lib/__fixtures__'; import { SpacesClientService } from '../../../spaces_client'; +import { SpacesService } from '../../../spaces_service'; import { usageStatsServiceMock } from '../../../usage_stats/usage_stats_service.mock'; +import { + createMockSavedObjectsRepository, + createSpaces, + mockRouteContext, + mockRouteContextWithInvalidLicense, +} from '../__fixtures__'; +import { initGetSpaceApi } from './get'; describe('GET space', () => { const spacesSavedObjects = createSpaces(); diff --git a/x-pack/plugins/spaces/server/routes/api/external/get.ts b/x-pack/plugins/spaces/server/routes/api/external/get.ts index a8bcc8d3738be..2cc0984b8ab93 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/get.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/get.ts @@ -6,10 +6,11 @@ */ import { schema } from '@kbn/config-schema'; + import { SavedObjectsErrorHelpers } from '../../../../../../../src/core/server'; import { wrapError } from '../../../lib/errors'; -import { ExternalRouteDeps } from '.'; import { createLicensedRouteHandler } from '../../lib'; +import type { ExternalRouteDeps } from './'; export function initGetSpaceApi(deps: ExternalRouteDeps) { const { externalRouter, getSpacesService } = deps; diff --git a/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts b/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts index 5dd78c6e8d5fb..e1d10ccd3cfe6 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts @@ -6,25 +6,27 @@ */ import * as Rx from 'rxjs'; -import { - createSpaces, - createMockSavedObjectsRepository, - mockRouteContext, - mockRouteContextWithInvalidLicense, -} from '../__fixtures__'; + +import type { ObjectType } from '@kbn/config-schema'; import { kibanaResponseFactory } from 'src/core/server'; import { - loggingSystemMock, - httpServiceMock, - httpServerMock, coreMock, + httpServerMock, + httpServiceMock, + loggingSystemMock, } from 'src/core/server/mocks'; -import { SpacesService } from '../../../spaces_service'; -import { initGetAllSpacesApi } from './get_all'; + import { spacesConfig } from '../../../lib/__fixtures__'; -import { ObjectType } from '@kbn/config-schema'; import { SpacesClientService } from '../../../spaces_client'; +import { SpacesService } from '../../../spaces_service'; import { usageStatsServiceMock } from '../../../usage_stats/usage_stats_service.mock'; +import { + createMockSavedObjectsRepository, + createSpaces, + mockRouteContext, + mockRouteContextWithInvalidLicense, +} from '../__fixtures__'; +import { initGetAllSpacesApi } from './get_all'; describe('GET /spaces/space', () => { const spacesSavedObjects = createSpaces(); diff --git a/x-pack/plugins/spaces/server/routes/api/external/get_all.ts b/x-pack/plugins/spaces/server/routes/api/external/get_all.ts index a9707bca9aa8a..7f0bd3231c6dd 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/get_all.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/get_all.ts @@ -6,10 +6,11 @@ */ import { schema } from '@kbn/config-schema'; -import { Space } from '../../../../../../../src/plugins/spaces_oss/common'; +import type { Space } from 'src/plugins/spaces_oss/common'; + import { wrapError } from '../../../lib/errors'; -import { ExternalRouteDeps } from '.'; import { createLicensedRouteHandler } from '../../lib'; +import type { ExternalRouteDeps } from './'; export function initGetAllSpacesApi(deps: ExternalRouteDeps) { const { externalRouter, log, getSpacesService } = deps; diff --git a/x-pack/plugins/spaces/server/routes/api/external/index.ts b/x-pack/plugins/spaces/server/routes/api/external/index.ts index 6cdf92d17a52a..3e2a523d767ea 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/index.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/index.ts @@ -5,17 +5,18 @@ * 2.0. */ -import { Logger, CoreSetup } from 'src/core/server'; +import type { CoreSetup, Logger } from 'src/core/server'; + +import type { SpacesServiceStart } from '../../../spaces_service'; +import type { SpacesRouter } from '../../../types'; +import type { UsageStatsServiceSetup } from '../../../usage_stats'; +import { initCopyToSpacesApi } from './copy_to_space'; import { initDeleteSpacesApi } from './delete'; import { initGetSpaceApi } from './get'; import { initGetAllSpacesApi } from './get_all'; import { initPostSpacesApi } from './post'; import { initPutSpacesApi } from './put'; -import { SpacesServiceStart } from '../../../spaces_service'; -import { UsageStatsServiceSetup } from '../../../usage_stats'; -import { initCopyToSpacesApi } from './copy_to_space'; import { initShareToSpacesApi } from './share_to_space'; -import type { SpacesRouter } from '../../../types'; export interface ExternalRouteDeps { externalRouter: SpacesRouter; diff --git a/x-pack/plugins/spaces/server/routes/api/external/post.test.ts b/x-pack/plugins/spaces/server/routes/api/external/post.test.ts index e77a712f4a0e4..56104c247e23d 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/post.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/post.test.ts @@ -6,25 +6,28 @@ */ import * as Rx from 'rxjs'; + +import type { ObjectType } from '@kbn/config-schema'; +import type { RouteValidatorConfig } from 'src/core/server'; +import { kibanaResponseFactory } from 'src/core/server'; import { - createSpaces, - createMockSavedObjectsRepository, - mockRouteContext, - mockRouteContextWithInvalidLicense, -} from '../__fixtures__'; -import { kibanaResponseFactory, RouteValidatorConfig } from 'src/core/server'; -import { - loggingSystemMock, + coreMock, httpServerMock, httpServiceMock, - coreMock, + loggingSystemMock, } from 'src/core/server/mocks'; -import { SpacesService } from '../../../spaces_service'; -import { initPostSpacesApi } from './post'; + import { spacesConfig } from '../../../lib/__fixtures__'; -import { ObjectType } from '@kbn/config-schema'; import { SpacesClientService } from '../../../spaces_client'; +import { SpacesService } from '../../../spaces_service'; import { usageStatsServiceMock } from '../../../usage_stats/usage_stats_service.mock'; +import { + createMockSavedObjectsRepository, + createSpaces, + mockRouteContext, + mockRouteContextWithInvalidLicense, +} from '../__fixtures__'; +import { initPostSpacesApi } from './post'; describe('Spaces Public API', () => { const spacesSavedObjects = createSpaces(); diff --git a/x-pack/plugins/spaces/server/routes/api/external/post.ts b/x-pack/plugins/spaces/server/routes/api/external/post.ts index 1a7a07d8be896..c2ea044741574 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/post.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/post.ts @@ -6,11 +6,12 @@ */ import Boom from '@hapi/boom'; + import { SavedObjectsErrorHelpers } from '../../../../../../../src/core/server'; import { wrapError } from '../../../lib/errors'; import { spaceSchema } from '../../../lib/space_schema'; -import { ExternalRouteDeps } from '.'; import { createLicensedRouteHandler } from '../../lib'; +import type { ExternalRouteDeps } from './'; export function initPostSpacesApi(deps: ExternalRouteDeps) { const { externalRouter, log, getSpacesService } = deps; diff --git a/x-pack/plugins/spaces/server/routes/api/external/put.test.ts b/x-pack/plugins/spaces/server/routes/api/external/put.test.ts index 2f8ee6c0d478f..760558572628a 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/put.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/put.test.ts @@ -6,25 +6,28 @@ */ import * as Rx from 'rxjs'; + +import type { ObjectType } from '@kbn/config-schema'; +import type { RouteValidatorConfig } from 'src/core/server'; +import { kibanaResponseFactory } from 'src/core/server'; import { - createSpaces, - createMockSavedObjectsRepository, - mockRouteContext, - mockRouteContextWithInvalidLicense, -} from '../__fixtures__'; -import { kibanaResponseFactory, RouteValidatorConfig } from 'src/core/server'; -import { - loggingSystemMock, - httpServiceMock, - httpServerMock, coreMock, + httpServerMock, + httpServiceMock, + loggingSystemMock, } from 'src/core/server/mocks'; -import { SpacesService } from '../../../spaces_service'; -import { initPutSpacesApi } from './put'; + import { spacesConfig } from '../../../lib/__fixtures__'; -import { ObjectType } from '@kbn/config-schema'; import { SpacesClientService } from '../../../spaces_client'; +import { SpacesService } from '../../../spaces_service'; import { usageStatsServiceMock } from '../../../usage_stats/usage_stats_service.mock'; +import { + createMockSavedObjectsRepository, + createSpaces, + mockRouteContext, + mockRouteContextWithInvalidLicense, +} from '../__fixtures__'; +import { initPutSpacesApi } from './put'; describe('PUT /api/spaces/space', () => { const spacesSavedObjects = createSpaces(); diff --git a/x-pack/plugins/spaces/server/routes/api/external/put.ts b/x-pack/plugins/spaces/server/routes/api/external/put.ts index c9d5e1343efbe..b58601c8c7e77 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/put.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/put.ts @@ -6,12 +6,13 @@ */ import { schema } from '@kbn/config-schema'; +import type { Space } from 'src/plugins/spaces_oss/common'; + import { SavedObjectsErrorHelpers } from '../../../../../../../src/core/server'; -import { Space } from '../../../../../../../src/plugins/spaces_oss/common'; import { wrapError } from '../../../lib/errors'; import { spaceSchema } from '../../../lib/space_schema'; -import { ExternalRouteDeps } from '.'; import { createLicensedRouteHandler } from '../../lib'; +import type { ExternalRouteDeps } from './'; export function initPutSpacesApi(deps: ExternalRouteDeps) { const { externalRouter, getSpacesService } = deps; diff --git a/x-pack/plugins/spaces/server/routes/api/external/share_to_space.test.ts b/x-pack/plugins/spaces/server/routes/api/external/share_to_space.test.ts index 04d481c7f77eb..cae6fd152d8ff 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/share_to_space.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/share_to_space.test.ts @@ -6,26 +6,29 @@ */ import * as Rx from 'rxjs'; + +import type { ObjectType } from '@kbn/config-schema'; +import type { RouteValidatorConfig } from 'src/core/server'; +import { kibanaResponseFactory } from 'src/core/server'; import { - createSpaces, - createMockSavedObjectsRepository, - mockRouteContext, - mockRouteContextWithInvalidLicense, - createMockSavedObjectsService, -} from '../__fixtures__'; -import { kibanaResponseFactory, RouteValidatorConfig } from 'src/core/server'; -import { - loggingSystemMock, - httpServiceMock, - httpServerMock, coreMock, + httpServerMock, + httpServiceMock, + loggingSystemMock, } from 'src/core/server/mocks'; -import { SpacesService } from '../../../spaces_service'; -import { initShareToSpacesApi } from './share_to_space'; + import { spacesConfig } from '../../../lib/__fixtures__'; -import { ObjectType } from '@kbn/config-schema'; import { SpacesClientService } from '../../../spaces_client'; +import { SpacesService } from '../../../spaces_service'; import { usageStatsServiceMock } from '../../../usage_stats/usage_stats_service.mock'; +import { + createMockSavedObjectsRepository, + createMockSavedObjectsService, + createSpaces, + mockRouteContext, + mockRouteContextWithInvalidLicense, +} from '../__fixtures__'; +import { initShareToSpacesApi } from './share_to_space'; describe('share to space', () => { const spacesSavedObjects = createSpaces(); diff --git a/x-pack/plugins/spaces/server/routes/api/external/share_to_space.ts b/x-pack/plugins/spaces/server/routes/api/external/share_to_space.ts index 699e0f45607c8..1c6f254354cb2 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/share_to_space.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/share_to_space.ts @@ -6,11 +6,12 @@ */ import { schema } from '@kbn/config-schema'; -import { wrapError } from '../../../lib/errors'; -import { ExternalRouteDeps } from '.'; + import { ALL_SPACES_ID } from '../../../../common/constants'; +import { wrapError } from '../../../lib/errors'; import { SPACE_ID_REGEX } from '../../../lib/space_schema'; import { createLicensedRouteHandler } from '../../lib'; +import type { ExternalRouteDeps } from './'; const uniq = (arr: T[]): T[] => Array.from(new Set(arr)); export function initShareToSpacesApi(deps: ExternalRouteDeps) { diff --git a/x-pack/plugins/spaces/server/routes/api/internal/get_active_space.test.ts b/x-pack/plugins/spaces/server/routes/api/internal/get_active_space.test.ts index 98868c7c50713..2a0b3821f1e7f 100644 --- a/x-pack/plugins/spaces/server/routes/api/internal/get_active_space.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/internal/get_active_space.test.ts @@ -5,12 +5,13 @@ * 2.0. */ -import { mockRouteContextWithInvalidLicense } from '../__fixtures__'; import { kibanaResponseFactory } from 'src/core/server'; -import { httpServiceMock, httpServerMock, coreMock } from 'src/core/server/mocks'; +import { coreMock, httpServerMock, httpServiceMock } from 'src/core/server/mocks'; + +import { spacesClientServiceMock } from '../../../spaces_client/spaces_client_service.mock'; import { SpacesService } from '../../../spaces_service'; +import { mockRouteContextWithInvalidLicense } from '../__fixtures__'; import { initGetActiveSpaceApi } from './get_active_space'; -import { spacesClientServiceMock } from '../../../spaces_client/spaces_client_service.mock'; describe('GET /internal/spaces/_active_space', () => { const setup = async () => { diff --git a/x-pack/plugins/spaces/server/routes/api/internal/get_active_space.ts b/x-pack/plugins/spaces/server/routes/api/internal/get_active_space.ts index 389b504dfc282..ba96620467376 100644 --- a/x-pack/plugins/spaces/server/routes/api/internal/get_active_space.ts +++ b/x-pack/plugins/spaces/server/routes/api/internal/get_active_space.ts @@ -6,8 +6,8 @@ */ import { wrapError } from '../../../lib/errors'; -import { InternalRouteDeps } from '.'; import { createLicensedRouteHandler } from '../../lib'; +import type { InternalRouteDeps } from './'; export function initGetActiveSpaceApi(deps: InternalRouteDeps) { const { internalRouter, getSpacesService } = deps; diff --git a/x-pack/plugins/spaces/server/routes/api/internal/index.ts b/x-pack/plugins/spaces/server/routes/api/internal/index.ts index dbd57e8817846..7263adad43f82 100644 --- a/x-pack/plugins/spaces/server/routes/api/internal/index.ts +++ b/x-pack/plugins/spaces/server/routes/api/internal/index.ts @@ -5,8 +5,8 @@ * 2.0. */ +import type { SpacesServiceStart } from '../../../spaces_service/spaces_service'; import type { SpacesRouter } from '../../../types'; -import { SpacesServiceStart } from '../../../spaces_service/spaces_service'; import { initGetActiveSpaceApi } from './get_active_space'; export interface InternalRouteDeps { diff --git a/x-pack/plugins/spaces/server/routes/lib/convert_saved_object_to_space.ts b/x-pack/plugins/spaces/server/routes/lib/convert_saved_object_to_space.ts index e82808835b51d..6a90d367fc4a7 100644 --- a/x-pack/plugins/spaces/server/routes/lib/convert_saved_object_to_space.ts +++ b/x-pack/plugins/spaces/server/routes/lib/convert_saved_object_to_space.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Space } from '../../../../../../src/plugins/spaces_oss/common'; +import type { Space } from 'src/plugins/spaces_oss/common'; export function convertSavedObjectToSpace(savedObject: any): Space { return { diff --git a/x-pack/plugins/spaces/server/routes/lib/licensed_route_handler.ts b/x-pack/plugins/spaces/server/routes/lib/licensed_route_handler.ts index 5e95fc5e277e7..42d5719006e54 100644 --- a/x-pack/plugins/spaces/server/routes/lib/licensed_route_handler.ts +++ b/x-pack/plugins/spaces/server/routes/lib/licensed_route_handler.ts @@ -5,7 +5,8 @@ * 2.0. */ -import type { RequestHandler, RequestHandlerContext } from 'kibana/server'; +import type { RequestHandler, RequestHandlerContext } from 'src/core/server'; + import type { LicensingApiRequestHandlerContext } from '../../../../licensing/server'; export const createLicensedRouteHandler = < diff --git a/x-pack/plugins/spaces/server/routes/views/index.ts b/x-pack/plugins/spaces/server/routes/views/index.ts index a10ffa11e8d09..18222f9122559 100644 --- a/x-pack/plugins/spaces/server/routes/views/index.ts +++ b/x-pack/plugins/spaces/server/routes/views/index.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { HttpResources, IBasePath, Logger } from 'src/core/server'; +import type { HttpResources, IBasePath, Logger } from 'src/core/server'; + import { ENTER_SPACE_PATH } from '../../../common'; import { wrapError } from '../../lib/errors'; diff --git a/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.test.ts b/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.test.ts index 8ee37dc635561..71d467f6f56d0 100644 --- a/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.test.ts +++ b/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.test.ts @@ -5,8 +5,9 @@ * 2.0. */ +import type { SavedObjectMigrationContext } from 'src/core/server'; + import { migrateToKibana660 } from './migrate_6x'; -import { SavedObjectMigrationContext } from 'src/core/server'; const mockContext = {} as SavedObjectMigrationContext; diff --git a/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.ts b/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.ts index cca792f810c15..973d6ec1751a2 100644 --- a/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.ts +++ b/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObjectMigrationFn } from 'src/core/server'; +import type { SavedObjectMigrationFn } from 'src/core/server'; export const migrateToKibana660: SavedObjectMigrationFn = (doc) => { if (!doc.attributes.hasOwnProperty('disabledFeatures')) { diff --git a/x-pack/plugins/spaces/server/saved_objects/saved_objects_client_wrapper_factory.ts b/x-pack/plugins/spaces/server/saved_objects/saved_objects_client_wrapper_factory.ts index 69bb53643e293..c9050da88d1bb 100644 --- a/x-pack/plugins/spaces/server/saved_objects/saved_objects_client_wrapper_factory.ts +++ b/x-pack/plugins/spaces/server/saved_objects/saved_objects_client_wrapper_factory.ts @@ -5,12 +5,13 @@ * 2.0. */ -import { +import type { SavedObjectsClientWrapperFactory, SavedObjectsClientWrapperOptions, } from 'src/core/server'; + +import type { SpacesServiceStart } from '../spaces_service/spaces_service'; import { SpacesSavedObjectsClient } from './spaces_saved_objects_client'; -import { SpacesServiceStart } from '../spaces_service/spaces_service'; export function spacesSavedObjectsClientWrapperFactory( getSpacesService: () => SpacesServiceStart diff --git a/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.test.ts b/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.test.ts index ec9c3c3608db3..4cc94a6c39da5 100644 --- a/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.test.ts +++ b/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.test.ts @@ -6,8 +6,9 @@ */ import { coreMock } from 'src/core/server/mocks'; -import { SPACES_USAGE_STATS_TYPE } from '../usage_stats'; + import { spacesServiceMock } from '../spaces_service/spaces_service.mock'; +import { SPACES_USAGE_STATS_TYPE } from '../usage_stats'; import { SpacesSavedObjectsService } from './saved_objects_service'; describe('SpacesSavedObjectsService', () => { diff --git a/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts b/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts index 73a59ab19b067..b1bf7fe580a03 100644 --- a/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts +++ b/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts @@ -5,12 +5,13 @@ * 2.0. */ -import { CoreSetup } from 'src/core/server'; +import type { CoreSetup } from 'src/core/server'; + +import type { SpacesServiceStart } from '../spaces_service'; +import { SPACES_USAGE_STATS_TYPE } from '../usage_stats'; import { SpacesSavedObjectMappings, UsageStatsMappings } from './mappings'; import { migrateToKibana660 } from './migrations'; import { spacesSavedObjectsClientWrapperFactory } from './saved_objects_client_wrapper_factory'; -import { SpacesServiceStart } from '../spaces_service'; -import { SPACES_USAGE_STATS_TYPE } from '../usage_stats'; interface SetupDeps { core: Pick; diff --git a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts index f5917e78135ec..fa53f110e30c3 100644 --- a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts +++ b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts @@ -5,14 +5,16 @@ * 2.0. */ -import { DEFAULT_SPACE_ID } from '../../common/constants'; -import { SpacesSavedObjectsClient } from './spaces_saved_objects_client'; -import { spacesServiceMock } from '../spaces_service/spaces_service.mock'; -import { savedObjectsClientMock } from '../../../../../src/core/server/mocks'; +import Boom from '@hapi/boom'; + import { SavedObjectTypeRegistry } from 'src/core/server'; -import { SpacesClient } from '../spaces_client'; +import { savedObjectsClientMock } from 'src/core/server/mocks'; + +import { DEFAULT_SPACE_ID } from '../../common/constants'; +import type { SpacesClient } from '../spaces_client'; import { spacesClientMock } from '../spaces_client/spaces_client.mock'; -import Boom from '@hapi/boom'; +import { spacesServiceMock } from '../spaces_service/spaces_service.mock'; +import { SpacesSavedObjectsClient } from './spaces_saved_objects_client'; const typeRegistry = new SavedObjectTypeRegistry(); typeRegistry.registerType({ diff --git a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts index 433f95d2b5cf6..f70714b8ad102 100644 --- a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts +++ b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts @@ -6,28 +6,30 @@ */ import Boom from '@hapi/boom'; -import { + +import type { + ISavedObjectTypeRegistry, + SavedObjectsAddToNamespacesOptions, SavedObjectsBaseOptions, SavedObjectsBulkCreateObject, SavedObjectsBulkGetObject, SavedObjectsBulkUpdateObject, SavedObjectsCheckConflictsObject, SavedObjectsClientContract, + SavedObjectsClosePointInTimeOptions, SavedObjectsCreateOptions, + SavedObjectsDeleteFromNamespacesOptions, SavedObjectsFindOptions, - SavedObjectsClosePointInTimeOptions, SavedObjectsOpenPointInTimeOptions, - SavedObjectsUpdateOptions, - SavedObjectsAddToNamespacesOptions, - SavedObjectsDeleteFromNamespacesOptions, SavedObjectsRemoveReferencesToOptions, - SavedObjectsUtils, - ISavedObjectTypeRegistry, -} from '../../../../../src/core/server'; + SavedObjectsUpdateOptions, +} from 'src/core/server'; + +import { SavedObjectsUtils } from '../../../../../src/core/server'; import { ALL_SPACES_ID } from '../../common/constants'; -import { SpacesServiceStart } from '../spaces_service/spaces_service'; import { spaceIdToNamespace } from '../lib/utils/namespace'; -import { ISpacesClient } from '../spaces_client'; +import type { ISpacesClient } from '../spaces_client'; +import type { SpacesServiceStart } from '../spaces_service/spaces_service'; interface SpacesSavedObjectsClientOptions { baseClient: SavedObjectsClientContract; diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client.mock.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client.mock.ts index 5b41eb6014a11..f03e26ea8e714 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client.mock.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client.mock.ts @@ -5,9 +5,10 @@ * 2.0. */ +import type { Space } from 'src/plugins/spaces_oss/common'; + import { DEFAULT_SPACE_ID } from '../../common/constants'; -import { Space } from '../../../../../src/plugins/spaces_oss/common'; -import { SpacesClient } from './spaces_client'; +import type { SpacesClient } from './spaces_client'; const createSpacesClientMock = () => (({ diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client.test.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client.test.ts index 4edfc70c7f3b2..1d5d3851ce9c5 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client.test.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client.test.ts @@ -5,10 +5,12 @@ * 2.0. */ +import { savedObjectsRepositoryMock } from 'src/core/server/mocks'; + +import type { GetAllSpacesPurpose } from '../../common'; +import type { ConfigType } from '../config'; +import { ConfigSchema } from '../config'; import { SpacesClient } from './spaces_client'; -import { ConfigType, ConfigSchema } from '../config'; -import { savedObjectsRepositoryMock } from '../../../../../src/core/server/mocks'; -import { GetAllSpacesPurpose } from '../../common'; const createMockDebugLogger = () => { return jest.fn(); diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts index e38d676197436..c4f5e9edaf494 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts @@ -7,12 +7,14 @@ import Boom from '@hapi/boom'; import { omit } from 'lodash'; -import { ISavedObjectsRepository, SavedObject } from 'src/core/server'; -import { PublicMethodsOf } from '@kbn/utility-types'; -import { Space } from 'src/plugins/spaces_oss/common'; -import { GetAllSpacesOptions, GetAllSpacesPurpose, GetSpaceResult } from '../../common'; + +import type { PublicMethodsOf } from '@kbn/utility-types'; +import type { ISavedObjectsRepository, SavedObject } from 'src/core/server'; +import type { Space } from 'src/plugins/spaces_oss/common'; + +import type { GetAllSpacesOptions, GetAllSpacesPurpose, GetSpaceResult } from '../../common'; import { isReservedSpace } from '../../common'; -import { ConfigType } from '../config'; +import type { ConfigType } from '../config'; const SUPPORTED_GET_SPACE_PURPOSES: GetAllSpacesPurpose[] = [ 'any', diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.mock.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.mock.ts index 102a478ef5a44..da9dcd86eed2a 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.mock.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.mock.ts @@ -6,8 +6,7 @@ */ import { spacesClientMock } from '../mocks'; - -import { SpacesClientServiceSetup, SpacesClientServiceStart } from './spaces_client_service'; +import type { SpacesClientServiceSetup, SpacesClientServiceStart } from './spaces_client_service'; const createSpacesClientServiceSetupMock = () => ({ diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.test.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.test.ts index e6d9c898d814d..263b470f0a6f6 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.test.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.test.ts @@ -6,10 +6,13 @@ */ import * as Rx from 'rxjs'; + import { coreMock, httpServerMock } from 'src/core/server/mocks'; -import { ConfigType } from '../config'; + +import type { ConfigType } from '../config'; import { spacesConfig } from '../lib/__fixtures__'; -import { ISpacesClient, SpacesClient } from './spaces_client'; +import type { ISpacesClient } from './spaces_client'; +import { SpacesClient } from './spaces_client'; import { SpacesClientService } from './spaces_client_service'; const debugLogger = jest.fn(); diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts index a49e9eed6ec08..4db27bac845f1 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts @@ -5,15 +5,18 @@ * 2.0. */ -import { Observable } from 'rxjs'; -import { - KibanaRequest, +import type { Observable } from 'rxjs'; + +import type { CoreStart, ISavedObjectsRepository, + KibanaRequest, SavedObjectsServiceStart, } from 'src/core/server'; -import { ConfigType } from '../config'; -import { SpacesClient, ISpacesClient } from './spaces_client'; + +import type { ConfigType } from '../config'; +import type { ISpacesClient } from './spaces_client'; +import { SpacesClient } from './spaces_client'; export type SpacesClientWrapper = ( request: KibanaRequest, diff --git a/x-pack/plugins/spaces/server/spaces_service/spaces_service.mock.ts b/x-pack/plugins/spaces/server/spaces_service/spaces_service.mock.ts index ec7a740bab0c5..625e02a557288 100644 --- a/x-pack/plugins/spaces/server/spaces_service/spaces_service.mock.ts +++ b/x-pack/plugins/spaces/server/spaces_service/spaces_service.mock.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { SpacesServiceSetup, SpacesServiceStart } from './spaces_service'; -import { spacesClientMock } from '../spaces_client/spaces_client.mock'; import { DEFAULT_SPACE_ID } from '../../common/constants'; import { namespaceToSpaceId, spaceIdToNamespace } from '../lib/utils/namespace'; +import { spacesClientMock } from '../spaces_client/spaces_client.mock'; +import type { SpacesServiceSetup, SpacesServiceStart } from './spaces_service'; const createSetupContractMock = (spaceId = DEFAULT_SPACE_ID) => { const setupContract: jest.Mocked = { diff --git a/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts b/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts index 93c5a5bce5963..a6631360d14e1 100644 --- a/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts +++ b/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts @@ -6,18 +6,16 @@ */ import * as Rx from 'rxjs'; -import { SpacesService } from './spaces_service'; + +import type { HttpServiceSetup, KibanaRequest, SavedObjectsRepository } from 'src/core/server'; +import { SavedObjectsErrorHelpers } from 'src/core/server'; import { coreMock, httpServerMock } from 'src/core/server/mocks'; -import { - KibanaRequest, - SavedObjectsErrorHelpers, - HttpServiceSetup, - SavedObjectsRepository, -} from 'src/core/server'; + import { DEFAULT_SPACE_ID } from '../../common/constants'; import { getSpaceIdFromPath } from '../../common/lib/spaces_url_parser'; import { spacesConfig } from '../lib/__fixtures__'; import { SpacesClientService } from '../spaces_client'; +import { SpacesService } from './spaces_service'; const createService = (serverBasePath: string = '') => { const spacesService = new SpacesService(); diff --git a/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts b/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts index 94e38716eda35..c76646fa8a2b4 100644 --- a/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts +++ b/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts @@ -5,12 +5,13 @@ * 2.0. */ -import type { KibanaRequest, IBasePath } from 'src/core/server'; -import { SpacesClientServiceStart } from '../spaces_client'; +import type { IBasePath, KibanaRequest } from 'src/core/server'; +import type { Space } from 'src/plugins/spaces_oss/common'; + import { getSpaceIdFromPath } from '../../common'; import { DEFAULT_SPACE_ID } from '../../common/constants'; -import { spaceIdToNamespace, namespaceToSpaceId } from '../lib/utils/namespace'; -import { Space } from '..'; +import { namespaceToSpaceId, spaceIdToNamespace } from '../lib/utils/namespace'; +import type { SpacesClientServiceStart } from '../spaces_client'; export interface SpacesServiceSetup { /** diff --git a/x-pack/plugins/spaces/server/types.ts b/x-pack/plugins/spaces/server/types.ts index 3a288bc161fff..9fc70fedd30d0 100644 --- a/x-pack/plugins/spaces/server/types.ts +++ b/x-pack/plugins/spaces/server/types.ts @@ -6,6 +6,7 @@ */ import type { IRouter, RequestHandlerContext } from 'src/core/server'; + import type { LicensingApiRequestHandlerContext } from '../../licensing/server'; /** diff --git a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts index 5624a46b35a0e..d3d2763da0d78 100644 --- a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts +++ b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts @@ -5,19 +5,22 @@ * 2.0. */ -import { getSpacesUsageCollector, UsageData } from './spaces_usage_collector'; import * as Rx from 'rxjs'; -import { PluginsSetup } from '../plugin'; -import { KibanaFeature } from '../../../features/server'; -import { ILicense, LicensingPluginSetup } from '../../../licensing/server'; -import { UsageStats } from '../usage_stats'; -import { usageStatsClientMock } from '../usage_stats/usage_stats_client.mock'; -import { usageStatsServiceMock } from '../usage_stats/usage_stats_service.mock'; + import { elasticsearchServiceMock, pluginInitializerContextConfigMock, } from 'src/core/server/mocks'; -import { createCollectorFetchContextMock } from 'src/plugins/usage_collection/server/mocks'; + +import { createCollectorFetchContextMock } from '../../../../../src/plugins/usage_collection/server/mocks'; +import type { KibanaFeature } from '../../../features/server'; +import type { ILicense, LicensingPluginSetup } from '../../../licensing/server'; +import type { PluginsSetup } from '../plugin'; +import type { UsageStats } from '../usage_stats'; +import { usageStatsClientMock } from '../usage_stats/usage_stats_client.mock'; +import { usageStatsServiceMock } from '../usage_stats/usage_stats_service.mock'; +import type { UsageData } from './spaces_usage_collector'; +import { getSpacesUsageCollector } from './spaces_usage_collector'; interface SetupOpts { license?: Partial; diff --git a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts index 4df39ffb4fa39..1c0bc42bc3535 100644 --- a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts +++ b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts @@ -5,12 +5,17 @@ * 2.0. */ -import { ElasticsearchClient } from 'src/core/server'; +import type { Observable } from 'rxjs'; import { take } from 'rxjs/operators'; -import { CollectorFetchContext, UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { Observable } from 'rxjs'; -import { PluginsSetup } from '../plugin'; -import { UsageStats, UsageStatsServiceSetup } from '../usage_stats'; + +import type { ElasticsearchClient } from 'src/core/server'; +import type { + CollectorFetchContext, + UsageCollectionSetup, +} from 'src/plugins/usage_collection/server'; + +import type { PluginsSetup } from '../plugin'; +import type { UsageStats, UsageStatsServiceSetup } from '../usage_stats'; interface SpacesAggregationResponse { hits: { diff --git a/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.mock.ts b/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.mock.ts index d2e71cd390719..32380b1a21048 100644 --- a/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.mock.ts +++ b/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { UsageStatsClient } from './usage_stats_client'; +import type { UsageStatsClient } from './usage_stats_client'; const createUsageStatsClientMock = () => (({ diff --git a/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.test.ts b/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.test.ts index 747a45ae4b75f..6a56cb68d2a16 100644 --- a/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.test.ts +++ b/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.test.ts @@ -6,13 +6,16 @@ */ import { savedObjectsRepositoryMock } from 'src/core/server/mocks'; -import { SPACES_USAGE_STATS_TYPE, SPACES_USAGE_STATS_ID } from './constants'; -import { - UsageStatsClient, + +import { SPACES_USAGE_STATS_ID, SPACES_USAGE_STATS_TYPE } from './constants'; +import type { IncrementCopySavedObjectsOptions, IncrementResolveCopySavedObjectsErrorsOptions, +} from './usage_stats_client'; +import { COPY_STATS_PREFIX, RESOLVE_COPY_STATS_PREFIX, + UsageStatsClient, } from './usage_stats_client'; describe('UsageStatsClient', () => { diff --git a/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.ts b/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.ts index e1d72c6ad34b0..5093dd42b5bfa 100644 --- a/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.ts +++ b/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.ts @@ -5,10 +5,11 @@ * 2.0. */ -import { ISavedObjectsRepository, Headers } from 'src/core/server'; -import { SPACES_USAGE_STATS_TYPE, SPACES_USAGE_STATS_ID } from './constants'; -import { CopyOptions, ResolveConflictsOptions } from '../lib/copy_to_spaces/types'; -import { UsageStats } from './types'; +import type { Headers, ISavedObjectsRepository } from 'src/core/server'; + +import type { CopyOptions, ResolveConflictsOptions } from '../lib/copy_to_spaces/types'; +import { SPACES_USAGE_STATS_ID, SPACES_USAGE_STATS_TYPE } from './constants'; +import type { UsageStats } from './types'; interface BaseIncrementOptions { headers?: Headers; diff --git a/x-pack/plugins/spaces/server/usage_stats/usage_stats_service.mock.ts b/x-pack/plugins/spaces/server/usage_stats/usage_stats_service.mock.ts index ab20c369fe1a0..d0156393be10c 100644 --- a/x-pack/plugins/spaces/server/usage_stats/usage_stats_service.mock.ts +++ b/x-pack/plugins/spaces/server/usage_stats/usage_stats_service.mock.ts @@ -6,7 +6,7 @@ */ import { usageStatsClientMock } from './usage_stats_client.mock'; -import { UsageStatsServiceSetup } from './usage_stats_service'; +import type { UsageStatsServiceSetup } from './usage_stats_service'; const createSetupContractMock = (usageStatsClient = usageStatsClientMock.create()) => { const setupContract: jest.Mocked = { diff --git a/x-pack/plugins/spaces/server/usage_stats/usage_stats_service.test.ts b/x-pack/plugins/spaces/server/usage_stats/usage_stats_service.test.ts index 2e9be3901b19b..5ea4d9ddd747c 100644 --- a/x-pack/plugins/spaces/server/usage_stats/usage_stats_service.test.ts +++ b/x-pack/plugins/spaces/server/usage_stats/usage_stats_service.test.ts @@ -6,9 +6,10 @@ */ import { coreMock, loggingSystemMock } from 'src/core/server/mocks'; -import { UsageStatsService } from '.'; -import { UsageStatsClient } from './usage_stats_client'; + import { SPACES_USAGE_STATS_TYPE } from './constants'; +import { UsageStatsClient } from './usage_stats_client'; +import { UsageStatsService } from './usage_stats_service'; describe('UsageStatsService', () => { const mockLogger = loggingSystemMock.createLogger(); diff --git a/x-pack/plugins/spaces/server/usage_stats/usage_stats_service.ts b/x-pack/plugins/spaces/server/usage_stats/usage_stats_service.ts index 9f5af9d346a6e..baf59cae2a638 100644 --- a/x-pack/plugins/spaces/server/usage_stats/usage_stats_service.ts +++ b/x-pack/plugins/spaces/server/usage_stats/usage_stats_service.ts @@ -5,9 +5,10 @@ * 2.0. */ -import { Logger, CoreSetup } from '../../../../../src/core/server'; -import { UsageStatsClient } from './usage_stats_client'; +import type { CoreSetup, Logger } from 'src/core/server'; + import { SPACES_USAGE_STATS_TYPE } from './constants'; +import { UsageStatsClient } from './usage_stats_client'; export interface UsageStatsServiceSetup { getClient(): UsageStatsClient; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 42552d756313b..91134f078adc9 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3880,7 +3880,6 @@ "visTypeMetric.metricDescription": "計算結果を単独の数字として表示します。", "visTypeMetric.metricTitle": "メトリック", "visTypeMetric.params.color.useForLabel": "使用する色", - "visTypeMetric.params.percentageModeLabel": "百分率モード", "visTypeMetric.params.rangesTitle": "範囲", "visTypeMetric.params.settingsTitle": "設定", "visTypeMetric.params.showTitleLabel": "タイトルを表示", @@ -4565,7 +4564,6 @@ "visTypeVislib.controls.gaugeOptions.extendRangeTooltip": "範囲をデータの最高値に広げます。", "visTypeVislib.controls.gaugeOptions.gaugeTypeLabel": "ゲージタイプ", "visTypeVislib.controls.gaugeOptions.labelsTitle": "ラベル", - "visTypeVislib.controls.gaugeOptions.percentageModeLabel": "百分率モード", "visTypeVislib.controls.gaugeOptions.rangesTitle": "範囲", "visTypeVislib.controls.gaugeOptions.showLabelsLabel": "ラベルを表示", "visTypeVislib.controls.gaugeOptions.showLegendLabel": "凡例を表示", @@ -4579,7 +4577,6 @@ "visTypeVislib.controls.heatmapOptions.colorsNumberLabel": "色の数", "visTypeVislib.controls.heatmapOptions.labelsTitle": "ラベル", "visTypeVislib.controls.heatmapOptions.overwriteAutomaticColorLabel": "自動からーを上書きする", - "visTypeVislib.controls.heatmapOptions.percentageModeLabel": "百分率モード", "visTypeVislib.controls.heatmapOptions.rotateLabel": "回転", "visTypeVislib.controls.heatmapOptions.scaleToDataBoundsLabel": "データバウンドに合わせる", "visTypeVislib.controls.heatmapOptions.showLabelsTitle": "ラベルを表示", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index ee9f1aefeae9b..2e71909ea6b84 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3884,7 +3884,6 @@ "visTypeMetric.metricDescription": "将计算结果显示为单个数字。", "visTypeMetric.metricTitle": "指标", "visTypeMetric.params.color.useForLabel": "将颜色用于", - "visTypeMetric.params.percentageModeLabel": "百分比模式", "visTypeMetric.params.rangesTitle": "范围", "visTypeMetric.params.settingsTitle": "设置", "visTypeMetric.params.showTitleLabel": "显示标题", @@ -4570,7 +4569,6 @@ "visTypeVislib.controls.gaugeOptions.extendRangeTooltip": "将数据范围扩展到数据中的最大值。", "visTypeVislib.controls.gaugeOptions.gaugeTypeLabel": "仪表类型", "visTypeVislib.controls.gaugeOptions.labelsTitle": "标签", - "visTypeVislib.controls.gaugeOptions.percentageModeLabel": "百分比模式", "visTypeVislib.controls.gaugeOptions.rangesTitle": "范围", "visTypeVislib.controls.gaugeOptions.showLabelsLabel": "显示标签", "visTypeVislib.controls.gaugeOptions.showLegendLabel": "显示图例", @@ -4584,7 +4582,6 @@ "visTypeVislib.controls.heatmapOptions.colorsNumberLabel": "颜色个数", "visTypeVislib.controls.heatmapOptions.labelsTitle": "标签", "visTypeVislib.controls.heatmapOptions.overwriteAutomaticColorLabel": "覆盖自动配色", - "visTypeVislib.controls.heatmapOptions.percentageModeLabel": "百分比模式", "visTypeVislib.controls.heatmapOptions.rotateLabel": "旋转", "visTypeVislib.controls.heatmapOptions.scaleToDataBoundsLabel": "缩放到数据边界", "visTypeVislib.controls.heatmapOptions.showLabelsTitle": "显示标签", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.ts index b308e92b41ddc..57fb079d97299 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.ts @@ -7,7 +7,7 @@ import { HttpSetup } from 'kibana/public'; import { BASE_ACTION_API_PATH } from '../constants'; -import { ActionConnector, ActionConnectorWithoutId, ActionType } from '../../types'; +import type { ActionConnector, ActionConnectorWithoutId, ActionType } from '../../types'; import { ActionTypeExecutorResult } from '../../../../../plugins/actions/common'; export async function loadActionTypes({ http }: { http: HttpSetup }): Promise { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx index 1fc0533f89abc..ad33b5815261d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx @@ -124,3 +124,6 @@ export const AlertConditions = ); }; + +// eslint-disable-next-line import/no-default-export +export { AlertConditions as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx index 6cb1e5e976c20..6c2f5aecfcb7c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx @@ -60,3 +60,6 @@ export const AlertConditionsGroup = ({ ); }; + +// eslint-disable-next-line import/no-default-export +export { AlertConditionsGroup as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx index 7b96de2421cef..ac2648eea3e89 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx @@ -7,12 +7,6 @@ import { lazy } from 'react'; import { suspendedComponentWithProps } from '../../lib/suspended_component_with_props'; -export { - AlertConditions, - ActionGroupWithCondition, - AlertConditionsProps, -} from './alert_conditions'; -export { AlertConditionsGroup } from './alert_conditions_group'; export const AlertAdd = suspendedComponentWithProps(lazy(() => import('./alert_add'))); export const AlertEdit = suspendedComponentWithProps(lazy(() => import('./alert_edit'))); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx index e369be1c95ac4..c3dd76094b5a9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx @@ -7,12 +7,11 @@ import { lazy } from 'react'; import { suspendedComponentWithProps } from '../lib/suspended_component_with_props'; -export { - ActionGroupWithCondition, - AlertConditionsProps, - AlertConditions, - AlertConditionsGroup, -} from './alert_form'; + +export type { ActionGroupWithCondition, AlertConditionsProps } from './alert_form/alert_conditions'; + +export const AlertConditions = lazy(() => import('./alert_form/alert_conditions')); +export const AlertConditionsGroup = lazy(() => import('./alert_form/alert_conditions_group')); export const AlertAdd = suspendedComponentWithProps(lazy(() => import('./alert_form/alert_add'))); export const AlertEdit = suspendedComponentWithProps(lazy(() => import('./alert_form/alert_edit'))); diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/for_the_last.tsx b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/for_the_last.tsx index b0113cdd70451..7f2cb13aa979f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/for_the_last.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/for_the_last.tsx @@ -23,7 +23,7 @@ import { getTimeOptions } from '../lib/get_time_options'; import { ClosablePopoverTitle } from './components'; import { IErrorObject } from '../../types'; -interface ForLastExpressionProps { +export interface ForLastExpressionProps { timeWindowSize?: number; timeWindowUnit?: string; errors: IErrorObject; @@ -129,3 +129,6 @@ export const ForLastExpression = ({ ); }; + +// eslint-disable-next-line import/no-default-export +export { ForLastExpression as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/group_by_over.tsx b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/group_by_over.tsx index 37894e6f5be98..a6ec9d1b39665 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/group_by_over.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/group_by_over.tsx @@ -22,7 +22,7 @@ import { GroupByType } from '../types'; import { ClosablePopoverTitle } from './components'; import { IErrorObject } from '../../types'; -interface GroupByExpressionProps { +export interface GroupByExpressionProps { groupBy: string; errors: IErrorObject; onChangeSelectedTermSize: (selectedTermSize?: number) => void; @@ -208,3 +208,6 @@ export const GroupByExpression = ({ ); }; + +// eslint-disable-next-line import/no-default-export +export { GroupByExpression as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/index.ts index f975375adcb07..eb91164baf5bb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/index.ts @@ -5,9 +5,13 @@ * 2.0. */ -export { WhenExpression } from './when'; -export { OfExpression } from './of'; -export { GroupByExpression } from './group_by_over'; -export { ThresholdExpression } from './threshold'; -export { ForLastExpression } from './for_the_last'; -export { ValueExpression } from './value'; +import { lazy } from 'react'; +import { suspendedComponentWithProps } from '../../application/lib/suspended_component_with_props'; + +export const GroupByExpression = suspendedComponentWithProps(lazy(() => import('./group_by_over'))); +export const ForLastExpression = suspendedComponentWithProps(lazy(() => import('./for_the_last'))); +export const ValueExpression = suspendedComponentWithProps(lazy(() => import('./value'))); + +export const WhenExpression = suspendedComponentWithProps(lazy(() => import('./when'))); +export const OfExpression = suspendedComponentWithProps(lazy(() => import('./of'))); +export const ThresholdExpression = suspendedComponentWithProps(lazy(() => import('./threshold'))); diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/of.tsx b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/of.tsx index fbc6691455989..ccba6df04a2aa 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/of.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/of.tsx @@ -22,7 +22,7 @@ import { IErrorObject } from '../../types'; import { ClosablePopoverTitle } from './components'; import './of.scss'; -interface OfExpressionProps { +export interface OfExpressionProps { aggType: string; aggField?: string; errors: IErrorObject; @@ -152,3 +152,6 @@ export const OfExpression = ({ ); }; + +// eslint-disable-next-line import/no-default-export +export { OfExpression as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx index 7a3571feef2a8..d650162816f2b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx @@ -22,7 +22,7 @@ import { Comparator } from '../types'; import { IErrorObject } from '../../types'; import { ClosablePopoverTitle } from './components'; -interface ThresholdExpressionProps { +export interface ThresholdExpressionProps { thresholdComparator: string; errors: IErrorObject; onChangeSelectedThresholdComparator: (selectedThresholdComparator?: string) => void; @@ -175,3 +175,6 @@ export const ThresholdExpression = ({ ); }; + +// eslint-disable-next-line import/no-default-export +export { ThresholdExpression as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/value.tsx b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/value.tsx index cdf57136fe4b2..a66d91987e3b2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/value.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/value.tsx @@ -17,7 +17,7 @@ import { import { ClosablePopoverTitle } from './components'; import { IErrorObject } from '../../types'; -interface ValueExpressionProps { +export interface ValueExpressionProps { description: string; value: number; onChangeSelectedValue: (updatedValue: number) => void; @@ -100,3 +100,6 @@ export const ValueExpression = ({ ); }; + +// eslint-disable-next-line import/no-default-export +export { ValueExpression as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/when.tsx b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/when.tsx index af7529a475d68..907db8135ff7d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/when.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/when.tsx @@ -13,7 +13,7 @@ import { builtInAggregationTypes } from '../constants'; import { AggregationType } from '../types'; import { ClosablePopoverTitle } from './components'; -interface WhenExpressionProps { +export interface WhenExpressionProps { aggType: string; customAggTypesOptions?: { [key: string]: AggregationType }; onChangeSelectedAggType: (selectedAggType: string) => void; @@ -97,3 +97,6 @@ export const WhenExpression = ({ ); }; + +// eslint-disable-next-line import/no-default-export +export { WhenExpression as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index 5ccda3cb64112..969e279cea6cf 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -14,8 +14,8 @@ export { AlertConditionsGroup, ActionGroupWithCondition, } from './application/sections'; -export { ActionForm } from './application/sections/action_connector_form'; -export { + +export type { AlertAction, Alert, AlertTypeModel, @@ -29,11 +29,15 @@ export { ActionConnector, IErrorObject, AlertFlyoutCloseReason, + AlertTypeParams, } from './types'; + export { + ActionForm, ConnectorAddFlyout, ConnectorEditFlyout, } from './application/sections/action_connector_form'; + export { loadActionTypes } from './application/lib/action_connector_api'; export * from './common'; @@ -46,5 +50,4 @@ export * from './plugin'; export { TIME_UNITS } from './application/constants'; export { getTimeUnitLabel } from './common/lib/get_time_unit_label'; -export { ForLastExpression } from './common/expression_items/for_the_last'; -export { TriggersAndActionsUiServices } from '../public/application/app'; +export type { TriggersAndActionsUiServices } from '../public/application/app'; diff --git a/x-pack/plugins/triggers_actions_ui/public/mocks.ts b/x-pack/plugins/triggers_actions_ui/public/mocks.ts new file mode 100644 index 0000000000000..9666f8ab1b16b --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/mocks.ts @@ -0,0 +1,58 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ConnectorAddFlyoutProps } from './application/sections/action_connector_form/connector_add_flyout'; +import type { ConnectorEditFlyoutProps } from './application/sections/action_connector_form/connector_edit_flyout'; +import type { AlertAddProps } from './application/sections/alert_form/alert_add'; +import type { AlertEditProps } from './application/sections/alert_form/alert_edit'; +import type { TriggersAndActionsUIPublicPluginStart } from './plugin'; + +import { getAddConnectorFlyoutLazy } from './common/get_add_connector_flyout'; +import { getEditConnectorFlyoutLazy } from './common/get_edit_connector_flyout'; +import { getAddAlertFlyoutLazy } from './common/get_add_alert_flyout'; +import { getEditAlertFlyoutLazy } from './common/get_edit_alert_flyout'; + +import { TypeRegistry } from './application/type_registry'; +import { ActionTypeModel, AlertTypeModel } from './types'; + +function createStartMock(): TriggersAndActionsUIPublicPluginStart { + const actionTypeRegistry = new TypeRegistry(); + const alertTypeRegistry = new TypeRegistry(); + return { + actionTypeRegistry, + alertTypeRegistry, + getAddConnectorFlyout: (props: Omit) => { + return getAddConnectorFlyoutLazy({ ...props, actionTypeRegistry }); + }, + getEditConnectorFlyout: (props: Omit) => { + return getEditConnectorFlyoutLazy({ + ...props, + actionTypeRegistry, + }); + }, + getAddAlertFlyout: (props: Omit) => { + return getAddAlertFlyoutLazy({ + ...props, + actionTypeRegistry, + alertTypeRegistry, + }); + }, + getEditAlertFlyout: ( + props: Omit + ) => { + return getEditAlertFlyoutLazy({ + ...props, + actionTypeRegistry, + alertTypeRegistry, + }); + }, + }; +} + +export const triggersActionsUiMock = { + createStart: createStartMock, +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index 65ab69d515f34..e4477c6a8e60e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -12,7 +12,6 @@ import { ReactElement } from 'react'; import { FeaturesPluginStart } from '../../features/public'; import { KibanaFeature } from '../../features/common'; import { registerBuiltInActionTypes } from './application/components/builtin_action_types'; -import { ActionTypeModel, AlertTypeModel } from './types'; import { TypeRegistry } from './application/type_registry'; import { ManagementAppMountParams, @@ -26,14 +25,17 @@ import { ChartsPluginStart } from '../../../../src/plugins/charts/public'; import { PluginStartContract as AlertingStart } from '../../alerts/public'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; import { Storage } from '../../../../src/plugins/kibana_utils/public'; -import type { ConnectorAddFlyoutProps } from './application/sections/action_connector_form/connector_add_flyout'; -import type { ConnectorEditFlyoutProps } from './application/sections/action_connector_form/connector_edit_flyout'; + import { getAddConnectorFlyoutLazy } from './common/get_add_connector_flyout'; import { getEditConnectorFlyoutLazy } from './common/get_edit_connector_flyout'; import { getAddAlertFlyoutLazy } from './common/get_add_alert_flyout'; import { getEditAlertFlyoutLazy } from './common/get_edit_alert_flyout'; -import { AlertAddProps } from './application/sections/alert_form/alert_add'; -import { AlertEditProps } from './application/sections/alert_form/alert_edit'; + +import type { ActionTypeModel, AlertTypeModel } from './types'; +import type { ConnectorAddFlyoutProps } from './application/sections/action_connector_form/connector_add_flyout'; +import type { ConnectorEditFlyoutProps } from './application/sections/action_connector_form/connector_edit_flyout'; +import type { AlertAddProps } from './application/sections/alert_form/alert_add'; +import type { AlertEditProps } from './application/sections/alert_form/alert_edit'; export interface TriggersAndActionsUIPublicPluginSetup { actionTypeRegistry: TypeRegistry; diff --git a/x-pack/test/accessibility/apps/search_profiler.ts b/x-pack/test/accessibility/apps/search_profiler.ts index 7fba45175c831..6559d58be6298 100644 --- a/x-pack/test/accessibility/apps/search_profiler.ts +++ b/x-pack/test/accessibility/apps/search_profiler.ts @@ -15,7 +15,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const a11y = getService('a11y'); const flyout = getService('flyout'); - describe('Accessibility Search Profiler Editor', () => { + // FLAKY: https://github.com/elastic/kibana/issues/91939 + describe.skip('Accessibility Search Profiler Editor', () => { before(async () => { await PageObjects.common.navigateToApp('searchProfiler'); await a11y.testAppSnapshot(); diff --git a/x-pack/test/apm_api_integration/tests/services/top_services.ts b/x-pack/test/apm_api_integration/tests/services/top_services.ts index 3896bc1c6fabc..37f7b09e8b7d2 100644 --- a/x-pack/test/apm_api_integration/tests/services/top_services.ts +++ b/x-pack/test/apm_api_integration/tests/services/top_services.ts @@ -255,6 +255,49 @@ export default function ApiTest({ getService }: FtrProviderContext) { } ); + registry.when( + 'APM Services Overview with a basic license when data is loaded excluding transaction events', + { config: 'basic', archives: [archiveName] }, + () => { + it('includes services that only report metric data', async () => { + interface Response { + status: number; + body: APIReturnType<'GET /api/apm/services'>; + } + + const [unfilteredResponse, filteredResponse] = await Promise.all([ + supertest.get(`/api/apm/services?start=${start}&end=${end}`) as Promise, + supertest.get( + `/api/apm/services?start=${start}&end=${end}&kuery=${encodeURIComponent( + 'not (processor.event:transaction)' + )}` + ) as Promise, + ]); + + expect(unfilteredResponse.body.items.length).to.be.greaterThan(0); + + const unfilteredServiceNames = unfilteredResponse.body.items + .map((item) => item.serviceName) + .sort(); + + const filteredServiceNames = filteredResponse.body.items + .map((item) => item.serviceName) + .sort(); + + expect(unfilteredServiceNames).to.eql(filteredServiceNames); + + expect( + filteredResponse.body.items.every((item) => { + // make sure it did not query transaction data + return isEmpty(item.avgResponseTime); + }) + ).to.be(true); + + expect(filteredResponse.body.items.every((item) => !!item.agentName)).to.be(true); + }); + } + ); + registry.when( 'APM Services overview with a trial license when data is loaded', { config: 'trial', archives: [archiveName] }, diff --git a/x-pack/test/functional/apps/uptime/locations.ts b/x-pack/test/functional/apps/uptime/locations.ts index 15b4773373bf7..e3f1d04640754 100644 --- a/x-pack/test/functional/apps/uptime/locations.ts +++ b/x-pack/test/functional/apps/uptime/locations.ts @@ -39,7 +39,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await makeChecksWithStatus(es, LessAvailMonitor, 5, 2, 10000, {}, 'down'); }; - describe('Observer location', () => { + // FLAKY: https://github.com/elastic/kibana/issues/85208 + describe.skip('Observer location', () => { const start = '~ 15 minutes ago'; const end = 'now'; diff --git a/x-pack/test/functional/apps/uptime/overview.ts b/x-pack/test/functional/apps/uptime/overview.ts index 6c9eb24070d8f..b9c1767e4a8cf 100644 --- a/x-pack/test/functional/apps/uptime/overview.ts +++ b/x-pack/test/functional/apps/uptime/overview.ts @@ -15,7 +15,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const testSubjects = getService('testSubjects'); - describe('overview page', function () { + // FLAKY: https://github.com/elastic/kibana/issues/89072 + describe.skip('overview page', function () { const DEFAULT_DATE_START = 'Sep 10, 2019 @ 12:40:08.078'; const DEFAULT_DATE_END = 'Sep 11, 2019 @ 19:40:08.078';