fix 'Save modal dark theme issues in Discover, Dashboard, and Maps' (#149147)

Fixes https://github.com/elastic/kibana/issues/149130 and
https://github.com/elastic/kibana/issues/142636

PR updates `showSaveModal` to wrap modal in `KibanaThemeProvider` to
resolve dark theme issues. Rather than passing in theme$ as a new
parameter, theme$ is imported from within the plugin itself.

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Nathan Reese 2023-01-19 07:55:22 -07:00 committed by GitHub
parent ff5101d3c1
commit d5f6fe84c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 48 additions and 71 deletions

View file

@ -27,7 +27,6 @@ export function runSaveAs(this: DashboardContainer) {
timefilter: { timefilter },
},
},
coreContext: { i18nContext },
savedObjectsTagging: { hasApi: hasSavedObjectsTagging },
dashboardSavedObject: { checkForDuplicateDashboardTitle, saveDashboardStateToSavedObject },
} = pluginServices.getServices();
@ -125,7 +124,7 @@ export function runSaveAs(this: DashboardContainer) {
/>
);
this.clearOverlays();
showSaveModal(dashboardSaveModal, i18nContext);
showSaveModal(dashboardSaveModal);
});
}

View file

@ -187,7 +187,7 @@ export async function onSaveSearch({
onClose={onClose ?? (() => {})}
/>
);
showSaveModal(saveModal, services.core.i18n.Context);
showSaveModal(saveModal);
}
const SaveSearchObjectModal: React.FC<{

View file

@ -27,8 +27,6 @@ export const mockAttributeService = <
const core = customCore ? customCore : coreMock.createStart();
return new AttributeService<A, V, R, M>(
type,
jest.fn(),
core.i18n.Context,
core.notifications.toasts,
options,
jest.fn().mockReturnValue(() => ({ getDisplayName: () => type }))

View file

@ -9,8 +9,13 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
import { get, omit } from 'lodash';
import { I18nStart, NotificationsStart } from '@kbn/core/public';
import { SavedObjectSaveModal, OnSaveProps, SaveResult } from '@kbn/saved-objects-plugin/public';
import { NotificationsStart } from '@kbn/core/public';
import {
SavedObjectSaveModal,
OnSaveProps,
SaveResult,
showSaveModal,
} from '@kbn/saved-objects-plugin/public';
import {
EmbeddableInput,
SavedObjectEmbeddableInput,
@ -61,11 +66,6 @@ export class AttributeService<
> {
constructor(
private type: string,
private showSaveModal: (
saveModal: React.ReactElement,
I18nContext: I18nStart['Context']
) => void,
private i18nContext: I18nStart['Context'],
private toasts: NotificationsStart['toasts'],
private options: AttributeServiceOptions<SavedObjectAttributes, MetaInfo>,
getEmbeddableFactory?: (embeddableFactoryId: string) => EmbeddableFactory
@ -178,7 +178,7 @@ export class AttributeService<
}
};
if (saveOptions && (saveOptions as { showSaveModal: boolean }).showSaveModal) {
this.showSaveModal(
showSaveModal(
<SavedObjectSaveModal
onSave={onSave}
onClose={() => {}}
@ -190,8 +190,7 @@ export class AttributeService<
showCopyOnSave={false}
objectType={this.type}
showDescription={false}
/>,
this.i18nContext
/>
);
}
});

View file

@ -10,7 +10,7 @@ import React from 'react';
import { Subscription } from 'rxjs';
import { identity } from 'lodash';
import type { SerializableRecord } from '@kbn/utility-types';
import { getSavedObjectFinder, showSaveModal } from '@kbn/saved-objects-plugin/public';
import { getSavedObjectFinder } from '@kbn/saved-objects-plugin/public';
import { UiActionsSetup, UiActionsStart } from '@kbn/ui-actions-plugin/public';
import { Start as InspectorStart } from '@kbn/inspector-plugin/public';
import {
@ -211,14 +211,7 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
getEmbeddableFactory: this.getEmbeddableFactory,
getEmbeddableFactories: this.getEmbeddableFactories,
getAttributeService: (type: string, options) =>
new AttributeService(
type,
showSaveModal,
core.i18n.Context,
core.notifications.toasts,
options,
this.getEmbeddableFactory
),
new AttributeService(type, core.notifications.toasts, options, this.getEmbeddableFactory),
getStateTransfer: (storage?: Storage) =>
storage
? new EmbeddableStateTransfer(

View file

@ -0,0 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { CoreStart } from '@kbn/core/public';
let coreStart: CoreStart;
export function setStartServices(core: CoreStart) {
coreStart = core;
}
export const getI18n = () => coreStart.i18n;
export const getTheme = () => coreStart.theme;

View file

@ -18,6 +18,7 @@ import {
} from './saved_object';
import { PER_PAGE_SETTING, LISTING_LIMIT_SETTING } from '../common';
import { SavedObject } from './types';
import { setStartServices } from './kibana_services';
export interface SavedObjectSetup {
registerDecorator: (config: SavedObjectDecoratorConfig<any>) => void;
@ -63,6 +64,7 @@ export class SavedObjectsPublicPlugin
};
}
public start(core: CoreStart, { data, dataViews }: SavedObjectsStartDeps) {
setStartServices(core);
return {
SavedObjectClass: createSavedObjectClass(
{

View file

@ -9,7 +9,8 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { I18nStart } from '@kbn/core/public';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import { getI18n, getTheme } from '../kibana_services';
/**
* Represents the result of trying to persist the saved object.
@ -31,7 +32,6 @@ interface MinimalSaveModalProps {
export function showSaveModal(
saveModal: React.ReactElement<MinimalSaveModalProps>,
I18nContext: I18nStart['Context'],
Wrapper?: React.FC
) {
const container = document.createElement('div');
@ -57,13 +57,11 @@ export function showSaveModal(
onClose: closeModal,
});
const wrappedElement = Wrapper ? (
<I18nContext>
<Wrapper>{element}</Wrapper>
</I18nContext>
) : (
<I18nContext>{element}</I18nContext>
const I18nContext = getI18n().Context;
ReactDOM.render(
<KibanaThemeProvider theme$={getTheme().theme$}>
<I18nContext>{Wrapper ? <Wrapper>{element}</Wrapper> : element}</I18nContext>
</KibanaThemeProvider>,
container
);
ReactDOM.render(wrappedElement, container);
}

View file

@ -15,7 +15,6 @@ import { parse } from 'query-string';
import { Capabilities } from '@kbn/core/public';
import { TopNavMenuData } from '@kbn/navigation-plugin/public';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import {
showSaveModal,
SavedObjectSaveModalOrigin,
@ -598,18 +597,7 @@ export const getTopNavConfig = (
);
}
const WrapperComponent = ({ children }: { children?: React.ReactNode }) => {
const ContextProvider = !originatingApp
? presentationUtil.ContextProvider
: React.Fragment;
return (
<KibanaThemeProvider theme$={theme.theme$}>
<ContextProvider>{children}</ContextProvider>
</KibanaThemeProvider>
);
};
showSaveModal(saveModal, I18nContext, WrapperComponent);
showSaveModal(saveModal, presentationUtil.ContextProvider);
},
},
]

View file

@ -10,7 +10,6 @@ import { I18nProvider } from '@kbn/i18n-react';
import { Provider } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { showSaveModal } from '@kbn/saved-objects-plugin/public';
import { Workspace } from '../types';
import { createGraphStore } from '../state_management';
import { createWorkspace } from '../services/workspace/graph_client_workspace';
@ -108,12 +107,10 @@ export const WorkspaceRoute = ({
http: coreStart.http,
overlays: coreStart.overlays,
savedObjectsClient,
showSaveModal,
savePolicy: graphSavePolicy,
changeUrl: (newUrl) => history.push(newUrl),
notifyReact: () => setRenderCounter((cur) => cur + 1),
chrome,
I18nContext: coreStart.i18n.Context,
handleSearchQueryError,
})
);

View file

@ -5,9 +5,10 @@
* 2.0.
*/
import React, { ReactElement } from 'react';
import { I18nStart, OverlayStart, SavedObjectsClientContract } from '@kbn/core/public';
import React from 'react';
import { OverlayStart, SavedObjectsClientContract } from '@kbn/core/public';
import { SaveResult } from '@kbn/saved-objects-plugin/public';
import { showSaveModal } from '@kbn/saved-objects-plugin/public';
import { GraphWorkspaceSavedObject, GraphSavePolicy } from '../types';
import { SaveModal, OnSaveGraphProps } from '../components/save_modal';
@ -31,16 +32,12 @@ export function openSaveModal({
hasData,
workspace,
saveWorkspace,
showSaveModal,
I18nContext,
services,
}: {
savePolicy: GraphSavePolicy;
hasData: boolean;
workspace: GraphWorkspaceSavedObject;
saveWorkspace: SaveWorkspaceHandler;
showSaveModal: (el: ReactElement, I18nContext: I18nStart['Context']) => void;
I18nContext: I18nStart['Context'];
services: SaveWorkspaceServices;
}) {
const currentTitle = workspace.title;
@ -79,7 +76,6 @@ export function openSaveModal({
title={workspace.title}
description={workspace.description}
showCopyOnSave={Boolean(workspace.id)}
/>,
I18nContext
/>
);
}

View file

@ -66,9 +66,6 @@ export function createMockGraphStore({
return { id: '123', title: 'test-pattern' } as unknown as DataView;
}),
},
I18nContext: jest
.fn()
.mockImplementation(({ children }: { children: React.ReactNode }) => children),
notifications: {
toasts: {
addDanger: jest.fn(),
@ -78,7 +75,6 @@ export function createMockGraphStore({
http: {} as HttpStart,
notifyReact: jest.fn(),
savePolicy: 'configAndData',
showSaveModal: jest.fn(),
overlays: {
openModal: jest.fn(),
} as unknown as OverlayStart,

View file

@ -229,9 +229,7 @@ function showModal(
savePolicy: deps.savePolicy,
hasData: workspace.nodes.length > 0 || workspace.blocklistedNodes.length > 0,
workspace: savedWorkspace,
showSaveModal: deps.showSaveModal,
saveWorkspace: saveWorkspaceHandler,
I18nContext: deps.I18nContext,
services: {
savedObjectsClient: deps.savedObjectsClient,
overlays: deps.overlays,

View file

@ -7,9 +7,8 @@
import createSagaMiddleware, { SagaMiddleware } from 'redux-saga';
import { combineReducers, createStore, Store, AnyAction, Dispatch, applyMiddleware } from 'redux';
import { ChromeStart, I18nStart, OverlayStart, SavedObjectsClientContract } from '@kbn/core/public';
import { ChromeStart, OverlayStart, SavedObjectsClientContract } from '@kbn/core/public';
import { CoreStart } from '@kbn/core/public';
import { ReactElement } from 'react';
import {
fieldsReducer,
FieldsState,
@ -48,12 +47,10 @@ export interface GraphStoreDependencies {
http: CoreStart['http'];
overlays: OverlayStart;
savedObjectsClient: SavedObjectsClientContract;
showSaveModal: (el: ReactElement, I18nContext: I18nStart['Context']) => void;
savePolicy: GraphSavePolicy;
changeUrl: (newUrl: string) => void;
notifyReact: () => void;
chrome: ChromeStart;
I18nContext: I18nStart['Context'];
basePath: string;
handleSearchQueryError: (err: Error | string) => void;
}

View file

@ -90,7 +90,7 @@ const attributeServiceMockFromSavedVis = (document: Document): LensAttributeServ
LensByValueInput,
LensByReferenceInput,
LensUnwrapMetaInfo
>('lens', jest.fn(), core.i18n.Context, core.notifications.toasts, options);
>('lens', core.notifications.toasts, options);
service.unwrapAttributes = jest.fn((input: LensByValueInput | LensByReferenceInput) => {
return Promise.resolve({
attributes: {

View file

@ -24,7 +24,6 @@ import {
getMapsCapabilities,
getIsAllowByValueEmbeddables,
getInspector,
getCoreI18n,
getSavedObjectsClient,
getCoreOverlays,
getSavedObjectsTagging,
@ -249,7 +248,7 @@ export function getTopNavConfig({
);
}
showSaveModal(saveModal, getCoreI18n().Context, PresentationUtilContext);
showSaveModal(saveModal, PresentationUtilContext);
},
});