diff --git a/examples/controls_example/public/app/control_group_renderer_examples/edit_example.tsx b/examples/controls_example/public/app/control_group_renderer_examples/edit_example.tsx index cf5a76956c36d..3d583dd64e499 100644 --- a/examples/controls_example/public/app/control_group_renderer_examples/edit_example.tsx +++ b/examples/controls_example/public/app/control_group_renderer_examples/edit_example.tsx @@ -70,7 +70,7 @@ export const EditExample = () => { INPUT_KEY, JSON.stringify({ ...controlGroupAPI.snapshotRuntimeState(), - disabledActions: controlGroupAPI.disabledActionIds.getValue(), // not part of runtime + disabledActions: controlGroupAPI.disabledActionIds$.getValue(), // not part of runtime }) ); diff --git a/examples/controls_example/public/app/react_control_example/react_control_example.tsx b/examples/controls_example/public/app/react_control_example/react_control_example.tsx index b6cb97720d79b..91e9d16a36205 100644 --- a/examples/controls_example/public/app/react_control_example/react_control_example.tsx +++ b/examples/controls_example/public/app/react_control_example/react_control_example.tsx @@ -119,9 +119,9 @@ export const ReactControlExample = ({ const children$ = new BehaviorSubject<{ [key: string]: unknown }>({}); return { - dataLoading: dataLoading$, + dataLoading$, unifiedSearchFilters$, - viewMode: viewMode$, + viewMode$, filters$, query$, timeRange$, @@ -149,7 +149,7 @@ export const ReactControlExample = ({ useEffect(() => { const subscription = combineCompatibleChildrenApis( dashboardApi, - 'dataLoading', + 'dataLoading$', apiPublishesDataLoading, undefined, // flatten method @@ -249,7 +249,7 @@ export const ReactControlExample = ({ if (!controlGroupApi) { return; } - const subscription = controlGroupApi.unsavedChanges.subscribe((nextUnsavedChanges) => { + const subscription = controlGroupApi.unsavedChanges$.subscribe((nextUnsavedChanges) => { if (!nextUnsavedChanges) { clearControlGroupRuntimeState(); setUnsavedChanges(undefined); diff --git a/examples/embeddable_examples/public/app/presentation_container_example/components/presentation_container_example.tsx b/examples/embeddable_examples/public/app/presentation_container_example/components/presentation_container_example.tsx index 567c915752d4f..2b6cb42c71dd1 100644 --- a/examples/embeddable_examples/public/app/presentation_container_example/components/presentation_container_example.tsx +++ b/examples/embeddable_examples/public/app/presentation_container_example/components/presentation_container_example.tsx @@ -37,7 +37,7 @@ export const PresentationContainerExample = ({ uiActions }: { uiActions: UiActio }, [cleanUp]); const [dataLoading, panels, timeRange] = useBatchedPublishingSubjects( - pageApi.dataLoading, + pageApi.dataLoading$, componentApi.panels$, pageApi.timeRange$ ); @@ -95,7 +95,7 @@ export const PresentationContainerExample = ({ uiActions }: { uiActions: UiActio diff --git a/examples/embeddable_examples/public/app/presentation_container_example/components/top_nav.tsx b/examples/embeddable_examples/public/app/presentation_container_example/components/top_nav.tsx index 92ec3afa259e5..7e288491610e2 100644 --- a/examples/embeddable_examples/public/app/presentation_container_example/components/top_nav.tsx +++ b/examples/embeddable_examples/public/app/presentation_container_example/components/top_nav.tsx @@ -15,7 +15,7 @@ import { PublishesUnsavedChanges } from '@kbn/presentation-publishing'; interface Props { onSave: () => Promise; resetUnsavedChanges: () => void; - unsavedChanges$: PublishesUnsavedChanges['unsavedChanges']; + unsavedChanges$: PublishesUnsavedChanges['unsavedChanges$']; } export function TopNav(props: Props) { diff --git a/examples/embeddable_examples/public/app/presentation_container_example/page_api.ts b/examples/embeddable_examples/public/app/presentation_container_example/page_api.ts index 1e06c5cb62b5b..ce0a9f20ceba7 100644 --- a/examples/embeddable_examples/public/app/presentation_container_example/page_api.ts +++ b/examples/embeddable_examples/public/app/presentation_container_example/page_api.ts @@ -81,7 +81,7 @@ export function getPageApi() { boolean | undefined >( { children$ }, - 'dataLoading', + 'dataLoading$', apiPublishesDataLoading, undefined, // flatten method @@ -193,7 +193,7 @@ export function getPageApi() { }, canRemovePanels: () => true, children$, - dataLoading: dataLoading$, + dataLoading$, executionContext: { type: 'presentationContainerEmbeddableExample', }, @@ -210,7 +210,7 @@ export function getPageApi() { children$.next(omit(children$.value, id)); }, saveNotification$, - viewMode: new BehaviorSubject('edit'), + viewMode$: new BehaviorSubject('edit'), /** * return last saved embeddable state */ @@ -252,7 +252,7 @@ export function getPageApi() { return true; }, timeRange$, - unsavedChanges: unsavedChanges$ as PublishingSubject, + unsavedChanges$: unsavedChanges$ as PublishingSubject, } as PageApi, }; } diff --git a/examples/embeddable_examples/public/app/render_examples.tsx b/examples/embeddable_examples/public/app/render_examples.tsx index d1c966400d6b6..8dcf6a128d4cd 100644 --- a/examples/embeddable_examples/public/app/render_examples.tsx +++ b/examples/embeddable_examples/public/app/render_examples.tsx @@ -47,7 +47,7 @@ export const RenderExamples = () => { const [api, setApi] = useState(null); const [hidePanelChrome, setHidePanelChrome] = useState(false); const [dataLoading, timeRange] = useBatchedOptionalPublishingSubjects( - api?.dataLoading, + api?.dataLoading$, parentApi.timeRange$ ); diff --git a/examples/embeddable_examples/public/app/state_management_example/state_management_example.tsx b/examples/embeddable_examples/public/app/state_management_example/state_management_example.tsx index 18ff194769b3d..8676b467ae221 100644 --- a/examples/embeddable_examples/public/app/state_management_example/state_management_example.tsx +++ b/examples/embeddable_examples/public/app/state_management_example/state_management_example.tsx @@ -40,10 +40,10 @@ export const StateManagementExample = ({ uiActions }: { uiActions: UiActionsStar const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); useEffect(() => { - if (!bookApi || !bookApi.unsavedChanges) { + if (!bookApi || !bookApi.unsavedChanges$) { return; } - const subscription = bookApi.unsavedChanges.subscribe((unsavedChanges) => { + const subscription = bookApi.unsavedChanges$.subscribe((unsavedChanges) => { setHasUnsavedChanges(unsavedChanges !== undefined); unsavedChangesSessionStorage.save(unsavedChanges ?? {}); }); @@ -158,7 +158,7 @@ export const StateManagementExample = ({ uiActions }: { uiActions: UiActionsStar return unsavedChangesSessionStorage.load(); }, saveNotification$, - viewMode: new BehaviorSubject('edit'), + viewMode$: new BehaviorSubject('edit'), }; }} onApiAvailable={(api) => { diff --git a/examples/embeddable_examples/public/react_embeddables/data_table/data_table_queries.ts b/examples/embeddable_examples/public/react_embeddables/data_table/data_table_queries.ts index c65dcd76ef883..4688882db3515 100644 --- a/examples/embeddable_examples/public/react_embeddables/data_table/data_table_queries.ts +++ b/examples/embeddable_examples/public/react_embeddables/data_table/data_table_queries.ts @@ -60,7 +60,7 @@ export const initializeDataTableQueries = async ( dataView$.next(defaultDataView); return; } - const dataViewSubscription = dataViewProvider.dataViews.subscribe((dataViews) => { + const dataViewSubscription = dataViewProvider.dataViews$.subscribe((dataViews) => { dataView$.next(dataViews?.[0] ?? defaultDataView); }); return () => dataViewSubscription.unsubscribe(); diff --git a/examples/embeddable_examples/public/react_embeddables/data_table/data_table_react_embeddable.tsx b/examples/embeddable_examples/public/react_embeddables/data_table/data_table_react_embeddable.tsx index 647c3884b4d0f..bd98877116f9f 100644 --- a/examples/embeddable_examples/public/react_embeddables/data_table/data_table_react_embeddable.tsx +++ b/examples/embeddable_examples/public/react_embeddables/data_table/data_table_react_embeddable.tsx @@ -17,7 +17,7 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import { initializeTimeRange, - initializeTitles, + initializeTitleManager, useBatchedPublishingSubjects, } from '@kbn/presentation-publishing'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; @@ -40,8 +40,8 @@ export const getDataTableFactory = ( buildEmbeddable: async (state, buildApi, uuid, parentApi) => { const storage = new Storage(localStorage); const timeRange = initializeTimeRange(state); - const queryLoading$ = new BehaviorSubject(true); - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const dataLoading$ = new BehaviorSubject(true); + const titleManager = initializeTitleManager(state); const allServices: UnifiedDataTableProps['services'] = { ...services, storage, @@ -53,18 +53,18 @@ export const getDataTableFactory = ( const api = buildApi( { ...timeRange.api, - ...titlesApi, - dataLoading: queryLoading$, + ...titleManager.api, + dataLoading$, serializeState: () => { return { - rawState: { ...serializeTitles(), ...timeRange.serialize() }, + rawState: { ...titleManager.serialize(), ...timeRange.serialize() }, }; }, }, - { ...titleComparators, ...timeRange.comparators } + { ...titleManager.comparators, ...timeRange.comparators } ); - const queryService = await initializeDataTableQueries(services, api, queryLoading$); + const queryService = await initializeDataTableQueries(services, api, dataLoading$); // Create the React Embeddable component return { @@ -74,7 +74,7 @@ export const getDataTableFactory = ( const [fields, rows, loading, dataView] = useBatchedPublishingSubjects( queryService.fields$, queryService.rows$, - queryLoading$, + dataLoading$, queryService.dataView$ ); diff --git a/examples/embeddable_examples/public/react_embeddables/eui_markdown/eui_markdown_react_embeddable.tsx b/examples/embeddable_examples/public/react_embeddables/eui_markdown/eui_markdown_react_embeddable.tsx index 2ad9cd639a223..9e8b5265f889d 100644 --- a/examples/embeddable_examples/public/react_embeddables/eui_markdown/eui_markdown_react_embeddable.tsx +++ b/examples/embeddable_examples/public/react_embeddables/eui_markdown/eui_markdown_react_embeddable.tsx @@ -12,7 +12,7 @@ import { css } from '@emotion/react'; import { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { i18n } from '@kbn/i18n'; import { - initializeTitles, + initializeTitleManager, useInheritedViewMode, useStateFromPublishingSubject, } from '@kbn/presentation-publishing'; @@ -41,7 +41,7 @@ export const markdownEmbeddableFactory: ReactEmbeddableFactory< /** * initialize state (source of truth) */ - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const titleManager = initializeTitleManager(state); const content$ = new BehaviorSubject(state.content); /** @@ -51,11 +51,11 @@ export const markdownEmbeddableFactory: ReactEmbeddableFactory< */ const api = buildApi( { - ...titlesApi, + ...titleManager.api, serializeState: () => { return { rawState: { - ...serializeTitles(), + ...titleManager.serialize(), content: content$.getValue(), }, }; @@ -70,7 +70,7 @@ export const markdownEmbeddableFactory: ReactEmbeddableFactory< */ { content: [content$, (value) => content$.next(value)], - ...titleComparators, + ...titleManager.comparators, } ); diff --git a/examples/embeddable_examples/public/react_embeddables/field_list/field_list_react_embeddable.tsx b/examples/embeddable_examples/public/react_embeddables/field_list/field_list_react_embeddable.tsx index c6b13d2419971..f116b55937599 100644 --- a/examples/embeddable_examples/public/react_embeddables/field_list/field_list_react_embeddable.tsx +++ b/examples/embeddable_examples/public/react_embeddables/field_list/field_list_react_embeddable.tsx @@ -15,7 +15,7 @@ import { DataView } from '@kbn/data-views-plugin/common'; import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/public'; import { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { i18n } from '@kbn/i18n'; -import { initializeTitles, useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; +import { initializeTitleManager, useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; import { LazyDataViewPicker, withSuspense } from '@kbn/presentation-util-plugin/public'; import { euiThemeVars } from '@kbn/ui-theme'; import { @@ -70,7 +70,7 @@ export const getFieldListFactory = ( }, buildEmbeddable: async (initialState, buildApi) => { const subscriptions = new Subscription(); - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(initialState); + const titleManager = initializeTitleManager(initialState); // set up data views const [allDataViews, defaultDataViewId] = await Promise.all([ @@ -106,8 +106,8 @@ export const getFieldListFactory = ( const api = buildApi( { - ...titlesApi, - dataViews: dataViews$, + ...titleManager.api, + dataViews$, selectedFields: selectedFieldNames$, serializeState: () => { const dataViewId = selectedDataViewId$.getValue(); @@ -122,7 +122,7 @@ export const getFieldListFactory = ( : []; return { rawState: { - ...serializeTitles(), + ...titleManager.serialize(), // here we skip serializing the dataViewId, because the reference contains that information. selectedFieldNames: selectedFieldNames$.getValue(), }, @@ -131,7 +131,7 @@ export const getFieldListFactory = ( }, }, { - ...titleComparators, + ...titleManager.comparators, dataViewId: [selectedDataViewId$, (value) => selectedDataViewId$.next(value)], selectedFieldNames: [ selectedFieldNames$, diff --git a/examples/embeddable_examples/public/react_embeddables/saved_book/saved_book_react_embeddable.tsx b/examples/embeddable_examples/public/react_embeddables/saved_book/saved_book_react_embeddable.tsx index 0353055481e8b..23bada8fc328d 100644 --- a/examples/embeddable_examples/public/react_embeddables/saved_book/saved_book_react_embeddable.tsx +++ b/examples/embeddable_examples/public/react_embeddables/saved_book/saved_book_react_embeddable.tsx @@ -15,7 +15,7 @@ import { i18n } from '@kbn/i18n'; import { apiHasParentApi, getUnchangingComparator, - initializeTitles, + initializeTitleManager, SerializedTitles, SerializedPanelState, useBatchedPublishingSubjects, @@ -74,7 +74,7 @@ export const getSavedBookEmbeddableFactory = (core: CoreStart) => { }; }, buildEmbeddable: async (state, buildApi) => { - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const titleManager = initializeTitleManager(state); const bookAttributesManager = stateManagerFromAttributes(state); const isByReference = Boolean(state.savedBookId); @@ -83,21 +83,21 @@ export const getSavedBookEmbeddableFactory = (core: CoreStart) => { // if this book is currently by reference, we serialize the reference only. const bookByReferenceState: BookByReferenceSerializedState = { savedBookId: newId ?? state.savedBookId!, - ...serializeTitles(), + ...titleManager.serialize(), }; return { rawState: bookByReferenceState }; } // if this book is currently by value, we serialize the entire state. const bookByValueState: BookByValueSerializedState = { attributes: serializeBookAttributes(bookAttributesManager), - ...serializeTitles(), + ...titleManager.serialize(), }; return { rawState: bookByValueState }; }; const api = buildApi( { - ...titlesApi, + ...titleManager.api, onEdit: async () => { openSavedBookEditor({ attributesManager: bookAttributesManager, @@ -145,7 +145,7 @@ export const getSavedBookEmbeddableFactory = (core: CoreStart) => { { savedBookId: getUnchangingComparator(), // saved book id will not change over the lifetime of the embeddable. ...bookAttributesManager.comparators, - ...titleComparators, + ...titleManager.comparators, } ); diff --git a/examples/embeddable_examples/public/react_embeddables/search/search_react_embeddable.tsx b/examples/embeddable_examples/public/react_embeddables/search/search_react_embeddable.tsx index 4196ae965c3eb..9db97ad338510 100644 --- a/examples/embeddable_examples/public/react_embeddables/search/search_react_embeddable.tsx +++ b/examples/embeddable_examples/public/react_embeddables/search/search_react_embeddable.tsx @@ -50,9 +50,9 @@ export const getSearchEmbeddableFactory = (services: Services) => { const api = buildApi( { ...timeRange.api, - blockingError: blockingError$, - dataViews: dataViews$, - dataLoading: dataLoading$, + blockingError$, + dataViews$, + dataLoading$, serializeState: () => { return { rawState: { diff --git a/examples/grid_example/public/app.tsx b/examples/grid_example/public/app.tsx index 21f05d613ad1a..30bfa74d041a9 100644 --- a/examples/grid_example/public/app.tsx +++ b/examples/grid_example/public/app.tsx @@ -72,8 +72,8 @@ export const GridExample = ({ const mockDashboardApi = useMockDashboardApi({ savedState: savedState.current }); const [viewMode, expandedPanelId] = useBatchedPublishingSubjects( - mockDashboardApi.viewMode, - mockDashboardApi.expandedPanelId + mockDashboardApi.viewMode$, + mockDashboardApi.expandedPanelId$ ); useEffect(() => { @@ -244,7 +244,7 @@ export const GridExample = ({ ]} idSelected={viewMode} onChange={(id) => { - mockDashboardApi.viewMode.next(id); + mockDashboardApi.viewMode$.next(id); }} /> diff --git a/examples/grid_example/public/use_mock_dashboard_api.tsx b/examples/grid_example/public/use_mock_dashboard_api.tsx index 5b26b6c7eca02..5268c65184b6b 100644 --- a/examples/grid_example/public/use_mock_dashboard_api.tsx +++ b/examples/grid_example/public/use_mock_dashboard_api.tsx @@ -48,10 +48,10 @@ export const useMockDashboardApi = ({ }), filters$: new BehaviorSubject([]), query$: new BehaviorSubject(''), - viewMode: new BehaviorSubject('edit'), + viewMode$: new BehaviorSubject('edit'), panels$, rows$: new BehaviorSubject(savedState.rows), - expandedPanelId: expandedPanelId$, + expandedPanelId$, expandPanel: (id: string) => { if (expandedPanelId$.getValue()) { expandedPanelId$.next(undefined); diff --git a/examples/portable_dashboards_example/public/dual_dashboards_example.tsx b/examples/portable_dashboards_example/public/dual_dashboards_example.tsx index 2e4fcbd130e23..83c2b9ef40ff2 100644 --- a/examples/portable_dashboards_example/public/dual_dashboards_example.tsx +++ b/examples/portable_dashboards_example/public/dual_dashboards_example.tsx @@ -27,7 +27,7 @@ export const DualDashboardsExample = () => { const [secondDashboardApi, setSecondDashboardApi] = useState(); const ButtonControls = ({ dashboardApi }: { dashboardApi: DashboardApi }) => { - const viewMode = useStateFromPublishingSubject(dashboardApi.viewMode); + const viewMode = useStateFromPublishingSubject(dashboardApi.viewMode$); return ( { }), hasTimeRange: () => true, parentApi: { - viewMode: new BehaviorSubject('view'), + viewMode$: new BehaviorSubject('view'), }, }, } as EmbeddableApiContext; diff --git a/src/platform/packages/shared/presentation/presentation_containers/interfaces/panel_management.ts b/src/platform/packages/shared/presentation/presentation_containers/interfaces/panel_management.ts index 2989632a66e40..c5bd8bc311461 100644 --- a/src/platform/packages/shared/presentation/presentation_containers/interfaces/panel_management.ts +++ b/src/platform/packages/shared/presentation/presentation_containers/interfaces/panel_management.ts @@ -21,7 +21,7 @@ export const apiCanDuplicatePanels = ( export interface CanExpandPanels { expandPanel: (panelId: string) => void; - expandedPanelId: PublishingSubject; + expandedPanelId$: PublishingSubject; } export const apiCanExpandPanels = (unknownApi: unknown | null): unknownApi is CanExpandPanels => { diff --git a/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.test.ts b/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.test.ts index cd03db5431bcc..ef44deb51f43b 100644 --- a/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.test.ts +++ b/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.test.ts @@ -13,11 +13,11 @@ import { waitFor } from '@testing-library/react'; describe('childrenUnsavedChanges$', () => { const child1Api = { - unsavedChanges: new BehaviorSubject(undefined), + unsavedChanges$: new BehaviorSubject(undefined), resetUnsavedChanges: () => true, }; const child2Api = { - unsavedChanges: new BehaviorSubject(undefined), + unsavedChanges$: new BehaviorSubject(undefined), resetUnsavedChanges: () => true, }; const children$ = new BehaviorSubject<{ [key: string]: unknown }>({}); @@ -25,8 +25,8 @@ describe('childrenUnsavedChanges$', () => { beforeEach(() => { onFireMock.mockReset(); - child1Api.unsavedChanges.next(undefined); - child2Api.unsavedChanges.next(undefined); + child1Api.unsavedChanges$.next(undefined); + child2Api.unsavedChanges$.next(undefined); children$.next({ child1: child1Api, child2: child2Api, @@ -61,7 +61,7 @@ describe('childrenUnsavedChanges$', () => { } ); - child1Api.unsavedChanges.next({ + child1Api.unsavedChanges$.next({ key1: 'modified value', }); @@ -98,7 +98,7 @@ describe('childrenUnsavedChanges$', () => { children$.next({ ...children$.value, child3: { - unsavedChanges: new BehaviorSubject({ key1: 'modified value' }), + unsavedChanges$: new BehaviorSubject({ key1: 'modified value' }), resetUnsavedChanges: () => true, }, }); diff --git a/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.ts b/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.ts index bbb16b9bb88a4..2e1fdd53c622c 100644 --- a/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.ts +++ b/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.ts @@ -33,7 +33,7 @@ export function childrenUnsavedChanges$(children$: PresentationContainer['childr ? of([]) : combineLatest( childrenThatPublishUnsavedChanges.map(([childId, child]) => - child.unsavedChanges.pipe(map((unsavedChanges) => ({ childId, unsavedChanges }))) + child.unsavedChanges$.pipe(map((unsavedChanges) => ({ childId, unsavedChanges }))) ) ); }), diff --git a/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.test.ts b/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.test.ts index de8c09ec3c7c5..5576942dece82 100644 --- a/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.test.ts +++ b/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.test.ts @@ -43,14 +43,14 @@ describe('unsavedChanges api', () => { }); test('should have no unsaved changes after initialization', () => { - expect(api?.unsavedChanges.value).toBeUndefined(); + expect(api?.unsavedChanges$.value).toBeUndefined(); }); test('should have unsaved changes when state changes', async () => { key1$.next('modified key1 value'); await waitFor( () => - expect(api?.unsavedChanges.value).toEqual({ + expect(api?.unsavedChanges$.value).toEqual({ key1: 'modified key1 value', }), { @@ -61,28 +61,28 @@ describe('unsavedChanges api', () => { test('should have no unsaved changes after save', async () => { key1$.next('modified key1 value'); - await waitFor(() => expect(api?.unsavedChanges.value).not.toBeUndefined(), { + await waitFor(() => expect(api?.unsavedChanges$.value).not.toBeUndefined(), { interval: COMPARATOR_SUBJECTS_DEBOUNCE + 1, }); // trigger save parentApi.saveNotification$.next(); - await waitFor(() => expect(api?.unsavedChanges.value).toBeUndefined(), { + await waitFor(() => expect(api?.unsavedChanges$.value).toBeUndefined(), { interval: COMPARATOR_SUBJECTS_DEBOUNCE + 1, }); }); test('should have no unsaved changes after reset', async () => { key1$.next('modified key1 value'); - await waitFor(() => expect(api?.unsavedChanges.value).not.toBeUndefined(), { + await waitFor(() => expect(api?.unsavedChanges$.value).not.toBeUndefined(), { interval: COMPARATOR_SUBJECTS_DEBOUNCE + 1, }); // trigger reset api?.resetUnsavedChanges(); - await waitFor(() => expect(api?.unsavedChanges.value).toBeUndefined(), { + await waitFor(() => expect(api?.unsavedChanges$.value).toBeUndefined(), { interval: COMPARATOR_SUBJECTS_DEBOUNCE + 1, }); }); diff --git a/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.ts b/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.ts index e28ff77f88e00..84ec84ef601c2 100644 --- a/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.ts +++ b/src/platform/packages/shared/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.ts @@ -62,7 +62,7 @@ export const initializeUnsavedChanges = ( comparatorKeys.push(key); } - const unsavedChanges = new BehaviorSubject | undefined>( + const unsavedChanges$ = new BehaviorSubject | undefined>( runComparators( comparators, comparatorKeys, @@ -84,7 +84,7 @@ export const initializeUnsavedChanges = ( combineLatestWith(lastSavedState$) ) .subscribe(([latestState, lastSavedState]) => { - unsavedChanges.next( + unsavedChanges$.next( runComparators(comparators, comparatorKeys, lastSavedState, latestState) ); }) @@ -92,7 +92,7 @@ export const initializeUnsavedChanges = ( return { api: { - unsavedChanges, + unsavedChanges$, resetUnsavedChanges: () => { const lastSaved = lastSavedState$.getValue(); diff --git a/src/platform/packages/shared/presentation/presentation_publishing/index.ts b/src/platform/packages/shared/presentation/presentation_publishing/index.ts index e24a2d26ba403..955260a486b89 100644 --- a/src/platform/packages/shared/presentation/presentation_publishing/index.ts +++ b/src/platform/packages/shared/presentation/presentation_publishing/index.ts @@ -127,24 +127,25 @@ export { type ViewMode, } from './interfaces/publishes_view_mode'; export { - apiPublishesPanelDescription, - apiPublishesWritablePanelDescription, - getPanelDescription, - type PublishesPanelDescription, - type PublishesWritablePanelDescription, -} from './interfaces/titles/publishes_panel_description'; -export { - apiPublishesPanelTitle, - apiPublishesWritablePanelTitle, - getPanelTitle, - type PublishesPanelTitle, - type PublishesWritablePanelTitle, -} from './interfaces/titles/publishes_panel_title'; -export { - initializeTitles, + apiPublishesDescription, + apiPublishesWritableDescription, + getDescription, + type PublishesDescription, + type PublishesWritableDescription, +} from './interfaces/titles/publishes_description'; +export { + apiPublishesTitle, + apiPublishesWritableTitle, + getTitle, + type PublishesTitle, + type PublishesWritableTitle, +} from './interfaces/titles/publishes_title'; +export { + initializeTitleManager, stateHasTitles, + type TitlesApi, type SerializedTitles, -} from './interfaces/titles/titles_api'; +} from './interfaces/titles/title_manager'; export { useBatchedOptionalPublishingSubjects, useBatchedPublishingSubjects, diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/can_access_view_mode.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/can_access_view_mode.ts index 2a61e8a01449f..047baa318acff 100644 --- a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/can_access_view_mode.ts +++ b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/can_access_view_mode.ts @@ -30,16 +30,16 @@ export const apiCanAccessViewMode = (api: unknown): api is CanAccessViewMode => * parent has a view mode, we consider the APIs version the source of truth. */ export const getInheritedViewMode = (api?: CanAccessViewMode) => { - if (apiPublishesViewMode(api)) return api.viewMode.getValue(); + if (apiPublishesViewMode(api)) return api.viewMode$.getValue(); if (apiHasParentApi(api) && apiPublishesViewMode(api.parentApi)) { - return api.parentApi.viewMode.getValue(); + return api.parentApi.viewMode$.getValue(); } }; export const getViewModeSubject = (api?: CanAccessViewMode) => { - if (apiPublishesViewMode(api)) return api.viewMode; + if (apiPublishesViewMode(api)) return api.viewMode$; if (apiHasParentApi(api) && apiPublishesViewMode(api.parentApi)) { - return api.parentApi.viewMode; + return api.parentApi.viewMode$; } }; diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_blocking_error.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_blocking_error.ts index d4dd8f9ab2c59..3cb514a76f17d 100644 --- a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_blocking_error.ts +++ b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_blocking_error.ts @@ -10,15 +10,17 @@ import { PublishingSubject } from '../publishing_subject'; export interface PublishesBlockingError { - blockingError: PublishingSubject; + blockingError$: PublishingSubject; } export const apiPublishesBlockingError = ( unknownApi: null | unknown ): unknownApi is PublishesBlockingError => { - return Boolean(unknownApi && (unknownApi as PublishesBlockingError)?.blockingError !== undefined); + return Boolean( + unknownApi && (unknownApi as PublishesBlockingError)?.blockingError$ !== undefined + ); }; export function hasBlockingError(api: unknown) { - return apiPublishesBlockingError(api) && api.blockingError?.value !== undefined; + return apiPublishesBlockingError(api) && api.blockingError$?.value !== undefined; } diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_data_loading.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_data_loading.ts index c01db9dcb9955..f60a9593057aa 100644 --- a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_data_loading.ts +++ b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_data_loading.ts @@ -10,11 +10,11 @@ import { PublishingSubject } from '../publishing_subject'; export interface PublishesDataLoading { - dataLoading: PublishingSubject; + dataLoading$: PublishingSubject; } export const apiPublishesDataLoading = ( unknownApi: null | unknown ): unknownApi is PublishesDataLoading => { - return Boolean(unknownApi && (unknownApi as PublishesDataLoading)?.dataLoading !== undefined); + return Boolean(unknownApi && (unknownApi as PublishesDataLoading)?.dataLoading$ !== undefined); }; diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_data_views.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_data_views.ts index 50649b1764c32..b3a9da716d6be 100644 --- a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_data_views.ts +++ b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_data_views.ts @@ -11,7 +11,7 @@ import { DataView } from '@kbn/data-views-plugin/common'; import { PublishingSubject } from '../publishing_subject'; export interface PublishesDataViews { - dataViews: PublishingSubject; + dataViews$: PublishingSubject; } export type PublishesWritableDataViews = PublishesDataViews & { @@ -21,5 +21,5 @@ export type PublishesWritableDataViews = PublishesDataViews & { export const apiPublishesDataViews = ( unknownApi: null | unknown ): unknownApi is PublishesDataViews => { - return Boolean(unknownApi && (unknownApi as PublishesDataViews)?.dataViews !== undefined); + return Boolean(unknownApi && (unknownApi as PublishesDataViews)?.dataViews$ !== undefined); }; diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_disabled_action_ids.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_disabled_action_ids.ts index 70864cc31a06d..c05084d26a87a 100644 --- a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_disabled_action_ids.ts +++ b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_disabled_action_ids.ts @@ -10,7 +10,7 @@ import { PublishingSubject } from '../publishing_subject'; export interface PublishesDisabledActionIds { - disabledActionIds: PublishingSubject; + disabledActionIds$: PublishingSubject; setDisabledActionIds: (ids: string[] | undefined) => void; getAllTriggersDisabled?: () => boolean; } @@ -24,7 +24,7 @@ export const apiPublishesDisabledActionIds = ( ): unknownApi is PublishesDisabledActionIds => { return Boolean( unknownApi && - (unknownApi as PublishesDisabledActionIds)?.disabledActionIds !== undefined && + (unknownApi as PublishesDisabledActionIds)?.disabledActionIds$ !== undefined && typeof (unknownApi as PublishesDisabledActionIds)?.setDisabledActionIds === 'function' ); }; diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_saved_object_id.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_saved_object_id.ts index 8860ade9e7dca..7d2b1bc759cd8 100644 --- a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_saved_object_id.ts +++ b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_saved_object_id.ts @@ -13,7 +13,7 @@ import { PublishingSubject } from '../publishing_subject'; * This API publishes a saved object id which can be used to determine which saved object this API is linked to. */ export interface PublishesSavedObjectId { - savedObjectId: PublishingSubject; + savedObjectId$: PublishingSubject; } /** @@ -22,5 +22,7 @@ export interface PublishesSavedObjectId { export const apiPublishesSavedObjectId = ( unknownApi: null | unknown ): unknownApi is PublishesSavedObjectId => { - return Boolean(unknownApi && (unknownApi as PublishesSavedObjectId)?.savedObjectId !== undefined); + return Boolean( + unknownApi && (unknownApi as PublishesSavedObjectId)?.savedObjectId$ !== undefined + ); }; diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_unsaved_changes.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_unsaved_changes.ts index e9b4adbec5384..919cc8c00ce80 100644 --- a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_unsaved_changes.ts +++ b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_unsaved_changes.ts @@ -10,14 +10,14 @@ import { PublishingSubject } from '../publishing_subject'; export interface PublishesUnsavedChanges { - unsavedChanges: PublishingSubject | undefined>; + unsavedChanges$: PublishingSubject | undefined>; resetUnsavedChanges: () => boolean; } export const apiPublishesUnsavedChanges = (api: unknown): api is PublishesUnsavedChanges => { return Boolean( api && - (api as PublishesUnsavedChanges).unsavedChanges && + (api as PublishesUnsavedChanges).unsavedChanges$ && (api as PublishesUnsavedChanges).resetUnsavedChanges ); }; diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_view_mode.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_view_mode.ts index 1b6495683bd68..7ee47d0a50dd5 100644 --- a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_view_mode.ts +++ b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/publishes_view_mode.ts @@ -16,7 +16,7 @@ export type ViewMode = 'view' | 'edit' | 'print' | 'preview'; * visibility of components. */ export interface PublishesViewMode { - viewMode: PublishingSubject; + viewMode$: PublishingSubject; } /** @@ -33,7 +33,7 @@ export type PublishesWritableViewMode = PublishesViewMode & { export const apiPublishesViewMode = ( unknownApi: null | unknown ): unknownApi is PublishesViewMode => { - return Boolean(unknownApi && (unknownApi as PublishesViewMode)?.viewMode !== undefined); + return Boolean(unknownApi && (unknownApi as PublishesViewMode)?.viewMode$ !== undefined); }; export const apiPublishesWritableViewMode = ( diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_panel_description.test.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_description.test.ts similarity index 51% rename from src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_panel_description.test.ts rename to src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_description.test.ts index 4ac7a08e6d0d6..047a9916785d3 100644 --- a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_panel_description.test.ts +++ b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_description.test.ts @@ -8,30 +8,30 @@ */ import { BehaviorSubject } from 'rxjs'; -import { getPanelDescription } from './publishes_panel_description'; +import { getDescription } from './publishes_description'; -describe('getPanelDescription', () => { +describe('getDescription', () => { test('should return default description when description is undefined', () => { const api = { - panelDescription: new BehaviorSubject(undefined), - defaultPanelDescription: new BehaviorSubject('default description'), + description$: new BehaviorSubject(undefined), + defaultDescription$: new BehaviorSubject('default description'), }; - expect(getPanelDescription(api)).toBe('default description'); + expect(getDescription(api)).toBe('default description'); }); test('should return empty description when description is empty string', () => { const api = { - panelDescription: new BehaviorSubject(''), - defaultPanelDescription: new BehaviorSubject('default description'), + description$: new BehaviorSubject(''), + defaultDescription$: new BehaviorSubject('default description'), }; - expect(getPanelDescription(api)).toBe(''); + expect(getDescription(api)).toBe(''); }); test('should return description when description is provided', () => { const api = { - panelDescription: new BehaviorSubject('custom description'), - defaultPanelDescription: new BehaviorSubject('default description'), + description$: new BehaviorSubject('custom description'), + defaultDescription$: new BehaviorSubject('default description'), }; - expect(getPanelDescription(api)).toBe('custom description'); + expect(getDescription(api)).toBe('custom description'); }); }); diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_description.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_description.ts new file mode 100644 index 0000000000000..651f324da7899 --- /dev/null +++ b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_description.ts @@ -0,0 +1,39 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { PublishingSubject } from '../../publishing_subject'; + +export interface PublishesDescription { + description$: PublishingSubject; + defaultDescription$?: PublishingSubject; +} + +export function getDescription(api: Partial): string | undefined { + return api.description$?.value ?? api.defaultDescription$?.value; +} + +export type PublishesWritableDescription = PublishesDescription & { + setDescription: (newTitle: string | undefined) => void; +}; + +export const apiPublishesDescription = ( + unknownApi: null | unknown +): unknownApi is PublishesDescription => { + return Boolean(unknownApi && (unknownApi as PublishesDescription)?.description$ !== undefined); +}; + +export const apiPublishesWritableDescription = ( + unknownApi: null | unknown +): unknownApi is PublishesWritableDescription => { + return ( + apiPublishesDescription(unknownApi) && + (unknownApi as PublishesWritableDescription).setDescription !== undefined && + typeof (unknownApi as PublishesWritableDescription).setDescription === 'function' + ); +}; diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_panel_description.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_panel_description.ts deleted file mode 100644 index d908b2795bdc4..0000000000000 --- a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_panel_description.ts +++ /dev/null @@ -1,41 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PublishingSubject } from '../../publishing_subject'; - -export interface PublishesPanelDescription { - panelDescription: PublishingSubject; - defaultPanelDescription?: PublishingSubject; -} - -export function getPanelDescription(api: Partial): string | undefined { - return api.panelDescription?.value ?? api.defaultPanelDescription?.value; -} - -export type PublishesWritablePanelDescription = PublishesPanelDescription & { - setPanelDescription: (newTitle: string | undefined) => void; -}; - -export const apiPublishesPanelDescription = ( - unknownApi: null | unknown -): unknownApi is PublishesPanelDescription => { - return Boolean( - unknownApi && (unknownApi as PublishesPanelDescription)?.panelDescription !== undefined - ); -}; - -export const apiPublishesWritablePanelDescription = ( - unknownApi: null | unknown -): unknownApi is PublishesWritablePanelDescription => { - return ( - apiPublishesPanelDescription(unknownApi) && - (unknownApi as PublishesWritablePanelDescription).setPanelDescription !== undefined && - typeof (unknownApi as PublishesWritablePanelDescription).setPanelDescription === 'function' - ); -}; diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_panel_title.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_panel_title.ts deleted file mode 100644 index 2c5e3f06a310e..0000000000000 --- a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_panel_title.ts +++ /dev/null @@ -1,47 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { PublishingSubject } from '../../publishing_subject'; - -export interface PublishesPanelTitle { - panelTitle: PublishingSubject; - hidePanelTitle: PublishingSubject; - defaultPanelTitle?: PublishingSubject; -} - -export function getPanelTitle(api: Partial): string | undefined { - return api.panelTitle?.value ?? api.defaultPanelTitle?.value; -} - -export type PublishesWritablePanelTitle = PublishesPanelTitle & { - setPanelTitle: (newTitle: string | undefined) => void; - setHidePanelTitle: (hide: boolean | undefined) => void; -}; - -export const apiPublishesPanelTitle = ( - unknownApi: null | unknown -): unknownApi is PublishesPanelTitle => { - return Boolean( - unknownApi && - (unknownApi as PublishesPanelTitle)?.panelTitle !== undefined && - (unknownApi as PublishesPanelTitle)?.hidePanelTitle !== undefined - ); -}; - -export const apiPublishesWritablePanelTitle = ( - unknownApi: null | unknown -): unknownApi is PublishesWritablePanelTitle => { - return ( - apiPublishesPanelTitle(unknownApi) && - (unknownApi as PublishesWritablePanelTitle).setPanelTitle !== undefined && - (typeof (unknownApi as PublishesWritablePanelTitle).setPanelTitle === 'function' && - (unknownApi as PublishesWritablePanelTitle).setHidePanelTitle) !== undefined && - typeof (unknownApi as PublishesWritablePanelTitle).setHidePanelTitle === 'function' - ); -}; diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_panel_title.test.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_title.test.ts similarity index 56% rename from src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_panel_title.test.ts rename to src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_title.test.ts index 7838cabe61171..dff2935ce94b6 100644 --- a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_panel_title.test.ts +++ b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_title.test.ts @@ -8,30 +8,30 @@ */ import { BehaviorSubject } from 'rxjs'; -import { getPanelTitle } from './publishes_panel_title'; +import { getTitle } from './publishes_title'; describe('getPanelTitle', () => { test('should return default title when title is undefined', () => { const api = { - panelTitle: new BehaviorSubject(undefined), - defaultPanelTitle: new BehaviorSubject('default title'), + title$: new BehaviorSubject(undefined), + defaultTitle$: new BehaviorSubject('default title'), }; - expect(getPanelTitle(api)).toBe('default title'); + expect(getTitle(api)).toBe('default title'); }); test('should return empty title when title is empty string', () => { const api = { - panelTitle: new BehaviorSubject(''), - defaultPanelTitle: new BehaviorSubject('default title'), + title$: new BehaviorSubject(''), + defaultTitle$: new BehaviorSubject('default title'), }; - expect(getPanelTitle(api)).toBe(''); + expect(getTitle(api)).toBe(''); }); test('should return title when title is provided', () => { const api = { - panelTitle: new BehaviorSubject('custom title'), - defaultPanelTitle: new BehaviorSubject('default title'), + title$: new BehaviorSubject('custom title'), + defaultTitle$: new BehaviorSubject('default title'), }; - expect(getPanelTitle(api)).toBe('custom title'); + expect(getTitle(api)).toBe('custom title'); }); }); diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_title.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_title.ts new file mode 100644 index 0000000000000..7889ba7b3641f --- /dev/null +++ b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/publishes_title.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { PublishingSubject } from '../../publishing_subject'; + +export interface PublishesTitle { + title$: PublishingSubject; + hideTitle$: PublishingSubject; + defaultTitle$?: PublishingSubject; +} + +export function getTitle(api: Partial): string | undefined { + return api.title$?.value ?? api.defaultTitle$?.value; +} + +export type PublishesWritableTitle = PublishesTitle & { + setTitle: (newTitle: string | undefined) => void; + setHideTitle: (hide: boolean | undefined) => void; +}; + +export const apiPublishesTitle = (unknownApi: null | unknown): unknownApi is PublishesTitle => { + return Boolean( + unknownApi && + (unknownApi as PublishesTitle)?.title$ !== undefined && + (unknownApi as PublishesTitle)?.hideTitle$ !== undefined + ); +}; + +export const apiPublishesWritableTitle = ( + unknownApi: null | unknown +): unknownApi is PublishesWritableTitle => { + return ( + apiPublishesTitle(unknownApi) && + (unknownApi as PublishesWritableTitle).setTitle !== undefined && + (typeof (unknownApi as PublishesWritableTitle).setTitle === 'function' && + (unknownApi as PublishesWritableTitle).setHideTitle) !== undefined && + typeof (unknownApi as PublishesWritableTitle).setHideTitle === 'function' + ); +}; diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/title_manager.test.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/title_manager.test.ts new file mode 100644 index 0000000000000..53f53ed379d48 --- /dev/null +++ b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/title_manager.test.ts @@ -0,0 +1,67 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { initializeTitleManager, SerializedTitles } from './title_manager'; + +describe('titles api', () => { + const rawState: SerializedTitles = { + title: 'very cool title', + description: 'less cool description', + hidePanelTitles: false, + }; + + it('should initialize publishing subjects with the provided rawState', () => { + const { api } = initializeTitleManager(rawState); + expect(api.title$.value).toBe(rawState.title); + expect(api.description$.value).toBe(rawState.description); + expect(api.hideTitle$.value).toBe(rawState.hidePanelTitles); + }); + + it('should update publishing subject values when set functions are called', () => { + const { api } = initializeTitleManager(rawState); + + api.setTitle('even cooler title'); + api.setDescription('super uncool description'); + api.setHideTitle(true); + + expect(api.title$.value).toEqual('even cooler title'); + expect(api.description$.value).toEqual('super uncool description'); + expect(api.hideTitle$.value).toBe(true); + }); + + it('should correctly serialize current state', () => { + const titleManager = initializeTitleManager(rawState); + titleManager.api.setTitle('UH OH, A TITLE'); + + const serializedTitles = titleManager.serialize(); + expect(serializedTitles).toMatchInlineSnapshot(` + Object { + "description": "less cool description", + "hidePanelTitles": false, + "title": "UH OH, A TITLE", + } + `); + }); + + it('should return the correct set of comparators', () => { + const { comparators } = initializeTitleManager(rawState); + + expect(comparators.title).toBeDefined(); + expect(comparators.description).toBeDefined(); + expect(comparators.hidePanelTitles).toBeDefined(); + }); + + it('should correctly compare hidePanelTitles with custom comparator', () => { + const { comparators } = initializeTitleManager(rawState); + + expect(comparators.hidePanelTitles![2]!(true, false)).toBe(false); + expect(comparators.hidePanelTitles![2]!(undefined, false)).toBe(true); + expect(comparators.hidePanelTitles![2]!(true, undefined)).toBe(false); + }); +}); diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/title_manager.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/title_manager.ts new file mode 100644 index 0000000000000..71ea93456cea8 --- /dev/null +++ b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/title_manager.ts @@ -0,0 +1,72 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { BehaviorSubject } from 'rxjs'; +import { StateComparators } from '../../comparators'; +import { PublishesWritableDescription } from './publishes_description'; +import { PublishesWritableTitle } from './publishes_title'; + +export interface SerializedTitles { + title?: string; + description?: string; + hidePanelTitles?: boolean; +} + +export const stateHasTitles = (state: unknown): state is SerializedTitles => { + return ( + (state as SerializedTitles)?.title !== undefined || + (state as SerializedTitles)?.description !== undefined || + (state as SerializedTitles)?.hidePanelTitles !== undefined + ); +}; + +export interface TitlesApi extends PublishesWritableTitle, PublishesWritableDescription {} + +export const initializeTitleManager = ( + rawState: SerializedTitles +): { + api: TitlesApi; + comparators: StateComparators; + serialize: () => SerializedTitles; +} => { + const title$ = new BehaviorSubject(rawState.title); + const description$ = new BehaviorSubject(rawState.description); + const hideTitle$ = new BehaviorSubject(rawState.hidePanelTitles); + + const setTitle = (value: string | undefined) => { + if (value !== title$.value) title$.next(value); + }; + const setHideTitle = (value: boolean | undefined) => { + if (value !== hideTitle$.value) hideTitle$.next(value); + }; + const setDescription = (value: string | undefined) => { + if (value !== description$.value) description$.next(value); + }; + + return { + api: { + title$, + hideTitle$, + setTitle, + setHideTitle, + description$, + setDescription, + }, + comparators: { + title: [title$, setTitle], + description: [description$, setDescription], + hidePanelTitles: [hideTitle$, setHideTitle, (a, b) => Boolean(a) === Boolean(b)], + } as StateComparators, + serialize: () => ({ + title: title$.value, + hidePanelTitles: hideTitle$.value, + description: description$.value, + }), + }; +}; diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/titles_api.test.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/titles_api.test.ts deleted file mode 100644 index d7978971b4971..0000000000000 --- a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/titles_api.test.ts +++ /dev/null @@ -1,67 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { initializeTitles, SerializedTitles } from './titles_api'; - -describe('titles api', () => { - const rawState: SerializedTitles = { - title: 'very cool title', - description: 'less cool description', - hidePanelTitles: false, - }; - - it('should initialize publishing subjects with the provided rawState', () => { - const { titlesApi } = initializeTitles(rawState); - expect(titlesApi.panelTitle.value).toBe(rawState.title); - expect(titlesApi.panelDescription.value).toBe(rawState.description); - expect(titlesApi.hidePanelTitle.value).toBe(rawState.hidePanelTitles); - }); - - it('should update publishing subject values when set functions are called', () => { - const { titlesApi } = initializeTitles(rawState); - - titlesApi.setPanelTitle('even cooler title'); - titlesApi.setPanelDescription('super uncool description'); - titlesApi.setHidePanelTitle(true); - - expect(titlesApi.panelTitle.value).toEqual('even cooler title'); - expect(titlesApi.panelDescription.value).toEqual('super uncool description'); - expect(titlesApi.hidePanelTitle.value).toBe(true); - }); - - it('should correctly serialize current state', () => { - const { serializeTitles, titlesApi } = initializeTitles(rawState); - titlesApi.setPanelTitle('UH OH, A TITLE'); - - const serializedTitles = serializeTitles(); - expect(serializedTitles).toMatchInlineSnapshot(` - Object { - "description": "less cool description", - "hidePanelTitles": false, - "title": "UH OH, A TITLE", - } - `); - }); - - it('should return the correct set of comparators', () => { - const { titleComparators } = initializeTitles(rawState); - - expect(titleComparators.title).toBeDefined(); - expect(titleComparators.description).toBeDefined(); - expect(titleComparators.hidePanelTitles).toBeDefined(); - }); - - it('should correctly compare hidePanelTitles with custom comparator', () => { - const { titleComparators } = initializeTitles(rawState); - - expect(titleComparators.hidePanelTitles![2]!(true, false)).toBe(false); - expect(titleComparators.hidePanelTitles![2]!(undefined, false)).toBe(true); - expect(titleComparators.hidePanelTitles![2]!(true, undefined)).toBe(false); - }); -}); diff --git a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/titles_api.ts b/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/titles_api.ts deleted file mode 100644 index 34723605e4813..0000000000000 --- a/src/platform/packages/shared/presentation/presentation_publishing/interfaces/titles/titles_api.ts +++ /dev/null @@ -1,76 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { BehaviorSubject } from 'rxjs'; -import { StateComparators } from '../../comparators'; -import { PublishesWritablePanelDescription } from './publishes_panel_description'; -import { PublishesWritablePanelTitle } from './publishes_panel_title'; - -export interface SerializedTitles { - title?: string; - description?: string; - hidePanelTitles?: boolean; -} - -export const stateHasTitles = (state: unknown): state is SerializedTitles => { - return ( - (state as SerializedTitles)?.title !== undefined || - (state as SerializedTitles)?.description !== undefined || - (state as SerializedTitles)?.hidePanelTitles !== undefined - ); -}; - -export interface TitlesApi extends PublishesWritablePanelTitle, PublishesWritablePanelDescription {} - -export const initializeTitles = ( - rawState: SerializedTitles -): { - titlesApi: TitlesApi; - titleComparators: StateComparators; - serializeTitles: () => SerializedTitles; -} => { - const panelTitle = new BehaviorSubject(rawState.title); - const panelDescription = new BehaviorSubject(rawState.description); - const hidePanelTitle = new BehaviorSubject(rawState.hidePanelTitles); - - const setPanelTitle = (value: string | undefined) => { - if (value !== panelTitle.value) panelTitle.next(value); - }; - const setHidePanelTitle = (value: boolean | undefined) => { - if (value !== hidePanelTitle.value) hidePanelTitle.next(value); - }; - const setPanelDescription = (value: string | undefined) => { - if (value !== panelDescription.value) panelDescription.next(value); - }; - - const titleComparators: StateComparators = { - title: [panelTitle, setPanelTitle], - description: [panelDescription, setPanelDescription], - hidePanelTitles: [hidePanelTitle, setHidePanelTitle, (a, b) => Boolean(a) === Boolean(b)], - }; - - const titlesApi = { - panelTitle, - hidePanelTitle, - setPanelTitle, - setHidePanelTitle, - panelDescription, - setPanelDescription, - }; - - return { - serializeTitles: () => ({ - title: panelTitle.value, - hidePanelTitles: hidePanelTitle.value, - description: panelDescription.value, - }), - titleComparators, - titlesApi, - }; -}; diff --git a/src/platform/plugins/private/image_embeddable/public/image_embeddable/get_image_embeddable_factory.tsx b/src/platform/plugins/private/image_embeddable/public/image_embeddable/get_image_embeddable_factory.tsx index c3eaaf5e32152..20759fb76c24e 100644 --- a/src/platform/plugins/private/image_embeddable/public/image_embeddable/get_image_embeddable_factory.tsx +++ b/src/platform/plugins/private/image_embeddable/public/image_embeddable/get_image_embeddable_factory.tsx @@ -15,7 +15,7 @@ import { EmbeddableEnhancedPluginStart } from '@kbn/embeddable-enhanced-plugin/p import { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { i18n } from '@kbn/i18n'; import { PresentationContainer } from '@kbn/presentation-containers'; -import { getUnchangingComparator, initializeTitles } from '@kbn/presentation-publishing'; +import { getUnchangingComparator, initializeTitleManager } from '@kbn/presentation-publishing'; import { IMAGE_CLICK_TRIGGER } from '../actions'; import { openImageEditor } from '../components/image_editor/open_image_editor'; @@ -38,11 +38,11 @@ export const getImageEmbeddableFactory = ({ type: IMAGE_EMBEDDABLE_TYPE, deserializeState: (state) => state.rawState, buildEmbeddable: async (initialState, buildApi, uuid) => { - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(initialState); + const titleManager = initializeTitleManager(initialState); const dynamicActionsApi = embeddableEnhanced?.initializeReactEmbeddableDynamicActions( uuid, - () => titlesApi.panelTitle.getValue(), + () => titleManager.api.title$.getValue(), initialState ); // if it is provided, start the dynamic actions manager @@ -54,9 +54,9 @@ export const getImageEmbeddableFactory = ({ const embeddable = buildApi( { - ...titlesApi, + ...titleManager.api, ...(dynamicActionsApi?.dynamicActionsApi ?? {}), - dataLoading: dataLoading$, + dataLoading$, supportedTriggers: () => [IMAGE_CLICK_TRIGGER], onEdit: async () => { try { @@ -77,7 +77,7 @@ export const getImageEmbeddableFactory = ({ serializeState: () => { return { rawState: { - ...serializeTitles(), + ...titleManager.serialize(), ...(dynamicActionsApi?.serializeDynamicActions() ?? {}), imageConfig: imageConfig$.getValue(), }, @@ -85,7 +85,7 @@ export const getImageEmbeddableFactory = ({ }, }, { - ...titleComparators, + ...titleManager.comparators, ...(dynamicActionsApi?.dynamicActionsComparator ?? { enhancements: getUnchangingComparator(), }), diff --git a/src/platform/plugins/private/links/public/actions/compatibility_check.ts b/src/platform/plugins/private/links/public/actions/compatibility_check.ts index 472d5096c8fde..ce81921939061 100644 --- a/src/platform/plugins/private/links/public/actions/compatibility_check.ts +++ b/src/platform/plugins/private/links/public/actions/compatibility_check.ts @@ -9,8 +9,8 @@ import { apiIsPresentationContainer } from '@kbn/presentation-containers'; import { - apiPublishesPanelDescription, - apiPublishesPanelTitle, + apiPublishesDescription, + apiPublishesTitle, apiPublishesSavedObjectId, } from '@kbn/presentation-publishing'; import { LinksParentApi } from '../types'; @@ -18,5 +18,5 @@ import { LinksParentApi } from '../types'; export const isParentApiCompatible = (parentApi: unknown): parentApi is LinksParentApi => apiIsPresentationContainer(parentApi) && apiPublishesSavedObjectId(parentApi) && - apiPublishesPanelTitle(parentApi) && - apiPublishesPanelDescription(parentApi); + apiPublishesTitle(parentApi) && + apiPublishesDescription(parentApi); diff --git a/src/platform/plugins/private/links/public/components/dashboard_link/dashboard_link_component.test.tsx b/src/platform/plugins/private/links/public/components/dashboard_link/dashboard_link_component.test.tsx index b245870a8757a..2d225b4760ac1 100644 --- a/src/platform/plugins/private/links/public/components/dashboard_link/dashboard_link_component.test.tsx +++ b/src/platform/plugins/private/links/public/components/dashboard_link/dashboard_link_component.test.tsx @@ -263,8 +263,8 @@ describe('Dashboard link component', () => { test('current dashboard is not a clickable href', async () => { const parentApi = createMockLinksParent({}); - parentApi.savedObjectId = new BehaviorSubject('123'); - parentApi.panelTitle = new BehaviorSubject('current dashboard'); + parentApi.savedObjectId$ = new BehaviorSubject('123'); + parentApi.title$ = new BehaviorSubject('current dashboard'); render( { test('current dashboard title updates when parent changes', async () => { const parentApi = { ...createMockLinksParent({}), - panelTitle: new BehaviorSubject('old title'), - panelDescription: new BehaviorSubject('old description'), - savedObjectId: new BehaviorSubject('123'), + title$: new BehaviorSubject('old title'), + description$: new BehaviorSubject('old description'), + savedObjectId$: new BehaviorSubject('123'), }; const { rerender } = render( @@ -328,7 +328,7 @@ describe('Dashboard link component', () => { ); expect(await screen.findByTestId('dashboardLink--bar')).toHaveTextContent('old title'); - parentApi.panelTitle.next('new title'); + parentApi.title$.next('new title'); rerender( { test('can override link label for the current dashboard', async () => { const customLabel = 'my new label for the current dashboard'; const parentApi = createMockLinksParent({}); - parentApi.savedObjectId = new BehaviorSubject('123'); + parentApi.savedObjectId$ = new BehaviorSubject('123'); render( ((resolve) => { diff --git a/src/platform/plugins/private/links/public/embeddable/links_embeddable.test.tsx b/src/platform/plugins/private/links/public/embeddable/links_embeddable.test.tsx index 3c949da0a3277..b4c9d601d2d6c 100644 --- a/src/platform/plugins/private/links/public/embeddable/links_embeddable.test.tsx +++ b/src/platform/plugins/private/links/public/embeddable/links_embeddable.test.tsx @@ -218,8 +218,8 @@ describe('getLinksEmbeddableFactory', () => { references: [], }); expect(await api.canUnlinkFromLibrary()).toBe(true); - expect(api.defaultPanelTitle!.value).toBe('links 001'); - expect(api.defaultPanelDescription!.value).toBe('some links'); + expect(api.defaultTitle$?.value).toBe('links 001'); + expect(api.defaultDescription$?.value).toBe('some links'); }); }); diff --git a/src/platform/plugins/private/links/public/embeddable/links_embeddable.tsx b/src/platform/plugins/private/links/public/embeddable/links_embeddable.tsx index 36bf21cb1a65b..86f06f6b475b4 100644 --- a/src/platform/plugins/private/links/public/embeddable/links_embeddable.tsx +++ b/src/platform/plugins/private/links/public/embeddable/links_embeddable.tsx @@ -15,7 +15,7 @@ import { EuiListGroup, EuiPanel } from '@elastic/eui'; import { PanelIncompatibleError, ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { SerializedTitles, - initializeTitles, + initializeTitleManager, SerializedPanelState, useBatchedOptionalPublishingSubjects, } from '@kbn/presentation-publishing'; @@ -94,25 +94,25 @@ export const getLinksEmbeddableFactory = () => { }; }, buildEmbeddable: async (state, buildApi, uuid, parentApi) => { - const error$ = new BehaviorSubject(state.error); - if (!isParentApiCompatible(parentApi)) error$.next(new PanelIncompatibleError()); + const blockingError$ = new BehaviorSubject(state.error); + if (!isParentApiCompatible(parentApi)) blockingError$.next(new PanelIncompatibleError()); const links$ = new BehaviorSubject(state.links); const layout$ = new BehaviorSubject(state.layout); - const defaultPanelTitle = new BehaviorSubject(state.defaultPanelTitle); - const defaultPanelDescription = new BehaviorSubject( + const defaultTitle$ = new BehaviorSubject(state.defaultPanelTitle); + const defaultDescription$ = new BehaviorSubject( state.defaultPanelDescription ); const savedObjectId$ = new BehaviorSubject(state.savedObjectId); const isByReference = Boolean(state.savedObjectId); - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const titleManager = initializeTitleManager(state); const serializeLinksState = (byReference: boolean, newId?: string) => { if (byReference) { const linksByReferenceState: LinksByReferenceSerializedState = { savedObjectId: newId ?? state.savedObjectId!, - ...serializeTitles(), + ...titleManager.serialize(), }; return { rawState: linksByReferenceState, references: [] }; } @@ -120,22 +120,22 @@ export const getLinksEmbeddableFactory = () => { const { attributes, references } = serializeLinksAttributes(runtimeState); const linksByValueState: LinksByValueSerializedState = { attributes, - ...serializeTitles(), + ...titleManager.serialize(), }; return { rawState: linksByValueState, references }; }; const api = buildApi( { - ...titlesApi, - blockingError: error$, - defaultPanelTitle, - defaultPanelDescription, - isEditingEnabled: () => Boolean(error$.value === undefined), + ...titleManager.api, + blockingError$, + defaultTitle$, + defaultDescription$, + isEditingEnabled: () => Boolean(blockingError$.value === undefined), getTypeDisplayName: () => DISPLAY_NAME, serializeState: () => serializeLinksState(isByReference), saveToLibrary: async (newTitle: string) => { - defaultPanelTitle.next(newTitle); + defaultTitle$.next(newTitle); const runtimeState = api.snapshotRuntimeState(); const { attributes, references } = serializeLinksAttributes(runtimeState); const { @@ -196,26 +196,23 @@ export const getLinksEmbeddableFactory = () => { } links$.next(newState.links); layout$.next(newState.layout); - defaultPanelTitle.next(newState.defaultPanelTitle); - defaultPanelDescription.next(newState.defaultPanelDescription); + defaultTitle$.next(newState.defaultPanelTitle); + defaultDescription$.next(newState.defaultPanelDescription); }, }, { - ...titleComparators, + ...titleManager.comparators, links: [links$, (nextLinks?: ResolvedLink[]) => links$.next(nextLinks ?? [])], layout: [ layout$, (nextLayout?: LinksLayoutType) => layout$.next(nextLayout ?? LINKS_VERTICAL_LAYOUT), ], - error: [error$, (nextError?: Error) => error$.next(nextError)], + error: [blockingError$, (nextError?: Error) => blockingError$.next(nextError)], defaultPanelDescription: [ - defaultPanelDescription, - (nextDescription?: string) => defaultPanelDescription.next(nextDescription), - ], - defaultPanelTitle: [ - defaultPanelTitle, - (nextTitle?: string) => defaultPanelTitle.next(nextTitle), + defaultDescription$, + (nextDescription?: string) => defaultDescription$.next(nextDescription), ], + defaultPanelTitle: [defaultTitle$, (nextTitle?: string) => defaultTitle$.next(nextTitle)], savedObjectId: [savedObjectId$, (val) => savedObjectId$.next(val)], } ); diff --git a/src/platform/plugins/private/links/public/mocks.ts b/src/platform/plugins/private/links/public/mocks.ts index dc4f5d57d479f..cc5738a945461 100644 --- a/src/platform/plugins/private/links/public/mocks.ts +++ b/src/platform/plugins/private/links/public/mocks.ts @@ -58,9 +58,9 @@ export const getMockLinksParentApi = ( to: 'now', }), timeslice$: new BehaviorSubject<[number, number] | undefined>(undefined), - savedObjectId: new BehaviorSubject('999'), - hidePanelTitle: new BehaviorSubject(false), - panelTitle: new BehaviorSubject('My Dashboard'), - panelDescription: new BehaviorSubject(''), + savedObjectId$: new BehaviorSubject('999'), + hideTitle$: new BehaviorSubject(false), + title$: new BehaviorSubject('My Dashboard'), + description$: new BehaviorSubject(''), getSerializedStateForChild: () => ({ rawState: serializedState, references }), }); diff --git a/src/platform/plugins/private/links/public/types.ts b/src/platform/plugins/private/links/public/types.ts index 90d545ac1cc1c..aaa5e34faa236 100644 --- a/src/platform/plugins/private/links/public/types.ts +++ b/src/platform/plugins/private/links/public/types.ts @@ -11,8 +11,8 @@ import { HasEditCapabilities, HasLibraryTransforms, HasType, - PublishesPanelDescription, - PublishesPanelTitle, + PublishesDescription, + PublishesTitle, PublishesSavedObjectId, PublishesUnifiedSearch, SerializedTitles, @@ -31,8 +31,8 @@ export type LinksParentApi = PresentationContainer & HasType & HasSerializedChildState & PublishesSavedObjectId & - PublishesPanelTitle & - PublishesPanelDescription & + PublishesTitle & + PublishesDescription & PublishesUnifiedSearch & { locator?: Pick, 'navigate' | 'getRedirectUrl'>; }; diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_action.test.ts b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_action.test.ts index fa37896a6f427..edeffb789c125 100644 --- a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_action.test.ts +++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_action.test.ts @@ -25,8 +25,8 @@ describe('Customize panel action', () => { context = { embeddable: { parentApi: {}, - viewMode: new BehaviorSubject('edit'), - dataViews: new BehaviorSubject(undefined), + viewMode$: new BehaviorSubject('edit'), + dataViews$: new BehaviorSubject(undefined), }, }; }); @@ -36,7 +36,7 @@ describe('Customize panel action', () => { }); it('is compatible in view mode when API exposes writable unified search', async () => { - (context.embeddable as PublishesViewMode).viewMode = new BehaviorSubject('view'); + (context.embeddable as PublishesViewMode).viewMode$ = new BehaviorSubject('view'); context.embeddable.timeRange$ = new BehaviorSubject({ from: 'now-15m', to: 'now', diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_action.tsx b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_action.tsx index 66ac3ff49b99e..24bc452274916 100644 --- a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_action.tsx +++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_action.tsx @@ -13,15 +13,15 @@ import { apiCanAccessViewMode, apiPublishesDataViews, apiPublishesUnifiedSearch, - apiPublishesPanelTitle, + apiPublishesTitle, CanAccessViewMode, EmbeddableApiContext, getInheritedViewMode, HasParentApi, PublishesDataViews, PublishesWritableUnifiedSearch, - PublishesWritablePanelDescription, - PublishesWritablePanelTitle, + PublishesWritableDescription, + PublishesWritableTitle, PublishesUnifiedSearch, } from '@kbn/presentation-publishing'; import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; @@ -32,15 +32,15 @@ export type CustomizePanelActionApi = CanAccessViewMode & Partial< PublishesDataViews & PublishesWritableUnifiedSearch & - PublishesWritablePanelDescription & - PublishesWritablePanelTitle & + PublishesWritableDescription & + PublishesWritableTitle & HasParentApi> >; export const isApiCompatibleWithCustomizePanelAction = ( api: unknown | null ): api is CustomizePanelActionApi => - apiCanAccessViewMode(api) && (apiPublishesDataViews(api) || apiPublishesPanelTitle(api)); + apiCanAccessViewMode(api) && (apiPublishesDataViews(api) || apiPublishesTitle(api)); export class CustomizePanelAction implements Action { public type = ACTION_CUSTOMIZE_PANEL; diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_editor.test.tsx b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_editor.test.tsx index 4257fdd5f5964..91be0c249be51 100644 --- a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_editor.test.tsx +++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_editor.test.tsx @@ -25,20 +25,20 @@ describe('customize panel editor', () => { let setDescription: (description?: string) => void; beforeEach(() => { - const titleSubject = new BehaviorSubject(undefined); - setTitle = jest.fn((title) => titleSubject.next(title)); - const descriptionSubject = new BehaviorSubject(undefined); - setDescription = jest.fn((description) => descriptionSubject.next(description)); - const viewMode = new BehaviorSubject('edit'); - setViewMode = jest.fn((nextViewMode) => viewMode.next(nextViewMode)); + const title$ = new BehaviorSubject(undefined); + setTitle = jest.fn((title) => title$.next(title)); + const description$ = new BehaviorSubject(undefined); + setDescription = jest.fn((description) => description$.next(description)); + const viewMode$ = new BehaviorSubject('edit'); + setViewMode = jest.fn((nextViewMode) => viewMode$.next(nextViewMode)); api = { - viewMode, - dataViews: new BehaviorSubject([]), - panelTitle: titleSubject, - setPanelTitle: setTitle, - panelDescription: descriptionSubject, - setPanelDescription: setDescription, + viewMode$, + dataViews$: new BehaviorSubject([]), + title$, + setTitle, + description$, + setDescription, }; }); @@ -61,7 +61,7 @@ describe('customize panel editor', () => { }); it('Initializes panel title with default title from API', () => { - api.defaultPanelTitle = new BehaviorSubject('Default title'); + api.defaultTitle$ = new BehaviorSubject('Default title'); renderPanelEditor(); expect(screen.getByTestId('customEmbeddablePanelTitleInput')).toHaveValue('Default title'); }); @@ -82,7 +82,7 @@ describe('customize panel editor', () => { }); it('should use default title when title is undefined', () => { - api.defaultPanelTitle = new BehaviorSubject('Default title'); + api.defaultTitle$ = new BehaviorSubject('Default title'); setTitle(undefined); renderPanelEditor(); const titleInput = screen.getByTestId('customEmbeddablePanelTitleInput'); @@ -90,7 +90,7 @@ describe('customize panel editor', () => { }); it('should use title even when empty string', () => { - api.defaultPanelTitle = new BehaviorSubject('Default title'); + api.defaultTitle$ = new BehaviorSubject('Default title'); setTitle(''); renderPanelEditor(); const titleInput = screen.getByTestId('customEmbeddablePanelTitleInput'); @@ -98,7 +98,7 @@ describe('customize panel editor', () => { }); it('Resets panel title to default when reset button is pressed', async () => { - api.defaultPanelTitle = new BehaviorSubject('Default title'); + api.defaultTitle$ = new BehaviorSubject('Default title'); setTitle('Initial title'); renderPanelEditor(); await userEvent.type(screen.getByTestId('customEmbeddablePanelTitleInput'), 'New title'); @@ -107,7 +107,7 @@ describe('customize panel editor', () => { }); it('should hide title reset when no default exists', async () => { - api.defaultPanelTitle = new BehaviorSubject(undefined); + api.defaultTitle$ = new BehaviorSubject(undefined); setTitle('Initial title'); renderPanelEditor(); await userEvent.type(screen.getByTestId('customEmbeddablePanelTitleInput'), 'New title'); @@ -129,7 +129,7 @@ describe('customize panel editor', () => { }); it('Initializes panel description with default description from API', () => { - api.defaultPanelDescription = new BehaviorSubject('Default description'); + api.defaultDescription$ = new BehaviorSubject('Default description'); renderPanelEditor(); expect(screen.getByTestId('customEmbeddablePanelDescriptionInput')).toHaveValue( 'Default description' @@ -155,7 +155,7 @@ describe('customize panel editor', () => { }); it('should use default description when description is undefined', () => { - api.defaultPanelDescription = new BehaviorSubject('Default description'); + api.defaultDescription$ = new BehaviorSubject('Default description'); setDescription(undefined); renderPanelEditor(); const descriptionInput = screen.getByTestId('customEmbeddablePanelDescriptionInput'); @@ -163,7 +163,7 @@ describe('customize panel editor', () => { }); it('should use description even when empty string', () => { - api.defaultPanelDescription = new BehaviorSubject('Default description'); + api.defaultDescription$ = new BehaviorSubject('Default description'); setDescription(''); renderPanelEditor(); const descriptionInput = screen.getByTestId('customEmbeddablePanelDescriptionInput'); @@ -171,7 +171,7 @@ describe('customize panel editor', () => { }); it('Resets panel description to default when reset button is pressed', async () => { - api.defaultPanelDescription = new BehaviorSubject('Default description'); + api.defaultDescription$ = new BehaviorSubject('Default description'); setDescription('Initial description'); renderPanelEditor(); await userEvent.type( @@ -185,7 +185,7 @@ describe('customize panel editor', () => { }); it('should hide description reset when no default exists', async () => { - api.defaultPanelDescription = new BehaviorSubject(undefined); + api.defaultDescription$ = new BehaviorSubject(undefined); setDescription('Initial description'); renderPanelEditor(); await userEvent.type( diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_editor.tsx b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_editor.tsx index d26a6fb4ad072..122fd05e5309c 100644 --- a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_editor.tsx +++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/customize_panel_editor.tsx @@ -34,8 +34,8 @@ import { apiPublishesTimeRange, apiPublishesUnifiedSearch, getInheritedViewMode, - getPanelDescription, - getPanelTitle, + getDescription, + getTitle, PublishesUnifiedSearch, } from '@kbn/presentation-publishing'; @@ -63,9 +63,9 @@ export const CustomizePanelEditor = ({ * For now, we copy the state here with `useState` initializing it to the latest value. */ const editMode = getInheritedViewMode(api) === 'edit'; - const [hideTitle, setHideTitle] = useState(api.hidePanelTitle?.value); - const [panelTitle, setPanelTitle] = useState(getPanelTitle(api)); - const [panelDescription, setPanelDescription] = useState(getPanelDescription(api)); + const [hideTitle, setHideTitle] = useState(api.hideTitle$?.value); + const [panelTitle, setPanelTitle] = useState(getTitle(api)); + const [panelDescription, setPanelDescription] = useState(getDescription(api)); const [timeRange, setTimeRange] = useState( api.timeRange$?.value ?? api.parentApi?.timeRange$?.value ); @@ -99,10 +99,9 @@ export const CustomizePanelEditor = ({ const dateFormat = useMemo(() => core.uiSettings.get(UI_SETTINGS.DATE_FORMAT), []); const save = () => { - if (panelTitle !== api.panelTitle?.value) api.setPanelTitle?.(panelTitle); - if (hideTitle !== api.hidePanelTitle?.value) api.setHidePanelTitle?.(hideTitle); - if (panelDescription !== api.panelDescription?.value) - api.setPanelDescription?.(panelDescription); + if (panelTitle !== api.title$?.value) api.setTitle?.(panelTitle); + if (hideTitle !== api.hideTitle$?.value) api.setHideTitle?.(hideTitle); + if (panelDescription !== api.description$?.value) api.setDescription?.(panelDescription); const newTimeRange = hasOwnTimeRange ? timeRange : undefined; if (newTimeRange !== api.timeRange$?.value) { @@ -139,12 +138,12 @@ export const CustomizePanelEditor = ({ /> } labelAppend={ - api?.defaultPanelTitle?.value && ( + api?.defaultTitle$?.value && ( setPanelTitle(api.defaultPanelTitle?.value)} - disabled={hideTitle || panelTitle === api?.defaultPanelTitle?.value} + onClick={() => setPanelTitle(api.defaultTitle$?.value)} + disabled={hideTitle || panelTitle === api?.defaultTitle$?.value} aria-label={i18n.translate( 'presentationPanel.action.customizePanel.flyout.optionsMenuForm.resetCustomTitleButtonAriaLabel', { @@ -186,12 +185,12 @@ export const CustomizePanelEditor = ({ /> } labelAppend={ - api.defaultPanelDescription?.value && ( + api.defaultDescription$?.value && ( setPanelDescription(api.defaultPanelDescription?.value)} - disabled={api.defaultPanelDescription?.value === panelDescription} + onClick={() => setPanelDescription(api.defaultDescription$?.value)} + disabled={api.defaultDescription$?.value === panelDescription} aria-label={i18n.translate( 'presentationPanel.action.customizePanel.flyout.optionsMenuForm.resetCustomDescriptionButtonAriaLabel', { diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/filters_details.tsx b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/filters_details.tsx index e8973792a17c9..db589cca94e10 100644 --- a/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/filters_details.tsx +++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/customize_panel_action/filters_details.tsx @@ -38,7 +38,7 @@ interface FiltersDetailsProps { export function FiltersDetails({ editMode, api }: FiltersDetailsProps) { const [queryString, setQueryString] = useState(''); const [queryLanguage, setQueryLanguage] = useState<'sql' | 'esql' | undefined>(); - const dataViews = api.dataViews?.value ?? []; + const dataViews = api.dataViews$?.value ?? []; const filters = useMemo(() => api.filters$?.value ?? [], [api]); diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/edit_panel_action/edit_panel_action.test.tsx b/src/platform/plugins/private/presentation_panel/public/panel_actions/edit_panel_action/edit_panel_action.test.tsx index 866cd876b0364..2c7447baf5006 100644 --- a/src/platform/plugins/private/presentation_panel/public/panel_actions/edit_panel_action/edit_panel_action.test.tsx +++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/edit_panel_action/edit_panel_action.test.tsx @@ -14,16 +14,16 @@ import { EditPanelAction, EditPanelActionApi } from './edit_panel_action'; describe('Edit panel action', () => { let action: EditPanelAction; let context: { embeddable: EditPanelActionApi }; - let updateViewMode: (viewMode: ViewMode) => void; + let setViewMode: (viewMode: ViewMode) => void; beforeEach(() => { - const viewModeSubject = new BehaviorSubject('edit'); - updateViewMode = (viewMode) => viewModeSubject.next(viewMode); + const viewMode$ = new BehaviorSubject('edit'); + setViewMode = (viewMode) => viewMode$.next(viewMode); action = new EditPanelAction(); context = { embeddable: { - viewMode: viewModeSubject, + viewMode$, onEdit: jest.fn(), isEditingEnabled: jest.fn().mockReturnValue(true), getTypeDisplayName: jest.fn().mockReturnValue('A very fun panel type'), @@ -43,7 +43,7 @@ describe('Edit panel action', () => { }); it('is incompatible when view mode is view', async () => { - (context.embeddable as PublishesViewMode).viewMode = new BehaviorSubject('view'); + (context.embeddable as PublishesViewMode).viewMode$ = new BehaviorSubject('view'); expect(await action.isCompatible(context)).toBe(false); }); @@ -66,7 +66,7 @@ describe('Edit panel action', () => { it('calls onChange when view mode changes', () => { const onChange = jest.fn(); action.subscribeToCompatibilityChanges(context, onChange); - updateViewMode('view'); + setViewMode('view'); expect(onChange).toHaveBeenCalledWith(false, action); }); }); diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/inspect_panel_action/inspect_panel_action.ts b/src/platform/plugins/private/presentation_panel/public/panel_actions/inspect_panel_action/inspect_panel_action.ts index 2b0417c6a10b2..51b76be1fe824 100644 --- a/src/platform/plugins/private/presentation_panel/public/panel_actions/inspect_panel_action/inspect_panel_action.ts +++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/inspect_panel_action/inspect_panel_action.ts @@ -12,16 +12,15 @@ import { apiHasInspectorAdapters, HasInspectorAdapters } from '@kbn/inspector-pl import { tracksOverlays } from '@kbn/presentation-containers'; import { EmbeddableApiContext, - getPanelTitle, - PublishesPanelTitle, + getTitle, + PublishesTitle, HasParentApi, } from '@kbn/presentation-publishing'; import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; import { ACTION_INSPECT_PANEL } from './constants'; import { inspector } from '../../kibana_services'; -export type InspectPanelActionApi = HasInspectorAdapters & - Partial; +export type InspectPanelActionApi = HasInspectorAdapters & Partial; const isApiCompatible = (api: unknown | null): api is InspectPanelActionApi => { return Boolean(api) && apiHasInspectorAdapters(api); }; @@ -57,7 +56,7 @@ export class InspectPanelAction implements Action { } const panelTitle = - getPanelTitle(embeddable) || + getTitle(embeddable) || i18n.translate('presentationPanel.action.inspectPanel.untitledEmbeddableFilename', { defaultMessage: '[No Title]', }); diff --git a/src/platform/plugins/private/presentation_panel/public/panel_actions/remove_panel_action/remove_panel_action.test.tsx b/src/platform/plugins/private/presentation_panel/public/panel_actions/remove_panel_action/remove_panel_action.test.tsx index 33ab3bf993f26..f5852d0121853 100644 --- a/src/platform/plugins/private/presentation_panel/public/panel_actions/remove_panel_action/remove_panel_action.test.tsx +++ b/src/platform/plugins/private/presentation_panel/public/panel_actions/remove_panel_action/remove_panel_action.test.tsx @@ -21,7 +21,7 @@ describe('Remove panel action', () => { context = { embeddable: { uuid: 'superId', - viewMode: new BehaviorSubject('edit'), + viewMode$: new BehaviorSubject('edit'), parentApi: getMockPresentationContainer(), }, }; @@ -39,7 +39,7 @@ describe('Remove panel action', () => { }); it('is incompatible when view mode is view', async () => { - context.embeddable.viewMode = new BehaviorSubject('view'); + context.embeddable.viewMode$ = new BehaviorSubject('view'); expect(await action.isCompatible(context)).toBe(false); }); diff --git a/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/presentation_panel_hover_actions.tsx b/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/presentation_panel_hover_actions.tsx index 31826d663f54e..eae123f3dfe29 100644 --- a/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/presentation_panel_hover_actions.tsx +++ b/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/presentation_panel_hover_actions.tsx @@ -206,12 +206,12 @@ export const PresentationPanelHoverActions = ({ parentHideTitle, parentViewMode, ] = useBatchedOptionalPublishingSubjects( - api?.defaultPanelTitle, - api?.panelTitle, - api?.panelDescription, - api?.hidePanelTitle, + api?.defaultTitle$, + api?.title$, + api?.description$, + api?.hideTitle$, api?.hasLockedHoverActions$, - api?.parentApi?.hidePanelTitle, + api?.parentApi?.hideTitle$, /** * View mode changes often have the biggest influence over which actions will be compatible, * so we build and update all actions when the view mode changes. This is temporary, as these @@ -329,7 +329,7 @@ export const PresentationPanelHoverActions = ({ })()) as AnyApiAction[]; if (canceled) return; - const disabledActions = api.disabledActionIds?.value; + const disabledActions = api.disabledActionIds$?.value; if (disabledActions) { compatibleActions = compatibleActions.filter( (action) => disabledActions.indexOf(action.id) === -1 diff --git a/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/use_presentation_panel_header_actions.tsx b/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/use_presentation_panel_header_actions.tsx index 7ffc356de1e20..e1e0a532129da 100644 --- a/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/use_presentation_panel_header_actions.tsx +++ b/src/platform/plugins/private/presentation_panel/public/panel_component/panel_header/use_presentation_panel_header_actions.tsx @@ -49,7 +49,7 @@ export const usePresentationPanelHeaderActions = < embeddable: api, })) as AnyApiAction[]) ?? []; - const disabledActions = (api.disabledActionIds?.value ?? []).concat(disabledNotifications); + const disabledActions = (api.disabledActionIds$?.value ?? []).concat(disabledNotifications); nextActions = nextActions.filter((badge) => disabledActions.indexOf(badge.id) === -1); return nextActions; }; diff --git a/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error_internal.tsx b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error_internal.tsx index 244dbba2449d2..3a5c7c5b7830d 100644 --- a/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error_internal.tsx +++ b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_error_internal.tsx @@ -62,7 +62,7 @@ export const PresentationPanelErrorInternal = ({ api, error }: PresentationPanel }); }, [api, isEditable]); - const panelTitle = useStateFromPublishingSubject(api?.panelTitle); + const panelTitle = useStateFromPublishingSubject(api?.title$); const ariaLabel = useMemo( () => panelTitle diff --git a/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_internal.test.tsx b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_internal.test.tsx index fa86060859098..c9bcb0caccb37 100644 --- a/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_internal.test.tsx +++ b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_internal.test.tsx @@ -51,7 +51,7 @@ describe('Presentation panel', () => { it('renders a blocking error when one is present', async () => { const api: DefaultPresentationPanelApi = { uuid: 'test', - blockingError: new BehaviorSubject(new Error('UH OH')), + blockingError$: new BehaviorSubject(new Error('UH OH')), }; render(); await waitFor(() => expect(screen.getByTestId('embeddableStackError')).toBeInTheDocument()); @@ -91,7 +91,7 @@ describe('Presentation panel', () => { it('gets compatible actions for the given API', async () => { const api: DefaultPresentationPanelApi = { uuid: 'test', - panelTitle: new BehaviorSubject('superTest'), + title$: new BehaviorSubject('superTest'), }; await renderPresentationPanel({ api }); expect(uiActions.getTriggerCompatibleActions).toHaveBeenCalledWith('CONTEXT_MENU_TRIGGER', { @@ -116,7 +116,7 @@ describe('Presentation panel', () => { it('does not show actions which are disabled by the API', async () => { const api: DefaultPresentationPanelApi = { uuid: 'test', - disabledActionIds: new BehaviorSubject(['actionA']), + disabledActionIds$: new BehaviorSubject(['actionA']), }; const getActions = jest.fn().mockReturnValue([mockAction('actionA'), mockAction('actionB')]); await renderPresentationPanel({ api, props: { getActions } }); @@ -161,8 +161,8 @@ describe('Presentation panel', () => { it('renders the panel title from the api and not the default title', async () => { const api: DefaultPresentationPanelApi = { uuid: 'test', - panelTitle: new BehaviorSubject('SUPER TITLE'), - defaultPanelTitle: new BehaviorSubject('SO Title'), + title$: new BehaviorSubject('SUPER TITLE'), + defaultTitle$: new BehaviorSubject('SO Title'), }; await renderPresentationPanel({ api }); await waitFor(() => { @@ -173,7 +173,7 @@ describe('Presentation panel', () => { it('renders the default title from the api when a panel title is not provided', async () => { const api: DefaultPresentationPanelApi = { uuid: 'test', - defaultPanelTitle: new BehaviorSubject('SO Title'), + defaultTitle$: new BehaviorSubject('SO Title'), }; await renderPresentationPanel({ api }); await waitFor(() => { @@ -184,7 +184,7 @@ describe('Presentation panel', () => { it("does not render an info icon when the api doesn't provide a panel description or default description", async () => { const api: DefaultPresentationPanelApi = { uuid: 'test', - panelTitle: new BehaviorSubject('SUPER TITLE'), + title$: new BehaviorSubject('SUPER TITLE'), }; await renderPresentationPanel({ api }); await waitFor(() => { @@ -195,8 +195,8 @@ describe('Presentation panel', () => { it('renders an info icon when the api provides a panel description', async () => { const api: DefaultPresentationPanelApi = { uuid: 'test', - panelTitle: new BehaviorSubject('SUPER TITLE'), - panelDescription: new BehaviorSubject('SUPER DESCRIPTION'), + title$: new BehaviorSubject('SUPER TITLE'), + description$: new BehaviorSubject('SUPER DESCRIPTION'), }; await renderPresentationPanel({ api }); await waitFor(() => { @@ -207,8 +207,8 @@ describe('Presentation panel', () => { it('renders an info icon when the api provides a default description', async () => { const api: DefaultPresentationPanelApi = { uuid: 'test', - panelTitle: new BehaviorSubject('SUPER TITLE'), - defaultPanelDescription: new BehaviorSubject('SO Description'), + title$: new BehaviorSubject('SUPER TITLE'), + defaultDescription$: new BehaviorSubject('SO Description'), }; await renderPresentationPanel({ api }); await waitFor(() => { @@ -219,8 +219,8 @@ describe('Presentation panel', () => { it('does not render a title when in view mode when the provided title is blank', async () => { const api: DefaultPresentationPanelApi & PublishesViewMode = { uuid: 'test', - panelTitle: new BehaviorSubject(''), - viewMode: new BehaviorSubject('view'), + title$: new BehaviorSubject(''), + viewMode$: new BehaviorSubject('view'), }; await renderPresentationPanel({ api }); expect(screen.queryByTestId('presentationPanelTitle')).not.toBeInTheDocument(); @@ -229,9 +229,9 @@ describe('Presentation panel', () => { it('does not render a title when in edit mode and the provided title is blank', async () => { const api: DefaultPresentationPanelApi & PublishesDataViews & PublishesViewMode = { uuid: 'test', - panelTitle: new BehaviorSubject(''), - viewMode: new BehaviorSubject('edit'), - dataViews: new BehaviorSubject([]), + title$: new BehaviorSubject(''), + viewMode$: new BehaviorSubject('edit'), + dataViews$: new BehaviorSubject([]), }; await renderPresentationPanel({ api }); expect(screen.queryByTestId('presentationPanelTitle')).not.toBeInTheDocument(); @@ -242,9 +242,9 @@ describe('Presentation panel', () => { const api: DefaultPresentationPanelApi & PublishesDataViews & PublishesViewMode = { uuid: 'test', - panelTitle: new BehaviorSubject('TITLE'), - viewMode: new BehaviorSubject('edit'), - dataViews: new BehaviorSubject([]), + title$: new BehaviorSubject('TITLE'), + viewMode$: new BehaviorSubject('edit'), + dataViews$: new BehaviorSubject([]), }; await renderPresentationPanel({ api }); await waitFor(() => { @@ -259,9 +259,9 @@ describe('Presentation panel', () => { it('does not show title customize link in view mode', async () => { const api: DefaultPresentationPanelApi & PublishesDataViews & PublishesViewMode = { uuid: 'test', - panelTitle: new BehaviorSubject('SUPER TITLE'), - viewMode: new BehaviorSubject('view'), - dataViews: new BehaviorSubject([]), + title$: new BehaviorSubject('SUPER TITLE'), + viewMode$: new BehaviorSubject('view'), + dataViews$: new BehaviorSubject([]), }; await renderPresentationPanel({ api }); await waitFor(() => { @@ -273,9 +273,9 @@ describe('Presentation panel', () => { it('hides title in view mode when API hide title option is true', async () => { const api: DefaultPresentationPanelApi & PublishesViewMode = { uuid: 'test', - panelTitle: new BehaviorSubject('SUPER TITLE'), - hidePanelTitle: new BehaviorSubject(true), - viewMode: new BehaviorSubject('view'), + title$: new BehaviorSubject('SUPER TITLE'), + hideTitle$: new BehaviorSubject(true), + viewMode$: new BehaviorSubject('view'), }; await renderPresentationPanel({ api }); expect(screen.queryByTestId('presentationPanelTitle')).not.toBeInTheDocument(); @@ -284,9 +284,9 @@ describe('Presentation panel', () => { it('hides title in edit mode when API hide title option is true', async () => { const api: DefaultPresentationPanelApi & PublishesViewMode = { uuid: 'test', - panelTitle: new BehaviorSubject('SUPER TITLE'), - hidePanelTitle: new BehaviorSubject(true), - viewMode: new BehaviorSubject('edit'), + title$: new BehaviorSubject('SUPER TITLE'), + hideTitle$: new BehaviorSubject(true), + viewMode$: new BehaviorSubject('edit'), }; await renderPresentationPanel({ api }); expect(screen.queryByTestId('presentationPanelTitle')).not.toBeInTheDocument(); @@ -295,10 +295,10 @@ describe('Presentation panel', () => { it('hides title in view mode when parent hide title option is true', async () => { const api: DefaultPresentationPanelApi & PublishesViewMode = { uuid: 'test', - panelTitle: new BehaviorSubject('SUPER TITLE'), - viewMode: new BehaviorSubject('view'), + title$: new BehaviorSubject('SUPER TITLE'), + viewMode$: new BehaviorSubject('view'), parentApi: { - viewMode: new BehaviorSubject('view'), + viewMode$: new BehaviorSubject('view'), ...getMockPresentationContainer(), }, }; @@ -309,10 +309,10 @@ describe('Presentation panel', () => { it('hides title in edit mode when parent hide title option is true', async () => { const api: DefaultPresentationPanelApi & PublishesViewMode = { uuid: 'test', - panelTitle: new BehaviorSubject('SUPER TITLE'), - viewMode: new BehaviorSubject('edit'), + title$: new BehaviorSubject('SUPER TITLE'), + viewMode$: new BehaviorSubject('edit'), parentApi: { - viewMode: new BehaviorSubject('edit'), + viewMode$: new BehaviorSubject('edit'), ...getMockPresentationContainer(), }, }; diff --git a/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_internal.tsx b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_internal.tsx index 2ae91b989921d..e9aa4691481a0 100644 --- a/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_internal.tsx +++ b/src/platform/plugins/private/presentation_panel/public/panel_component/presentation_panel_internal.tsx @@ -50,8 +50,8 @@ export const PresentationPanelInternal = < const dragHandles = useRef<{ [dragHandleKey: string]: HTMLElement | null }>({}); const viewModeSubject = (() => { - if (apiPublishesViewMode(api)) return api.viewMode; - if (apiHasParentApi(api) && apiPublishesViewMode(api.parentApi)) return api.parentApi.viewMode; + if (apiPublishesViewMode(api)) return api.viewMode$; + if (apiHasParentApi(api) && apiPublishesViewMode(api.parentApi)) return api.parentApi.viewMode$; })(); const [ @@ -65,20 +65,20 @@ export const PresentationPanelInternal = < rawViewMode, parentHidePanelTitle, ] = useBatchedOptionalPublishingSubjects( - api?.dataLoading, - api?.blockingError, - api?.panelTitle, - api?.hidePanelTitle, - api?.panelDescription, - api?.defaultPanelTitle, - api?.defaultPanelDescription, + api?.dataLoading$, + api?.blockingError$, + api?.title$, + api?.hideTitle$, + api?.description$, + api?.defaultTitle$, + api?.defaultDescription$, viewModeSubject, - api?.parentApi?.hidePanelTitle + api?.parentApi?.hideTitle$ ); const viewMode = rawViewMode ?? 'view'; const [initialLoadComplete, setInitialLoadComplete] = useState(!dataLoading); - if (!initialLoadComplete && (dataLoading === false || (api && !api.dataLoading))) { + if (!initialLoadComplete && (dataLoading === false || (api && !api.dataLoading$))) { setInitialLoadComplete(true); } diff --git a/src/platform/plugins/private/presentation_panel/public/panel_component/types.ts b/src/platform/plugins/private/presentation_panel/public/panel_component/types.ts index d31914f8266a1..ff1b68e01d36f 100644 --- a/src/platform/plugins/private/presentation_panel/public/panel_component/types.ts +++ b/src/platform/plugins/private/presentation_panel/public/panel_component/types.ts @@ -15,8 +15,8 @@ import { PublishesBlockingError, PublishesDataLoading, PublishesDisabledActionIds, - PublishesPanelDescription, - PublishesPanelTitle, + PublishesDescription, + PublishesTitle, PublishesViewMode, } from '@kbn/presentation-publishing'; import { UiActionsService } from '@kbn/ui-actions-plugin/public'; @@ -74,14 +74,13 @@ export interface PresentationPanelInternalProps< export interface DefaultPresentationPanelApi extends HasUniqueId, Partial< - PublishesPanelTitle & + PublishesTitle & PublishesDataLoading & PublishesBlockingError & - PublishesPanelDescription & + PublishesDescription & PublishesDisabledActionIds & HasParentApi< - PresentationContainer & - Partial & PublishesViewMode> + PresentationContainer & Partial & PublishesViewMode> > & CanLockHoverActions > {} diff --git a/src/platform/plugins/shared/controls/public/actions/clear_control_action.test.tsx b/src/platform/plugins/shared/controls/public/actions/clear_control_action.test.tsx index 1c0b0d0392bfb..19b182ecb6d62 100644 --- a/src/platform/plugins/shared/controls/public/actions/clear_control_action.test.tsx +++ b/src/platform/plugins/shared/controls/public/actions/clear_control_action.test.tsx @@ -14,7 +14,7 @@ import { ClearControlAction } from './clear_control_action'; import type { ViewMode } from '@kbn/presentation-publishing'; const dashboardApi = { - viewMode: new BehaviorSubject('view'), + viewMode$: new BehaviorSubject('view'), }; const controlGroupApi = getMockedControlGroupApi(dashboardApi, { removePanel: jest.fn(), diff --git a/src/platform/plugins/shared/controls/public/actions/delete_control_action.test.tsx b/src/platform/plugins/shared/controls/public/actions/delete_control_action.test.tsx index 56b020962a9f7..776eb7c969ca0 100644 --- a/src/platform/plugins/shared/controls/public/actions/delete_control_action.test.tsx +++ b/src/platform/plugins/shared/controls/public/actions/delete_control_action.test.tsx @@ -17,7 +17,7 @@ import { coreServices } from '../services/kibana_services'; import { DeleteControlAction } from './delete_control_action'; const dashboardApi = { - viewMode: new BehaviorSubject('view'), + viewMode$: new BehaviorSubject('view'), }; const controlGroupApi = getMockedControlGroupApi(dashboardApi, { removePanel: jest.fn(), diff --git a/src/platform/plugins/shared/controls/public/actions/edit_control_action.test.tsx b/src/platform/plugins/shared/controls/public/actions/edit_control_action.test.tsx index 497223d9f0889..668aaca003fbb 100644 --- a/src/platform/plugins/shared/controls/public/actions/edit_control_action.test.tsx +++ b/src/platform/plugins/shared/controls/public/actions/edit_control_action.test.tsx @@ -29,7 +29,7 @@ dataService.query.timefilter.timefilter.calculateBounds = (timeRange: TimeRange) }; const dashboardApi = { - viewMode: new BehaviorSubject('view'), + viewMode$: new BehaviorSubject('view'), }; const controlGroupApi = getMockedControlGroupApi(dashboardApi, { removePanel: jest.fn(), @@ -88,7 +88,7 @@ describe('Incompatible embeddables', () => { describe('Compatible embeddables', () => { beforeAll(() => { - dashboardApi.viewMode.next('edit'); + dashboardApi.viewMode$.next('edit'); }); test('Action is compatible with embeddables that are editable', async () => { diff --git a/src/platform/plugins/shared/controls/public/control_group/components/control_clone.tsx b/src/platform/plugins/shared/controls/public/control_group/components/control_clone.tsx index 7002bbf78d5d4..6602b6f41c179 100644 --- a/src/platform/plugins/shared/controls/public/control_group/components/control_clone.tsx +++ b/src/platform/plugins/shared/controls/public/control_group/components/control_clone.tsx @@ -31,8 +31,8 @@ export const ControlClone = ({ }) => { const [width, panelTitle, defaultPanelTitle] = useBatchedPublishingSubjects( controlApi ? controlApi.width : new BehaviorSubject(DEFAULT_CONTROL_GROW), - controlApi?.panelTitle ? controlApi.panelTitle : new BehaviorSubject(undefined), - controlApi?.defaultPanelTitle ? controlApi.defaultPanelTitle : new BehaviorSubject('') + controlApi?.title$ ? controlApi.title$ : new BehaviorSubject(undefined), + controlApi?.defaultTitle$ ? controlApi.defaultTitle$ : new BehaviorSubject('') ); return ( diff --git a/src/platform/plugins/shared/controls/public/control_group/components/control_panel.tsx b/src/platform/plugins/shared/controls/public/control_group/components/control_panel.tsx index 9a21c28eb2f1d..8e6ec4f9ee148 100644 --- a/src/platform/plugins/shared/controls/public/control_group/components/control_panel.tsx +++ b/src/platform/plugins/shared/controls/public/control_group/components/control_panel.tsx @@ -87,7 +87,7 @@ export const ControlPanel = controlGroupApi apiPublishesViewMode(api.parentApi.parentApi) // controlGroupApi.parentApi => dashboardApi ) - return api.parentApi.parentApi.viewMode; // get view mode from dashboard API + return api.parentApi.parentApi.viewMode$; // get view mode from dashboard API })(); const [ @@ -101,21 +101,21 @@ export const ControlPanel = { if (!controlGroup) return; - const stateChangeSubscription = controlGroup.unsavedChanges.subscribe((changes) => { + const stateChangeSubscription = controlGroup.unsavedChanges$.subscribe((changes) => { runtimeState$.next({ ...runtimeState$.getValue(), ...changes }); }); return () => { @@ -168,8 +168,8 @@ export const ControlGroupRenderer = ({ type={CONTROL_GROUP_TYPE} getParentApi={() => ({ reload$, - dataLoading: dataLoading$, - viewMode: viewMode$, + dataLoading$, + viewMode$, query$: searchApi.query$, timeRange$: searchApi.timeRange$, unifiedSearchFilters$: searchApi.filters$, diff --git a/src/platform/plugins/shared/controls/public/control_group/control_group_unsaved_changes_api.ts b/src/platform/plugins/shared/controls/public/control_group/control_group_unsaved_changes_api.ts index 5f01410a85718..1331afc4e8f12 100644 --- a/src/platform/plugins/shared/controls/public/control_group/control_group_unsaved_changes_api.ts +++ b/src/platform/plugins/shared/controls/public/control_group/control_group_unsaved_changes_api.ts @@ -55,8 +55,8 @@ export function initializeControlGroupUnsavedChanges( return { api: { - unsavedChanges: combineLatest([ - controlGroupUnsavedChanges.api.unsavedChanges, + unsavedChanges$: combineLatest([ + controlGroupUnsavedChanges.api.unsavedChanges$, childrenUnsavedChanges$(children$), ]).pipe( map(([unsavedControlGroupState, unsavedControlsState]) => { @@ -87,7 +87,7 @@ export function initializeControlGroupUnsavedChanges( applySelections(); } }, - } as Pick & { + } as Pick & { asyncResetUnsavedChanges: () => Promise; }, }; diff --git a/src/platform/plugins/shared/controls/public/control_group/get_control_group_factory.tsx b/src/platform/plugins/shared/controls/public/control_group/get_control_group_factory.tsx index c8ee296d8a305..eea50462ddc11 100644 --- a/src/platform/plugins/shared/controls/public/control_group/get_control_group_factory.tsx +++ b/src/platform/plugins/shared/controls/public/control_group/get_control_group_factory.tsx @@ -85,7 +85,7 @@ export const getControlGroupEmbeddableFactory = () => { ...controlsManager.api, autoApplySelections$, }); - const dataViews = new BehaviorSubject(undefined); + const dataViews$ = new BehaviorSubject(undefined); const chainingSystem$ = new BehaviorSubject( chainingSystem ?? DEFAULT_CONTROL_CHAINING ); @@ -130,7 +130,7 @@ export const getControlGroupEmbeddableFactory = () => { const api = setApi({ ...controlsManager.api, - disabledActionIds: disabledActionIds$, + disabledActionIds$, ...unsavedChanges.api, ...selectionsManager.api, controlFetch$: (controlUuid: string) => @@ -166,7 +166,7 @@ export const getControlGroupEmbeddableFactory = () => { isEditingEnabled: () => true, openAddDataControlFlyout: (settings) => { const parentDataViewId = apiPublishesDataViews(parentApi) - ? parentApi.dataViews.value?.[0]?.id + ? parentApi.dataViews$.value?.[0]?.id : undefined; const newControlState = controlsManager.getNewControlState(); @@ -201,7 +201,7 @@ export const getControlGroupEmbeddableFactory = () => { references, }; }, - dataViews, + dataViews$, labelPosition: labelPosition$, saveNotification$: apiHasSaveNotification(parentApi) ? parentApi.saveNotification$ @@ -227,8 +227,8 @@ export const getControlGroupEmbeddableFactory = () => { const childrenDataViewsSubscription = combineCompatibleChildrenApis< PublishesDataViews, DataView[] - >(api, 'dataViews', apiPublishesDataViews, []).subscribe((newDataViews) => - dataViews.next(newDataViews) + >(api, 'dataViews$', apiPublishesDataViews, []).subscribe((newDataViews) => + dataViews$.next(newDataViews) ); const saveNotificationSubscription = apiHasSaveNotification(parentApi) diff --git a/src/platform/plugins/shared/controls/public/control_group/types.ts b/src/platform/plugins/shared/controls/public/control_group/types.ts index 7a2bf74b5a27d..6453e8b6d4a64 100644 --- a/src/platform/plugins/shared/controls/public/control_group/types.ts +++ b/src/platform/plugins/shared/controls/public/control_group/types.ts @@ -53,7 +53,7 @@ export type ControlGroupApi = PresentationContainer & PublishesDataViews & HasSerializedChildState & HasEditCapabilities & - Pick, 'unsavedChanges'> & + Pick, 'unsavedChanges$'> & PublishesTimeslice & PublishesDisabledActionIds & Partial & HasSaveNotification & PublishesReload> & { diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/initialize_data_control.test.tsx b/src/platform/plugins/shared/controls/public/controls/data_controls/initialize_data_control.test.tsx index c3c4dd0d6da77..7a5a41fcb800c 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/initialize_data_control.test.tsx +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/initialize_data_control.test.tsx @@ -52,20 +52,20 @@ describe('initializeDataControl', () => { controlGroupApi ); - dataControl.api.defaultPanelTitle!.pipe(skip(1), first()).subscribe(() => { + dataControl.api.defaultTitle$!.pipe(skip(1), first()).subscribe(() => { done(); }); }); test('should set data view', () => { - const dataViews = dataControl!.api.dataViews.value; + const dataViews = dataControl!.api.dataViews$.value; expect(dataViews).not.toBeUndefined(); expect(dataViews!.length).toBe(1); expect(dataViews![0].id).toBe('myDataViewId'); }); test('should set default panel title', () => { - const defaultPanelTitle = dataControl!.api.defaultPanelTitle!.value; + const defaultPanelTitle = dataControl!.api.defaultTitle$!.value; expect(defaultPanelTitle).not.toBeUndefined(); expect(defaultPanelTitle).toBe('My field name'); }); @@ -86,13 +86,13 @@ describe('initializeDataControl', () => { controlGroupApi ); - dataControl.api.dataViews.pipe(skip(1), first()).subscribe(() => { + dataControl.api.dataViews$.pipe(skip(1), first()).subscribe(() => { done(); }); }); test('should set blocking error', () => { - const error = dataControl!.api.blockingError.value; + const error = dataControl!.api.blockingError$.value; expect(error).not.toBeUndefined(); expect(error!.message).toBe( 'Simulated error: no data view found for id notGonnaFindMeDataViewId' @@ -100,9 +100,9 @@ describe('initializeDataControl', () => { }); test('should clear blocking error when valid data view id provided', (done) => { - dataControl!.api.dataViews.pipe(skip(1), first()).subscribe((dataView) => { + dataControl!.api.dataViews$.pipe(skip(1), first()).subscribe((dataView) => { expect(dataView).not.toBeUndefined(); - expect(dataControl!.api.blockingError.value).toBeUndefined(); + expect(dataControl!.api.blockingError$.value).toBeUndefined(); done(); }); dataControl!.stateManager.dataViewId.next('myDataViewId'); @@ -124,25 +124,23 @@ describe('initializeDataControl', () => { controlGroupApi ); - dataControl.api.defaultPanelTitle!.pipe(skip(1), first()).subscribe(() => { + dataControl.api.defaultTitle$!.pipe(skip(1), first()).subscribe(() => { done(); }); }); test('should set blocking error', () => { - const error = dataControl!.api.blockingError.value; + const error = dataControl!.api.blockingError$.value; expect(error).not.toBeUndefined(); expect(error!.message).toBe('Could not locate field: notGonnaFindMeFieldName'); }); test('should clear blocking error when valid field name provided', (done) => { - dataControl!.api - .defaultPanelTitle!.pipe(skip(1), first()) - .subscribe((defaultPanelTitle) => { - expect(defaultPanelTitle).toBe('My field name'); - expect(dataControl!.api.blockingError.value).toBeUndefined(); - done(); - }); + dataControl!.api.defaultTitle$!.pipe(skip(1), first()).subscribe((defaultTitle) => { + expect(defaultTitle).toBe('My field name'); + expect(dataControl!.api.blockingError$.value).toBeUndefined(); + done(); + }); dataControl!.stateManager.fieldName.next('myFieldName'); }); }); diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/initialize_data_control.ts b/src/platform/plugins/shared/controls/public/controls/data_controls/initialize_data_control.ts index 68affc22ad4cb..136c029943eb0 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/initialize_data_control.ts +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/initialize_data_control.ts @@ -52,11 +52,11 @@ export const initializeDataControl = ( } => { const defaultControl = initializeDefaultControlApi(state); - const panelTitle = new BehaviorSubject(state.title); - const defaultPanelTitle = new BehaviorSubject(undefined); + const title$ = new BehaviorSubject(state.title); + const defaultTitle$ = new BehaviorSubject(undefined); const dataViewId = new BehaviorSubject(state.dataViewId); const fieldName = new BehaviorSubject(state.fieldName); - const dataViews = new BehaviorSubject(undefined); + const dataViews$ = new BehaviorSubject(undefined); const filters$ = new BehaviorSubject(undefined); const filtersReady$ = new BehaviorSubject(false); const field$ = new BehaviorSubject(undefined); @@ -68,14 +68,14 @@ export const initializeDataControl = ( ...defaultControl.stateManager, dataViewId, fieldName, - title: panelTitle, + title: title$, }; const dataViewIdSubscription = dataViewId .pipe( tap(() => { filtersReady$.next(false); - if (defaultControl.api.blockingError.value) { + if (defaultControl.api.blockingError$.value) { defaultControl.api.setBlockingError(undefined); } }), @@ -93,10 +93,10 @@ export const initializeDataControl = ( if (error) { defaultControl.api.setBlockingError(error); } - dataViews.next(dataView ? [dataView] : undefined); + dataViews$.next(dataView ? [dataView] : undefined); }); - const fieldNameSubscription = combineLatest([dataViews, fieldName]) + const fieldNameSubscription = combineLatest([dataViews$, fieldName]) .pipe( tap(() => { filtersReady$.next(false); @@ -120,12 +120,12 @@ export const initializeDataControl = ( }) ) ); - } else if (defaultControl.api.blockingError.value) { + } else if (defaultControl.api.blockingError$.value) { defaultControl.api.setBlockingError(undefined); } field$.next(field); - defaultPanelTitle.next(field ? field.displayName || field.name : nextFieldName); + defaultTitle$.next(field ? field.displayName || field.name : nextFieldName); const spec = field?.toSpec(); if (spec) { fieldFormatter.next(dataView.getFormatterForField(spec).getConverterFor('text')); @@ -172,7 +172,7 @@ export const initializeDataControl = ( }, controlType, controlId, - initialDefaultPanelTitle: defaultPanelTitle.getValue(), + initialDefaultPanelTitle: defaultTitle$.getValue(), controlGroupApi, }); }; @@ -186,9 +186,9 @@ export const initializeDataControl = ( const api: ControlApiInitialization = { ...defaultControl.api, - panelTitle, - defaultPanelTitle, - dataViews, + title$, + defaultTitle$, + dataViews$, field$, fieldFormatter, onEdit, @@ -196,7 +196,7 @@ export const initializeDataControl = ( isEditingEnabled: () => true, untilFiltersReady: async () => { return new Promise((resolve) => { - combineLatest([defaultControl.api.blockingError, filtersReady$]) + combineLatest([defaultControl.api.blockingError$, filtersReady$]) .pipe( first(([blockingError, filtersReady]) => filtersReady || blockingError !== undefined) ) @@ -216,7 +216,7 @@ export const initializeDataControl = ( }, comparators: { ...defaultControl.comparators, - title: [panelTitle, (value: string | undefined) => panelTitle.next(value)], + title: [title$, (value: string | undefined) => title$.next(value)], dataViewId: [dataViewId, (value: string) => dataViewId.next(value)], fieldName: [fieldName, (value: string) => fieldName.next(value)], }, @@ -235,7 +235,7 @@ export const initializeDataControl = ( ...defaultControl.serialize().rawState, dataViewId: dataViewId.getValue(), fieldName: fieldName.getValue(), - title: panelTitle.getValue(), + title: title$.getValue(), }, references: [ { diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/mocks/api_mocks.tsx b/src/platform/plugins/shared/controls/public/controls/data_controls/mocks/api_mocks.tsx index ade12fda012d6..8c07026b7bf28 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/mocks/api_mocks.tsx +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/mocks/api_mocks.tsx @@ -31,7 +31,7 @@ export const getOptionsListMocks = () => { availableOptions$: new BehaviorSubject(undefined), invalidSelections$: new BehaviorSubject>(new Set([])), totalCardinality$: new BehaviorSubject(undefined), - dataLoading: new BehaviorSubject(false), + dataLoading$: new BehaviorSubject(false), parentApi: { allowExpensiveQueries$: new BehaviorSubject(true), }, diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_control.tsx b/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_control.tsx index ebb5458478531..de867bf79c737 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_control.tsx +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_control.tsx @@ -58,12 +58,12 @@ export const OptionsListControl = ({ stateManager.selectedOptions, api.invalidSelections$, api.field$, - api.dataLoading, - api.panelTitle, + api.dataLoading$, + api.title$, api.fieldFormatter ); - const [defaultPanelTitle] = useBatchedOptionalPublishingSubjects(api.defaultPanelTitle); + const [defaultPanelTitle] = useBatchedOptionalPublishingSubjects(api.defaultTitle$); const delimiter = useMemo(() => OptionsListStrings.control.getSeparator(field?.type), [field]); diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover.tsx b/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover.tsx index 41f69ab506ca9..8429eae3d7949 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover.tsx +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover.tsx @@ -23,7 +23,7 @@ export const OptionsListPopover = () => { api.field$, api.availableOptions$, api.invalidSelections$, - api.dataLoading + api.dataLoading$ ); const [showOnlySelected, setShowOnlySelected] = useState(false); diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover_footer.tsx b/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover_footer.tsx index e17a1dcf32a97..0f55bdbde10b3 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover_footer.tsx +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover_footer.tsx @@ -43,7 +43,7 @@ export const OptionsListPopoverFooter = () => { const [exclude, loading, allowExpensiveQueries] = useBatchedPublishingSubjects( stateManager.exclude, - api.dataLoading, + api.dataLoading$, api.parentApi.allowExpensiveQueries$ ); diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover_invalid_selections.tsx b/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover_invalid_selections.tsx index fcfcd25ba6e20..a44c94e70bf93 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover_invalid_selections.tsx +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover_invalid_selections.tsx @@ -34,7 +34,7 @@ export const OptionsListPopoverInvalidSelections = () => { api.invalidSelections$, api.fieldFormatter ); - const defaultPanelTitle = useStateFromPublishingSubject(api.defaultPanelTitle); + const defaultPanelTitle = useStateFromPublishingSubject(api.defaultTitle$); const [selectableOptions, setSelectableOptions] = useState([]); // will be set in following useEffect useEffect(() => { diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover_suggestions.tsx b/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover_suggestions.tsx index 9372c2a091de3..921531b5ce105 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover_suggestions.tsx +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/components/options_list_popover_suggestions.tsx @@ -58,7 +58,7 @@ export const OptionsListPopoverSuggestions = ({ api.invalidSelections$, api.availableOptions$, api.totalCardinality$, - api.dataLoading, + api.dataLoading$, api.fieldFormatter, api.parentApi.allowExpensiveQueries$ ); diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/fetch_and_validate.tsx b/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/fetch_and_validate.tsx index ca71fc46a72c1..595bacb2bc5aa 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/fetch_and_validate.tsx +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/fetch_and_validate.tsx @@ -33,7 +33,7 @@ export function fetchAndValidate$({ api, stateManager, }: { - api: Pick & + api: Pick & Pick & { controlFetch$: Observable; loadingSuggestions$: BehaviorSubject; @@ -49,7 +49,7 @@ export function fetchAndValidate$({ let abortController: AbortController | undefined; return combineLatest([ - api.dataViews, + api.dataViews$, api.field$, api.controlFetch$, api.parentApi.allowExpensiveQueries$, diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/get_options_list_control_factory.tsx b/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/get_options_list_control_factory.tsx index 5c990fae85d19..b5e33657efc95 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/get_options_list_control_factory.tsx +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/options_list_control/get_options_list_control_factory.tsx @@ -126,7 +126,7 @@ export const getOptionsListControlFactory = (): DataControlFactory< const loadingSuggestions$ = new BehaviorSubject(false); const dataLoadingSubscription = combineLatest([ loadingSuggestions$, - dataControl.api.dataLoading, + dataControl.api.dataLoading$, ]) .pipe( debounceTime(100), // debounce set loading so that it doesn't flash as the user types @@ -188,7 +188,7 @@ export const getOptionsListControlFactory = (): DataControlFactory< if (Object.hasOwn(result, 'error')) { dataControl.api.setBlockingError((result as { error: Error }).error); return; - } else if (dataControl.api.blockingError.getValue()) { + } else if (dataControl.api.blockingError$.getValue()) { // otherwise, if there was a previous error, clear it dataControl.api.setBlockingError(undefined); } @@ -231,7 +231,7 @@ export const getOptionsListControlFactory = (): DataControlFactory< }); /** Output filters when selections change */ const outputFilterSubscription = combineLatest([ - dataControl.api.dataViews, + dataControl.api.dataViews$, dataControl.stateManager.fieldName, selections.selectedOptions$, selections.existsSelected$, @@ -263,7 +263,7 @@ export const getOptionsListControlFactory = (): DataControlFactory< const api = buildApi( { ...dataControl.api, - dataLoading: dataLoading$, + dataLoading$, getTypeDisplayName: OptionsListStrings.control.getDisplayName, serializeState: () => { const { rawState: dataControlState, references } = dataControl.serialize(); diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/get_range_slider_control_factory.test.tsx b/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/get_range_slider_control_factory.test.tsx index 785ff41a7e48d..026abce4538b7 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/get_range_slider_control_factory.test.tsx +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/get_range_slider_control_factory.test.tsx @@ -147,7 +147,7 @@ describe('RangesliderControlApi', () => { controlGroupApi ); expect(api.filters$.value).toBeUndefined(); - expect(api.blockingError.value?.message).toEqual( + expect(api.blockingError$.value?.message).toEqual( 'no data view found for id notGonnaFindMeDataView' ); }); diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/get_range_slider_control_factory.tsx b/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/get_range_slider_control_factory.tsx index c691ef8181fbe..c91e2aa34b11e 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/get_range_slider_control_factory.tsx +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/get_range_slider_control_factory.tsx @@ -85,7 +85,7 @@ export const getRangesliderControlFactory = (): DataControlFactory< const api = buildApi( { ...dataControl.api, - dataLoading: dataLoading$, + dataLoading$, getTypeDisplayName: RangeSliderStrings.control.getDisplayName, serializeState: () => { const { rawState: dataControlState, references } = dataControl.serialize(); @@ -117,7 +117,7 @@ export const getRangesliderControlFactory = (): DataControlFactory< const dataLoadingSubscription = combineLatest([ loadingMinMax$, loadingHasNoResults$, - dataControl.api.dataLoading, + dataControl.api.dataLoading$, ]) .pipe( debounceTime(100), @@ -142,11 +142,11 @@ export const getRangesliderControlFactory = (): DataControlFactory< const min$ = new BehaviorSubject(undefined); const minMaxSubscription = minMax$({ controlFetch$, - dataViews$: dataControl.api.dataViews, + dataViews$: dataControl.api.dataViews$, fieldName$: dataControl.stateManager.fieldName, setIsLoading: (isLoading: boolean) => { // clear previous loading error on next loading start - if (isLoading && dataControl.api.blockingError.value) { + if (isLoading && dataControl.api.blockingError$.value) { dataControl.api.setBlockingError(undefined); } loadingMinMax$.next(isLoading); @@ -171,7 +171,7 @@ export const getRangesliderControlFactory = (): DataControlFactory< ); const outputFilterSubscription = combineLatest([ - dataControl.api.dataViews, + dataControl.api.dataViews$, dataControl.stateManager.fieldName, selections.value$, ]) @@ -201,7 +201,7 @@ export const getRangesliderControlFactory = (): DataControlFactory< const selectionHasNoResults$ = new BehaviorSubject(false); const hasNotResultsSubscription = hasNoResults$({ controlFetch$, - dataViews$: dataControl.api.dataViews, + dataViews$: dataControl.api.dataViews$, rangeFilters$: dataControl.api.filters$, ignoreParentSettings$: controlGroupApi.ignoreParentSettings$, setIsLoading: (isLoading: boolean) => { diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/has_no_results.ts b/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/has_no_results.ts index 5b5cfd33788cb..fae0f816492f5 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/has_no_results.ts +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/has_no_results.ts @@ -25,7 +25,7 @@ export function hasNoResults$({ setIsLoading, }: { controlFetch$: Observable; - dataViews$?: PublishesDataViews['dataViews']; + dataViews$?: PublishesDataViews['dataViews$']; rangeFilters$: DataControlApi['filters$']; ignoreParentSettings$: ControlGroupApi['ignoreParentSettings$']; setIsLoading: (isLoading: boolean) => void; diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/min_max.ts b/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/min_max.ts index f118e2da24c9b..e38902bee5a4f 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/min_max.ts +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/range_slider/min_max.ts @@ -26,7 +26,7 @@ export function minMax$({ }: { controlFetch$: Observable; controlGroupApi: ControlGroupApi; - dataViews$: PublishesDataViews['dataViews']; + dataViews$: PublishesDataViews['dataViews$']; fieldName$: PublishingSubject; setIsLoading: (isLoading: boolean) => void; }) { diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/types.ts b/src/platform/plugins/shared/controls/public/controls/data_controls/types.ts index 24eb9e73fb49e..cc5b47f5c8a07 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/types.ts +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/types.ts @@ -12,7 +12,7 @@ import { FieldFormatConvertFunction } from '@kbn/field-formats-plugin/common'; import { HasEditCapabilities, PublishesDataViews, - PublishesPanelTitle, + PublishesTitle, PublishingSubject, } from '@kbn/presentation-publishing'; @@ -29,7 +29,7 @@ export interface PublishesField { } export type DataControlApi = DefaultControlApi & - Omit & // control titles cannot be hidden + Omit & // control titles cannot be hidden HasEditCapabilities & PublishesDataViews & PublishesField & diff --git a/src/platform/plugins/shared/controls/public/controls/initialize_default_control_api.tsx b/src/platform/plugins/shared/controls/public/controls/initialize_default_control_api.tsx index 77227e0c13eed..61a5d0dcdc8eb 100644 --- a/src/platform/plugins/shared/controls/public/controls/initialize_default_control_api.tsx +++ b/src/platform/plugins/shared/controls/public/controls/initialize_default_control_api.tsx @@ -23,8 +23,8 @@ export const initializeDefaultControlApi = ( comparators: StateComparators; serialize: () => SerializedPanelState; } => { - const dataLoading = new BehaviorSubject(false); - const blockingError = new BehaviorSubject(undefined); + const dataLoading$ = new BehaviorSubject(false); + const blockingError$ = new BehaviorSubject(undefined); const grow = new BehaviorSubject(state.grow); const width = new BehaviorSubject(state.width); @@ -32,10 +32,10 @@ export const initializeDefaultControlApi = ( api: { grow, width, - dataLoading, - blockingError, - setBlockingError: (error) => blockingError.next(error), - setDataLoading: (loading) => dataLoading.next(loading), + dataLoading$, + blockingError$, + setBlockingError: (error) => blockingError$.next(error), + setDataLoading: (loading) => dataLoading$.next(loading), }, comparators: { grow: [grow, (newGrow: boolean | undefined) => grow.next(newGrow)], diff --git a/src/platform/plugins/shared/controls/public/controls/mocks/control_mocks.ts b/src/platform/plugins/shared/controls/public/controls/mocks/control_mocks.ts index 128e89c5c6028..8b94ef9c4a256 100644 --- a/src/platform/plugins/shared/controls/public/controls/mocks/control_mocks.ts +++ b/src/platform/plugins/shared/controls/public/controls/mocks/control_mocks.ts @@ -42,7 +42,7 @@ export const getMockedBuildApi = ...api, uuid, parentApi: controlGroupApi ?? getMockedControlGroupApi(), - unsavedChanges: new BehaviorSubject | undefined>(undefined), + unsavedChanges$: new BehaviorSubject | undefined>(undefined), resetUnsavedChanges: () => { return true; }, diff --git a/src/platform/plugins/shared/controls/public/controls/timeslider_control/get_timeslider_control_factory.test.tsx b/src/platform/plugins/shared/controls/public/controls/timeslider_control/get_timeslider_control_factory.test.tsx index 44574757837ce..b97eb38725719 100644 --- a/src/platform/plugins/shared/controls/public/controls/timeslider_control/get_timeslider_control_factory.test.tsx +++ b/src/platform/plugins/shared/controls/public/controls/timeslider_control/get_timeslider_control_factory.test.tsx @@ -47,7 +47,7 @@ describe('TimesliderControlApi', () => { ...api, uuid, parentApi: controlGroupApi, - unsavedChanges: new BehaviorSubject | undefined>(undefined), + unsavedChanges$: new BehaviorSubject | undefined>(undefined), resetUnsavedChanges: () => { return true; }, diff --git a/src/platform/plugins/shared/controls/public/controls/timeslider_control/get_timeslider_control_factory.tsx b/src/platform/plugins/shared/controls/public/controls/timeslider_control/get_timeslider_control_factory.tsx index 7e81fa075334e..dcd09097fd8f4 100644 --- a/src/platform/plugins/shared/controls/public/controls/timeslider_control/get_timeslider_control_factory.tsx +++ b/src/platform/plugins/shared/controls/public/controls/timeslider_control/get_timeslider_control_factory.tsx @@ -194,7 +194,7 @@ export const getTimesliderControlFactory = (): ControlFactory< const dashboardDataLoading$ = apiHasParentApi(controlGroupApi) && apiPublishesDataLoading(controlGroupApi.parentApi) - ? controlGroupApi.parentApi.dataLoading + ? controlGroupApi.parentApi.dataLoading$ : new BehaviorSubject(false); const waitForDashboardPanelsToLoad$ = dashboardDataLoading$.pipe( // debounce to give time for panels to start loading if they are going to load from time changes @@ -212,7 +212,7 @@ export const getTimesliderControlFactory = (): ControlFactory< const api = buildApi( { ...defaultControl.api, - defaultPanelTitle: new BehaviorSubject(displayName), + defaultTitle$: new BehaviorSubject(displayName), timeslice$, serializeState: () => { const { rawState: defaultControlState } = defaultControl.serialize(); diff --git a/src/platform/plugins/shared/controls/public/controls/timeslider_control/types.ts b/src/platform/plugins/shared/controls/public/controls/timeslider_control/types.ts index 48c7a5a76469c..a97c8c8e4672b 100644 --- a/src/platform/plugins/shared/controls/public/controls/timeslider_control/types.ts +++ b/src/platform/plugins/shared/controls/public/controls/timeslider_control/types.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { PublishesPanelTitle, PublishesTimeslice } from '@kbn/presentation-publishing'; +import type { PublishesTitle, PublishesTimeslice } from '@kbn/presentation-publishing'; import type { DefaultControlState } from '../../../common'; import type { DefaultControlApi } from '../types'; @@ -21,5 +21,5 @@ export interface TimesliderControlState extends DefaultControlState { } export type TimesliderControlApi = DefaultControlApi & - Pick & + Pick & PublishesTimeslice; diff --git a/src/platform/plugins/shared/controls/public/controls/types.ts b/src/platform/plugins/shared/controls/public/controls/types.ts index 36b5ae6571169..a998ea4d54c75 100644 --- a/src/platform/plugins/shared/controls/public/controls/types.ts +++ b/src/platform/plugins/shared/controls/public/controls/types.ts @@ -18,7 +18,7 @@ import { PublishesBlockingError, PublishesDataLoading, PublishesDisabledActionIds, - PublishesPanelTitle, + PublishesTitle, PublishesUnsavedChanges, PublishingSubject, StateComparators, @@ -35,7 +35,7 @@ export interface HasCustomPrepend { export type DefaultControlApi = PublishesDataLoading & PublishesBlockingError & PublishesUnsavedChanges & - Partial & + Partial & CanClearSelections & HasType & HasUniqueId & @@ -49,7 +49,7 @@ export type DefaultControlApi = PublishesDataLoading & export type ControlApiRegistration = Omit< ControlApi, - 'uuid' | 'parentApi' | 'type' | 'unsavedChanges' | 'resetUnsavedChanges' + 'uuid' | 'parentApi' | 'type' | 'unsavedChanges$' | 'resetUnsavedChanges' >; export type ControlApiInitialization = diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_actions/clone_panel_action.test.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_actions/clone_panel_action.test.tsx index 9f1063b4f5298..14f938a787a86 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_actions/clone_panel_action.test.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_actions/clone_panel_action.test.tsx @@ -20,7 +20,7 @@ describe('Clone panel action', () => { context = { embeddable: { uuid: 'superId', - viewMode: new BehaviorSubject('edit'), + viewMode$: new BehaviorSubject('edit'), serializeState: () => { return { rawState: {}, @@ -45,7 +45,7 @@ describe('Clone panel action', () => { }); it('is incompatible when view mode is view', async () => { - (context.embeddable as PublishesViewMode).viewMode = new BehaviorSubject('view'); + (context.embeddable as PublishesViewMode).viewMode$ = new BehaviorSubject('view'); expect(await action.isCompatible(context)).toBe(false); }); diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_actions/clone_panel_action.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_actions/clone_panel_action.tsx index 82a5b02059fd5..c68dd9a8363a7 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_actions/clone_panel_action.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_actions/clone_panel_action.tsx @@ -58,7 +58,9 @@ export class ClonePanelAction implements Action { public async isCompatible({ embeddable }: EmbeddableApiContext) { if (!isApiCompatible(embeddable)) return false; - return Boolean(!embeddable.blockingError?.value && getInheritedViewMode(embeddable) === 'edit'); + return Boolean( + !embeddable.blockingError$?.value && getInheritedViewMode(embeddable) === 'edit' + ); } public async execute({ embeddable }: EmbeddableApiContext) { diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_actions/copy_to_dashboard_modal.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_actions/copy_to_dashboard_modal.tsx index 9c04e0fff1e2c..ba2633a868b81 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_actions/copy_to_dashboard_modal.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_actions/copy_to_dashboard_modal.tsx @@ -48,7 +48,7 @@ export function CopyToDashboardModal({ api, closeModal }: CopyToDashboardModalPr null ); - const dashboardId = api.parentApi.savedObjectId.value; + const dashboardId = api.parentApi.savedObjectId$.value; const onSubmit = useCallback(() => { const dashboard = api.parentApi; diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_actions/expand_panel_action.test.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_actions/expand_panel_action.test.tsx index 1ebf937e470e5..a2806a641db6a 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_actions/expand_panel_action.test.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_actions/expand_panel_action.test.tsx @@ -13,17 +13,17 @@ import { ExpandPanelActionApi, ExpandPanelAction } from './expand_panel_action'; describe('Expand panel action', () => { let action: ExpandPanelAction; let context: { embeddable: ExpandPanelActionApi }; - let expandPanelIdSubject: BehaviorSubject; + let expandedPanelId$: BehaviorSubject; beforeEach(() => { - expandPanelIdSubject = new BehaviorSubject(undefined); + expandedPanelId$ = new BehaviorSubject(undefined); action = new ExpandPanelAction(); context = { embeddable: { uuid: 'superId', parentApi: { expandPanel: jest.fn(), - expandedPanelId: expandPanelIdSubject, + expandedPanelId$, }, }, }; @@ -43,19 +43,19 @@ describe('Expand panel action', () => { it('calls onChange when expandedPanelId changes', async () => { const onChange = jest.fn(); action.subscribeToCompatibilityChanges(context, onChange); - expandPanelIdSubject.next('superPanelId'); + expandedPanelId$.next('superPanelId'); expect(onChange).toHaveBeenCalledWith(true, action); }); it('returns the correct icon based on expanded panel id', async () => { expect(await action.getIconType(context)).toBe('expand'); - expandPanelIdSubject.next('superPanelId'); + expandedPanelId$.next('superPanelId'); expect(await action.getIconType(context)).toBe('minimize'); }); it('returns the correct display name based on expanded panel id', async () => { expect(await action.getDisplayName(context)).toBe('Maximize'); - expandPanelIdSubject.next('superPanelId'); + expandedPanelId$.next('superPanelId'); expect(await action.getDisplayName(context)).toBe('Minimize'); }); diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_actions/expand_panel_action.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_actions/expand_panel_action.tsx index 1e50decc9cadc..a1d915deec22b 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_actions/expand_panel_action.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_actions/expand_panel_action.tsx @@ -34,14 +34,14 @@ export class ExpandPanelAction implements Action { public getDisplayName({ embeddable }: EmbeddableApiContext) { if (!isApiCompatible(embeddable)) throw new IncompatibleActionError(); - return embeddable.parentApi.expandedPanelId.value + return embeddable.parentApi.expandedPanelId$.value ? dashboardExpandPanelActionStrings.getMinimizeTitle() : dashboardExpandPanelActionStrings.getMaximizeTitle(); } public getIconType({ embeddable }: EmbeddableApiContext) { if (!isApiCompatible(embeddable)) throw new IncompatibleActionError(); - return embeddable.parentApi.expandedPanelId.value ? 'minimize' : 'expand'; + return embeddable.parentApi.expandedPanelId$.value ? 'minimize' : 'expand'; } public async isCompatible({ embeddable }: EmbeddableApiContext) { @@ -57,7 +57,7 @@ export class ExpandPanelAction implements Action { onChange: (isCompatible: boolean, action: ExpandPanelAction) => void ) { if (!isApiCompatible(embeddable)) return; - return embeddable.parentApi.expandedPanelId.pipe(skip(1)).subscribe(() => { + return embeddable.parentApi.expandedPanelId$.pipe(skip(1)).subscribe(() => { onChange(isApiCompatible(embeddable), this); }); } diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_actions/export_csv_action.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_actions/export_csv_action.tsx index e323c4d1c77ea..150e750ef7f8b 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_actions/export_csv_action.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_actions/export_csv_action.tsx @@ -18,11 +18,7 @@ import { apiHasInspectorAdapters, type Adapters, } from '@kbn/inspector-plugin/public'; -import { - EmbeddableApiContext, - PublishesPanelTitle, - getPanelTitle, -} from '@kbn/presentation-publishing'; +import { EmbeddableApiContext, PublishesTitle, getTitle } from '@kbn/presentation-publishing'; import { coreServices, fieldFormatService } from '../services/kibana_services'; import { dashboardExportCsvActionStrings } from './_dashboard_actions_strings'; import { ACTION_EXPORT_CSV } from './constants'; @@ -32,7 +28,7 @@ export type ExportContext = EmbeddableApiContext & { asString?: boolean; }; -export type ExportCsvActionApi = HasInspectorAdapters & Partial; +export type ExportCsvActionApi = HasInspectorAdapters & Partial; const isApiCompatible = (api: unknown | null): api is ExportCsvActionApi => Boolean(apiHasInspectorAdapters(api)); @@ -90,7 +86,7 @@ export class ExportCSVAction implements Action { const postFix = datatables.length > 1 ? `-${i + 1}` : ''; const untitledFilename = dashboardExportCsvActionStrings.getUntitledFilename(); - memo[`${getPanelTitle(embeddable) || untitledFilename}${postFix}.csv`] = { + memo[`${getTitle(embeddable) || untitledFilename}${postFix}.csv`] = { content: exporters.datatableToCSV(datatable, { csvSeparator: coreServices.uiSettings.get('csv:separator', ','), quoteValues: coreServices.uiSettings.get('csv:quoteValues', true), diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.test.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.test.tsx index b077e48b93fa1..ec6913d97b954 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.test.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.test.tsx @@ -59,15 +59,15 @@ describe('filters notification popover', () => { updateFilters = (filters) => filtersSubject.next(filters); const querySubject = new BehaviorSubject(undefined); updateQuery = (query) => querySubject.next(query); - const viewModeSubject = new BehaviorSubject('view'); - updateViewMode = (viewMode) => viewModeSubject.next(viewMode); + const viewMode$ = new BehaviorSubject('view'); + updateViewMode = (viewMode) => viewMode$.next(viewMode); api = { uuid: 'testId', filters$: filtersSubject, query$: querySubject, parentApi: { - viewMode: viewModeSubject, + viewMode$, }, }; }); diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.tsx index 243ececf6830b..8a90ecc509f8a 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_actions/filters_notification_popover.tsx @@ -77,7 +77,7 @@ export function FiltersNotificationPopover({ api }: { api: FiltersNotificationAc }, [api, setDisableEditButton]); const [dataViews, parentViewMode] = useBatchedOptionalPublishingSubjects( - api.parentApi?.dataViews, + api.parentApi?.dataViews$, getViewModeSubject(api ?? undefined) ); diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_actions/library_add_action.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_actions/library_add_action.tsx index c74c1d45f2b54..7f464289c36f1 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_actions/library_add_action.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_actions/library_add_action.tsx @@ -18,14 +18,14 @@ import { HasType, HasTypeDisplayName, HasUniqueId, - PublishesPanelTitle, + PublishesTitle, apiCanAccessViewMode, apiHasLibraryTransforms, apiHasParentApi, apiHasType, apiHasUniqueId, getInheritedViewMode, - getPanelTitle, + getTitle, } from '@kbn/presentation-publishing'; import { OnSaveProps, @@ -44,7 +44,7 @@ export type AddPanelToLibraryActionApi = CanAccessViewMode & HasUniqueId & HasLibraryTransforms & HasParentApi> & - Partial; + Partial; const isApiCompatible = (api: unknown | null): api is AddPanelToLibraryActionApi => Boolean( @@ -79,7 +79,8 @@ export class AddToLibraryAction implements Action { public async execute({ embeddable }: EmbeddableApiContext) { if (!isApiCompatible(embeddable)) throw new IncompatibleActionError(); - const lastTitle = getPanelTitle(embeddable); + + const lastTitle = getTitle(embeddable); try { const { byRefPackage, libraryTitle } = await new Promise<{ byRefPackage: PanelPackage; diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_actions/library_unlink_action.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_actions/library_unlink_action.tsx index 8c496d8374005..295c7d546688a 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_actions/library_unlink_action.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_actions/library_unlink_action.tsx @@ -15,14 +15,14 @@ import { HasParentApi, HasType, HasUniqueId, - PublishesPanelTitle, + PublishesTitle, apiCanAccessViewMode, apiHasLibraryTransforms, apiHasParentApi, apiHasType, apiHasUniqueId, getInheritedViewMode, - getPanelTitle, + getTitle, } from '@kbn/presentation-publishing'; import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; @@ -35,7 +35,7 @@ export type UnlinkPanelFromLibraryActionApi = CanAccessViewMode & HasType & HasUniqueId & HasParentApi> & - Partial; + Partial; export const isApiCompatible = (api: unknown | null): api is UnlinkPanelFromLibraryActionApi => Boolean( @@ -73,7 +73,7 @@ export class UnlinkFromLibraryAction implements Action { public async execute({ embeddable }: EmbeddableApiContext) { if (!isApiCompatible(embeddable)) throw new IncompatibleActionError(); - const title = getPanelTitle(embeddable); + const title = getTitle(embeddable); try { const { references, rawState } = embeddable.getSerializedStateByValue(); await embeddable.parentApi.replacePanel(embeddable.uuid, { diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_api/data_loading_manager.ts b/src/platform/plugins/shared/dashboard/public/dashboard_api/data_loading_manager.ts index 064ea20672d63..bda7c13f9f5bd 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_api/data_loading_manager.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_api/data_loading_manager.ts @@ -25,7 +25,7 @@ export function initializeDataLoadingManager( boolean | undefined >( { children$ }, - 'dataLoading', + 'dataLoading$', apiPublishesDataLoading, undefined, // flatten method @@ -38,7 +38,7 @@ export function initializeDataLoadingManager( return { api: { - dataLoading: dataLoading$, + dataLoading$, }, internalApi: { waitForPanelsToLoad$: dataLoading$.pipe( diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_api/data_views_manager.ts b/src/platform/plugins/shared/dashboard/public/dashboard_api/data_views_manager.ts index 000c1e815b2b1..ba3032c656c0d 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_api/data_views_manager.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_api/data_views_manager.ts @@ -25,17 +25,17 @@ export function initializeDataViewsManager( controlGroupApi$: PublishingSubject, children$: PublishingSubject<{ [key: string]: unknown }> ) { - const dataViews = new BehaviorSubject([]); + const dataViews$ = new BehaviorSubject([]); const controlGroupDataViewsPipe: Observable = controlGroupApi$.pipe( switchMap((controlGroupApi) => { - return controlGroupApi ? controlGroupApi.dataViews : of([]); + return controlGroupApi ? controlGroupApi.dataViews$ : of([]); }) ); const childDataViewsPipe = combineCompatibleChildrenApis( { children$ }, - 'dataViews', + 'dataViews$', apiPublishesDataViews, [] ); @@ -57,13 +57,13 @@ export function initializeDataViewsManager( return uniqBy(allDataViews, 'id'); }) ) - .subscribe((newDataViews) => { - dataViews.next(newDataViews); + .subscribe((nextDataViews) => { + dataViews$.next(nextDataViews); }); return { api: { - dataViews, + dataViews$, }, cleanup: () => { dataViewsSubscription.unsubscribe(); diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_api/get_dashboard_api.ts b/src/platform/plugins/shared/dashboard/public/dashboard_api/get_dashboard_api.ts index 2f6e680f77871..9ee38abb73d31 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_api/get_dashboard_api.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_api/get_dashboard_api.ts @@ -127,7 +127,7 @@ export function getDashboardApi({ ...settingsManager.internalApi.getState(), ...unifiedSearchState, panels, - viewMode: viewModeManager.api.viewMode.value, + viewMode: viewModeManager.api.viewMode$.value, }; const controlGroupApi = controlGroupApi$.value; @@ -163,7 +163,7 @@ export function getDashboardApi({ controlGroupApi$, executionContext: { type: 'dashboard', - description: settingsManager.api.panelTitle.value, + description: settingsManager.api.title$.value, }, fullScreenMode$, getAppContext: () => { @@ -185,7 +185,7 @@ export function getDashboardApi({ const saveResult = await openSaveModal({ isManaged, lastSavedId: savedObjectId$.value, - viewMode: viewModeManager.api.viewMode.value, + viewMode: viewModeManager.api.viewMode$.value, ...getState(), }); @@ -225,7 +225,7 @@ export function getDashboardApi({ return; }, - savedObjectId: savedObjectId$, + savedObjectId$, setFullScreenMode: (fullScreenMode: boolean) => fullScreenMode$.next(fullScreenMode), setSavedObjectId: (id: string | undefined) => savedObjectId$.next(id), type: DASHBOARD_API_TYPE as 'dashboard', diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_api/panels_manager.ts b/src/platform/plugins/shared/dashboard/public/dashboard_api/panels_manager.ts index fc313cea93c85..3ca9489c417ea 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_api/panels_manager.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_api/panels_manager.ts @@ -22,10 +22,10 @@ import { import { StateComparators, apiHasLibraryTransforms, - apiPublishesPanelTitle, + apiPublishesTitle, apiPublishesUnsavedChanges, apiHasSerializableState, - getPanelTitle, + getTitle, } from '@kbn/presentation-publishing'; import { i18n } from '@kbn/i18n'; import { coreServices, usageCollectionService } from '../services/kibana_services'; @@ -163,7 +163,7 @@ export function initializePanelsManager( const titles: string[] = []; await asyncForEach(Object.keys(panels$.value), async (id) => { const childApi = await untilEmbeddableLoaded(id); - const title = apiPublishesPanelTitle(childApi) ? getPanelTitle(childApi) : ''; + const title = apiPublishesTitle(childApi) ? getTitle(childApi) : ''; if (title) titles.push(title); }); return titles; @@ -223,7 +223,7 @@ export function initializePanelsManager( } return await untilEmbeddableLoaded(newId); }, - canRemovePanels: () => trackPanel.expandedPanelId.value === undefined, + canRemovePanels: () => trackPanel.expandedPanelId$.value === undefined, children$, duplicatePanel: async (idToDuplicate: string) => { const panelToClone = getDashboardPanelFromId(idToDuplicate); @@ -234,7 +234,7 @@ export function initializePanelsManager( const id = v4(); const allPanelTitles = await getPanelTitles(); - const lastTitle = apiPublishesPanelTitle(childApi) ? getPanelTitle(childApi) ?? '' : ''; + const lastTitle = apiPublishesTitle(childApi) ? getTitle(childApi) ?? '' : ''; const newTitle = getClonedPanelTitle(allPanelTitles, lastTitle); /** diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_api/settings_manager.ts b/src/platform/plugins/shared/dashboard/public/dashboard_api/settings_manager.ts index e81a9dbba275b..dfa079effd152 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_api/settings_manager.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_api/settings_manager.ts @@ -8,7 +8,7 @@ */ import fastIsEqual from 'fast-deep-equal'; -import { StateComparators, initializeTitles } from '@kbn/presentation-publishing'; +import { StateComparators, initializeTitleManager } from '@kbn/presentation-publishing'; import { BehaviorSubject } from 'rxjs'; import { DashboardSettings, DashboardState } from './types'; import { DEFAULT_DASHBOARD_INPUT } from './default_dashboard_input'; @@ -36,7 +36,7 @@ export function initializeSettingsManager(initialState?: DashboardState) { function setTags(tags: string[]) { if (!fastIsEqual(tags, tags$.value)) tags$.next(tags); } - const titleManager = initializeTitles(initialState ?? {}); + const titleManager = initializeTitleManager(initialState ?? {}); const timeRestore$ = new BehaviorSubject( initialState?.timeRestore ?? DEFAULT_DASHBOARD_INPUT.timeRestore ); @@ -52,7 +52,7 @@ export function initializeSettingsManager(initialState?: DashboardState) { function getSettings() { return { - ...titleManager.serializeTitles(), + ...titleManager.serialize(), syncColors: syncColors$.value, syncCursor: syncCursor$.value, syncTooltips: syncTooltips$.value, @@ -69,14 +69,14 @@ export function initializeSettingsManager(initialState?: DashboardState) { setTags(settings.tags); setTimeRestore(settings.timeRestore); setUseMargins(settings.useMargins); - titleManager.titlesApi.setHidePanelTitle(settings.hidePanelTitles); - titleManager.titlesApi.setPanelDescription(settings.description); - titleManager.titlesApi.setPanelTitle(settings.title); + titleManager.api.setHideTitle(settings.hidePanelTitles); + titleManager.api.setDescription(settings.description); + titleManager.api.setTitle(settings.title); } return { api: { - ...titleManager.titlesApi, + ...titleManager.api, getSettings, settings: { syncColors$, @@ -89,7 +89,7 @@ export function initializeSettingsManager(initialState?: DashboardState) { timeRestore$, }, comparators: { - ...titleManager.titleComparators, + ...titleManager.comparators, syncColors: [syncColors$, setSyncColors], syncCursor: [syncCursor$, setSyncCursor], syncTooltips: [syncTooltips$, setSyncTooltips], diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_api/track_panel.ts b/src/platform/plugins/shared/dashboard/public/dashboard_api/track_panel.ts index 81e206d006495..bdbe14c96ef7c 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_api/track_panel.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_api/track_panel.ts @@ -25,7 +25,7 @@ export function initializeTrackPanel(untilEmbeddableLoaded: (id: string) => Prom } return { - expandedPanelId: expandedPanelId$, + expandedPanelId$, expandPanel: (panelId: string) => { const isPanelExpanded = panelId === expandedPanelId$.value; diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_api/types.ts b/src/platform/plugins/shared/dashboard/public/dashboard_api/types.ts index 8b5c23bdf9bf2..ac0c477e98595 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_api/types.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_api/types.ts @@ -36,8 +36,8 @@ import { HasUniqueId, PublishesDataLoading, PublishesDataViews, - PublishesPanelDescription, - PublishesPanelTitle, + PublishesDescription, + PublishesTitle, PublishesSavedObjectId, PublishesUnifiedSearch, PublishesViewMode, @@ -131,8 +131,8 @@ export type DashboardApi = CanExpandPanels & PresentationContainer & PublishesDataLoading & PublishesDataViews & - PublishesPanelDescription & - Pick & + PublishesDescription & + Pick & PublishesReload & PublishesSavedObjectId & PublishesSearchSession & diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_api/unsaved_changes_manager.ts b/src/platform/plugins/shared/dashboard/public/dashboard_api/unsaved_changes_manager.ts index fef1175916a03..337e7d86893fe 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_api/unsaved_changes_manager.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_api/unsaved_changes_manager.ts @@ -41,7 +41,7 @@ export function initializeUnsavedChangesManager({ controlGroupApi$: PublishingSubject; lastSavedState: DashboardState; panelsManager: ReturnType; - savedObjectId$: PublishesSavedObjectId['savedObjectId']; + savedObjectId$: PublishesSavedObjectId['savedObjectId$']; settingsManager: ReturnType; viewModeManager: ReturnType; unifiedSearchManager: ReturnType; @@ -66,12 +66,12 @@ export function initializeUnsavedChangesManager({ ); const unsavedChangesSubscription = combineLatest([ - dashboardUnsavedChanges.api.unsavedChanges, + dashboardUnsavedChanges.api.unsavedChanges$, childrenUnsavedChanges$(panelsManager.api.children$), controlGroupApi$.pipe( skipWhile((controlGroupApi) => !controlGroupApi), switchMap((controlGroupApi) => { - return controlGroupApi!.unsavedChanges; + return controlGroupApi!.unsavedChanges$; }) ), ]) diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_api/view_mode_manager.ts b/src/platform/plugins/shared/dashboard/public/dashboard_api/view_mode_manager.ts index 1ef1a19c9563e..24c0a0a6ba75a 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_api/view_mode_manager.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_api/view_mode_manager.ts @@ -47,7 +47,7 @@ export function initializeViewModeManager( return { api: { - viewMode: viewMode$, + viewMode$, setViewMode, }, comparators: { diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_app/dashboard_app.test.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_app/dashboard_app.test.tsx index c34893cc19da7..eecded7ab0906 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_app/dashboard_app.test.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_app/dashboard_app.test.tsx @@ -68,7 +68,7 @@ describe('Dashboard App', () => { await waitFor(() => { expect(expandPanelSpy).not.toHaveBeenCalled(); // this value should be undefined by default - expect(dashboardApi.expandedPanelId.getValue()).toBe(undefined); + expect(dashboardApi.expandedPanelId$.getValue()).toBe(undefined); // history should not be called expect(historySpy).toHaveBeenCalledTimes(0); expect(mockHistory.location.pathname).toBe('/'); @@ -78,7 +78,7 @@ describe('Dashboard App', () => { dashboardApi.expandPanel('123'); await waitFor(() => { - expect(dashboardApi.expandedPanelId.getValue()).toBe('123'); + expect(dashboardApi.expandedPanelId$.getValue()).toBe('123'); expect(historySpy).toHaveBeenCalledTimes(1); expect(mockHistory.location.pathname).toBe('/create/123'); }); @@ -96,7 +96,7 @@ describe('Dashboard App', () => { dashboardApi.expandPanel('456'); await waitFor(() => { - expect(dashboardApi.expandedPanelId.getValue()).toBe(undefined); + expect(dashboardApi.expandedPanelId$.getValue()).toBe(undefined); expect(historySpy).toHaveBeenCalledTimes(1); expect(mockHistory.location.pathname).toBe('/create'); }); diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_app/tab_title_setter/dashboard_tab_title_setter.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_app/tab_title_setter/dashboard_tab_title_setter.tsx index e102e6f898c9b..e2b09252b8dc7 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_app/tab_title_setter/dashboard_tab_title_setter.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_app/tab_title_setter/dashboard_tab_title_setter.tsx @@ -16,8 +16,8 @@ import { coreServices } from '../../services/kibana_services'; export const DashboardTabTitleSetter = ({ dashboardApi }: { dashboardApi: DashboardApi }) => { const [title, lastSavedId] = useBatchedPublishingSubjects( - dashboardApi.panelTitle, - dashboardApi.savedObjectId + dashboardApi.title$, + dashboardApi.savedObjectId$ ); /** diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx index 1cb5ef6dad964..d1493b9cfa761 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_app/top_nav/use_dashboard_menu_items.tsx @@ -43,11 +43,11 @@ export const useDashboardMenuItems = ({ const [dashboardTitle, hasOverlays, hasUnsavedChanges, lastSavedId, viewMode] = useBatchedPublishingSubjects( - dashboardApi.panelTitle, + dashboardApi.title$, dashboardApi.hasOverlays$, dashboardApi.hasUnsavedChanges$, - dashboardApi.savedObjectId, - dashboardApi.viewMode + dashboardApi.savedObjectId$, + dashboardApi.viewMode$ ); const disableTopNav = isSaveInProgress || hasOverlays; diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_app/url/search_sessions_integration.ts b/src/platform/plugins/shared/dashboard/public/dashboard_app/url/search_sessions_integration.ts index 64e10faa39dd0..e3d6d683f1a20 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_app/url/search_sessions_integration.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_app/url/search_sessions_integration.ts @@ -50,7 +50,7 @@ export function createSessionRestorationDataProvider( ): SearchSessionInfoProvider { return { getName: async () => - dashboardApi.panelTitle.value ?? dashboardApi.savedObjectId.value ?? dashboardApi.uuid, + dashboardApi.title$.value ?? dashboardApi.savedObjectId$.value ?? dashboardApi.uuid, getLocatorData: async () => ({ id: DASHBOARD_APP_LOCATOR, initialState: getLocatorParams({ dashboardApi, shouldRestoreSearchSession: false }), @@ -70,9 +70,9 @@ function getLocatorParams({ dashboardApi: DashboardApi; shouldRestoreSearchSession: boolean; }): DashboardLocatorParams { - const savedObjectId = dashboardApi.savedObjectId.value; + const savedObjectId = dashboardApi.savedObjectId$.value; return { - viewMode: dashboardApi.viewMode.value ?? 'view', + viewMode: dashboardApi.viewMode$.value ?? 'view', useHash: false, preserveSavedFilters: false, filters: dataService.query.filterManager.getFilters(), diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_app/url/url_utils.ts b/src/platform/plugins/shared/dashboard/public/dashboard_app/url/url_utils.ts index b739df4f91e94..41e28ba24f73b 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_app/url/url_utils.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_app/url/url_utils.ts @@ -112,13 +112,13 @@ export const startSyncingExpandedPanelState = ({ dashboardApi: DashboardApi; history: History; }) => { - const expandedPanelSubscription = dashboardApi?.expandedPanelId + const expandedPanelSubscription = dashboardApi?.expandedPanelId$ // skip the first value because we don't want to trigger a history.replace on initial load .pipe(skip(1)) .subscribe((expandedPanelId) => { history.replace({ ...history.location, - pathname: `${createDashboardEditUrl(dashboardApi.savedObjectId.value)}${ + pathname: `${createDashboardEditUrl(dashboardApi.savedObjectId$.value)}${ Boolean(expandedPanelId) ? `/${expandedPanelId}` : '' }`, }); diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.test.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.test.tsx index 6a8da6aa9f218..a8cdd21335c94 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.test.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.test.tsx @@ -23,7 +23,7 @@ visualizationsService.getAliases = jest.fn().mockReturnValue([{ name: 'lens' }]) describe('DashboardEmptyScreen', () => { function mountComponent(viewMode: ViewMode) { const mockDashboardApi = { - viewMode: new BehaviorSubject(viewMode), + viewMode$: new BehaviorSubject(viewMode), } as unknown as DashboardApi; return mountWithIntl( diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx index 36c2e1c0c16bb..a4e3953a25383 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx @@ -45,7 +45,7 @@ export function DashboardEmptyScreen() { const dashboardApi = useDashboardApi(); const isDarkTheme = useObservable(coreServices.theme.theme$)?.darkMode; - const viewMode = useStateFromPublishingSubject(dashboardApi.viewMode); + const viewMode = useStateFromPublishingSubject(dashboardApi.viewMode$); const isEditMode = useMemo(() => { return viewMode === 'edit'; }, [viewMode]); diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_container/component/grid/dashboard_grid.test.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_container/component/grid/dashboard_grid.test.tsx index 4a0c8ec92612c..9f86d6d6678d0 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_container/component/grid/dashboard_grid.test.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_container/component/grid/dashboard_grid.test.tsx @@ -29,7 +29,7 @@ jest.mock('./dashboard_grid_item', () => { const dashboardApi = mockUseDashboardApi(); const [expandedPanelId, focusedPanelId] = mockUseBatchedPublishingSubjects( - dashboardApi.expandedPanelId, + dashboardApi.expandedPanelId$, dashboardApi.focusedPanelId$ ); diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx index c5a7a0bf94d2c..bc243441aafac 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx @@ -28,10 +28,10 @@ export const DashboardGrid = ({ dashboardContainer }: { dashboardContainer?: HTM const panelRefs = useRef<{ [panelId: string]: React.Ref }>({}); const [expandedPanelId, panels, useMargins, viewMode] = useBatchedPublishingSubjects( - dashboardApi.expandedPanelId, + dashboardApi.expandedPanelId$, dashboardApi.panels$, dashboardApi.settings.useMargins$, - dashboardApi.viewMode + dashboardApi.viewMode$ ); const appFixedViewport = useAppFixedViewport(); diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx index e1d50a8d6c1d7..8a9ba1b4b9260 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx @@ -62,10 +62,10 @@ export const Item = React.forwardRef( ] = useBatchedPublishingSubjects( dashboardApi.highlightPanelId$, dashboardApi.scrollToPanelId$, - dashboardApi.expandedPanelId, + dashboardApi.expandedPanelId$, dashboardApi.focusedPanelId$, dashboardApi.settings.useMargins$, - dashboardApi.viewMode + dashboardApi.viewMode$ ); const expandPanel = expandedPanelId !== undefined && expandedPanelId === id; @@ -200,7 +200,7 @@ export const DashboardGridItem = React.forwardRef((props, const dashboardApi = useDashboardApi(); const [focusedPanelId, viewMode] = useBatchedPublishingSubjects( dashboardApi.focusedPanelId$, - dashboardApi.viewMode + dashboardApi.viewMode$ ); const deferBelowFoldEnabled = useMemo( diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_container/component/settings/settings_flyout.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_container/component/settings/settings_flyout.tsx index e0de277fc3c06..9b8f0ac5f0373 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_container/component/settings/settings_flyout.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_container/component/settings/settings_flyout.tsx @@ -65,7 +65,7 @@ export const DashboardSettingsFlyout = ({ onClose }: DashboardSettingsProps) => { title: localSettings.title, copyOnSave: false, - lastSavedTitle: dashboardApi.panelTitle.value ?? '', + lastSavedTitle: dashboardApi.title$.value ?? '', onTitleDuplicate, isTitleDuplicateConfirmed, } diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx index dbc90cd2c58ed..4d3e1367bffa7 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx @@ -41,11 +41,11 @@ export const DashboardViewport = ({ dashboardContainer }: { dashboardContainer?: fullScreenMode, ] = useBatchedPublishingSubjects( dashboardApi.controlGroupApi$, - dashboardApi.panelTitle, - dashboardApi.panelDescription, - dashboardApi.expandedPanelId, + dashboardApi.title$, + dashboardApi.description$, + dashboardApi.expandedPanelId$, dashboardApi.panels$, - dashboardApi.viewMode, + dashboardApi.viewMode$, dashboardApi.settings.useMargins$, dashboardApi.fullScreenMode$ ); diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_container/external_api/dashboard_renderer.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_container/external_api/dashboard_renderer.tsx index c6b5467e25be8..bf6826fb2df62 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_container/external_api/dashboard_renderer.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_container/external_api/dashboard_renderer.tsx @@ -157,7 +157,7 @@ const ParentClassController = ({ dashboardApi: DashboardApi; viewportRef: HTMLDivElement; }) => { - const maximizedPanelId = useStateFromPublishingSubject(dashboardApi.expandedPanelId); + const maximizedPanelId = useStateFromPublishingSubject(dashboardApi.expandedPanelId$); useLayoutEffect(() => { const parentDiv = viewportRef.parentElement; diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.tsx index 7cb39231e814a..0635cfa9f6995 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.tsx @@ -94,14 +94,14 @@ export function InternalDashboardTopNav({ title, viewMode, ] = useBatchedPublishingSubjects( - dashboardApi.dataViews, + dashboardApi.dataViews$, dashboardApi.focusedPanelId$, dashboardApi.fullScreenMode$, dashboardApi.hasUnsavedChanges$, - dashboardApi.savedObjectId, + dashboardApi.savedObjectId$, dashboardApi.query$, - dashboardApi.panelTitle, - dashboardApi.viewMode + dashboardApi.title$, + dashboardApi.viewMode$ ); const [savedQueryId, setSavedQueryId] = useState(); diff --git a/src/platform/plugins/shared/dashboard/public/mocks.tsx b/src/platform/plugins/shared/dashboard/public/mocks.tsx index 334bf9ee05208..b10e91d810ab8 100644 --- a/src/platform/plugins/shared/dashboard/public/mocks.tsx +++ b/src/platform/plugins/shared/dashboard/public/mocks.tsx @@ -73,8 +73,8 @@ export const mockControlGroupApi = { filters$: new BehaviorSubject(undefined), query$: new BehaviorSubject(undefined), timeslice$: new BehaviorSubject(undefined), - dataViews: new BehaviorSubject(undefined), - unsavedChanges: new BehaviorSubject(undefined), + dataViews$: new BehaviorSubject(undefined), + unsavedChanges$: new BehaviorSubject(undefined), } as unknown as ControlGroupApi; export function buildMockDashboardApi({ diff --git a/src/platform/plugins/shared/discover/public/embeddable/__mocks__/get_mocked_api.ts b/src/platform/plugins/shared/discover/public/embeddable/__mocks__/get_mocked_api.ts index 592cf3d80faef..6047fd6354453 100644 --- a/src/platform/plugins/shared/discover/public/embeddable/__mocks__/get_mocked_api.ts +++ b/src/platform/plugins/shared/discover/public/embeddable/__mocks__/get_mocked_api.ts @@ -27,21 +27,23 @@ export const getMockedSearchApi = ({ searchSource: SearchSource; savedSearch: SavedSearch; }) => { + const dataLoading$ = new BehaviorSubject(undefined); + const blockingError$ = new BehaviorSubject(undefined); return { api: { uuid: 'testEmbeddable', - savedObjectId: new BehaviorSubject(undefined), - dataViews: new BehaviorSubject([ + savedObjectId$: new BehaviorSubject(undefined), + dataViews$: new BehaviorSubject([ searchSource.getField('index') ?? dataViewMock, ]), - panelTitle: new BehaviorSubject(undefined), - defaultPanelTitle: new BehaviorSubject(undefined), - hidePanelTitle: new BehaviorSubject(false), + title$: new BehaviorSubject(undefined), + defaultTitle$: new BehaviorSubject(undefined), + hideTitle$: new BehaviorSubject(false), fetchContext$: new BehaviorSubject(undefined), timeRange$: new BehaviorSubject(undefined), setTimeRange: jest.fn(), - dataLoading: new BehaviorSubject(undefined), - blockingError: new BehaviorSubject(undefined), + dataLoading$, + blockingError$, fetchWarnings$: new BehaviorSubject([]), savedSearch$: new BehaviorSubject(savedSearch), }, @@ -60,5 +62,9 @@ export const getMockedSearchApi = ({ columnsMeta: new BehaviorSubject | undefined>(undefined), inspectorAdapters: new BehaviorSubject({}), }, + setters: { + setDataLoading: (dataLoading: boolean | undefined) => dataLoading$.next(dataLoading), + setBlockingError: (error: Error | undefined) => blockingError$.next(error), + }, }; }; diff --git a/src/platform/plugins/shared/discover/public/embeddable/actions/view_saved_search_action.test.ts b/src/platform/plugins/shared/discover/public/embeddable/actions/view_saved_search_action.test.ts index d6895beb2cee7..324b73682d5f5 100644 --- a/src/platform/plugins/shared/discover/public/embeddable/actions/view_saved_search_action.test.ts +++ b/src/platform/plugins/shared/discover/public/embeddable/actions/view_saved_search_action.test.ts @@ -27,7 +27,7 @@ const compatibleEmbeddableApi: SearchEmbeddableApi = { searchSource: { getField: jest.fn() }, } as unknown as SavedSearch), parentApi: { - viewMode: new BehaviorSubject('view'), + viewMode$: new BehaviorSubject('view'), }, } as unknown as SearchEmbeddableApi; @@ -54,7 +54,7 @@ describe('view saved search action', () => { const action = new ViewSavedSearchAction(applicationMock, services.locator); expect( await action.isCompatible({ - embeddable: { ...compatibleEmbeddableApi, viewMode: new BehaviorSubject(ViewMode.EDIT) }, + embeddable: { ...compatibleEmbeddableApi, viewMode$: new BehaviorSubject(ViewMode.EDIT) }, }) ).toBe(false); }); diff --git a/src/platform/plugins/shared/discover/public/embeddable/components/search_embeddable_grid_component.tsx b/src/platform/plugins/shared/discover/public/embeddable/components/search_embeddable_grid_component.tsx index 9c7a54054d265..746ecbcb705b3 100644 --- a/src/platform/plugins/shared/discover/public/embeddable/components/search_embeddable_grid_component.tsx +++ b/src/platform/plugins/shared/discover/public/embeddable/components/search_embeddable_grid_component.tsx @@ -77,9 +77,9 @@ export function SearchEmbeddableGridComponent({ columnsMeta, grid, ] = useBatchedPublishingSubjects( - api.dataLoading, + api.dataLoading$, api.savedSearch$, - api.savedObjectId, + api.savedObjectId$, api.fetchWarnings$, api.query$, api.filters$, @@ -98,10 +98,10 @@ export function SearchEmbeddableGridComponent({ const [panelTitle, panelDescription, savedSearchTitle, savedSearchDescription] = useBatchedOptionalPublishingSubjects( - api.panelTitle, - api.panelDescription, - api.defaultPanelTitle, - api.defaultPanelDescription + api.title$, + api.description$, + api.defaultTitle$, + api.defaultDescription$ ); const isEsql = useMemo(() => isEsqlMode(savedSearch), [savedSearch]); diff --git a/src/platform/plugins/shared/discover/public/embeddable/get_search_embeddable_factory.test.tsx b/src/platform/plugins/shared/discover/public/embeddable/get_search_embeddable_factory.test.tsx index dbabe1a6bfd28..68f88d9ac4674 100644 --- a/src/platform/plugins/shared/discover/public/embeddable/get_search_embeddable_factory.test.tsx +++ b/src/platform/plugins/shared/discover/public/embeddable/get_search_embeddable_factory.test.tsx @@ -137,10 +137,10 @@ describe('saved search embeddable', () => { const discoverComponent = render(); // wait for data fetching - expect(api.dataLoading.getValue()).toBe(true); + expect(api.dataLoading$.getValue()).toBe(true); resolveSearch(); await waitOneTick(); - expect(api.dataLoading.getValue()).toBe(false); + expect(api.dataLoading$.getValue()).toBe(false); expect(discoverComponent.queryByTestId('embeddedSavedSearchDocTable')).toBeInTheDocument(); await waitFor(() => @@ -173,10 +173,10 @@ describe('saved search embeddable', () => { const discoverComponent = render(); // wait for data fetching - expect(api.dataLoading.getValue()).toBe(true); + expect(api.dataLoading$.getValue()).toBe(true); resolveSearch(); await waitOneTick(); - expect(api.dataLoading.getValue()).toBe(false); + expect(api.dataLoading$.getValue()).toBe(false); expect(discoverComponent.queryByTestId('dscFieldStatsEmbeddedContent')).toBeInTheDocument(); }); @@ -200,13 +200,13 @@ describe('saved search embeddable', () => { await waitOneTick(); // wait for build to complete // wait for data fetching - expect(api.dataLoading.getValue()).toBe(true); + expect(api.dataLoading$.getValue()).toBe(true); resolveSearch(); await waitOneTick(); - expect(api.dataLoading.getValue()).toBe(false); + expect(api.dataLoading$.getValue()).toBe(false); expect(search).toHaveBeenCalledTimes(1); - api.setPanelTitle('custom title'); + api.setTitle('custom title'); await waitOneTick(); expect(search).toHaveBeenCalledTimes(1); }); @@ -318,10 +318,10 @@ describe('saved search embeddable', () => { const discoverComponent = render(); // wait for data fetching - expect(api.dataLoading.getValue()).toBe(true); + expect(api.dataLoading$.getValue()).toBe(true); resolveSearch(); await waitOneTick(); - expect(api.dataLoading.getValue()).toBe(false); + expect(api.dataLoading$.getValue()).toBe(false); const discoverGridComponent = discoverComponent.queryByTestId('discoverDocTable'); expect(discoverGridComponent).toBeInTheDocument(); diff --git a/src/platform/plugins/shared/discover/public/embeddable/get_search_embeddable_factory.tsx b/src/platform/plugins/shared/discover/public/embeddable/get_search_embeddable_factory.tsx index 7766765a75e52..265beaefaf0c8 100644 --- a/src/platform/plugins/shared/discover/public/embeddable/get_search_embeddable_factory.tsx +++ b/src/platform/plugins/shared/discover/public/embeddable/get_search_embeddable_factory.tsx @@ -21,7 +21,7 @@ import { FetchContext, getUnchangingComparator, initializeTimeRange, - initializeTitles, + initializeTitleManager, useBatchedPublishingSubjects, } from '@kbn/presentation-publishing'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; @@ -78,10 +78,8 @@ export const getSearchEmbeddableFactory = ({ /** Specific by-reference state */ const savedObjectId$ = new BehaviorSubject(initialState?.savedObjectId); - const defaultPanelTitle$ = new BehaviorSubject( - initialState?.savedObjectTitle - ); - const defaultPanelDescription$ = new BehaviorSubject( + const defaultTitle$ = new BehaviorSubject(initialState?.savedObjectTitle); + const defaultDescription$ = new BehaviorSubject( initialState?.savedObjectDescription ); @@ -97,7 +95,7 @@ export const getSearchEmbeddableFactory = ({ const fetchWarnings$ = new BehaviorSubject([]); /** Build API */ - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(initialState); + const titleManager = initializeTitleManager(initialState); const timeRange = initializeTimeRange(initialState); const searchEmbeddable = await initializeSearchEmbeddableApi(initialState, { discoverServices, @@ -105,18 +103,20 @@ export const getSearchEmbeddableFactory = ({ const unsubscribeFromFetch = initializeFetch({ api: { parentApi, - ...titlesApi, + ...titleManager.api, ...timeRange.api, savedSearch$: searchEmbeddable.api.savedSearch$, - dataViews: searchEmbeddable.api.dataViews, - savedObjectId: savedObjectId$, - dataLoading: dataLoading$, - blockingError: blockingError$, + dataViews$: searchEmbeddable.api.dataViews$, + savedObjectId$, + dataLoading$, + blockingError$, fetchContext$, fetchWarnings$, }, discoverServices, stateManager: searchEmbeddable.stateManager, + setDataLoading: (dataLoading: boolean | undefined) => dataLoading$.next(dataLoading), + setBlockingError: (error: Error | undefined) => blockingError$.next(error), }); const serialize = (savedObjectId?: string) => @@ -124,28 +124,28 @@ export const getSearchEmbeddableFactory = ({ uuid, initialState, savedSearch: searchEmbeddable.api.savedSearch$.getValue(), - serializeTitles, + serializeTitles: titleManager.serialize, serializeTimeRange: timeRange.serialize, savedObjectId, }); const api: SearchEmbeddableApi = buildApi( { - ...titlesApi, + ...titleManager.api, ...searchEmbeddable.api, ...timeRange.api, ...initializeEditApi({ uuid, parentApi, - partialApi: { ...searchEmbeddable.api, fetchContext$, savedObjectId: savedObjectId$ }, + partialApi: { ...searchEmbeddable.api, fetchContext$, savedObjectId$ }, discoverServices, isEditable: startServices.isEditable, }), - dataLoading: dataLoading$, - blockingError: blockingError$, - savedObjectId: savedObjectId$, - defaultPanelTitle: defaultPanelTitle$, - defaultPanelDescription: defaultPanelDescription$, + dataLoading$, + blockingError$, + savedObjectId$, + defaultTitle$, + defaultDescription$, hasTimeRange: () => { const fetchContext = fetchContext$.getValue(); return fetchContext?.timeslice !== undefined || fetchContext?.timeRange !== undefined; @@ -165,7 +165,7 @@ export const getSearchEmbeddableFactory = ({ ...api.savedSearch$.getValue(), title, }); - defaultPanelTitle$.next(title); + defaultTitle$.next(title); return savedObjectId!; }, checkForDuplicateTitle: (newTitle, isTitleDuplicateConfirmed, onTitleDuplicate) => @@ -180,16 +180,13 @@ export const getSearchEmbeddableFactory = ({ getInspectorAdapters: () => searchEmbeddable.stateManager.inspectorAdapters.getValue(), }, { - ...titleComparators, + ...titleManager.comparators, ...timeRange.comparators, ...searchEmbeddable.comparators, rawSavedObjectAttributes: getUnchangingComparator(), savedObjectId: [savedObjectId$, (value) => savedObjectId$.next(value)], - savedObjectTitle: [defaultPanelTitle$, (value) => defaultPanelTitle$.next(value)], - savedObjectDescription: [ - defaultPanelDescription$, - (value) => defaultPanelDescription$.next(value), - ], + savedObjectTitle: [defaultTitle$, (value) => defaultTitle$.next(value)], + savedObjectDescription: [defaultDescription$, (value) => defaultDescription$.next(value)], nonPersistedDisplayOptions: [ nonPersistedDisplayOptions$, (value) => nonPersistedDisplayOptions$.next(value), @@ -202,7 +199,7 @@ export const getSearchEmbeddableFactory = ({ Component: () => { const [savedSearch, dataViews] = useBatchedPublishingSubjects( api.savedSearch$, - api.dataViews + api.dataViews$ ); useEffect(() => { diff --git a/src/platform/plugins/shared/discover/public/embeddable/initialize_edit_api.test.ts b/src/platform/plugins/shared/discover/public/embeddable/initialize_edit_api.test.ts index 87e4010b96127..148a37977776c 100644 --- a/src/platform/plugins/shared/discover/public/embeddable/initialize_edit_api.test.ts +++ b/src/platform/plugins/shared/discover/public/embeddable/initialize_edit_api.test.ts @@ -45,14 +45,14 @@ describe('initialize edit api', () => { .mockReturnValueOnce('/mock-url'); if (dataView) { - mockedApi.dataViews.next([dataView]); + mockedApi.dataViews$.next([dataView]); } else { - mockedApi.dataViews.next([dataViewMock]); + mockedApi.dataViews$.next([dataViewMock]); } if (byValue) { - mockedApi.savedObjectId.next(undefined); + mockedApi.savedObjectId$.next(undefined); } else { - mockedApi.savedObjectId.next('test-id'); + mockedApi.savedObjectId$.next('test-id'); } await waitOneTick(); @@ -129,7 +129,7 @@ describe('initialize edit api', () => { discoverServiceMock.embeddable.getStateTransfer = jest.fn().mockImplementation(() => ({ navigateToEditor: mockedNavigate, })); - mockedApi.dataViews.next([dataViewMock]); + mockedApi.dataViews$.next([dataViewMock]); await waitOneTick(); const { onEdit } = initializeEditApi({ diff --git a/src/platform/plugins/shared/discover/public/embeddable/initialize_edit_api.ts b/src/platform/plugins/shared/discover/public/embeddable/initialize_edit_api.ts index acb489735d053..d6aeca947e337 100644 --- a/src/platform/plugins/shared/discover/public/embeddable/initialize_edit_api.ts +++ b/src/platform/plugins/shared/discover/public/embeddable/initialize_edit_api.ts @@ -29,8 +29,8 @@ export async function getAppTarget( partialApi: SavedSearchPartialApi, discoverServices: DiscoverServices ) { - const savedObjectId = partialApi.savedObjectId.getValue(); - const dataViews = partialApi.dataViews.getValue(); + const savedObjectId = partialApi.savedObjectId$.getValue(); + const dataViews = partialApi.dataViews$.getValue(); const locatorParams = getDiscoverLocatorParams(partialApi); // We need to use a redirect URL if this is a by value saved search using diff --git a/src/platform/plugins/shared/discover/public/embeddable/initialize_fetch.test.ts b/src/platform/plugins/shared/discover/public/embeddable/initialize_fetch.test.ts index 061a934dfa2b5..d9ce8304367b2 100644 --- a/src/platform/plugins/shared/discover/public/embeddable/initialize_fetch.test.ts +++ b/src/platform/plugins/shared/discover/public/embeddable/initialize_fetch.test.ts @@ -29,7 +29,11 @@ describe('initialize fetch', () => { managed: false, }; - const { api: mockedApi, stateManager } = getMockedSearchApi({ searchSource, savedSearch }); + const { + api: mockedApi, + stateManager, + setters, + } = getMockedSearchApi({ searchSource, savedSearch }); const waitOneTick = () => new Promise((resolve) => setTimeout(resolve, 0)); @@ -38,6 +42,7 @@ describe('initialize fetch', () => { api: mockedApi, stateManager, discoverServices: discoverServiceMock, + ...setters, }); await waitOneTick(); }); @@ -73,7 +78,7 @@ describe('initialize fetch', () => { }); it('should catch and emit error', async () => { - expect(mockedApi.blockingError.getValue()).toBeUndefined(); + expect(mockedApi.blockingError$.getValue()).toBeUndefined(); searchSource.fetch$ = jest.fn().mockImplementation( () => new Observable(() => { @@ -82,8 +87,8 @@ describe('initialize fetch', () => { ); mockedApi.savedSearch$.next(savedSearch); await waitOneTick(); - expect(mockedApi.blockingError.getValue()).toBeDefined(); - expect(mockedApi.blockingError.getValue()?.message).toBe('Search failed'); + expect(mockedApi.blockingError$.getValue()).toBeDefined(); + expect(mockedApi.blockingError$.getValue()?.message).toBe('Search failed'); }); it('should correctly handle aborted requests', async () => { diff --git a/src/platform/plugins/shared/discover/public/embeddable/initialize_fetch.ts b/src/platform/plugins/shared/discover/public/embeddable/initialize_fetch.ts index 41635cf3fecf2..2a94ebc460a31 100644 --- a/src/platform/plugins/shared/discover/public/embeddable/initialize_fetch.ts +++ b/src/platform/plugins/shared/discover/public/embeddable/initialize_fetch.ts @@ -26,8 +26,10 @@ import { FetchContext, HasParentApi, PublishesDataViews, - PublishesPanelTitle, + PublishesTitle, PublishesSavedObjectId, + PublishesDataLoading, + PublishesBlockingError, } from '@kbn/presentation-publishing'; import { PublishesWritableTimeRange } from '@kbn/presentation-publishing/interfaces/fetch/publishes_unified_search'; import { SavedSearch } from '@kbn/saved-search-plugin/public'; @@ -45,12 +47,12 @@ import { createDataSource } from '../../common/data_sources'; type SavedSearchPartialFetchApi = PublishesSavedSearch & PublishesSavedObjectId & + PublishesBlockingError & + PublishesDataLoading & PublishesDataViews & - PublishesPanelTitle & + PublishesTitle & PublishesWritableTimeRange & { fetchContext$: BehaviorSubject; - dataLoading: BehaviorSubject; - blockingError: BehaviorSubject; fetchWarnings$: BehaviorSubject; } & Partial; @@ -67,8 +69,8 @@ const getExecutionContext = async ( const childContext: KibanaExecutionContext = { type: SEARCH_EMBEDDABLE_TYPE, name: 'discover', - id: api.savedObjectId.getValue(), - description: api.panelTitle?.getValue() || api.defaultPanelTitle?.getValue() || '', + id: api.savedObjectId$.getValue(), + description: api.title$?.getValue() || api.defaultTitle$?.getValue() || '', url: editUrl, }; const executionContext = @@ -85,15 +87,19 @@ export function initializeFetch({ api, stateManager, discoverServices, + setDataLoading, + setBlockingError, }: { api: SavedSearchPartialFetchApi; stateManager: SearchEmbeddableStateManager; discoverServices: DiscoverServices; + setDataLoading: (dataLoading: boolean | undefined) => void; + setBlockingError: (error: Error | undefined) => void; }) { const inspectorAdapters = { requests: new RequestAdapter() }; let abortController: AbortController | undefined; - const fetchSubscription = combineLatest([fetch$(api), api.savedSearch$, api.dataViews]) + const fetchSubscription = combineLatest([fetch$(api), api.savedSearch$, api.dataViews$]) .pipe( tap(() => { // abort any in-progress requests @@ -104,7 +110,7 @@ export function initializeFetch({ }), switchMap(async ([fetchContext, savedSearch, dataViews]) => { const dataView = dataViews?.length ? dataViews[0] : undefined; - api.blockingError.next(undefined); + setBlockingError(undefined); if (!dataView || !savedSearch.searchSource) { return; } @@ -130,7 +136,7 @@ export function initializeFetch({ inspectorAdapters.requests.reset(); try { - api.dataLoading.next(true); + setDataLoading(true); // Get new abort controller const currentAbortController = new AbortController(); @@ -217,9 +223,9 @@ export function initializeFetch({ }) ) .subscribe((next) => { - api.dataLoading.next(false); + setDataLoading(false); if (!next || Object.hasOwn(next, 'error')) { - api.blockingError.next(next?.error); + setBlockingError(next?.error); return; } diff --git a/src/platform/plugins/shared/discover/public/embeddable/initialize_search_embeddable_api.tsx b/src/platform/plugins/shared/discover/public/embeddable/initialize_search_embeddable_api.tsx index 65584892ff7d3..e646fcab09b5a 100644 --- a/src/platform/plugins/shared/discover/public/embeddable/initialize_search_embeddable_api.tsx +++ b/src/platform/plugins/shared/discover/public/embeddable/initialize_search_embeddable_api.tsx @@ -83,7 +83,7 @@ export const initializeSearchEmbeddableApi = async ( initialState.serializedSearchSource ); const searchSource$ = new BehaviorSubject(searchSource); - const dataViews = new BehaviorSubject(dataView ? [dataView] : undefined); + const dataViews$ = new BehaviorSubject(dataView ? [dataView] : undefined); const defaults = getSearchEmbeddableDefaults(discoverServices.uiSettings); @@ -149,7 +149,7 @@ export const initializeSearchEmbeddableApi = async ( /** APIs for updating search source properties */ const setDataViews = (nextDataViews: DataView[]) => { searchSource.setField('index', nextDataViews[0]); - dataViews.next(nextDataViews); + dataViews$.next(nextDataViews); searchSource$.next(searchSource); }; @@ -185,7 +185,7 @@ export const initializeSearchEmbeddableApi = async ( }, api: { setDataViews, - dataViews, + dataViews$, savedSearch$, filters$, setFilters, diff --git a/src/platform/plugins/shared/discover/public/embeddable/types.ts b/src/platform/plugins/shared/discover/public/embeddable/types.ts index 05c1991bce611..3598552e65d2d 100644 --- a/src/platform/plugins/shared/discover/public/embeddable/types.ts +++ b/src/platform/plugins/shared/discover/public/embeddable/types.ts @@ -17,7 +17,7 @@ import { PublishesBlockingError, PublishesDataLoading, PublishesSavedObjectId, - PublishesWritablePanelTitle, + PublishesWritableTitle, PublishesWritableUnifiedSearch, PublishingSubject, SerializedTimeRange, @@ -100,7 +100,7 @@ export type SearchEmbeddableApi = DefaultEmbeddableApi< PublishesSavedObjectId & PublishesDataLoading & PublishesBlockingError & - PublishesWritablePanelTitle & + PublishesWritableTitle & PublishesSavedSearch & PublishesWritableDataViews & PublishesWritableUnifiedSearch & diff --git a/src/platform/plugins/shared/discover/public/embeddable/utils/get_discover_locator_params.test.ts b/src/platform/plugins/shared/discover/public/embeddable/utils/get_discover_locator_params.test.ts index 4b8fdc51b3f32..d5c8aeab0752b 100644 --- a/src/platform/plugins/shared/discover/public/embeddable/utils/get_discover_locator_params.test.ts +++ b/src/platform/plugins/shared/discover/public/embeddable/utils/get_discover_locator_params.test.ts @@ -16,7 +16,7 @@ describe('getDiscoverLocatorParams', () => { it('should return saved search id if input has savedObjectId', () => { expect( getDiscoverLocatorParams({ - savedObjectId: new BehaviorSubject('savedObjectId'), + savedObjectId$: new BehaviorSubject('savedObjectId'), savedSearch$: new BehaviorSubject(savedSearchMock), }) ).toEqual({ diff --git a/src/platform/plugins/shared/discover/public/embeddable/utils/get_discover_locator_params.ts b/src/platform/plugins/shared/discover/public/embeddable/utils/get_discover_locator_params.ts index 03c556f12cf3b..1b12fdeeb36ef 100644 --- a/src/platform/plugins/shared/discover/public/embeddable/utils/get_discover_locator_params.ts +++ b/src/platform/plugins/shared/discover/public/embeddable/utils/get_discover_locator_params.ts @@ -18,7 +18,7 @@ export const getDiscoverLocatorParams = ( const savedSearch = api.savedSearch$.getValue(); const dataView = savedSearch?.searchSource.getField('index'); - const savedObjectId = api.savedObjectId?.getValue(); + const savedObjectId = api.savedObjectId$?.getValue(); const locatorParams: DiscoverAppLocatorParams = savedObjectId ? { savedSearchId: savedObjectId } : { diff --git a/src/platform/plugins/shared/embeddable/public/react_embeddable_system/phase_tracker.test.ts b/src/platform/plugins/shared/embeddable/public/react_embeddable_system/phase_tracker.test.ts index 700c90f08ce5b..040bf5023d79e 100644 --- a/src/platform/plugins/shared/embeddable/public/react_embeddable_system/phase_tracker.test.ts +++ b/src/platform/plugins/shared/embeddable/public/react_embeddable_system/phase_tracker.test.ts @@ -35,7 +35,7 @@ describe('PhaseTracker', () => { expect(phaseEvent?.status).toBe('loading'); done(); }); - phaseTracker.trackPhaseEvents('1', { dataLoading: new BehaviorSubject(true) }); + phaseTracker.trackPhaseEvents('1', { dataLoading$: new BehaviorSubject(true) }); }); test(`should emit 'rendered' event when dataLoading is false`, (done) => { @@ -47,7 +47,7 @@ describe('PhaseTracker', () => { expect(phaseEvent?.status).toBe('rendered'); done(); }); - phaseTracker.trackPhaseEvents('1', { dataLoading: new BehaviorSubject(false) }); + phaseTracker.trackPhaseEvents('1', { dataLoading$: new BehaviorSubject(false) }); }); }); @@ -62,7 +62,7 @@ describe('PhaseTracker', () => { done(); }); phaseTracker.trackPhaseEvents('1', { - dataLoading: new BehaviorSubject(true), + dataLoading$: new BehaviorSubject(true), rendered$: new BehaviorSubject(false), }); }); @@ -77,7 +77,7 @@ describe('PhaseTracker', () => { done(); }); phaseTracker.trackPhaseEvents('1', { - dataLoading: new BehaviorSubject(false), + dataLoading$: new BehaviorSubject(false), rendered$: new BehaviorSubject(false), }); }); @@ -92,7 +92,7 @@ describe('PhaseTracker', () => { done(); }); phaseTracker.trackPhaseEvents('1', { - dataLoading: new BehaviorSubject(false), + dataLoading$: new BehaviorSubject(false), rendered$: new BehaviorSubject(true), }); }); diff --git a/src/platform/plugins/shared/embeddable/public/react_embeddable_system/phase_tracker.ts b/src/platform/plugins/shared/embeddable/public/react_embeddable_system/phase_tracker.ts index 037599ab646cc..a60784ec3e641 100644 --- a/src/platform/plugins/shared/embeddable/public/react_embeddable_system/phase_tracker.ts +++ b/src/platform/plugins/shared/embeddable/public/react_embeddable_system/phase_tracker.ts @@ -26,7 +26,7 @@ export class PhaseTracker { public trackPhaseEvents(uuid: string, api: unknown) { const dataLoading$ = apiPublishesDataLoading(api) - ? api.dataLoading + ? api.dataLoading$ : new BehaviorSubject(false); const rendered$ = apiPublishesRendered(api) ? api.rendered$ : new BehaviorSubject(true); diff --git a/src/platform/plugins/shared/embeddable/public/react_embeddable_system/react_embeddable_renderer.test.tsx b/src/platform/plugins/shared/embeddable/public/react_embeddable_system/react_embeddable_renderer.test.tsx index 63433d1d1319b..6cdf49249e9a9 100644 --- a/src/platform/plugins/shared/embeddable/public/react_embeddable_system/react_embeddable_renderer.test.tsx +++ b/src/platform/plugins/shared/embeddable/public/react_embeddable_system/react_embeddable_renderer.test.tsx @@ -189,7 +189,7 @@ describe('react embeddable renderer', () => { type: 'test', uuid: '12345', parentApi: expect.any(Object), - unsavedChanges: expect.any(Object), + unsavedChanges$: expect.any(Object), serializeState: expect.any(Function), resetUnsavedChanges: expect.any(Function), snapshotRuntimeState: expect.any(Function), @@ -296,7 +296,7 @@ describe('reactEmbeddable phase events', () => { ...testEmbeddableFactory, type: 'loadClicker', buildEmbeddable: async (state, registerApi) => { - const dataLoading = new BehaviorSubject(true); + const dataLoading$ = new BehaviorSubject(true); const api = registerApi( { serializeState: () => ({ @@ -305,7 +305,7 @@ describe('reactEmbeddable phase events', () => { bork: state.bork, }, }), - dataLoading, + dataLoading$, }, { name: [new BehaviorSubject(state.name), () => {}], @@ -318,7 +318,7 @@ describe('reactEmbeddable phase events', () => {
SUPER TEST COMPONENT, name: {state.name} bork: {state.bork}
- diff --git a/src/platform/plugins/shared/embeddable/public/react_embeddable_system/react_embeddable_renderer.tsx b/src/platform/plugins/shared/embeddable/public/react_embeddable_system/react_embeddable_renderer.tsx index 65cbbf29f8aba..1508ed13f0ef0 100644 --- a/src/platform/plugins/shared/embeddable/public/react_embeddable_system/react_embeddable_renderer.tsx +++ b/src/platform/plugins/shared/embeddable/public/react_embeddable_system/react_embeddable_renderer.tsx @@ -202,7 +202,7 @@ export const ReactEmbeddableRenderer = < * */ const errorApi = { uuid, - blockingError: new BehaviorSubject(e), + blockingError$: new BehaviorSubject(e), } as unknown as Api; if (apiIsPresentationContainer(parentApi)) { errorApi.parentApi = parentApi; diff --git a/src/platform/plugins/shared/unified_histogram/public/chart/chart.tsx b/src/platform/plugins/shared/unified_histogram/public/chart/chart.tsx index 0b3ee3a0d7395..b9140e71d0d35 100644 --- a/src/platform/plugins/shared/unified_histogram/public/chart/chart.tsx +++ b/src/platform/plugins/shared/unified_histogram/public/chart/chart.tsx @@ -80,7 +80,7 @@ export interface ChartProps { disabledActions?: LensEmbeddableInput['disabledActions']; input$?: UnifiedHistogramInput$; lensAdapters?: UnifiedHistogramChartLoadEvent['adapters']; - dataLoading$?: LensEmbeddableOutput['dataLoading']; + dataLoading$?: LensEmbeddableOutput['dataLoading$']; isChartLoading?: boolean; onChartHiddenChange?: (chartHidden: boolean) => void; onTimeIntervalChange?: (timeInterval: string) => void; diff --git a/src/platform/plugins/shared/unified_histogram/public/chart/chart_config_panel.tsx b/src/platform/plugins/shared/unified_histogram/public/chart/chart_config_panel.tsx index edcd831d3f7ac..a7a82cff8604c 100644 --- a/src/platform/plugins/shared/unified_histogram/public/chart/chart_config_panel.tsx +++ b/src/platform/plugins/shared/unified_histogram/public/chart/chart_config_panel.tsx @@ -41,7 +41,7 @@ export function ChartConfigPanel({ isFlyoutVisible: boolean; setIsFlyoutVisible: (flag: boolean) => void; lensAdapters?: UnifiedHistogramChartLoadEvent['adapters']; - dataLoading$?: LensEmbeddableOutput['dataLoading']; + dataLoading$?: LensEmbeddableOutput['dataLoading$']; currentSuggestionContext: UnifiedHistogramSuggestionContext; isPlainRecord?: boolean; query?: Query | AggregateQuery; diff --git a/src/platform/plugins/shared/unified_histogram/public/layout/layout.tsx b/src/platform/plugins/shared/unified_histogram/public/layout/layout.tsx index c384102ed1691..5d497990e30f3 100644 --- a/src/platform/plugins/shared/unified_histogram/public/layout/layout.tsx +++ b/src/platform/plugins/shared/unified_histogram/public/layout/layout.tsx @@ -98,7 +98,7 @@ export interface UnifiedHistogramLayoutProps extends PropsWithChildren */ hits?: UnifiedHistogramHitsContext; lensAdapters?: UnifiedHistogramChartLoadEvent['adapters']; - dataLoading$?: LensEmbeddableOutput['dataLoading']; + dataLoading$?: LensEmbeddableOutput['dataLoading$']; /** * Context object for the chart -- leave undefined to hide the chart */ diff --git a/src/platform/plugins/shared/visualizations/public/actions/edit_in_lens_action.tsx b/src/platform/plugins/shared/visualizations/public/actions/edit_in_lens_action.tsx index f8ed7a6294dbf..4049c6669341e 100644 --- a/src/platform/plugins/shared/visualizations/public/actions/edit_in_lens_action.tsx +++ b/src/platform/plugins/shared/visualizations/public/actions/edit_in_lens_action.tsx @@ -20,8 +20,8 @@ import { getInheritedViewMode, HasUniqueId, PublishesUnifiedSearch, - PublishesPanelDescription, - PublishesPanelTitle, + PublishesDescription, + PublishesTitle, } from '@kbn/presentation-publishing'; import { Action } from '@kbn/ui-actions-plugin/public'; import React from 'react'; @@ -67,12 +67,7 @@ const MenuItem: React.FC = () => { type EditInLensActionApi = HasUniqueId & HasVisualizeConfig & CanAccessViewMode & - Partial< - PublishesUnifiedSearch & - HasExpressionVariables & - PublishesPanelTitle & - PublishesPanelDescription - >; + Partial; const compatibilityCheck = (api: EmbeddableApiContext['embeddable']): api is EditInLensActionApi => apiHasUniqueId(api) && apiCanAccessViewMode(api) && apiHasVisualizeConfig(api); @@ -108,7 +103,7 @@ export class EditInLensAction implements Action { const parentSearchSource = vis.data.searchSource?.getParent(); const searchFilters = parentSearchSource?.getField('filter') ?? visFilters; const searchQuery = parentSearchSource?.getField('query') ?? visQuery; - const title = vis.title || embeddable.panelTitle?.getValue(); + const title = vis.title || embeddable.title$?.getValue(); const panelTimeRange = embeddable.timeRange$?.getValue(); const updatedWithMeta = { ...navigateToLensConfig, @@ -119,7 +114,7 @@ export class EditInLensAction implements Action { searchFilters, searchQuery, isEmbeddable: true, - description: vis.description || embeddable.panelDescription?.getValue(), + description: vis.description || embeddable.description$?.getValue(), panelTimeRange, }; if (navigateToLensConfig) { diff --git a/src/platform/plugins/shared/visualizations/public/embeddable/visualize_embeddable.tsx b/src/platform/plugins/shared/visualizations/public/embeddable/visualize_embeddable.tsx index c1e069e370137..873e030fdfb9f 100644 --- a/src/platform/plugins/shared/visualizations/public/embeddable/visualize_embeddable.tsx +++ b/src/platform/plugins/shared/visualizations/public/embeddable/visualize_embeddable.tsx @@ -31,7 +31,7 @@ import { fetch$, getUnchangingComparator, initializeTimeRange, - initializeTitles, + initializeTitleManager, useStateFromPublishingSubject, } from '@kbn/presentation-publishing'; import { apiPublishesSearchSession } from '@kbn/presentation-publishing/interfaces/fetch/publishes_search_session'; @@ -80,13 +80,13 @@ export const getVisualizeEmbeddableFactory: (deps: { // Initialize dynamic actions const dynamicActionsApi = embeddableEnhancedStart?.initializeReactEmbeddableDynamicActions( uuid, - () => titlesApi.panelTitle.getValue(), + () => titleManager.api.title$.getValue(), state ); // if it is provided, start the dynamic actions manager const maybeStopDynamicActions = dynamicActionsApi?.startDynamicActions(); - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const titleManager = initializeTitleManager(state); // Count renders; mostly used for testing. const renderCount$ = new BehaviorSubject(0); @@ -169,7 +169,7 @@ export const getVisualizeEmbeddableFactory: (deps: { const dataLoading$ = new BehaviorSubject(true); - const defaultPanelTitle = new BehaviorSubject(initialVisInstance.title); + const defaultTitle$ = new BehaviorSubject(initialVisInstance.title); const serializeVisualizeEmbeddable = ( savedObjectId: string | undefined, @@ -178,7 +178,7 @@ export const getVisualizeEmbeddableFactory: (deps: { const savedObjectProperties = savedObjectProperties$.getValue(); return serializeState({ serializedVis: vis$.getValue().serialize(), - titles: serializeTitles(), + titles: titleManager.serialize(), id: savedObjectId, linkedToLibrary, ...(savedObjectProperties ? { savedObjectProperties } : {}), @@ -190,11 +190,11 @@ export const getVisualizeEmbeddableFactory: (deps: { const api = buildApi( { ...customTimeRangeApi, - ...titlesApi, + ...titleManager.api, ...(dynamicActionsApi?.dynamicActionsApi ?? {}), - defaultPanelTitle, - dataLoading: dataLoading$, - dataViews: new BehaviorSubject(initialDataViews), + defaultTitle$, + dataLoading$, + dataViews$: new BehaviorSubject(initialDataViews), rendered$: hasRendered$, supportedTriggers: () => [ ACTION_CONVERT_TO_LENS, @@ -214,11 +214,11 @@ export const getVisualizeEmbeddableFactory: (deps: { getInspectorAdapters: () => inspectorAdapters$.getValue(), ...initializeEditApi({ customTimeRange$: customTimeRangeApi.timeRange$, - description$: titlesApi.panelDescription, + description$: titleManager.api.description$, parentApi, savedObjectId$, searchSessionId$, - title$: titlesApi.panelTitle, + title$: titleManager.api.title$, vis$, uuid, }), @@ -237,7 +237,7 @@ export const getVisualizeEmbeddableFactory: (deps: { }, } as SerializedVis); if (visUpdates.title) { - titlesApi.setPanelTitle(visUpdates.title); + titleManager.api.setTitle(visUpdates.title); } }, openInspector: () => { @@ -247,7 +247,7 @@ export const getVisualizeEmbeddableFactory: (deps: { if (!inspector.isAvailable(adapters)) return; return getInspector().open(adapters, { title: - titlesApi.panelTitle?.getValue() || + titleManager.api.title$?.getValue() || i18n.translate('visualizations.embeddable.inspectorTitle', { defaultMessage: 'Inspector', }), @@ -255,11 +255,11 @@ export const getVisualizeEmbeddableFactory: (deps: { }, // Library transforms saveToLibrary: (newTitle: string) => { - titlesApi.setPanelTitle(newTitle); + titleManager.api.setTitle(newTitle); const { rawState, references } = serializeState({ serializedVis: vis$.getValue().serialize(), titles: { - ...serializeTitles(), + ...titleManager.serialize(), title: newTitle, }, }); @@ -276,7 +276,7 @@ export const getVisualizeEmbeddableFactory: (deps: { getSerializedStateByReference: (libraryId) => serializeVisualizeEmbeddable(libraryId, true), }, { - ...titleComparators, + ...titleManager.comparators, ...customTimeRangeComparators, ...(dynamicActionsApi?.dynamicActionsComparator ?? { enhancements: getUnchangingComparator(), @@ -477,8 +477,8 @@ export const getVisualizeEmbeddableFactory: (deps: { data-test-subj="visualizationLoader" data-rendering-count={renderCount /* Used for functional tests */} data-render-complete={hasRendered} - data-title={!api.hidePanelTitle?.getValue() ? api.panelTitle?.getValue() ?? '' : ''} - data-description={api.panelDescription?.getValue() ?? ''} + data-title={!api.hideTitle$?.getValue() ? api.title$?.getValue() ?? '' : ''} + data-description={api.description$?.getValue() ?? ''} data-shared-item > {/* Replicate the loading state for the expression renderer to avoid FOUC */} diff --git a/test/plugin_functional/plugins/kbn_sample_panel_action/public/sample_panel_action.tsx b/test/plugin_functional/plugins/kbn_sample_panel_action/public/sample_panel_action.tsx index bbb06616b6e42..bd756865499c0 100644 --- a/test/plugin_functional/plugins/kbn_sample_panel_action/public/sample_panel_action.tsx +++ b/test/plugin_functional/plugins/kbn_sample_panel_action/public/sample_panel_action.tsx @@ -38,7 +38,7 @@ export function createSamplePanelAction(getStartServices: CoreSetup['getStartSer -

{embeddable.panelTitle?.value}

+

{embeddable.title$?.value}

diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/drilldown.tsx b/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/drilldown.tsx index 66b4f465a1101..0a683db3f28d3 100644 --- a/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/drilldown.tsx +++ b/x-pack/examples/ui_actions_enhanced_examples/public/drilldowns/dashboard_to_discover_drilldown/drilldown.tsx @@ -61,7 +61,7 @@ export class DashboardToDiscoverDrilldown !!config.customIndexPattern && !!config.indexPatternId ? config.indexPatternId : ''; if (!indexPatternId) { - const dataViews = (context?.embeddable as ActionApi).dataViews?.value; + const dataViews = (context?.embeddable as ActionApi).dataViews$?.value; if (dataViews?.[0].id) { indexPatternId = dataViews[0].id; } diff --git a/x-pack/platform/plugins/private/canvas/public/components/hooks/use_canvas_api.tsx b/x-pack/platform/plugins/private/canvas/public/components/hooks/use_canvas_api.tsx index d815864fb4a60..c1462d9bac371 100644 --- a/x-pack/platform/plugins/private/canvas/public/components/hooks/use_canvas_api.tsx +++ b/x-pack/platform/plugins/private/canvas/public/components/hooks/use_canvas_api.tsx @@ -38,7 +38,7 @@ export const useCanvasApi: () => CanvasContainerApi = () => { const getCanvasApi = useCallback((): CanvasContainerApi => { return { - viewMode: new BehaviorSubject('edit'), // always in edit mode + viewMode$: new BehaviorSubject('edit'), // always in edit mode addNewPanel: async ({ panelType, initialState, diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_factory.tsx b/x-pack/platform/plugins/private/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_factory.tsx index 8b3e153ed8cb6..49ed991cd8a95 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_factory.tsx +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_factory.tsx @@ -19,7 +19,7 @@ import { apiHasExecutionContext, fetch$, initializeTimeRange, - initializeTitles, + initializeTitleManager, useBatchedPublishingSubjects, useFetchContext, } from '@kbn/presentation-publishing'; @@ -139,13 +139,8 @@ export const getFieldStatsChartEmbeddableFactory = ( fieldFormats, ...startServices, }; - const { - api: timeRangeApi, - comparators: timeRangeComparators, - serialize: serializeTimeRange, - } = initializeTimeRange(state); - - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const timeRangeManager = initializeTimeRange(state); + const titleManager = initializeTitleManager(state); const { fieldStatsControlsApi, @@ -155,7 +150,7 @@ export const getFieldStatsChartEmbeddableFactory = ( onFieldStatsTableDestroy, resetData$, } = initializeFieldStatsControls(state, deps.uiSettings); - const { onError, dataLoading, blockingError } = dataLoadingApi; + const { onError, dataLoading$, blockingError$ } = dataLoadingApi; const validDataViewId: string | undefined = isDefined(state.dataViewId) && state.dataViewId !== '' ? state.dataViewId : undefined; @@ -205,13 +200,13 @@ export const getFieldStatsChartEmbeddableFactory = ( const api = buildApi( { - ...timeRangeApi, - ...titlesApi, + ...timeRangeManager.api, + ...titleManager.api, ...fieldStatsControlsApi, // PublishesDataLoading - dataLoading, + dataLoading$, // PublishesBlockingError - blockingError, + blockingError$, getTypeDisplayName: () => i18n.translate('xpack.dataVisualizer.fieldStats.typeDisplayName', { defaultMessage: 'field statistics', @@ -237,7 +232,7 @@ export const getFieldStatsChartEmbeddableFactory = ( toasts.addError(e, { title: ERROR_MSG.UPDATE_CONFIG_ERROR }); } }, - dataViews: dataViews$, + dataViews$, serializeState: () => { const dataViewId = fieldStatsControlsApi.dataViewId$?.getValue(); const references: Reference[] = dataViewId @@ -251,8 +246,8 @@ export const getFieldStatsChartEmbeddableFactory = ( : []; return { rawState: { - ...serializeTitles(), - ...serializeTimeRange(), + ...titleManager.serialize(), + ...timeRangeManager.serialize(), ...serializeFieldStatsChartState(), }, references, @@ -260,8 +255,8 @@ export const getFieldStatsChartEmbeddableFactory = ( }, }, { - ...timeRangeComparators, - ...titleComparators, + ...timeRangeManager.comparators, + ...titleManager.comparators, ...fieldStatsControlsComparators, } ); @@ -324,7 +319,7 @@ export const getFieldStatsChartEmbeddableFactory = ( const { filters: globalFilters, query: globalQuery, timeRange } = useFetchContext(api); const [dataViews, esqlQuery, viewType, showPreviewByDefault] = useBatchedPublishingSubjects( - api.dataViews, + api.dataViews$, api.query$, api.viewType$, api.showDistributions$ diff --git a/x-pack/platform/plugins/private/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/initialize_field_stats_controls.ts b/x-pack/platform/plugins/private/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/initialize_field_stats_controls.ts index fd6a7b43347c5..b3396ad88bb33 100644 --- a/x-pack/platform/plugins/private/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/initialize_field_stats_controls.ts +++ b/x-pack/platform/plugins/private/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/initialize_field_stats_controls.ts @@ -38,7 +38,7 @@ export const initializeFieldStatsControls = ( const resetData$ = new BehaviorSubject(Date.now()); const dataLoading$ = new BehaviorSubject(true); - const blockingError = new BehaviorSubject(undefined); + const blockingError$ = new BehaviorSubject(undefined); const updateUserInput = (update: FieldStatsInitialState, shouldResetData = false) => { if (shouldResetData) { @@ -67,7 +67,7 @@ export const initializeFieldStatsControls = ( const onRenderComplete = () => dataLoading$.next(false); const onLoading = (v: boolean) => dataLoading$.next(v); - const onError = (error?: Error) => blockingError.next(error); + const onError = (error?: Error) => blockingError$.next(error); return { fieldStatsControlsApi: { @@ -78,11 +78,11 @@ export const initializeFieldStatsControls = ( showDistributions$, } as unknown as FieldStatsControlsApi, dataLoadingApi: { - dataLoading: dataLoading$, + dataLoading$, onRenderComplete, onLoading, onError, - blockingError, + blockingError$, }, // Reset data is internal state management, so no need to expose this in api resetData$, diff --git a/x-pack/platform/plugins/private/discover_enhanced/public/actions/explore_data/explore_data_chart_action.test.ts b/x-pack/platform/plugins/private/discover_enhanced/public/actions/explore_data/explore_data_chart_action.test.ts index c8e775ab4db6c..037cb31afe6e3 100644 --- a/x-pack/platform/plugins/private/discover_enhanced/public/actions/explore_data/explore_data_chart_action.test.ts +++ b/x-pack/platform/plugins/private/discover_enhanced/public/actions/explore_data/explore_data_chart_action.test.ts @@ -67,14 +67,14 @@ const setup = ( const embeddable = { type: 'anyEmbeddable', - dataViews: new BehaviorSubject([ + dataViews$: new BehaviorSubject([ { id: 'index-ptr-foo', } as DataView, ]), filters$: new BehaviorSubject([]), parentApi: { - viewMode: new BehaviorSubject('view'), + viewMode$: new BehaviorSubject('view'), }, }; @@ -130,7 +130,7 @@ describe('"Explore underlying data" panel action', () => { test('returns false if embeddable has more than one data view', async () => { const { action, embeddable, context } = setup(); - embeddable.dataViews = new BehaviorSubject([ + embeddable.dataViews$ = new BehaviorSubject([ { id: 'index-ptr-foo', } as DataView, @@ -147,7 +147,7 @@ describe('"Explore underlying data" panel action', () => { test('returns false if embeddable does not have data views', async () => { const { action, embeddable, context } = setup(); // @ts-expect-error - embeddable.dataViews = undefined; + embeddable.dataViews$ = undefined; const isCompatible = await action.isCompatible(context); @@ -156,7 +156,7 @@ describe('"Explore underlying data" panel action', () => { test('returns false if embeddable data views are empty', async () => { const { action, embeddable, context } = setup(); - embeddable.dataViews = new BehaviorSubject([]); + embeddable.dataViews$ = new BehaviorSubject([]); const isCompatible = await action.isCompatible(context); @@ -166,7 +166,7 @@ describe('"Explore underlying data" panel action', () => { test('returns false if dashboard is in edit mode', async () => { const { action, embeddable, context } = setup(); if (embeddable.parentApi) { - embeddable.parentApi.viewMode = new BehaviorSubject('edit'); + embeddable.parentApi.viewMode$ = new BehaviorSubject('edit'); } const isCompatible = await action.isCompatible(context); diff --git a/x-pack/platform/plugins/private/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.test.ts b/x-pack/platform/plugins/private/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.test.ts index abe0776d57f5f..47aba299d65ed 100644 --- a/x-pack/platform/plugins/private/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.test.ts +++ b/x-pack/platform/plugins/private/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.test.ts @@ -57,13 +57,13 @@ const setup = () => { const embeddable = { type: 'anyEmbeddable', - dataViews: new BehaviorSubject([ + dataViews$: new BehaviorSubject([ { id: 'index-ptr-foo', } as DataView, ]), parentApi: { - viewMode: new BehaviorSubject('view'), + viewMode$: new BehaviorSubject('view'), }, }; @@ -117,7 +117,7 @@ describe('"Explore underlying data" panel action', () => { test('returns false if embeddable has more than one index pattern', async () => { const { action, embeddable, context } = setup(); - embeddable.dataViews = new BehaviorSubject([ + embeddable.dataViews$ = new BehaviorSubject([ { id: 'index-ptr-foo', } as DataView, @@ -134,7 +134,7 @@ describe('"Explore underlying data" panel action', () => { test('returns false if embeddable does not have index patterns', async () => { const { action, embeddable, context } = setup(); // @ts-expect-error - embeddable.dataViews = undefined; + embeddable.dataViews$ = undefined; const isCompatible = await action.isCompatible(context); @@ -143,7 +143,7 @@ describe('"Explore underlying data" panel action', () => { test('returns false if embeddable index patterns are empty', async () => { const { action, embeddable, context } = setup(); - embeddable.dataViews = new BehaviorSubject([]); + embeddable.dataViews$ = new BehaviorSubject([]); const isCompatible = await action.isCompatible(context); @@ -153,7 +153,7 @@ describe('"Explore underlying data" panel action', () => { test('returns false if dashboard is in edit mode', async () => { const { action, embeddable, context } = setup(); if (embeddable.parentApi) { - embeddable.parentApi.viewMode = new BehaviorSubject('edit'); + embeddable.parentApi.viewMode$ = new BehaviorSubject('edit'); } const isCompatible = await action.isCompatible(context); diff --git a/x-pack/platform/plugins/private/discover_enhanced/public/actions/explore_data/shared.ts b/x-pack/platform/plugins/private/discover_enhanced/public/actions/explore_data/shared.ts index aa96ac17e5bbe..49ee103ca273d 100644 --- a/x-pack/platform/plugins/private/discover_enhanced/public/actions/explore_data/shared.ts +++ b/x-pack/platform/plugins/private/discover_enhanced/public/actions/explore_data/shared.ts @@ -11,7 +11,7 @@ import { apiPublishesDataViews, EmbeddableApiContext } from '@kbn/presentation-p export const getDataViews = (embeddable: EmbeddableApiContext['embeddable']): string[] => { if (!apiPublishesDataViews(embeddable)) return []; - const dataViews: DataView[] = embeddable.dataViews.getValue() ?? []; + const dataViews: DataView[] = embeddable.dataViews$.getValue() ?? []; return dataViews.reduce( (prev: string[], current: DataView) => (current.id ? [...prev, current.id] : prev), [] diff --git a/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/url_drilldown.test.tsx b/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/url_drilldown.test.tsx index 8eefae138b6c3..549a3e0e3b34f 100644 --- a/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/url_drilldown.test.tsx +++ b/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/url_drilldown.test.tsx @@ -62,7 +62,7 @@ const mockEmbeddableApi = { filters$: new BehaviorSubject([]), query$: new BehaviorSubject({ query: 'test', language: 'kuery' }), timeRange$: new BehaviorSubject({ from: 'now-15m', to: 'now' }), - viewMode: new BehaviorSubject('edit'), + viewMode$: new BehaviorSubject('edit'), }, }; @@ -200,7 +200,7 @@ describe('UrlDrilldown', () => { }); test('compatible in view mode if url is valid', async () => { - mockEmbeddableApi.parentApi.viewMode.next('view'); + mockEmbeddableApi.parentApi.viewMode$.next('view'); const config: Config = { url: { @@ -222,7 +222,7 @@ describe('UrlDrilldown', () => { }); test('not compatible in view mode if url is invalid', async () => { - mockEmbeddableApi.parentApi.viewMode.next('view'); + mockEmbeddableApi.parentApi.viewMode$.next('view'); const config: Config = { url: { template: `https://elasti.co/?{{event.value}}&{{rison context.panel.somethingFake}}`, @@ -242,7 +242,7 @@ describe('UrlDrilldown', () => { }); test('not compatible in view mode if external URL is denied', async () => { - mockEmbeddableApi.parentApi.viewMode.next('view'); + mockEmbeddableApi.parentApi.viewMode$.next('view'); const drilldown1 = createDrilldown(true); const drilldown2 = createDrilldown(false); const config: Config = { @@ -359,9 +359,9 @@ describe('UrlDrilldown', () => { describe('variables', () => { const embeddable1 = { - dataViews: new BehaviorSubject([{ id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }]), - panelTitle: new BehaviorSubject('The Title'), - savedObjectId: new BehaviorSubject('SAVED_OBJECT_IDxx'), + dataViews$: new BehaviorSubject([{ id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }]), + title$: new BehaviorSubject('The Title'), + savedObjectId$: new BehaviorSubject('SAVED_OBJECT_IDxx'), uuid: 'test', }; const data = { @@ -373,7 +373,7 @@ describe('UrlDrilldown', () => { }; const embeddable2 = { - dataViews: new BehaviorSubject([ + dataViews$: new BehaviorSubject([ { id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }, { id: 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' }, ]), @@ -393,8 +393,8 @@ describe('UrlDrilldown', () => { }), timeRange$: new BehaviorSubject({ from: 'FROM', to: 'TO' }), }, - panelTitle: new BehaviorSubject('The Title'), - savedObjectId: new BehaviorSubject('SAVED_OBJECT_ID'), + title$: new BehaviorSubject('The Title'), + savedObjectId$: new BehaviorSubject('SAVED_OBJECT_ID'), uuid: 'the-id', }; diff --git a/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/variables/context_variables.test.ts b/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/variables/context_variables.test.ts index ba55c25a3a10e..cd5a552abd769 100644 --- a/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/variables/context_variables.test.ts +++ b/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/variables/context_variables.test.ts @@ -34,8 +34,8 @@ describe('getContextScopeValues()', () => { }), timeRange$: new BehaviorSubject({ from: 'FROM', to: 'TO' }), }, - panelTitle: new BehaviorSubject('title1'), - savedObjectId: new BehaviorSubject('1234'), + title$: new BehaviorSubject('title1'), + savedObjectId$: new BehaviorSubject('1234'), uuid: 'test', }; expect(getContextScopeValues({ embeddable: embeddableApi })).toEqual({ @@ -66,7 +66,7 @@ describe('getContextScopeValues()', () => { test('returns a single index pattern from output', () => { const embeddableApi = { - dataViews: new BehaviorSubject([{ id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }]), + dataViews$: new BehaviorSubject([{ id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }]), }; expect(getContextScopeValues({ embeddable: embeddableApi })).toEqual({ panel: { @@ -77,7 +77,7 @@ describe('getContextScopeValues()', () => { test('returns multiple index patterns from output', () => { const embeddableApi = { - dataViews: new BehaviorSubject([ + dataViews$: new BehaviorSubject([ { id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }, { id: 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' }, ]), diff --git a/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/variables/context_variables.ts b/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/variables/context_variables.ts index 85921bd992a4c..10cb64260be66 100644 --- a/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/variables/context_variables.ts +++ b/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/variables/context_variables.ts @@ -13,12 +13,12 @@ import type { EmbeddableApiContext, HasParentApi, HasUniqueId, - PublishesPanelTitle, + PublishesTitle, PublishesSavedObjectId, PublishesUnifiedSearch, PublishesDataViews, } from '@kbn/presentation-publishing'; -import { getPanelTitle } from '@kbn/presentation-publishing'; +import { getTitle } from '@kbn/presentation-publishing'; import type { UrlTemplateEditorVariable } from '@kbn/kibana-react-plugin/public'; import { txtValue } from './i18n'; import { deleteUndefinedKeys } from './util'; @@ -66,21 +66,21 @@ export const getContextScopeValues = (context: Partial): C ); const api = context.embeddable as Partial< HasUniqueId & - PublishesPanelTitle & + PublishesTitle & PublishesSavedObjectId & PublishesUnifiedSearch & PublishesDataViews & HasParentApi> >; - const dataViewIds = api.dataViews?.value - ? (api.dataViews?.value.map((dataView) => dataView.id).filter(Boolean) as string[]) + const dataViewIds = api.dataViews$?.value + ? (api.dataViews$?.value.map((dataView) => dataView.id).filter(Boolean) as string[]) : []; return { panel: deleteUndefinedKeys({ id: api.uuid, - title: getPanelTitle(api), - savedObjectId: api.savedObjectId?.value, + title: getTitle(api), + savedObjectId: api.savedObjectId$?.value, query: api.parentApi?.query$?.value, timeRange: api.timeRange$?.value ?? api.parentApi?.timeRange$?.value, filters: api.parentApi?.filters$?.value, diff --git a/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/variables/event_variables.ts b/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/variables/event_variables.ts index 7ef361172967a..f7917454e4245 100644 --- a/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/variables/event_variables.ts +++ b/x-pack/platform/plugins/private/drilldowns/url_drilldown/public/lib/variables/event_variables.ts @@ -8,9 +8,9 @@ import { i18n } from '@kbn/i18n'; import { monaco } from '@kbn/monaco'; import { - getPanelTitle, + getTitle, isEmbeddableApiContext, - type PublishesPanelTitle, + type PublishesTitle, } from '@kbn/presentation-publishing'; import { ChartActionContext, @@ -94,7 +94,7 @@ const getEventScopeFromRowClickTriggerContext = ( ctx: RowClickContext ): RowClickTriggerEventScope => { const { data } = ctx; - const api = ctx.embeddable as Partial; + const api = ctx.embeddable as Partial; const { rowIndex } = data; const columns = data.columns || data.table.columns.map(({ id }) => id); @@ -107,7 +107,7 @@ const getEventScopeFromRowClickTriggerContext = ( const column = data.table.columns.find(({ id }) => id === columnId); if (!column) { // This should never happen, but in case it does we log data necessary for debugging. - const title = getPanelTitle(api); + const title = getTitle(api); // eslint-disable-next-line no-console console.error(data, title ? `Embeddable [${title}]` : null); throw new Error('Could not find a datatable column.'); diff --git a/x-pack/platform/plugins/shared/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.tsx b/x-pack/platform/plugins/shared/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.tsx index 5f7ff6ff67f76..5eeba37317770 100644 --- a/x-pack/platform/plugins/shared/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.tsx +++ b/x-pack/platform/plugins/shared/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.tsx @@ -15,6 +15,13 @@ import type { DataView } from '@kbn/data-views-plugin/common'; import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/common'; import type { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { i18n } from '@kbn/i18n'; +import { + apiHasExecutionContext, + fetch$, + initializeTimeRange, + initializeTitleManager, + useBatchedPublishingSubjects, +} from '@kbn/presentation-publishing'; import fastIsEqual from 'fast-deep-equal'; import { cloneDeep } from 'lodash'; @@ -54,23 +61,10 @@ export const getChangePointChartEmbeddableFactory = ( return serializedState; }, buildEmbeddable: async (state, buildApi, uuid, parentApi) => { - const { - apiHasExecutionContext, - fetch$, - initializeTimeRange, - initializeTitles, - useBatchedPublishingSubjects, - } = await import('@kbn/presentation-publishing'); - const [coreStart, pluginStart] = await getStartServices(); - const { - api: timeRangeApi, - comparators: timeRangeComparators, - serialize: serializeTimeRange, - } = initializeTimeRange(state); - - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const timeRangeManager = initializeTimeRange(state); + const titleManager = initializeTitleManager(state); const { changePointControlsApi, @@ -78,8 +72,8 @@ export const getChangePointChartEmbeddableFactory = ( serializeChangePointChartState, } = initializeChangePointControls(state); - const dataLoading = new BehaviorSubject(true); - const blockingError = new BehaviorSubject(undefined); + const dataLoading$ = new BehaviorSubject(true); + const blockingError$ = new BehaviorSubject(undefined); const dataViews$ = new BehaviorSubject([ await pluginStart.data.dataViews.get(state.dataViewId), @@ -87,8 +81,8 @@ export const getChangePointChartEmbeddableFactory = ( const api = buildApi( { - ...timeRangeApi, - ...titlesApi, + ...timeRangeManager.api, + ...titleManager.api, ...changePointControlsApi, getTypeDisplayName: () => i18n.translate('xpack.aiops.changePointDetection.typeDisplayName', { @@ -114,9 +108,9 @@ export const getChangePointChartEmbeddableFactory = ( return Promise.reject(); } }, - dataLoading, - blockingError, - dataViews: dataViews$, + dataLoading$, + blockingError$, + dataViews$, serializeState: () => { const dataViewId = changePointControlsApi.dataViewId.getValue(); const references: Reference[] = dataViewId @@ -131,8 +125,8 @@ export const getChangePointChartEmbeddableFactory = ( return { rawState: { timeRange: undefined, - ...serializeTitles(), - ...serializeTimeRange(), + ...titleManager.serialize(), + ...timeRangeManager.serialize(), ...serializeChangePointChartState(), }, references, @@ -140,8 +134,8 @@ export const getChangePointChartEmbeddableFactory = ( }, }, { - ...timeRangeComparators, - ...titleComparators, + ...timeRangeManager.comparators, + ...titleManager.comparators, ...changePointControlsComparators, } ); @@ -151,9 +145,9 @@ export const getChangePointChartEmbeddableFactory = ( pluginStart ); - const onLoading = (v: boolean) => dataLoading.next(v); - const onRenderComplete = () => dataLoading.next(false); - const onError = (error: Error) => blockingError.next(error); + const onLoading = (v: boolean) => dataLoading$.next(v); + const onRenderComplete = () => dataLoading$.next(false); + const onError = (error: Error) => blockingError$.next(error); return { api, diff --git a/x-pack/platform/plugins/shared/aiops/public/embeddables/log_rate_analysis/embeddable_log_rate_analysis_factory.tsx b/x-pack/platform/plugins/shared/aiops/public/embeddables/log_rate_analysis/embeddable_log_rate_analysis_factory.tsx index 6d1ea7d2e03c1..0b419d96dfae8 100644 --- a/x-pack/platform/plugins/shared/aiops/public/embeddables/log_rate_analysis/embeddable_log_rate_analysis_factory.tsx +++ b/x-pack/platform/plugins/shared/aiops/public/embeddables/log_rate_analysis/embeddable_log_rate_analysis_factory.tsx @@ -19,7 +19,7 @@ import { apiHasExecutionContext, fetch$, initializeTimeRange, - initializeTitles, + initializeTitleManager, useBatchedPublishingSubjects, } from '@kbn/presentation-publishing'; @@ -63,13 +63,8 @@ export const getLogRateAnalysisEmbeddableFactory = ( buildEmbeddable: async (state, buildApi, uuid, parentApi) => { const [coreStart, pluginStart] = await getStartServices(); - const { - api: timeRangeApi, - comparators: timeRangeComparators, - serialize: serializeTimeRange, - } = initializeTimeRange(state); - - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const timeRangeManager = initializeTimeRange(state); + const titleManager = initializeTitleManager(state); const { logRateAnalysisControlsApi, @@ -77,8 +72,8 @@ export const getLogRateAnalysisEmbeddableFactory = ( logRateAnalysisControlsComparators, } = initializeLogRateAnalysisControls(state); - const dataLoading = new BehaviorSubject(true); - const blockingError = new BehaviorSubject(undefined); + const dataLoading$ = new BehaviorSubject(true); + const blockingError$ = new BehaviorSubject(undefined); const dataViews$ = new BehaviorSubject([ await pluginStart.data.dataViews.get( @@ -88,8 +83,8 @@ export const getLogRateAnalysisEmbeddableFactory = ( const api = buildApi( { - ...timeRangeApi, - ...titlesApi, + ...timeRangeManager.api, + ...titleManager.api, ...logRateAnalysisControlsApi, getTypeDisplayName: () => i18n.translate('xpack.aiops.logRateAnalysis.typeDisplayName', { @@ -118,9 +113,9 @@ export const getLogRateAnalysisEmbeddableFactory = ( return Promise.reject(); } }, - dataLoading, - blockingError, - dataViews: dataViews$, + dataLoading$, + blockingError$, + dataViews$, serializeState: () => { const dataViewId = logRateAnalysisControlsApi.dataViewId.getValue(); const references: Reference[] = dataViewId @@ -135,8 +130,8 @@ export const getLogRateAnalysisEmbeddableFactory = ( return { rawState: { timeRange: undefined, - ...serializeTitles(), - ...serializeTimeRange(), + ...titleManager.serialize(), + ...timeRangeManager.serialize(), ...serializeLogRateAnalysisChartState(), }, references, @@ -144,8 +139,8 @@ export const getLogRateAnalysisEmbeddableFactory = ( }, }, { - ...timeRangeComparators, - ...titleComparators, + ...timeRangeManager.comparators, + ...titleManager.comparators, ...logRateAnalysisControlsComparators, } ); @@ -155,9 +150,9 @@ export const getLogRateAnalysisEmbeddableFactory = ( pluginStart ); - const onLoading = (v: boolean) => dataLoading.next(v); - const onRenderComplete = () => dataLoading.next(false); - const onError = (error: Error) => blockingError.next(error); + const onLoading = (v: boolean) => dataLoading$.next(v); + const onRenderComplete = () => dataLoading$.next(false); + const onError = (error: Error) => blockingError$.next(error); return { api, diff --git a/x-pack/platform/plugins/shared/aiops/public/embeddables/pattern_analysis/embeddable_pattern_analysis_factory.tsx b/x-pack/platform/plugins/shared/aiops/public/embeddables/pattern_analysis/embeddable_pattern_analysis_factory.tsx index 0327ce6e72031..34b5d17a199f0 100644 --- a/x-pack/platform/plugins/shared/aiops/public/embeddables/pattern_analysis/embeddable_pattern_analysis_factory.tsx +++ b/x-pack/platform/plugins/shared/aiops/public/embeddables/pattern_analysis/embeddable_pattern_analysis_factory.tsx @@ -15,6 +15,13 @@ import type { DataView } from '@kbn/data-views-plugin/common'; import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/common'; import type { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { i18n } from '@kbn/i18n'; +import { + apiHasExecutionContext, + fetch$, + initializeTimeRange, + initializeTitleManager, + useBatchedPublishingSubjects, +} from '@kbn/presentation-publishing'; import fastIsEqual from 'fast-deep-equal'; import { cloneDeep } from 'lodash'; import React, { useMemo } from 'react'; @@ -53,23 +60,10 @@ export const getPatternAnalysisEmbeddableFactory = ( return serializedState; }, buildEmbeddable: async (state, buildApi, uuid, parentApi) => { - const { - apiHasExecutionContext, - fetch$, - initializeTimeRange, - initializeTitles, - useBatchedPublishingSubjects, - } = await import('@kbn/presentation-publishing'); - const [coreStart, pluginStart] = await getStartServices(); - const { - api: timeRangeApi, - comparators: timeRangeComparators, - serialize: serializeTimeRange, - } = initializeTimeRange(state); - - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const timeRangeManager = initializeTimeRange(state); + const titleManager = initializeTitleManager(state); const { patternAnalysisControlsApi, @@ -77,8 +71,8 @@ export const getPatternAnalysisEmbeddableFactory = ( patternAnalysisControlsComparators, } = initializePatternAnalysisControls(state); - const dataLoading = new BehaviorSubject(true); - const blockingError = new BehaviorSubject(undefined); + const dataLoading$ = new BehaviorSubject(true); + const blockingError$ = new BehaviorSubject(undefined); const dataViews$ = new BehaviorSubject([ await pluginStart.data.dataViews.get( @@ -88,8 +82,8 @@ export const getPatternAnalysisEmbeddableFactory = ( const api = buildApi( { - ...timeRangeApi, - ...titlesApi, + ...timeRangeManager.api, + ...titleManager.api, ...patternAnalysisControlsApi, getTypeDisplayName: () => i18n.translate('xpack.aiops.patternAnalysis.typeDisplayName', { @@ -118,9 +112,9 @@ export const getPatternAnalysisEmbeddableFactory = ( return Promise.reject(); } }, - dataLoading, - blockingError, - dataViews: dataViews$, + dataLoading$, + blockingError$, + dataViews$, serializeState: () => { const dataViewId = patternAnalysisControlsApi.dataViewId.getValue(); const references: Reference[] = dataViewId @@ -135,8 +129,8 @@ export const getPatternAnalysisEmbeddableFactory = ( return { rawState: { timeRange: undefined, - ...serializeTitles(), - ...serializeTimeRange(), + ...titleManager.serialize(), + ...timeRangeManager.serialize(), ...serializePatternAnalysisChartState(), }, references, @@ -144,17 +138,17 @@ export const getPatternAnalysisEmbeddableFactory = ( }, }, { - ...timeRangeComparators, - ...titleComparators, + ...timeRangeManager.comparators, + ...titleManager.comparators, ...patternAnalysisControlsComparators, } ); const PatternAnalysisComponent = getPatternAnalysisComponent(coreStart, pluginStart); - const onLoading = (v: boolean) => dataLoading.next(v); - const onRenderComplete = () => dataLoading.next(false); - const onError = (error: Error) => blockingError.next(error); + const onLoading = (v: boolean) => dataLoading$.next(v); + const onRenderComplete = () => dataLoading$.next(false); + const onError = (error: Error) => blockingError$.next(error); return { api, diff --git a/x-pack/platform/plugins/shared/cases/public/components/visualizations/actions/is_compatible.test.ts b/x-pack/platform/plugins/shared/cases/public/components/visualizations/actions/is_compatible.test.ts index c9b7d4e71f3bb..40280083e3cbb 100644 --- a/x-pack/platform/plugins/shared/cases/public/components/visualizations/actions/is_compatible.test.ts +++ b/x-pack/platform/plugins/shared/cases/public/components/visualizations/actions/is_compatible.test.ts @@ -38,7 +38,7 @@ describe('isCompatible', () => { test('should return false if error embeddable', async () => { const errorApi = { ...getMockLensApi(), - blockingError: new BehaviorSubject(new Error('Simulated blocking error')), + blockingError$: new BehaviorSubject(new Error('Simulated blocking error')), }; expect(isCompatible(errorApi, appId, mockCoreStart)).toBe(false); }); diff --git a/x-pack/platform/plugins/shared/cases/public/components/visualizations/actions/mocks.ts b/x-pack/platform/plugins/shared/cases/public/components/visualizations/actions/mocks.ts index 94c7e5a1c939a..79ac894c270ce 100644 --- a/x-pack/platform/plugins/shared/cases/public/components/visualizations/actions/mocks.ts +++ b/x-pack/platform/plugins/shared/cases/public/components/visualizations/actions/mocks.ts @@ -43,7 +43,7 @@ export const getMockLensApi = ( getFullAttributes: () => { return mockLensAttributes; }, - panelTitle: new BehaviorSubject('myPanel'), + title$: new BehaviorSubject('myPanel'), timeRange$: new BehaviorSubject({ from, to, diff --git a/x-pack/platform/plugins/shared/dashboard_enhanced/public/services/drilldowns/actions/drilldown_shared.ts b/x-pack/platform/plugins/shared/dashboard_enhanced/public/services/drilldowns/actions/drilldown_shared.ts index 7d4458d02b556..aa20de96c103b 100644 --- a/x-pack/platform/plugins/shared/dashboard_enhanced/public/services/drilldowns/actions/drilldown_shared.ts +++ b/x-pack/platform/plugins/shared/dashboard_enhanced/public/services/drilldowns/actions/drilldown_shared.ts @@ -12,8 +12,8 @@ import { type PresentationContainer, } from '@kbn/presentation-containers'; import { - getPanelTitle, - type PublishesPanelTitle, + getTitle, + type PublishesTitle, type HasUniqueId, type HasParentApi, } from '@kbn/presentation-publishing'; @@ -54,7 +54,7 @@ export const createDrilldownTemplatesFromSiblings = ( const templates: DrilldownTemplate[] = []; for (const childId of Object.keys(parentApi.children$.value)) { - const child = parentApi.children$.value[childId] as Partial; + const child = parentApi.children$.value[childId] as Partial; if (childId === embeddable.uuid) continue; if (!apiHasDynamicActions(child)) continue; const events = child.enhancements.dynamicActions.state.get().events; @@ -64,7 +64,7 @@ export const createDrilldownTemplatesFromSiblings = ( id: event.eventId, name: event.action.name, icon: 'dashboardApp', - description: getPanelTitle(child) ?? child.uuid ?? '', + description: getTitle(child) ?? child.uuid ?? '', config: event.action.config, factoryId: event.action.factoryId, triggers: event.triggers, diff --git a/x-pack/platform/plugins/shared/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.test.tsx b/x-pack/platform/plugins/shared/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.test.tsx index 441e7525ce3a4..415a2c29b10d9 100644 --- a/x-pack/platform/plugins/shared/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.test.tsx +++ b/x-pack/platform/plugins/shared/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.test.tsx @@ -73,7 +73,7 @@ const compatibleEmbeddableApi = { supportedTriggers: () => { return ['VALUE_CLICK_TRIGGER']; }, - viewMode: new BehaviorSubject('edit'), + viewMode$: new BehaviorSubject('edit'), }; test('title is a string', () => { @@ -114,7 +114,7 @@ describe('isCompatible', () => { const action = createAction(); const embeddableApi = { ...compatibleEmbeddableApi, - viewMode: new BehaviorSubject('view'), + viewMode$: new BehaviorSubject('view'), }; expect(await action.isCompatible({ embeddable: embeddableApi })).toBe(false); }); diff --git a/x-pack/platform/plugins/shared/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/flyout_edit_drilldown.test.tsx b/x-pack/platform/plugins/shared/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/flyout_edit_drilldown.test.tsx index bd9ac25429768..179ad83c06b76 100644 --- a/x-pack/platform/plugins/shared/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/flyout_edit_drilldown.test.tsx +++ b/x-pack/platform/plugins/shared/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/flyout_edit_drilldown.test.tsx @@ -58,7 +58,7 @@ const compatibleEmbeddableApi = { supportedTriggers: () => { return ['VALUE_CLICK_TRIGGER']; }, - viewMode: new BehaviorSubject('edit'), + viewMode$: new BehaviorSubject('edit'), }; beforeAll(async () => { @@ -120,7 +120,7 @@ describe('isCompatible', () => { test('not compatible in view mode', async () => { const embeddableApi = { ...compatibleEmbeddableApi, - viewMode: new BehaviorSubject('view'), + viewMode$: new BehaviorSubject('view'), }; const action = createAction(); expect(await action.isCompatible({ embeddable: embeddableApi })).toBe(false); diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.test.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.test.ts index e2456abb9fcf3..57bdf3889397e 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.test.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.test.ts @@ -209,7 +209,7 @@ describe('Data Loader', () => { }, }); // trigger a change by changing the title in the attributes - (api.viewMode as BehaviorSubject).next('view'); + (api.viewMode$ as BehaviorSubject).next('view'); }); }); @@ -217,7 +217,7 @@ describe('Data Loader', () => { await expectRerenderOnDataLoder(async ({ api }) => { // the default get state does not have dynamic actions // trigger a change by changing the title in the attributes - (api.viewMode as BehaviorSubject).next('view'); + (api.viewMode$ as BehaviorSubject).next('view'); // should not re-render return false; }); @@ -322,7 +322,7 @@ describe('Data Loader', () => { // trigger a onData params?.onData$?.(1); expect(parentApi.onLoad).toHaveBeenCalledTimes(2); - expect(parentApi.onLoad).toHaveBeenNthCalledWith(2, false, undefined, api.dataLoading); + expect(parentApi.onLoad).toHaveBeenNthCalledWith(2, false, undefined, api.dataLoading$); // turn off the re-render check, that will be performed here return false; @@ -333,10 +333,10 @@ describe('Data Loader', () => { await expectRerenderOnDataLoder( async ({ internalApi }) => { await waitForValue( - internalApi.dataViews, + internalApi.dataViews$, (v: NonNullable) => Array.isArray(v) && v.length > 0 ); - const outputIndexPatterns = internalApi.dataViews.getValue() || []; + const outputIndexPatterns = internalApi.dataViews$.getValue() || []; expect(outputIndexPatterns.length).toEqual(2); expect(outputIndexPatterns[0].id).toEqual('123'); @@ -376,7 +376,7 @@ describe('Data Loader', () => { ...internalApi.attributes$.getValue(), title: faker.lorem.word(), }); - (api.savedObjectId as BehaviorSubject).next('newSavedObjectId'); + (api.savedObjectId$ as BehaviorSubject).next('newSavedObjectId'); }); }); diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.ts index 4a9a5c03fb095..571094c864b8c 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.ts @@ -165,7 +165,7 @@ export function loadEmbeddableData( internalApi.updateDataLoading(false); // The third argument here is an observable to let the // consumer to be notified on data change - onLoad?.(false, adapters, api.dataLoading); + onLoad?.(false, adapters, api.dataLoading$); api.loadViewUnderlyingData(); @@ -265,7 +265,7 @@ export function loadEmbeddableData( }), map(() => 'attributes' as ReloadReason) ), - api.savedObjectId.pipe( + api.savedObjectId$.pipe( waitUntilChanged(), map(() => 'savedObjectId' as ReloadReason) ), @@ -282,7 +282,7 @@ export function loadEmbeddableData( const subscriptions: Subscription[] = [ mergedSubscriptions.pipe(debounceTime(0)).subscribe(reload), // make sure to reload on viewMode change - api.viewMode.subscribe(() => { + api.viewMode$.subscribe(() => { // only reload if drilldowns are set if (getState().enhancements?.dynamicActions) { reload('viewMode'); diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/expressions/variables.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/expressions/variables.ts index c1fdda750199f..c01dca1b56ed2 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/expressions/variables.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/expressions/variables.ts @@ -28,7 +28,7 @@ function getInternalTables(states: Record) { */ export function getVariables(api: LensApi, state: LensRuntimeState) { return { - embeddableTitle: api.defaultPanelTitle?.getValue(), + embeddableTitle: api.defaultTitle$?.getValue(), ...(state.palette ? { theme: { palette: state.palette } } : {}), ...('overrides' in state ? { overrides: state.overrides } : {}), ...getInternalTables(state.attributes.state.datasourceStates), diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/helper.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/helper.ts index 4e85d1f7c429e..a0f7121c5e2e7 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/helper.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/helper.ts @@ -138,7 +138,7 @@ export function extractInheritedViewModeObservable( parentApi?: unknown ): PublishingSubject { if (apiPublishesViewMode(parentApi)) { - return parentApi.viewMode; + return parentApi.viewMode$; } if (apiHasParentApi(parentApi)) { return extractInheritedViewModeObservable(parentApi.parentApi); diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_actions.test.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_actions.test.ts index f669a7091053f..2f2398da9333b 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_actions.test.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_actions.test.ts @@ -58,7 +58,7 @@ function setupActionsApi( () => runtimeState, createUnifiedSearchApi(), pick(apiMock, ['timeRange$']), - pick(apiMock, ['panelTitle']), + apiMock.title$, internalApi, { ...services, diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_actions.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_actions.ts index ac6739d9ded1c..6b08879b577ad 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_actions.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_actions.ts @@ -16,6 +16,7 @@ import { isOfQueryType, } from '@kbn/es-query'; import { + PublishesTitle, PublishingSubject, StateComparators, apiPublishesUnifiedSearch, @@ -241,7 +242,7 @@ export function initializeActionApi( getLatestState: GetStateType, parentApi: unknown, searchContextApi: { timeRange$: PublishingSubject }, - titleApi: { panelTitle: PublishingSubject }, + title$: PublishesTitle['title$'], internalApi: LensInternalApi, services: LensEmbeddableStartServices ): { @@ -252,7 +253,7 @@ export function initializeActionApi( } { const dynamicActionsApi = services.embeddableEnhanced?.initializeReactEmbeddableDynamicActions( uuid, - () => titleApi.panelTitle.getValue(), + () => title$.getValue(), initialState ); const maybeStopDynamicActions = dynamicActionsApi?.startDynamicActions(); diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_dashboard_service.test.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_dashboard_service.test.ts index 2a0c469b3bbfb..9490f0b9bf6e0 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_dashboard_service.test.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_dashboard_service.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { initializeTitleManager } from '@kbn/presentation-publishing'; import type { LensRuntimeState } from '../types'; import { getLensRuntimeStateMock, getLensInternalApiMock, makeEmbeddableServices } from '../mocks'; import { initializeStateManagement } from './initialize_state_management'; @@ -17,12 +18,14 @@ function setupDashboardServicesApi(runtimeOverrides?: Partial) const internalApiMock = getLensInternalApiMock(); const runtimeState = getLensRuntimeStateMock(runtimeOverrides); const stateManagementConfig = initializeStateManagement(runtimeState, internalApiMock); + const titleManager = initializeTitleManager(runtimeState); const { api } = initializeDashboardServices( runtimeState, () => runtimeState, internalApiMock, stateManagementConfig, {}, + titleManager, services ); return api; diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_dashboard_services.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_dashboard_services.ts index 86309823a5f1c..3b20f89b11fa4 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_dashboard_services.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_dashboard_services.ts @@ -8,12 +8,12 @@ import { noop } from 'lodash'; import { HasLibraryTransforms, - PublishesWritablePanelTitle, - PublishesWritablePanelDescription, + PublishesWritableTitle, + PublishesWritableDescription, SerializedTitles, StateComparators, getUnchangingComparator, - initializeTitles, + initializeTitleManager, } from '@kbn/presentation-publishing'; import { apiIsPresentationContainer, apiPublishesSettings } from '@kbn/presentation-containers'; import { buildObservableVariable, isTextBasedLanguage } from '../helper'; @@ -36,8 +36,8 @@ import { StateManagementConfig } from './initialize_state_management'; type SerializedProps = SerializedTitles & LensPanelProps & LensOverrides & LensSharedProps; export interface DashboardServicesConfig { - api: PublishesWritablePanelTitle & - PublishesWritablePanelDescription & + api: PublishesWritableTitle & + PublishesWritableDescription & HasLibraryTransforms & Pick & Pick; @@ -57,15 +57,15 @@ export function initializeDashboardServices( internalApi: LensInternalApi, stateConfig: StateManagementConfig, parentApi: unknown, + titleManager: ReturnType, { attributeService, uiActions }: LensEmbeddableStartServices ): DashboardServicesConfig { - const { titlesApi, serializeTitles, titleComparators } = initializeTitles(initialState); // For some legacy reason the title and description default value is picked differently // ( based on existing FTR tests ). - const [defaultPanelTitle$] = buildObservableVariable( + const [defaultTitle$] = buildObservableVariable( initialState.title || internalApi.attributes$.getValue().title ); - const [defaultPanelDescription$] = buildObservableVariable( + const [defaultDescription$] = buildObservableVariable( initialState.savedObjectId ? internalApi.attributes$.getValue().description || initialState.description : initialState.description @@ -82,9 +82,9 @@ export function initializeDashboardServices( return { api: { parentApi: apiIsPresentationContainer(parentApi) ? parentApi : undefined, - defaultPanelTitle: defaultPanelTitle$, - defaultPanelDescription: defaultPanelDescription$, - ...titlesApi, + defaultTitle$, + defaultDescription$, + ...titleManager.api, updateOverrides: internalApi.updateOverrides, getTriggerCompatibleActions: uiActions.getTriggerCompatibleActions, @@ -143,7 +143,7 @@ export function initializeDashboardServices( } : {}; return { - ...serializeTitles(), + ...titleManager.serialize(), style, className, ...settings, @@ -153,7 +153,7 @@ export function initializeDashboardServices( }; }, comparators: { - ...titleComparators, + ...titleManager.comparators, id: getUnchangingComparator(), palette: getUnchangingComparator(), renderMode: getUnchangingComparator(), diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_edit.test.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_edit.test.ts index 2cd9a810bcbaa..af3bcb6282714 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_edit.test.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_edit.test.ts @@ -38,7 +38,7 @@ function createEditApi(servicesOverrides: Partial = api, () => false, // DSL based services, - { getAppContext: () => ({ currentAppId: 'lens' }), viewMode: new BehaviorSubject('edit') } + { getAppContext: () => ({ currentAppId: 'lens' }), viewMode$: new BehaviorSubject('edit') } ); } diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_edit.tsx b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_edit.tsx index bf2d9ded0c80c..c4e4b547dc70e 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_edit.tsx +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_edit.tsx @@ -19,6 +19,7 @@ import { noop } from 'lodash'; import { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public'; import { tracksOverlays } from '@kbn/presentation-containers'; import { i18n } from '@kbn/i18n'; +import { BehaviorSubject } from 'rxjs'; import { APP_ID, getEditPath } from '../../../common/constants'; import { GetStateType, @@ -83,16 +84,16 @@ export function initializeEditApi( extractInheritedViewModeObservable(parentApi) ); - const { disabledActionIds, setDisabledActionIds } = apiPublishesDisabledActionIds(parentApi) + const { disabledActionIds$, setDisabledActionIds } = apiPublishesDisabledActionIds(parentApi) ? parentApi - : { disabledActionIds: undefined, setDisabledActionIds: noop }; - const [disabledActionIds$, disabledActionIdsComparator] = buildObservableVariable< - string[] | undefined - >(disabledActionIds); + : { + disabledActionIds$: new BehaviorSubject(undefined), + setDisabledActionIds: noop, + }; if (isTextBasedLanguage(initialState)) { // do not expose the drilldown action for ES|QL - disabledActionIds$.next(disabledActionIds$.getValue()?.concat(['OPEN_FLYOUT_ADD_DRILLDOWN'])); + setDisabledActionIds(disabledActionIds$?.getValue()?.concat(['OPEN_FLYOUT_ADD_DRILLDOWN'])); } /** @@ -192,18 +193,18 @@ export function initializeEditApi( : true; return { - comparators: { disabledActionIds: disabledActionIdsComparator }, + comparators: { disabledActionIds$: [disabledActionIds$, setDisabledActionIds] }, serialize: emptySerializer, cleanup: noop, api: { uuid, - viewMode: viewMode$, + viewMode$, getTypeDisplayName: () => i18n.translate('xpack.lens.embeddableDisplayName', { defaultMessage: 'Lens', }), supportedTriggers, - disabledActionIds: disabledActionIds$, + disabledActionIds$, setDisabledActionIds, /** diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_internal_api.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_internal_api.ts index cf871d6c71a2d..280fa1a2b6e9c 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_internal_api.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_internal_api.ts @@ -6,7 +6,7 @@ */ import { BehaviorSubject } from 'rxjs'; -import { initializeTitles } from '@kbn/presentation-publishing'; +import { initializeTitleManager } from '@kbn/presentation-publishing'; import type { DataView } from '@kbn/data-views-plugin/common'; import { buildObservableVariable, createEmptyLensState } from '../helper'; import type { @@ -23,9 +23,9 @@ import type { UserMessage } from '../../types'; export function initializeInternalApi( initialState: LensRuntimeState, parentApi: unknown, + titleManager: ReturnType, { visualizationMap }: LensEmbeddableStartServices ): LensInternalApi { - const { titlesApi } = initializeTitles(initialState); const [hasRenderCompleted$] = buildObservableVariable(false); const [expressionParams$] = buildObservableVariable(null); const expressionAbortController$ = new BehaviorSubject(undefined); @@ -78,7 +78,7 @@ export function initializeInternalApi( expressionAbortController$, renderCount$, isNewlyCreated$, - dataViews: dataViews$, + dataViews$, blockingError$, messages$, validationMessages$, @@ -123,7 +123,7 @@ export function initializeInternalApi( }; } - if (displayOptions.noPanelTitle == null && titlesApi.hidePanelTitle?.getValue()) { + if (displayOptions.noPanelTitle == null && titleManager.api.hideTitle$?.getValue()) { displayOptions = { ...displayOptions, noPanelTitle: true, diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_state_management.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_state_management.ts index 850bace89fae8..b085f84eb5267 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_state_management.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_state_management.ts @@ -53,7 +53,7 @@ export function initializeStateManagement( LensRuntimeState['savedObjectId'] >(initialState.savedObjectId); - const [dataViews$] = buildObservableVariable(internalApi.dataViews); + const [dataViews$] = buildObservableVariable(internalApi.dataViews$); const [dataLoading$] = buildObservableVariable(internalApi.dataLoading$); const [rendered$] = buildObservableVariable(internalApi.hasRenderCompleted$); const [abortController$, abortControllerComparator] = buildObservableVariable< @@ -69,10 +69,10 @@ export function initializeStateManagement( updateAttributes: internalApi.updateAttributes, updateSavedObjectId: (newSavedObjectId: LensRuntimeState['savedObjectId']) => savedObjectId$.next(newSavedObjectId), - savedObjectId: savedObjectId$, - dataViews: dataViews$, - dataLoading: dataLoading$, - blockingError: blockingError$, + savedObjectId$, + dataViews$, + dataLoading$, + blockingError$, rendered$, }, serialize: () => { diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/lens_embeddable.tsx b/x-pack/platform/plugins/shared/lens/public/react_embeddable/lens_embeddable.tsx index c193e02c06f0e..f3a76432afcca 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/lens_embeddable.tsx +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/lens_embeddable.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; +import { initializeTitleManager } from '@kbn/presentation-publishing'; import { DOC_TYPE } from '../../common/constants'; import { LensApi, @@ -54,11 +55,13 @@ export const createLensEmbeddableFactory = ( * @returns an object with the Lens API and the React component to render in the Embeddable */ buildEmbeddable: async (initialState, buildApi, uuid, parentApi) => { + const titleManager = initializeTitleManager(initialState); + /** * Observables and functions declared here are used internally to store mutating state values * This is an internal API not exposed outside of the embeddable. */ - const internalApi = initializeInternalApi(initialState, parentApi, services); + const internalApi = initializeInternalApi(initialState, parentApi, titleManager, services); /** * Initialize various configurations required to build all the required @@ -79,6 +82,7 @@ export const createLensEmbeddableFactory = ( internalApi, stateConfig, parentApi, + titleManager, services ); @@ -111,7 +115,7 @@ export const createLensEmbeddableFactory = ( getState, parentApi, searchContextConfig.api, - dashboardConfig.api, + titleManager.api.title$, internalApi, services ); diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/mocks/index.tsx b/x-pack/platform/plugins/shared/lens/public/react_embeddable/mocks/index.tsx index 578e426654631..29b73c7a41c2d 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/mocks/index.tsx +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/mocks/index.tsx @@ -10,7 +10,7 @@ import deepMerge from 'deepmerge'; import React from 'react'; import faker from 'faker'; import { Query, Filter, AggregateQuery, TimeRange } from '@kbn/es-query'; -import { PhaseEvent, ViewMode } from '@kbn/presentation-publishing'; +import { initializeTitleManager, PhaseEvent, ViewMode } from '@kbn/presentation-publishing'; import { DataView } from '@kbn/data-views-plugin/common'; import { Adapters } from '@kbn/inspector-plugin/common'; import { coreMock } from '@kbn/core/public/mocks'; @@ -41,15 +41,15 @@ function getDefaultLensApiMock() { type: DOC_TYPE, uuid: faker.random.uuid(), // Shared Embeddable Observables - panelTitle: new BehaviorSubject(faker.lorem.words()), - hidePanelTitle: new BehaviorSubject(false), + title$: new BehaviorSubject(faker.lorem.words()), + hideTitle$: new BehaviorSubject(false), filters$: new BehaviorSubject([]), query$: new BehaviorSubject({ query: 'test', language: 'kuery', }), timeRange$: new BehaviorSubject({ from: 'now-15m', to: 'now' }), - dataLoading: new BehaviorSubject(false), + dataLoading$: new BehaviorSubject(false), // Methods getSavedVis: jest.fn(), getFullAttributes: jest.fn(), @@ -79,16 +79,16 @@ function getDefaultLensApiMock() { onEdit: jest.fn(), isEditingEnabled: jest.fn(() => true), getTypeDisplayName: jest.fn(() => 'Lens'), - setPanelTitle: jest.fn(), - setHidePanelTitle: jest.fn(), + setTitle: jest.fn(), + setHideTitle: jest.fn(), phase$: new BehaviorSubject({ id: faker.random.uuid(), status: 'rendered', timeToEvent: 1000, }), - unsavedChanges: new BehaviorSubject(undefined), - dataViews: new BehaviorSubject(undefined), - savedObjectId: new BehaviorSubject(undefined), + unsavedChanges$: new BehaviorSubject(undefined), + dataViews$: new BehaviorSubject(undefined), + savedObjectId$: new BehaviorSubject(undefined), adapters$: new BehaviorSubject({}), updateAttributes: jest.fn(), updateSavedObjectId: jest.fn(), @@ -96,11 +96,11 @@ function getDefaultLensApiMock() { getSerializedStateByReference: jest.fn(), getSerializedStateByValue: jest.fn(), getTriggerCompatibleActions: jest.fn(), - blockingError: new BehaviorSubject(undefined), - panelDescription: new BehaviorSubject(undefined), - setPanelDescription: jest.fn(), - viewMode: new BehaviorSubject('view'), - disabledActionIds: new BehaviorSubject(undefined), + blockingError$: new BehaviorSubject(undefined), + description$: new BehaviorSubject(undefined), + setDescription: jest.fn(), + viewMode$: new BehaviorSubject('view'), + disabledActionIds$: new BehaviorSubject(undefined), setDisabledActionIds: jest.fn(), rendered$: new BehaviorSubject(false), searchSessionId$: new BehaviorSubject(undefined), @@ -277,7 +277,13 @@ export function getValidExpressionParams( } function getInternalApiWithFunctionWrappers() { - const newApi = initializeInternalApi(getLensRuntimeStateMock(), {}, makeEmbeddableServices()); + const mockRuntimeState = getLensRuntimeStateMock(); + const newApi = initializeInternalApi( + mockRuntimeState, + {}, + initializeTitleManager(mockRuntimeState), + makeEmbeddableServices() + ); const fns: Array = ( Object.keys(newApi) as Array ).filter((key) => typeof newApi[key] === 'function'); diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/renderer/hooks.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/renderer/hooks.ts index c6d97d16ad386..b9053e19e8a1e 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/renderer/hooks.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/renderer/hooks.ts @@ -29,7 +29,7 @@ export function useMessages({ messages$ }: LensInternalApi) { export function useDispatcher(hasRendered: boolean, api: LensApi) { const rootRef = useRef(null); useEffect(() => { - if (!rootRef.current || api.blockingError?.getValue()) { + if (!rootRef.current || api.blockingError$?.getValue()) { return; } if (hasRendered) { @@ -37,6 +37,6 @@ export function useDispatcher(hasRendered: boolean, api: LensApi) { } else { dispatchRenderStart(rootRef.current); } - }, [hasRendered, api.blockingError, rootRef]); + }, [hasRendered, api.blockingError$, rootRef]); return rootRef; } diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/renderer/lens_custom_renderer_component.tsx b/x-pack/platform/plugins/shared/lens/public/react_embeddable/renderer/lens_custom_renderer_component.tsx index 3caada55b81db..1943f8ae47d18 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/renderer/lens_custom_renderer_component.tsx +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/renderer/lens_custom_renderer_component.tsx @@ -75,7 +75,7 @@ export function LensRenderer({ const disabledActionIds$ = useObservableVariable(disabledActions); const viewMode$ = useObservableVariable(viewMode); const searchSessionId$ = useObservableVariable(searchSessionId); - const hidePanelTitles$ = useObservableVariable(hidePanelTitles); + const hideTitle$ = useObservableVariable(hidePanelTitles); // Lens API will be set once, but when set trigger a reflow to adopt the latest attributes const [lensApi, setLensApi] = useState(undefined); @@ -145,9 +145,9 @@ export function LensRenderer({ // forward the unified search context ...searchApi, searchSessionId$, - disabledActionIds: disabledActionIds$, + disabledActionIds$, setDisabledActionIds: (ids: string[] | undefined) => disabledActionIds$.next(ids), - viewMode: viewMode$, + viewMode$, // pass the sync* settings with the unified settings interface settings, // make sure to provide the initial state (useful for the comparison check) @@ -157,7 +157,7 @@ export function LensRenderer({ ...initialStateRef.current, attributes: props.attributes, }), - hidePanelTitle: hidePanelTitles$, + hideTitle$, reload$, // trigger a reload (replacement for deprepcated searchSessionId) })} onApiAvailable={setLensApi} diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/renderer/lens_embeddable_component.tsx b/x-pack/platform/plugins/shared/lens/public/react_embeddable/renderer/lens_embeddable_component.tsx index 122891788a808..a3bee0d91bfd6 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/renderer/lens_embeddable_component.tsx +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/renderer/lens_embeddable_component.tsx @@ -41,7 +41,7 @@ export function LensEmbeddableComponent({ internalApi.renderCount$, internalApi.validationMessages$, api.rendered$, - api.viewMode + api.viewMode$ ); const canEdit = Boolean(api.isEditingEnabled?.() && getViewMode(latestViewMode) === 'edit'); @@ -49,7 +49,7 @@ export function LensEmbeddableComponent({ // On unmount call all the cleanups useEffect(() => { - addLog(`Mounting Lens Embeddable component: ${api.defaultPanelTitle?.getValue()}`); + addLog(`Mounting Lens Embeddable component: ${api.defaultTitle$?.getValue()}`); return onUnmount; }, [api, onUnmount]); @@ -59,11 +59,10 @@ export function LensEmbeddableComponent({ // Publish the data attributes only if avaialble/visible const title = internalApi.getDisplayOptions()?.noPanelTitle ? undefined - : { 'data-title': api.panelTitle?.getValue() ?? api.defaultPanelTitle?.getValue() }; - const description = api.panelDescription?.getValue() + : { 'data-title': api.title$?.getValue() ?? api.defaultTitle$?.getValue() }; + const description = api.description$?.getValue() ? { - 'data-description': - api.panelDescription?.getValue() ?? api.defaultPanelDescription?.getValue(), + 'data-description': api.description$?.getValue() ?? api.defaultDescription$?.getValue(), } : undefined; diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/type_guards.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/type_guards.ts index 95e8311a7a3c0..96f43d0256d4a 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/type_guards.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/type_guards.ts @@ -7,7 +7,7 @@ import { apiIsOfType, - apiPublishesPanelTitle, + apiPublishesTitle, apiPublishesUnifiedSearch, } from '@kbn/presentation-publishing'; import { isObject } from 'lodash'; @@ -34,7 +34,7 @@ export const isLensApi = (api: unknown): api is LensApi => { apiIsOfType(api, 'lens') && 'canViewUnderlyingData$' in api && apiHasLensCallbacks(api) && - apiPublishesPanelTitle(api) && + apiPublishesTitle(api) && apiPublishesUnifiedSearch(api) ); }; diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/types.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/types.ts index 12ace8f467d26..cfca83c9d3f4e 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/types.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/types.ts @@ -26,8 +26,8 @@ import type { PublishesUnifiedSearch, PublishesViewMode, PublishesRendered, - PublishesWritablePanelDescription, - PublishesWritablePanelTitle, + PublishesWritableDescription, + PublishesWritableTitle, PublishingSubject, SerializedTitles, ViewMode, @@ -378,8 +378,8 @@ export type LensApi = Simplify< // Let the container know the used data views PublishesDataViews & // Let the container operate on panel title/description - PublishesWritablePanelTitle & - PublishesWritablePanelDescription & + PublishesWritableTitle & + PublishesWritableDescription & // This embeddable can narrow down specific triggers usage HasSupportedTriggers & PublishesDisabledActionIds & diff --git a/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_cross_panel_actions.ts b/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_cross_panel_actions.ts index 145c55131dfa8..69a304dd40501 100644 --- a/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_cross_panel_actions.ts +++ b/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_cross_panel_actions.ts @@ -10,7 +10,7 @@ import { ACTION_GLOBAL_APPLY_FILTER } from '@kbn/unified-search-plugin/public'; import { i18n } from '@kbn/i18n'; import { ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; import { BehaviorSubject } from 'rxjs'; -import { getPanelTitle, StateComparators } from '@kbn/presentation-publishing'; +import { getTitle, StateComparators } from '@kbn/presentation-publishing'; import { createExtentFilter } from '../../common/elasticsearch_util'; import { SavedMap } from '../routes/map_page'; import { mapEmbeddablesSingleton } from './map_embeddables_singleton'; @@ -150,7 +150,7 @@ export function initializeCrossPanelActions({ mapEmbeddablesSingleton.register(uuid, { getTitle: () => { const mapApi = getApi(); - const title = mapApi ? getPanelTitle(mapApi) : undefined; + const title = mapApi ? getTitle(mapApi) : undefined; return title ? title : i18n.translate('xpack.maps.embeddable.untitleMap', { diff --git a/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_data_views.test.ts b/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_data_views.test.ts index b240208bc6050..0430e7b49021b 100644 --- a/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_data_views.test.ts +++ b/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_data_views.test.ts @@ -56,7 +56,7 @@ describe('dataViews$', () => { test('Should emit when data view added', async () => { const dataViewApi = initializeDataViews(createMapStore()); - const subscription = dataViewApi.dataViews.pipe(skip(1)).subscribe(onEmitMock); + const subscription = dataViewApi.dataViews$.pipe(skip(1)).subscribe(onEmitMock); dataViewApi.setLayerList([ createLayerDescriptor({ @@ -85,7 +85,7 @@ describe('dataViews$', () => { geoFieldType: ES_GEO_FIELD_TYPE.GEO_POINT, }), ]); - const subscription = dataViewApi.dataViews.pipe(skip(1)).subscribe(onEmitMock); + const subscription = dataViewApi.dataViews$.pipe(skip(1)).subscribe(onEmitMock); dataViewApi.setLayerList([]); await new Promise((resolve) => setTimeout(resolve, 0)); @@ -111,7 +111,7 @@ describe('dataViews$', () => { ]); await new Promise((resolve) => setTimeout(resolve, 0)); - const subscription = dataViewApi.dataViews.pipe(skip(1)).subscribe(onEmitMock); + const subscription = dataViewApi.dataViews$.pipe(skip(1)).subscribe(onEmitMock); dataViewApi.setLayerList([ createLayerDescriptor({ diff --git a/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_data_views.ts b/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_data_views.ts index 6361f59eeef6c..cb3ea5b5b0f4d 100644 --- a/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_data_views.ts +++ b/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_data_views.ts @@ -42,7 +42,7 @@ export function initializeDataViews(store: MapStore) { const syncLayerTokens: Record = {}; return { - dataViews: dataViews$, + dataViews$, setLayerList(layerList: LayerDescriptor[]) { store.dispatch(replaceLayerList(layerList)); updateDataViews(); diff --git a/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_redux_sync.ts b/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_redux_sync.ts index 97e19582e0206..2d651770d19d9 100644 --- a/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_redux_sync.ts +++ b/x-pack/platform/plugins/shared/maps/public/react_embeddable/initialize_redux_sync.ts @@ -170,7 +170,7 @@ export function initializeReduxSync({ unsubscribeFromStore(); }, api: { - dataLoading: dataLoading$, + dataLoading$, filters$, getInspectorAdapters: () => { return getInspectorAdapters(store.getState()); diff --git a/x-pack/platform/plugins/shared/maps/public/react_embeddable/map_react_embeddable.tsx b/x-pack/platform/plugins/shared/maps/public/react_embeddable/map_react_embeddable.tsx index b20c05dba244b..d1f861ace715d 100644 --- a/x-pack/platform/plugins/shared/maps/public/react_embeddable/map_react_embeddable.tsx +++ b/x-pack/platform/plugins/shared/maps/public/react_embeddable/map_react_embeddable.tsx @@ -16,7 +16,7 @@ import { areTriggersDisabled, getUnchangingComparator, initializeTimeRange, - initializeTitles, + initializeTitleManager, useBatchedPublishingSubjects, } from '@kbn/presentation-publishing'; import { BehaviorSubject } from 'rxjs'; @@ -79,19 +79,17 @@ export const mapEmbeddableFactory: ReactEmbeddableFactory< const sharingSavedObjectProps = savedMap.getSharingSavedObjectProps(); const spaces = getSpacesApi(); const controlledBy = getControlledBy(uuid); - const title = initializeTitles(state); + const titleManager = initializeTitleManager(state); const timeRange = initializeTimeRange(state); const dynamicActionsApi = getEmbeddableEnhanced()?.initializeReactEmbeddableDynamicActions( uuid, - () => title.titlesApi.panelTitle.getValue(), + () => titleManager.api.title$.getValue(), state ); const maybeStopDynamicActions = dynamicActionsApi?.startDynamicActions(); - const defaultPanelTitle$ = new BehaviorSubject( - savedMap.getAttributes().title - ); - const defaultPanelDescription$ = new BehaviorSubject( + const defaultTitle$ = new BehaviorSubject(savedMap.getAttributes().title); + const defaultDescription$ = new BehaviorSubject( savedMap.getAttributes().description ); const reduxSync = initializeReduxSync({ @@ -114,7 +112,7 @@ export const mapEmbeddableFactory: ReactEmbeddableFactory< return { ...state, ...timeRange.serialize(), - ...title.serializeTitles(), + ...titleManager.serialize(), ...(dynamicActionsApi?.serializeDynamicActions() ?? {}), ...crossPanelActions.serialize(), ...reduxSync.serialize(), @@ -156,11 +154,11 @@ export const mapEmbeddableFactory: ReactEmbeddableFactory< api = buildApi( { - defaultPanelTitle: defaultPanelTitle$, - defaultPanelDescription: defaultPanelDescription$, + defaultTitle$, + defaultDescription$, ...timeRange.api, ...(dynamicActionsApi?.dynamicActionsApi ?? {}), - ...title.titlesApi, + ...titleManager.api, ...reduxSync.api, ...initializeEditApi(uuid, getState, parentApi, state.savedObjectId), ...initializeLibraryTransforms(savedMap, serializeState), @@ -172,7 +170,7 @@ export const mapEmbeddableFactory: ReactEmbeddableFactory< }, { ...timeRange.comparators, - ...title.titleComparators, + ...titleManager.comparators, ...(dynamicActionsApi?.dynamicActionsComparator ?? { enhancements: getUnchangingComparator(), }), @@ -200,13 +198,12 @@ export const mapEmbeddableFactory: ReactEmbeddableFactory< return { api, Component: () => { - const [defaultPanelTitle, panelTitle, defaultPanelDescription, panelDescription] = - useBatchedPublishingSubjects( - defaultPanelTitle$, - title.titlesApi.panelTitle, - defaultPanelDescription$, - title.titlesApi.panelDescription - ); + const [defaultTitle, title, defaultDescription, description] = useBatchedPublishingSubjects( + defaultTitle$, + titleManager.api.title$, + defaultDescription$, + titleManager.api.description$ + ); useEffect(() => { return () => { @@ -250,8 +247,8 @@ export const mapEmbeddableFactory: ReactEmbeddableFactory< ? parentApi.getTooltipRenderer() : undefined } - title={panelTitle ?? defaultPanelTitle} - description={panelDescription ?? defaultPanelDescription} + title={title ?? defaultTitle} + description={description ?? defaultDescription} waitUntilTimeLayersLoad$={waitUntilTimeLayersLoad$(savedMap.getStore())} isSharable={ isMapRendererApi(parentApi) && typeof parentApi.isSharable === 'boolean' diff --git a/x-pack/platform/plugins/shared/maps/public/react_embeddable/types.ts b/x-pack/platform/plugins/shared/maps/public/react_embeddable/types.ts index 45ed4a07a7398..ca3bf87c531aa 100644 --- a/x-pack/platform/plugins/shared/maps/public/react_embeddable/types.ts +++ b/x-pack/platform/plugins/shared/maps/public/react_embeddable/types.ts @@ -10,7 +10,7 @@ import { TimeRange } from '@kbn/es-query'; import { HasInspectorAdapters } from '@kbn/inspector-plugin/public'; import { apiIsOfType, - apiPublishesPanelTitle, + apiPublishesTitle, apiPublishesUnifiedSearch, HasEditCapabilities, HasLibraryTransforms, @@ -75,7 +75,7 @@ export const isMapApi = (api: unknown): api is MapApi => { api && apiIsOfType(api, 'map') && typeof (api as MapApi).getLayerList === 'function' && - apiPublishesPanelTitle(api) && + apiPublishesTitle(api) && apiPublishesUnifiedSearch(api) ); }; diff --git a/x-pack/platform/plugins/shared/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts b/x-pack/platform/plugins/shared/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts index 8ec738af55d02..b871a47744dcd 100644 --- a/x-pack/platform/plugins/shared/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts +++ b/x-pack/platform/plugins/shared/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts @@ -16,7 +16,7 @@ import type { DashboardLocatorParams, DashboardStart, } from '@kbn/dashboard-plugin/public'; -import { getPanelTitle } from '@kbn/presentation-publishing'; +import { getTitle } from '@kbn/presentation-publishing'; import type { Filter, Query, DataViewBase } from '@kbn/es-query'; import { FilterStateStore } from '@kbn/es-query'; import type { ErrorType } from '@kbn/ml-error-utils'; @@ -226,7 +226,7 @@ export class QuickJobCreatorBase { } private async createDashboardLink(dashboard: DashboardApi, datafeedConfig: estypes.MlDatafeed) { - const savedObjectId = dashboard.savedObjectId?.value; + const savedObjectId = dashboard.savedObjectId$?.value; if (!savedObjectId) { return null; } @@ -254,7 +254,7 @@ export class QuickJobCreatorBase { const url = `${location.app}${location.path}`; const urlName = i18n.translate('xpack.ml.newJob.fromLens.createJob.namedUrlDashboard', { defaultMessage: 'Open {dashboardTitle}', - values: { dashboardTitle: getPanelTitle(dashboard) ?? 'dashboard' }, + values: { dashboardTitle: getTitle(dashboard) ?? 'dashboard' }, }); return { url_name: urlName, url_value: url, time_range: 'auto' }; diff --git a/x-pack/platform/plugins/shared/ml/public/application/jobs/new_job/job_from_map/visualization_extractor.ts b/x-pack/platform/plugins/shared/ml/public/application/jobs/new_job/job_from_map/visualization_extractor.ts index ab319fab9f34a..a80c2470afcc4 100644 --- a/x-pack/platform/plugins/shared/ml/public/application/jobs/new_job/job_from_map/visualization_extractor.ts +++ b/x-pack/platform/plugins/shared/ml/public/application/jobs/new_job/job_from_map/visualization_extractor.ts @@ -30,7 +30,7 @@ export class VisualizationExtractor { embeddable: MapApi & Partial ): Promise { const layers: LayerResult[] = []; - const dataViews: DataView[] = embeddable.dataViews?.value ?? []; + const dataViews: DataView[] = embeddable.dataViews$?.value ?? []; // Keep track of geoFields for layers as they can be repeated const layerGeoFields: Record = {}; diff --git a/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.tsx b/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.tsx index fe844ae26905b..bebbd398e6666 100644 --- a/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.tsx +++ b/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.tsx @@ -17,7 +17,7 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { apiHasExecutionContext, initializeTimeRange, - initializeTitles, + initializeTitleManager, } from '@kbn/presentation-publishing'; import { distinctUntilChanged } from 'rxjs'; import fastIsEqual from 'fast-deep-equal'; @@ -58,12 +58,8 @@ export const getAnomalyChartsReactEmbeddableFactory = ( const subscriptions = new Subscription(); - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); - const { - api: timeRangeApi, - comparators: timeRangeComparators, - serialize: serializeTimeRange, - } = initializeTimeRange(state); + const titleManager = initializeTitleManager(state); + const timeRangeManager = initializeTimeRange(state); const { anomalyChartsControlsApi, @@ -71,7 +67,7 @@ export const getAnomalyChartsReactEmbeddableFactory = ( serializeAnomalyChartsState, anomalyChartsComparators, onAnomalyChartsDestroy, - } = initializeAnomalyChartsControls(state, titlesApi, parentApi); + } = initializeAnomalyChartsControls(state, titleManager.api, parentApi); const api = buildApi( { @@ -91,7 +87,7 @@ export const getAnomalyChartsReactEmbeddableFactory = ( parentApi, uuid, { - ...serializeTitles(), + ...titleManager.serialize(), ...serializeAnomalyChartsState(), } ); @@ -102,11 +98,11 @@ export const getAnomalyChartsReactEmbeddableFactory = ( return Promise.reject(); } }, - ...titlesApi, - ...timeRangeApi, + ...titleManager.api, + ...timeRangeManager.api, ...anomalyChartsControlsApi, ...dataLoadingApi, - dataViews: buildDataViewPublishingApi( + dataViews$: buildDataViewPublishingApi( { anomalyDetectorService: mlServices.anomalyDetectorService, dataViewsService: pluginsStartServices.data.dataViews, @@ -118,8 +114,8 @@ export const getAnomalyChartsReactEmbeddableFactory = ( return { rawState: { timeRange: undefined, - ...serializeTitles(), - ...serializeTimeRange(), + ...titleManager.serialize(), + ...timeRangeManager.serialize(), ...serializeAnomalyChartsState(), }, references: [], @@ -127,8 +123,8 @@ export const getAnomalyChartsReactEmbeddableFactory = ( }, }, { - ...timeRangeComparators, - ...titleComparators, + ...timeRangeManager.comparators, + ...titleManager.comparators, ...anomalyChartsComparators, } ); diff --git a/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_charts/initialize_anomaly_charts_controls.ts b/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_charts/initialize_anomaly_charts_controls.ts index b5ed19391237b..c5f622e901242 100644 --- a/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_charts/initialize_anomaly_charts_controls.ts +++ b/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_charts/initialize_anomaly_charts_controls.ts @@ -5,11 +5,10 @@ * 2.0. */ -import type { TitlesApi } from '@kbn/presentation-publishing/interfaces/titles/titles_api'; import { BehaviorSubject } from 'rxjs'; import fastIsEqual from 'fast-deep-equal'; import type { MlEntityField } from '@kbn/ml-anomaly-utils'; -import type { StateComparators } from '@kbn/presentation-publishing'; +import type { StateComparators, TitlesApi } from '@kbn/presentation-publishing'; import type { JobId } from '../../../common/types/anomaly_detection_jobs'; import { DEFAULT_MAX_SERIES_TO_PLOT } from '../../application/services/anomaly_explorer_charts_service'; import type { @@ -40,7 +39,7 @@ export const initializeAnomalyChartsControls = ( jobIds$.next(update.jobIds); maxSeriesToPlot$.next(update.maxSeriesToPlot); if (titlesApi) { - titlesApi.setPanelTitle(update.title); + titlesApi.setTitle(update.title); } }; @@ -82,7 +81,7 @@ export const initializeAnomalyChartsControls = ( updateSelectedEntities, } as AnomalyChartsComponentApi, dataLoadingApi: { - dataLoading: dataLoading$, + dataLoading$, setInterval, onRenderComplete, onLoading, diff --git a/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.test.tsx b/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.test.tsx index 61a2fadeb6df8..5c2db5bdb7370 100644 --- a/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.test.tsx +++ b/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.test.tsx @@ -118,7 +118,7 @@ describe('getAnomalySwimLaneEmbeddableFactory', () => { await waitFor(() => { const resultApi = onApiAvailable.mock.calls[0][0]; - expect(resultApi.dataLoading!.value).toEqual(false); + expect(resultApi.dataLoading$?.value).toEqual(false); expect(resultApi.jobIds.value).toEqual(['my-job']); expect(resultApi.viewBy.value).toEqual('overall'); diff --git a/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.tsx b/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.tsx index 6969f43c105f3..851cfd7a7a378 100644 --- a/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.tsx +++ b/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.tsx @@ -20,7 +20,7 @@ import { apiPublishesTimeRange, fetch$, initializeTimeRange, - initializeTitles, + initializeTitleManager, useBatchedPublishingSubjects, } from '@kbn/presentation-publishing'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; @@ -109,8 +109,8 @@ export const getAnomalySwimLaneEmbeddableFactory = ( const interval = new BehaviorSubject(undefined); - const dataLoading = new BehaviorSubject(true); - const blockingError = new BehaviorSubject(undefined); + const dataLoading$ = new BehaviorSubject(true); + const blockingError$ = new BehaviorSubject(undefined); const query$ = // @ts-ignore (state.query ? new BehaviorSubject(state.query) : parentApi?.query$) ?? @@ -122,19 +122,15 @@ export const getAnomalySwimLaneEmbeddableFactory = ( const refresh$ = new BehaviorSubject(undefined); - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); - const { - api: timeRangeApi, - comparators: timeRangeComparators, - serialize: serializeTimeRange, - } = initializeTimeRange(state); + const titleManager = initializeTitleManager(state); + const timeRangeManager = initializeTimeRange(state); const { swimLaneControlsApi, serializeSwimLaneState, swimLaneComparators, onSwimLaneDestroy, - } = initializeSwimLaneControls(state, titlesApi); + } = initializeSwimLaneControls(state, titleManager.api); // Helpers for swim lane data fetching const chartWidth$ = new BehaviorSubject(undefined); @@ -157,7 +153,7 @@ export const getAnomalySwimLaneEmbeddableFactory = ( parentApi, uuid, { - ...serializeTitles(), + ...titleManager.serialize(), ...serializeSwimLaneState(), } ); @@ -167,14 +163,14 @@ export const getAnomalySwimLaneEmbeddableFactory = ( return Promise.reject(); } }, - ...titlesApi, - ...timeRangeApi, + ...titleManager.api, + ...timeRangeManager.api, ...swimLaneControlsApi, query$, filters$, interval, setInterval: (v) => interval.next(v), - dataViews: buildDataViewPublishingApi( + dataViews$: buildDataViewPublishingApi( { anomalyDetectorService: services[2].anomalyDetectorService, dataViewsService: services[1].data.dataViews, @@ -182,13 +178,13 @@ export const getAnomalySwimLaneEmbeddableFactory = ( { jobIds: swimLaneControlsApi.jobIds }, subscriptions ), - dataLoading, + dataLoading$, serializeState: () => { return { rawState: { timeRange: undefined, - ...serializeTitles(), - ...serializeTimeRange(), + ...titleManager.serialize(), + ...timeRangeManager.serialize(), ...serializeSwimLaneState(), }, references: [], @@ -196,8 +192,8 @@ export const getAnomalySwimLaneEmbeddableFactory = ( }, }, { - ...timeRangeComparators, - ...titleComparators, + ...timeRangeManager.comparators, + ...titleManager.comparators, ...swimLaneComparators, } ); @@ -228,8 +224,8 @@ export const getAnomalySwimLaneEmbeddableFactory = ( const { swimLaneData$, onDestroy } = initializeSwimLaneDataFetcher( api, chartWidth$.asObservable(), - dataLoading, - blockingError, + dataLoading$, + blockingError$, appliedTimeRange$, query$, filters$, @@ -285,7 +281,7 @@ export const getAnomalySwimLaneEmbeddableFactory = ( api.perPage, api.swimlaneType, swimLaneData$, - blockingError + blockingError$ ); const [selectedCells, setSelectedCells] = useState(); @@ -357,7 +353,7 @@ export const getAnomalySwimLaneEmbeddableFactory = ( api.updatePagination({ perPage: update.perPage, fromPage: 1 }); } }} - isLoading={dataLoading.value!} + isLoading={dataLoading$.value!} yAxisWidth={{ max: Y_AXIS_LABEL_WIDTH }} noDataWarning={ { diff --git a/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/initialize_swim_lane_data_fetcher.ts b/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/initialize_swim_lane_data_fetcher.ts index be678af02a65b..32b41c5c685f4 100644 --- a/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/initialize_swim_lane_data_fetcher.ts +++ b/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/initialize_swim_lane_data_fetcher.ts @@ -40,8 +40,8 @@ const FETCH_RESULTS_DEBOUNCE_MS = 500; export const initializeSwimLaneDataFetcher = ( swimLaneApi: AnomalySwimLaneEmbeddableApi, chartWidth$: Observable, - dataLoading: BehaviorSubject, - blockingError: BehaviorSubject, + dataLoading$: BehaviorSubject, + blockingError$: BehaviorSubject, appliedTimeRange$: Observable, query$: PublishesUnifiedSearch['query$'], filters$: PublishesUnifiedSearch['filters$'], @@ -53,7 +53,7 @@ export const initializeSwimLaneDataFetcher = ( const swimLaneData$ = new BehaviorSubject(undefined); const selectedJobs$ = getJobsObservable(swimLaneApi.jobIds, anomalyDetectorService, (error) => { - blockingError.next(error); + blockingError$.next(error); }).pipe(shareReplay(1)); const swimLaneInput$ = combineLatest({ @@ -84,7 +84,7 @@ export const initializeSwimLaneDataFetcher = ( ]) .pipe( tap(() => { - dataLoading.next(true); + dataLoading$.next(true); }), debounceTime(FETCH_RESULTS_DEBOUNCE_MS), switchMap(([explorerJobs, input, query, filters, bucketInterval]) => { @@ -102,7 +102,7 @@ export const initializeSwimLaneDataFetcher = ( } } catch (e) { // handle query syntax errors - blockingError.next(e); + blockingError$.next(e); return EMPTY; } @@ -141,7 +141,7 @@ export const initializeSwimLaneDataFetcher = ( return of(overallSwimlaneData); }), catchError((error) => { - blockingError.next(error); + blockingError$.next(error); return EMPTY; }) ); @@ -150,8 +150,8 @@ export const initializeSwimLaneDataFetcher = ( .subscribe((data) => { swimLaneApi.setInterval(data?.interval); - dataLoading.next(false); - blockingError.next(undefined); + dataLoading$.next(false); + blockingError$.next(undefined); swimLaneData$.next(data); }); diff --git a/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/types.ts b/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/types.ts index cde7ff1350687..9687ac713db94 100644 --- a/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/types.ts +++ b/x-pack/platform/plugins/shared/ml/public/embeddables/anomaly_swimlane/types.ts @@ -10,7 +10,7 @@ import type { HasEditCapabilities, PublishesDataViews, PublishesUnifiedSearch, - PublishesWritablePanelTitle, + PublishesWritableTitle, PublishingSubject, SerializedTitles, } from '@kbn/presentation-publishing'; @@ -41,7 +41,7 @@ export interface AnomalySwimLaneComponentApi { export type AnomalySwimLaneEmbeddableApi = MlEmbeddableBaseApi & PublishesDataViews & PublishesUnifiedSearch & - PublishesWritablePanelTitle & + PublishesWritableTitle & HasEditCapabilities & AnomalySwimLaneComponentApi; diff --git a/x-pack/platform/plugins/shared/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/flyout.tsx b/x-pack/platform/plugins/shared/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/flyout.tsx index 8e0b4178b5a1f..e365fce2c06d7 100644 --- a/x-pack/platform/plugins/shared/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/flyout.tsx +++ b/x-pack/platform/plugins/shared/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/flyout.tsx @@ -19,7 +19,7 @@ import { EuiSpacer, EuiText, } from '@elastic/eui'; -import { getPanelTitle } from '@kbn/presentation-publishing'; +import { getTitle } from '@kbn/presentation-publishing'; import type { LensApi } from '@kbn/lens-plugin/public'; import { Layer } from './layer'; import type { LayerResult } from '../../../../application/jobs/new_job/job_from_lens'; @@ -67,7 +67,7 @@ export const LensLayerSelectionFlyout: FC = ({ onClose, embeddable }) => diff --git a/x-pack/platform/plugins/shared/ml/public/embeddables/job_creation/map/flyout.tsx b/x-pack/platform/plugins/shared/ml/public/embeddables/job_creation/map/flyout.tsx index a10ed011f7e84..05fe239d2cc06 100644 --- a/x-pack/platform/plugins/shared/ml/public/embeddables/job_creation/map/flyout.tsx +++ b/x-pack/platform/plugins/shared/ml/public/embeddables/job_creation/map/flyout.tsx @@ -20,7 +20,7 @@ import { EuiTitle, useEuiTheme, } from '@elastic/eui'; -import { getPanelTitle } from '@kbn/presentation-publishing'; +import { getTitle } from '@kbn/presentation-publishing'; import type { MapApi } from '@kbn/maps-plugin/public'; import { Layer } from './map_vis_layer_selection_flyout/layer'; import type { LayerResult } from '../../../application/jobs/new_job/job_from_map'; @@ -63,7 +63,7 @@ export const GeoJobFlyout: FC = ({ onClose, embeddable }) => { diff --git a/x-pack/platform/plugins/shared/ml/public/embeddables/single_metric_viewer/single_metric_viewer_controls_initializer.ts b/x-pack/platform/plugins/shared/ml/public/embeddables/single_metric_viewer/single_metric_viewer_controls_initializer.ts index 8f1b9dde3c67f..97a391a4e60fc 100644 --- a/x-pack/platform/plugins/shared/ml/public/embeddables/single_metric_viewer/single_metric_viewer_controls_initializer.ts +++ b/x-pack/platform/plugins/shared/ml/public/embeddables/single_metric_viewer/single_metric_viewer_controls_initializer.ts @@ -5,8 +5,7 @@ * 2.0. */ -import type { StateComparators } from '@kbn/presentation-publishing'; -import type { TitlesApi } from '@kbn/presentation-publishing/interfaces/titles/titles_api'; +import type { StateComparators, TitlesApi } from '@kbn/presentation-publishing'; import fastIsEqual from 'fast-deep-equal'; import { BehaviorSubject } from 'rxjs'; import type { JobId } from '../../../common/types/anomaly_detection_jobs'; @@ -42,7 +41,7 @@ export const initializeSingleMetricViewerControls = ( functionDescription.next(update.functionDescription); selectedDetectorIndex.next(update.selectedDetectorIndex); selectedEntities.next(update.selectedEntities); - titlesApi.setPanelTitle(update.panelTitle); + titlesApi.setTitle(update.panelTitle); }; const updateForecastId = (id: string | undefined) => { diff --git a/x-pack/platform/plugins/shared/ml/public/embeddables/single_metric_viewer/single_metric_viewer_data_fetcher.ts b/x-pack/platform/plugins/shared/ml/public/embeddables/single_metric_viewer/single_metric_viewer_data_fetcher.ts index a1c2f63a3c261..eca393ea0c63e 100644 --- a/x-pack/platform/plugins/shared/ml/public/embeddables/single_metric_viewer/single_metric_viewer_data_fetcher.ts +++ b/x-pack/platform/plugins/shared/ml/public/embeddables/single_metric_viewer/single_metric_viewer_data_fetcher.ts @@ -31,8 +31,6 @@ interface SingleMetricViewerData { export const initializeSingleMetricViewerDataFetcher = ( api: SingleMetricViewerEmbeddableApi, - dataLoading: BehaviorSubject, - blockingError: BehaviorSubject, timefilter: TimefilterContract ) => { const singleMetricViewerData$ = new BehaviorSubject({ diff --git a/x-pack/platform/plugins/shared/ml/public/embeddables/single_metric_viewer/single_metric_viewer_embeddable_factory.tsx b/x-pack/platform/plugins/shared/ml/public/embeddables/single_metric_viewer/single_metric_viewer_embeddable_factory.tsx index befe551fa2dc7..72be81bdc16f9 100644 --- a/x-pack/platform/plugins/shared/ml/public/embeddables/single_metric_viewer/single_metric_viewer_embeddable_factory.tsx +++ b/x-pack/platform/plugins/shared/ml/public/embeddables/single_metric_viewer/single_metric_viewer_embeddable_factory.tsx @@ -14,7 +14,7 @@ import useUnmount from 'react-use/lib/useUnmount'; import { apiHasExecutionContext, initializeTimeRange, - initializeTitles, + initializeTitleManager, useStateFromPublishingSubject, } from '@kbn/presentation-publishing'; import { BehaviorSubject, Subscription } from 'rxjs'; @@ -44,23 +44,18 @@ export const getSingleMetricViewerEmbeddableFactory = ( buildEmbeddable: async (state, buildApi, uuid, parentApi) => { const services = await getServices(getStartServices); const subscriptions = new Subscription(); - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); - - const { - api: timeRangeApi, - comparators: timeRangeComparators, - serialize: serializeTimeRange, - } = initializeTimeRange(state); + const titleManager = initializeTitleManager(state); + const timeRangeManager = initializeTimeRange(state); const { singleMetricViewerControlsApi, serializeSingleMetricViewerState, singleMetricViewerComparators, onSingleMetricViewerDestroy, - } = initializeSingleMetricViewerControls(state, titlesApi); + } = initializeSingleMetricViewerControls(state, titleManager.api); - const dataLoading = new BehaviorSubject(true); - const blockingError = new BehaviorSubject(undefined); + const dataLoading$ = new BehaviorSubject(true); + const blockingError$ = new BehaviorSubject(undefined); const api = buildApi( { @@ -82,7 +77,7 @@ export const getSingleMetricViewerEmbeddableFactory = ( { data, share }, mlApi, { - ...serializeTitles(), + ...titleManager.serialize(), ...serializeSingleMetricViewerState(), } ); @@ -92,17 +87,17 @@ export const getSingleMetricViewerEmbeddableFactory = ( return Promise.reject(); } }, - ...titlesApi, - ...timeRangeApi, + ...titleManager.api, + ...timeRangeManager.api, ...singleMetricViewerControlsApi, - dataLoading, - blockingError, + dataLoading$, + blockingError$, serializeState: () => { return { rawState: { timeRange: undefined, - ...serializeTitles(), - ...serializeTimeRange(), + ...titleManager.serialize(), + ...timeRangeManager.serialize(), ...serializeSingleMetricViewerState(), }, references: [], @@ -110,16 +105,14 @@ export const getSingleMetricViewerEmbeddableFactory = ( }, }, { - ...timeRangeComparators, - ...titleComparators, + ...timeRangeManager.comparators, + ...titleManager.comparators, ...singleMetricViewerComparators, } ); const { singleMetricViewerData$, onDestroy } = initializeSingleMetricViewerDataFetcher( api, - dataLoading, - blockingError, services[1].data.query.timefilter.timefilter ); @@ -161,7 +154,7 @@ export const getSingleMetricViewerEmbeddableFactory = ( bounds={bounds} functionDescription={functionDescription} lastRefresh={lastRefresh} - onError={(error) => blockingError.next(error)} + onError={(error) => blockingError$.next(error)} selectedDetectorIndex={singleMetricViewerData?.selectedDetectorIndex} selectedEntities={singleMetricViewerData?.selectedEntities} selectedJobId={singleMetricViewerData?.jobIds[0]} @@ -169,7 +162,7 @@ export const getSingleMetricViewerEmbeddableFactory = ( uuid={api.uuid} onForecastIdChange={api.updateForecastId} onRenderComplete={() => { - dataLoading.next(false); + dataLoading$.next(false); }} /> ); diff --git a/x-pack/platform/plugins/shared/ml/public/embeddables/types.ts b/x-pack/platform/plugins/shared/ml/public/embeddables/types.ts index 56749e7ab40cb..7ca47ddf3de78 100644 --- a/x-pack/platform/plugins/shared/ml/public/embeddables/types.ts +++ b/x-pack/platform/plugins/shared/ml/public/embeddables/types.ts @@ -19,7 +19,7 @@ import type { PublishesUnifiedSearch, PublishingSubject, PublishesTimeRange, - PublishesWritablePanelTitle, + PublishesWritableTitle, PublishesDataViews, SerializedTitles, } from '@kbn/presentation-publishing'; @@ -144,7 +144,7 @@ export type AnomalyChartsApi = AnomalyChartsComponentApi & AnomalyChartsDataLoad export type AnomalyChartsEmbeddableApi = MlEmbeddableBaseApi & PublishesDataViews & - PublishesWritablePanelTitle & + PublishesWritableTitle & HasEditCapabilities & AnomalyChartsApi; @@ -204,7 +204,7 @@ export interface SingleMetricViewerEmbeddableState export type SingleMetricViewerEmbeddableApi = MlEmbeddableBaseApi & - PublishesWritablePanelTitle & + PublishesWritableTitle & HasEditCapabilities & SingleMetricViewerComponentApi; diff --git a/x-pack/solutions/observability/plugins/apm/public/embeddable/alerting/alerting_failed_transactions_chart/react_embeddable_factory.tsx b/x-pack/solutions/observability/plugins/apm/public/embeddable/alerting/alerting_failed_transactions_chart/react_embeddable_factory.tsx index 6eb52a58d0c59..c0d9ce32f06f6 100644 --- a/x-pack/solutions/observability/plugins/apm/public/embeddable/alerting/alerting_failed_transactions_chart/react_embeddable_factory.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/embeddable/alerting/alerting_failed_transactions_chart/react_embeddable_factory.tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { DefaultEmbeddableApi } from '@kbn/embeddable-plugin/public'; import type { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; -import { initializeTitles, useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; +import { initializeTitleManager, useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; import { BehaviorSubject } from 'rxjs'; import type { EmbeddableApmAlertingVizProps } from '../types'; import type { EmbeddableDeps } from '../../types'; @@ -26,7 +26,7 @@ export const getApmAlertingFailedTransactionsChartEmbeddableFactory = (deps: Emb return state.rawState as EmbeddableApmAlertingVizProps; }, buildEmbeddable: async (state, buildApi, uuid, parentApi) => { - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const titleManager = initializeTitleManager(state); const serviceName$ = new BehaviorSubject(state.serviceName); const transactionType$ = new BehaviorSubject(state.transactionType); const transactionName$ = new BehaviorSubject(state.transactionName); @@ -40,11 +40,11 @@ export const getApmAlertingFailedTransactionsChartEmbeddableFactory = (deps: Emb const api = buildApi( { - ...titlesApi, + ...titleManager.api, serializeState: () => { return { rawState: { - ...serializeTitles(), + ...titleManager.serialize(), serviceName: serviceName$.getValue(), transactionType: transactionType$.getValue(), transactionName: transactionName$.getValue(), @@ -70,7 +70,7 @@ export const getApmAlertingFailedTransactionsChartEmbeddableFactory = (deps: Emb alert: [alert$, (value) => alert$.next(value)], kuery: [kuery$, (value) => kuery$.next(value)], filters: [filters$, (value) => filters$.next(value)], - ...titleComparators, + ...titleManager.comparators, } ); diff --git a/x-pack/solutions/observability/plugins/apm/public/embeddable/alerting/alerting_latency_chart/react_embeddable_factory.tsx b/x-pack/solutions/observability/plugins/apm/public/embeddable/alerting/alerting_latency_chart/react_embeddable_factory.tsx index 27f5eec4f097a..9b924e0964ba1 100644 --- a/x-pack/solutions/observability/plugins/apm/public/embeddable/alerting/alerting_latency_chart/react_embeddable_factory.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/embeddable/alerting/alerting_latency_chart/react_embeddable_factory.tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { DefaultEmbeddableApi } from '@kbn/embeddable-plugin/public'; import type { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; -import { initializeTitles, useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; +import { initializeTitleManager, useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; import { BehaviorSubject } from 'rxjs'; import type { EmbeddableApmAlertingLatencyVizProps } from '../types'; import type { EmbeddableDeps } from '../../types'; @@ -26,7 +26,7 @@ export const getApmAlertingLatencyChartEmbeddableFactory = (deps: EmbeddableDeps return state.rawState as EmbeddableApmAlertingLatencyVizProps; }, buildEmbeddable: async (state, buildApi, uuid, parentApi) => { - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const titleManager = initializeTitleManager(state); const serviceName$ = new BehaviorSubject(state.serviceName); const transactionType$ = new BehaviorSubject(state.transactionType); const transactionName$ = new BehaviorSubject(state.transactionName); @@ -43,11 +43,11 @@ export const getApmAlertingLatencyChartEmbeddableFactory = (deps: EmbeddableDeps const api = buildApi( { - ...titlesApi, + ...titleManager.api, serializeState: () => { return { rawState: { - ...serializeTitles(), + ...titleManager.serialize(), serviceName: serviceName$.getValue(), transactionType: transactionType$.getValue(), transactionName: transactionName$.getValue(), @@ -78,7 +78,7 @@ export const getApmAlertingLatencyChartEmbeddableFactory = (deps: EmbeddableDeps alert: [alert$, (value) => alert$.next(value)], kuery: [kuery$, (value) => kuery$.next(value)], filters: [filters$, (value) => filters$.next(value)], - ...titleComparators, + ...titleManager.comparators, } ); diff --git a/x-pack/solutions/observability/plugins/apm/public/embeddable/alerting/alerting_throughput_chart/react_embeddable_factory.tsx b/x-pack/solutions/observability/plugins/apm/public/embeddable/alerting/alerting_throughput_chart/react_embeddable_factory.tsx index df61e1558e9cd..8dfc20ed9d833 100644 --- a/x-pack/solutions/observability/plugins/apm/public/embeddable/alerting/alerting_throughput_chart/react_embeddable_factory.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/embeddable/alerting/alerting_throughput_chart/react_embeddable_factory.tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { DefaultEmbeddableApi } from '@kbn/embeddable-plugin/public'; import type { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; -import { initializeTitles, useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; +import { initializeTitleManager, useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; import { BehaviorSubject } from 'rxjs'; import type { EmbeddableApmAlertingVizProps } from '../types'; import type { EmbeddableDeps } from '../../types'; @@ -26,7 +26,7 @@ export const getApmAlertingThroughputChartEmbeddableFactory = (deps: EmbeddableD return state.rawState as EmbeddableApmAlertingVizProps; }, buildEmbeddable: async (state, buildApi, uuid, parentApi) => { - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const titleManager = initializeTitleManager(state); const serviceName$ = new BehaviorSubject(state.serviceName); const transactionType$ = new BehaviorSubject(state.transactionType); const transactionName$ = new BehaviorSubject(state.transactionName); @@ -40,11 +40,11 @@ export const getApmAlertingThroughputChartEmbeddableFactory = (deps: EmbeddableD const api = buildApi( { - ...titlesApi, + ...titleManager.api, serializeState: () => { return { rawState: { - ...serializeTitles(), + ...titleManager.serialize(), serviceName: serviceName$.getValue(), transactionType: transactionType$.getValue(), transactionName: transactionName$.getValue(), @@ -70,7 +70,7 @@ export const getApmAlertingThroughputChartEmbeddableFactory = (deps: EmbeddableD alert: [alert$, (value) => alert$.next(value)], kuery: [kuery$, (value) => kuery$.next(value)], filters: [filters$, (value) => filters$.next(value)], - ...titleComparators, + ...titleManager.comparators, } ); diff --git a/x-pack/solutions/observability/plugins/infra/public/components/log_stream/log_stream_react_embeddable.tsx b/x-pack/solutions/observability/plugins/infra/public/components/log_stream/log_stream_react_embeddable.tsx index 54d02879f405e..e8dfd3e7b8a2f 100644 --- a/x-pack/solutions/observability/plugins/infra/public/components/log_stream/log_stream_react_embeddable.tsx +++ b/x-pack/solutions/observability/plugins/infra/public/components/log_stream/log_stream_react_embeddable.tsx @@ -12,7 +12,7 @@ import { EuiCallOut, EuiLink } from '@elastic/eui'; import type { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { initializeTimeRange, - initializeTitles, + initializeTitleManager, useFetchContext, } from '@kbn/presentation-publishing'; import { LogStream } from '@kbn/logs-shared-plugin/public'; @@ -39,24 +39,24 @@ export function getLogStreamEmbeddableFactory(services: Services) { deserializeState: (state) => state.rawState, buildEmbeddable: async (state, buildApi) => { const timeRangeContext = initializeTimeRange(state); - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const titleManager = initializeTitleManager(state); const api = buildApi( { ...timeRangeContext.api, - ...titlesApi, + ...titleManager.api, serializeState: () => { return { rawState: { ...timeRangeContext.serialize(), - ...serializeTitles(), + ...titleManager.serialize(), }, }; }, }, { ...timeRangeContext.comparators, - ...titleComparators, + ...titleManager.comparators, } ); diff --git a/x-pack/solutions/observability/plugins/infra/public/pages/metrics/hosts/components/search_bar/controls_content.tsx b/x-pack/solutions/observability/plugins/infra/public/pages/metrics/hosts/components/search_bar/controls_content.tsx index 5608898ce7ba8..30f1ef448459b 100644 --- a/x-pack/solutions/observability/plugins/infra/public/pages/metrics/hosts/components/search_bar/controls_content.tsx +++ b/x-pack/solutions/observability/plugins/infra/public/pages/metrics/hosts/components/search_bar/controls_content.tsx @@ -60,7 +60,7 @@ export const ControlsContent: React.FC = ({ Object.keys(children).map((childId) => { const child = children[childId] as DataControlApi; child.CustomPrependComponent = () => ( - + ); }); }); diff --git a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.tsx b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.tsx index c3a505463e885..9d967686d6e02 100644 --- a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.tsx @@ -13,7 +13,7 @@ import { Storage } from '@kbn/kibana-utils-plugin/public'; import { FetchContext, fetch$, - initializeTitles, + initializeTitleManager, useBatchedPublishingSubjects, useFetchContext, } from '@kbn/presentation-publishing'; @@ -73,15 +73,15 @@ export function getAlertsEmbeddableFactory({ } } - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const titleManager = initializeTitleManager(state); const defaultTitle$ = new BehaviorSubject(getAlertsPanelTitle()); const slos$ = new BehaviorSubject(state.slos); const showAllGroupByInstances$ = new BehaviorSubject(state.showAllGroupByInstances); const reload$ = new Subject(); const api = buildApi( { - ...titlesApi, - defaultPanelTitle: defaultTitle$, + ...titleManager.api, + defaultTitle$, getTypeDisplayName: () => i18n.translate('xpack.slo.editSloAlertswEmbeddable.typeDisplayName', { defaultMessage: 'configuration', @@ -93,7 +93,7 @@ export function getAlertsEmbeddableFactory({ serializeState: () => { return { rawState: { - ...serializeTitles(), + ...titleManager.serialize(), slos: slos$.getValue(), showAllGroupByInstances: showAllGroupByInstances$.getValue(), }, @@ -116,7 +116,7 @@ export function getAlertsEmbeddableFactory({ showAllGroupByInstances$, (value) => showAllGroupByInstances$.next(value), ], - ...titleComparators, + ...titleManager.comparators, } ); diff --git a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/alerts/types.ts b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/alerts/types.ts index c411514e7269d..0efe4b931a63a 100644 --- a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/alerts/types.ts +++ b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/alerts/types.ts @@ -20,8 +20,8 @@ import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { ServerlessPluginStart } from '@kbn/serverless/public'; import { SerializedTitles, - PublishesWritablePanelTitle, - PublishesPanelTitle, + PublishesWritableTitle, + PublishesTitle, HasEditCapabilities, } from '@kbn/presentation-publishing'; import { ObservabilityPublicStart } from '@kbn/observability-plugin/public'; @@ -41,8 +41,8 @@ export interface EmbeddableSloProps { export type SloAlertsEmbeddableState = SerializedTitles & EmbeddableSloProps; export type SloAlertsApi = DefaultEmbeddableApi & - PublishesWritablePanelTitle & - PublishesPanelTitle & + PublishesWritableTitle & + PublishesTitle & HasSloAlertsConfig & HasEditCapabilities; diff --git a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/burn_rate/burn_rate_react_embeddable_factory.tsx b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/burn_rate/burn_rate_react_embeddable_factory.tsx index 5cceab9fa10d8..993f5b1577af1 100644 --- a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/burn_rate/burn_rate_react_embeddable_factory.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/burn_rate/burn_rate_react_embeddable_factory.tsx @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { fetch$, - initializeTitles, + initializeTitleManager, useBatchedPublishingSubjects, } from '@kbn/presentation-publishing'; import { Router } from '@kbn/shared-ux-router'; @@ -49,7 +49,7 @@ export const getBurnRateEmbeddableFactory = ({ }, buildEmbeddable: async (state, buildApi, uuid, parentApi) => { const deps = { ...coreStart, ...pluginsStart }; - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const titleManager = initializeTitleManager(state); const defaultTitle$ = new BehaviorSubject(getTitle()); const sloId$ = new BehaviorSubject(state.sloId); const sloInstanceId$ = new BehaviorSubject(state.sloInstanceId); @@ -58,12 +58,12 @@ export const getBurnRateEmbeddableFactory = ({ const api = buildApi( { - ...titlesApi, - defaultPanelTitle: defaultTitle$, + ...titleManager.api, + defaultTitle$, serializeState: () => { return { rawState: { - ...serializeTitles(), + ...titleManager.serialize(), sloId: sloId$.getValue(), sloInstanceId: sloInstanceId$.getValue(), duration: duration$.getValue(), @@ -75,7 +75,7 @@ export const getBurnRateEmbeddableFactory = ({ sloId: [sloId$, (value) => sloId$.next(value)], sloInstanceId: [sloInstanceId$, (value) => sloInstanceId$.next(value)], duration: [duration$, (value) => duration$.next(value)], - ...titleComparators, + ...titleManager.comparators, } ); diff --git a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/burn_rate/types.ts b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/burn_rate/types.ts index 3e95151afb986..0a8aeb066c761 100644 --- a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/burn_rate/types.ts +++ b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/burn_rate/types.ts @@ -12,8 +12,8 @@ import { } from '@kbn/core/public'; import { DefaultEmbeddableApi } from '@kbn/embeddable-plugin/public'; import { - PublishesPanelTitle, - PublishesWritablePanelTitle, + PublishesTitle, + PublishesWritableTitle, SerializedTitles, } from '@kbn/presentation-publishing'; import { Subject } from 'rxjs'; @@ -33,8 +33,8 @@ interface BurnRateCustomInput { export type SloBurnRateEmbeddableState = SerializedTitles & BurnRateCustomInput; export type BurnRateApi = DefaultEmbeddableApi & - PublishesWritablePanelTitle & - PublishesPanelTitle; + PublishesWritableTitle & + PublishesTitle; export interface SloEmbeddableDeps { uiSettings: IUiSettingsClient; diff --git a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/error_budget/error_budget_react_embeddable_factory.tsx b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/error_budget/error_budget_react_embeddable_factory.tsx index b76152124825d..8e8af9a4b57e6 100644 --- a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/error_budget/error_budget_react_embeddable_factory.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/error_budget/error_budget_react_embeddable_factory.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { fetch$, - initializeTitles, + initializeTitleManager, useBatchedPublishingSubjects, } from '@kbn/presentation-publishing'; import { Router } from '@kbn/shared-ux-router'; @@ -49,7 +49,7 @@ export const getErrorBudgetEmbeddableFactory = ({ }, buildEmbeddable: async (state, buildApi, uuid, parentApi) => { const deps = { ...coreStart, ...pluginsStart }; - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const titleManager = initializeTitleManager(state); const defaultTitle$ = new BehaviorSubject(getErrorBudgetPanelTitle()); const sloId$ = new BehaviorSubject(state.sloId); const sloInstanceId$ = new BehaviorSubject(state.sloInstanceId); @@ -57,12 +57,12 @@ export const getErrorBudgetEmbeddableFactory = ({ const api = buildApi( { - ...titlesApi, - defaultPanelTitle: defaultTitle$, + ...titleManager.api, + defaultTitle$, serializeState: () => { return { rawState: { - ...serializeTitles(), + ...titleManager.serialize(), sloId: sloId$.getValue(), sloInstanceId: sloInstanceId$.getValue(), }, @@ -72,7 +72,7 @@ export const getErrorBudgetEmbeddableFactory = ({ { sloId: [sloId$, (value) => sloId$.next(value)], sloInstanceId: [sloInstanceId$, (value) => sloInstanceId$.next(value)], - ...titleComparators, + ...titleManager.comparators, } ); diff --git a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/error_budget/types.ts b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/error_budget/types.ts index 83d2667f04b4c..1bfba16416ee4 100644 --- a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/error_budget/types.ts +++ b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/error_budget/types.ts @@ -6,8 +6,8 @@ */ import { SerializedTitles, - PublishesWritablePanelTitle, - PublishesPanelTitle, + PublishesWritableTitle, + PublishesTitle, } from '@kbn/presentation-publishing'; import { DefaultEmbeddableApi } from '@kbn/embeddable-plugin/public'; import { Subject } from 'rxjs'; @@ -32,8 +32,8 @@ interface ErrorBudgetCustomInput { export type SloErrorBudgetEmbeddableState = SerializedTitles & ErrorBudgetCustomInput; export type ErrorBudgetApi = DefaultEmbeddableApi & - PublishesWritablePanelTitle & - PublishesPanelTitle; + PublishesWritableTitle & + PublishesTitle; export interface SloEmbeddableDeps { uiSettings: IUiSettingsClient; diff --git a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/slo_embeddable_factory.tsx b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/slo_embeddable_factory.tsx index c1b19c7381acb..e4e916c313376 100644 --- a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/slo_embeddable_factory.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/slo_embeddable_factory.tsx @@ -15,7 +15,7 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { fetch$, getUnchangingComparator, - initializeTitles, + initializeTitleManager, useBatchedPublishingSubjects, } from '@kbn/presentation-publishing'; import { Router } from '@kbn/shared-ux-router'; @@ -58,13 +58,13 @@ export const getOverviewEmbeddableFactory = ({ const dynamicActionsApi = deps.embeddableEnhanced?.initializeReactEmbeddableDynamicActions( uuid, - () => titlesApi.panelTitle.getValue(), + () => titleManager.api.title$.getValue(), state ); const maybeStopDynamicActions = dynamicActionsApi?.startDynamicActions(); - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const titleManager = initializeTitleManager(state); const defaultTitle$ = new BehaviorSubject(getOverviewPanelTitle()); const sloId$ = new BehaviorSubject(state.sloId); const sloInstanceId$ = new BehaviorSubject(state.sloInstanceId); @@ -76,10 +76,10 @@ export const getOverviewEmbeddableFactory = ({ const api = buildApi( { - ...titlesApi, + ...titleManager.api, ...(dynamicActionsApi?.dynamicActionsApi ?? {}), supportedTriggers: () => [], - defaultPanelTitle: defaultTitle$, + defaultTitle$, getTypeDisplayName: () => i18n.translate('xpack.slo.editSloOverviewEmbeddableTitle.typeDisplayName', { defaultMessage: 'criteria', @@ -103,7 +103,7 @@ export const getOverviewEmbeddableFactory = ({ serializeState: () => { return { rawState: { - ...serializeTitles(), + ...titleManager.serialize(), sloId: sloId$.getValue(), sloInstanceId: sloInstanceId$.getValue(), showAllGroupByInstances: showAllGroupByInstances$.getValue(), @@ -134,7 +134,7 @@ export const getOverviewEmbeddableFactory = ({ ], remoteName: [remoteName$, (value) => remoteName$.next(value)], overviewMode: [overviewMode$, (value) => overviewMode$.next(value)], - ...titleComparators, + ...titleManager.comparators, ...(dynamicActionsApi?.dynamicActionsComparator ?? { enhancements: getUnchangingComparator(), }), diff --git a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/types.ts b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/types.ts index d79a0ecd8a4dc..9b8421ff6c0dc 100644 --- a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/types.ts +++ b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/types.ts @@ -10,8 +10,8 @@ import { Filter } from '@kbn/es-query'; import type { EmbeddableApiContext, HasSupportedTriggers } from '@kbn/presentation-publishing'; import { HasEditCapabilities, - PublishesPanelTitle, - PublishesWritablePanelTitle, + PublishesTitle, + PublishesWritableTitle, SerializedTitles, } from '@kbn/presentation-publishing'; @@ -45,8 +45,8 @@ export type SloOverviewEmbeddableState = SerializedTitles & Partial; export type SloOverviewApi = DefaultEmbeddableApi & - PublishesWritablePanelTitle & - PublishesPanelTitle & + PublishesWritableTitle & + PublishesTitle & HasSloGroupOverviewConfig & HasEditCapabilities & HasSupportedTriggers; diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/embeddables/monitors_overview/monitors_embeddable_factory.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/embeddables/monitors_overview/monitors_embeddable_factory.tsx index 3908063e68116..b499c92631f01 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/embeddables/monitors_overview/monitors_embeddable_factory.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/embeddables/monitors_overview/monitors_embeddable_factory.tsx @@ -9,11 +9,11 @@ import React, { useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { DefaultEmbeddableApi, ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { - initializeTitles, + initializeTitleManager, useBatchedPublishingSubjects, fetch$, - PublishesWritablePanelTitle, - PublishesPanelTitle, + PublishesWritableTitle, + PublishesTitle, SerializedTitles, HasEditCapabilities, } from '@kbn/presentation-publishing'; @@ -34,8 +34,8 @@ export type OverviewEmbeddableState = SerializedTitles & { }; export type StatusOverviewApi = DefaultEmbeddableApi & - PublishesWritablePanelTitle & - PublishesPanelTitle & + PublishesWritableTitle & + PublishesTitle & HasEditCapabilities; export const getMonitorsEmbeddableFactory = ( @@ -53,15 +53,15 @@ export const getMonitorsEmbeddableFactory = ( buildEmbeddable: async (state, buildApi, uuid, parentApi) => { const [coreStart, pluginStart] = await getStartServices(); - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const titleManager = initializeTitleManager(state); const defaultTitle$ = new BehaviorSubject(getOverviewPanelTitle()); const reload$ = new Subject(); const filters$ = new BehaviorSubject(state.filters); const api = buildApi( { - ...titlesApi, - defaultPanelTitle: defaultTitle$, + ...titleManager.api, + defaultTitle$, getTypeDisplayName: () => i18n.translate('xpack.synthetics.editSloOverviewEmbeddableTitle.typeDisplayName', { defaultMessage: 'filters', @@ -70,7 +70,7 @@ export const getMonitorsEmbeddableFactory = ( serializeState: () => { return { rawState: { - ...serializeTitles(), + ...titleManager.serialize(), filters: filters$.getValue(), }, }; @@ -101,7 +101,7 @@ export const getMonitorsEmbeddableFactory = ( }, }, { - ...titleComparators, + ...titleManager.comparators, filters: [filters$, (value) => filters$.next(value)], } ); diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/embeddables/stats_overview/stats_overview_embeddable_factory.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/embeddables/stats_overview/stats_overview_embeddable_factory.tsx index df311819399f3..27feeeba180aa 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/embeddables/stats_overview/stats_overview_embeddable_factory.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/embeddables/stats_overview/stats_overview_embeddable_factory.tsx @@ -10,11 +10,11 @@ import { i18n } from '@kbn/i18n'; import React, { useEffect } from 'react'; import { DefaultEmbeddableApi, ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { - initializeTitles, + initializeTitleManager, useBatchedPublishingSubjects, fetch$, - PublishesWritablePanelTitle, - PublishesPanelTitle, + PublishesWritableTitle, + PublishesTitle, SerializedTitles, HasEditCapabilities, } from '@kbn/presentation-publishing'; @@ -35,8 +35,8 @@ export type OverviewEmbeddableState = SerializedTitles & { }; export type StatsOverviewApi = DefaultEmbeddableApi & - PublishesWritablePanelTitle & - PublishesPanelTitle & + PublishesWritableTitle & + PublishesTitle & HasEditCapabilities; export const getStatsOverviewEmbeddableFactory = ( @@ -54,15 +54,15 @@ export const getStatsOverviewEmbeddableFactory = ( buildEmbeddable: async (state, buildApi, uuid, parentApi) => { const [coreStart, pluginStart] = await getStartServices(); - const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const titleManager = initializeTitleManager(state); const defaultTitle$ = new BehaviorSubject(getOverviewPanelTitle()); const reload$ = new Subject(); const filters$ = new BehaviorSubject(state.filters); const api = buildApi( { - ...titlesApi, - defaultPanelTitle: defaultTitle$, + ...titleManager.api, + defaultTitle$, getTypeDisplayName: () => i18n.translate('xpack.synthetics.editSloOverviewEmbeddableTitle.typeDisplayName', { defaultMessage: 'filters', @@ -93,14 +93,14 @@ export const getStatsOverviewEmbeddableFactory = ( serializeState: () => { return { rawState: { - ...serializeTitles(), + ...titleManager.serialize(), filters: filters$.getValue(), }, }; }, }, { - ...titleComparators, + ...titleManager.comparators, filters: [filters$, (value) => filters$.next(value)], } ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/app/actions/add_to_timeline/lens/add_to_timeline.test.ts b/x-pack/solutions/security/plugins/security_solution/public/app/actions/add_to_timeline/lens/add_to_timeline.test.ts index 06437f3851762..65f1938141551 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/app/actions/add_to_timeline/lens/add_to_timeline.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/app/actions/add_to_timeline/lens/add_to_timeline.test.ts @@ -85,7 +85,7 @@ describe('createAddToTimelineLensAction', () => { ...context, embeddable: { ...getMockLensApi(), - blockingError: new BehaviorSubject(new Error('some error')), + blockingError$: new BehaviorSubject(new Error('some error')), }, }) ).toEqual(false); diff --git a/x-pack/solutions/security/plugins/security_solution/public/app/actions/copy_to_clipboard/lens/copy_to_clipboard.test.ts b/x-pack/solutions/security/plugins/security_solution/public/app/actions/copy_to_clipboard/lens/copy_to_clipboard.test.ts index 98a8b07fd127d..71f6fa575b7b9 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/app/actions/copy_to_clipboard/lens/copy_to_clipboard.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/app/actions/copy_to_clipboard/lens/copy_to_clipboard.test.ts @@ -77,7 +77,7 @@ describe('createCopyToClipboardLensAction', () => { ...context, embeddable: { ...getMockLensApi(), - blockingError: new BehaviorSubject(new Error('some error')), + blockingError$: new BehaviorSubject(new Error('some error')), }, }) ).toEqual(false); diff --git a/x-pack/solutions/security/plugins/security_solution/public/dashboards/components/dashboard_title.tsx b/x-pack/solutions/security/plugins/security_solution/public/dashboards/components/dashboard_title.tsx index dacc99176475a..5374bdf2ae62f 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/dashboards/components/dashboard_title.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/dashboards/components/dashboard_title.tsx @@ -18,7 +18,7 @@ const DashboardTitleComponent = ({ dashboardContainer: DashboardApi; onTitleLoaded: (title: string) => void; }) => { - const dashboardTitle = useStateFromPublishingSubject(dashboardContainer.panelTitle); + const dashboardTitle = useStateFromPublishingSubject(dashboardContainer.title$); const title = useMemo(() => { return dashboardTitle && dashboardTitle.length !== 0 ? dashboardTitle : EDIT_DASHBOARD_TITLE; }, [dashboardTitle]); diff --git a/x-pack/solutions/security/plugins/security_solution/public/dashboards/components/dashboard_tool_bar.tsx b/x-pack/solutions/security/plugins/security_solution/public/dashboards/components/dashboard_tool_bar.tsx index c3783f359031b..b2673d4770d0b 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/dashboards/components/dashboard_tool_bar.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/dashboards/components/dashboard_tool_bar.tsx @@ -27,7 +27,7 @@ const DashboardToolBarComponent = ({ }) => { const { setHeaderActionMenu } = useKibana().services; - const viewMode = useStateFromPublishingSubject(dashboardContainer.viewMode); + const viewMode = useStateFromPublishingSubject(dashboardContainer.viewMode$); const { navigateTo, getAppUrl } = useNavigation(); const redirectTo = useCallback(