diff --git a/config/opensearch_dashboards.yml b/config/opensearch_dashboards.yml index 197a1106d62a..267406fb704f 100644 --- a/config/opensearch_dashboards.yml +++ b/config/opensearch_dashboards.yml @@ -149,6 +149,13 @@ # ] #vis_type_timeline.graphiteBlockedIPs: [] -# user input URL for customized logo +# full version customized logo URL # opensearchDashboards.branding.logoUrl: "" +# smaller version customized logo URL +# opensearchDashboards.branding.smallLogoUrl: "" + +# custom application title +# opensearchDashboards.branding.title: "" + + diff --git a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap index 139c4510de33..23021f21c535 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap @@ -250,6 +250,8 @@ exports[`Header renders 1`] = ` branding={ Object { "logoUrl": "/", + "smallLogoUrl": "/", + "title": "OpenSearch Dashboards", } } breadcrumbs$={ @@ -2936,6 +2938,8 @@ exports[`Header renders 1`] = ` logo diff --git a/src/core/public/chrome/ui/header/branding/__snapshots__/opensearch_dashboards_custom_logo.test.tsx.snap b/src/core/public/chrome/ui/header/branding/__snapshots__/opensearch_dashboards_custom_logo.test.tsx.snap index 11c1aa814449..650ef0460fe3 100644 --- a/src/core/public/chrome/ui/header/branding/__snapshots__/opensearch_dashboards_custom_logo.test.tsx.snap +++ b/src/core/public/chrome/ui/header/branding/__snapshots__/opensearch_dashboards_custom_logo.test.tsx.snap @@ -1,8 +1,186 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Custom Logo Take in a invalid URL string 1`] = ` + + + + + + + + + + + + + + + + + + + +`; + exports[`Custom Logo Take in a normal URL string 1`] = ` logo `; diff --git a/src/core/public/chrome/ui/header/branding/opensearch_dashboards_custom_logo.test.tsx b/src/core/public/chrome/ui/header/branding/opensearch_dashboards_custom_logo.test.tsx index 3a8d057a7e04..66a3f7a3694a 100644 --- a/src/core/public/chrome/ui/header/branding/opensearch_dashboards_custom_logo.test.tsx +++ b/src/core/public/chrome/ui/header/branding/opensearch_dashboards_custom_logo.test.tsx @@ -12,7 +12,12 @@ import { CustomLogo } from './opensearch_dashboards_custom_logo'; describe('Custom Logo', () => { it('Take in a normal URL string', () => { - const branding = { logoUrl: '/', className: '' }; + const branding = { logoUrl: '/custom' }; + const component = mountWithIntl(); + expect(component).toMatchSnapshot(); + }); + it('Take in a invalid URL string', () => { + const branding = {}; const component = mountWithIntl(); expect(component).toMatchSnapshot(); }); diff --git a/src/core/public/chrome/ui/header/branding/opensearch_dashboards_custom_logo.tsx b/src/core/public/chrome/ui/header/branding/opensearch_dashboards_custom_logo.tsx index 00f5a70d915a..bdf240cb1a7c 100644 --- a/src/core/public/chrome/ui/header/branding/opensearch_dashboards_custom_logo.tsx +++ b/src/core/public/chrome/ui/header/branding/opensearch_dashboards_custom_logo.tsx @@ -32,13 +32,19 @@ import React from 'react'; import '../header_logo.scss'; +import { OpenSearchDashboardsLogoDarkMode } from './opensearch_dashboards_logo_darkmode'; +/** + * @param {string} logoUrl - custom URL for the top left logo of the main screen + */ export interface CustomLogoType { - logoUrl: string; + logoUrl?: string; } export const CustomLogo = ({ ...branding }: CustomLogoType) => { - return ( + return !branding.logoUrl ? ( + OpenSearchDashboardsLogoDarkMode() + ) : ( {}, - branding: { logoUrl: '/' }, + branding: { logoUrl: '/', smallLogoUrl: '/', title: 'OpenSearch Dashboards' }, }; } diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx index 4eae83b0ba07..e7a4abd53cba 100644 --- a/src/core/public/chrome/ui/header/header.tsx +++ b/src/core/public/chrome/ui/header/header.tsx @@ -87,7 +87,7 @@ export interface HeaderProps { isLocked$: Observable; loadingCount$: ReturnType; onIsLockedUpdate: OnIsLockedUpdate; - branding: { logoUrl: string }; + branding: { logoUrl?: string }; } export function Header({ diff --git a/src/core/public/chrome/ui/header/header_logo.tsx b/src/core/public/chrome/ui/header/header_logo.tsx index eb12804e7f6f..b7219a2e1863 100644 --- a/src/core/public/chrome/ui/header/header_logo.tsx +++ b/src/core/public/chrome/ui/header/header_logo.tsx @@ -104,7 +104,7 @@ interface Props { navLinks$: Observable; forceNavigation$: Observable; navigateToApp: (appId: string) => void; - logoUrl: string; + logoUrl?: string; } export function HeaderLogo({ href, navigateToApp, logoUrl, ...observables }: Props) { diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index 9a071bfdf2fa..bb873ddd3f45 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -239,7 +239,7 @@ export class CoreSystem { docLinks, http, i18n, - injectedMetadata: pick(injectedMetadata, ['getInjectedVar']), + injectedMetadata: pick(injectedMetadata, ['getInjectedVar', 'getBranding']), notifications, overlays, savedObjects, diff --git a/src/core/public/index.ts b/src/core/public/index.ts index f4ecc3848b96..2e20956330f1 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -236,6 +236,11 @@ export interface CoreSetup unknown; + getBranding: () => { + logoUrl?: string; + smallLogoUrl?: string; + title: string; + }; }; /** {@link StartServicesAccessor} */ getStartServices: StartServicesAccessor; @@ -291,6 +296,11 @@ export interface CoreStart { * */ injectedMetadata: { getInjectedVar: (name: string, defaultValue?: any) => unknown; + getBranding: () => { + logoUrl?: string; + smallLogoUrl?: string; + title: string; + }; }; } diff --git a/src/core/public/injected_metadata/injected_metadata_service.test.ts b/src/core/public/injected_metadata/injected_metadata_service.test.ts index 185cb23b6c58..140fa96f72f3 100644 --- a/src/core/public/injected_metadata/injected_metadata_service.test.ts +++ b/src/core/public/injected_metadata/injected_metadata_service.test.ts @@ -234,11 +234,11 @@ describe('setup.getBranding()', () => { it('returns injectedMetadata.branding', () => { const injectedMetadata = new InjectedMetadataService({ injectedMetadata: { - branding: { logoUrl: '/' }, + branding: { logoUrl: '/', smallLogoUrl: '/', title: 'title' }, }, } as any); - const logoURL = injectedMetadata.setup().getBranding(); - expect(logoURL).toEqual({ logoUrl: '/' }); + const branding = injectedMetadata.setup().getBranding(); + expect(branding).toEqual({ logoUrl: '/', smallLogoUrl: '/', title: 'title' }); }); }); diff --git a/src/core/public/injected_metadata/injected_metadata_service.ts b/src/core/public/injected_metadata/injected_metadata_service.ts index 9e57f09191c1..2cb1eef378ad 100644 --- a/src/core/public/injected_metadata/injected_metadata_service.ts +++ b/src/core/public/injected_metadata/injected_metadata_service.ts @@ -77,7 +77,9 @@ export interface InjectedMetadataParams { }; }; branding: { - logoUrl: string; + logoUrl?: string; + smallLogoUrl?: string; + title: string; }; }; } @@ -184,7 +186,9 @@ export interface InjectedMetadataSetup { [key: string]: unknown; }; getBranding: () => { - logoUrl: string; + logoUrl?: string; + smallLogoUrl?: string; + title: string; }; } diff --git a/src/core/public/mocks.ts b/src/core/public/mocks.ts index 166a335a3556..ac96b745dc90 100644 --- a/src/core/public/mocks.ts +++ b/src/core/public/mocks.ts @@ -103,6 +103,7 @@ function createCoreStartMock({ basePath = '' } = {}) { savedObjects: savedObjectsServiceMock.createStartContract(), injectedMetadata: { getInjectedVar: injectedMetadataServiceMock.createStartContract().getInjectedVar, + getBranding: injectedMetadataServiceMock.createStartContract().getBranding, }, fatalErrors: fatalErrorsServiceMock.createStartContract(), }; diff --git a/src/core/public/plugins/plugin_context.ts b/src/core/public/plugins/plugin_context.ts index 69779b3162c1..d6c98e7126b2 100644 --- a/src/core/public/plugins/plugin_context.ts +++ b/src/core/public/plugins/plugin_context.ts @@ -120,6 +120,7 @@ export function createPluginSetupContext< uiSettings: deps.uiSettings, injectedMetadata: { getInjectedVar: deps.injectedMetadata.getInjectedVar, + getBranding: deps.injectedMetadata.getBranding, }, getStartServices: () => plugin.startDependencies, }; @@ -166,6 +167,7 @@ export function createPluginStartContext< savedObjects: deps.savedObjects, injectedMetadata: { getInjectedVar: deps.injectedMetadata.getInjectedVar, + getBranding: deps.injectedMetadata.getBranding, }, fatalErrors: deps.fatalErrors, }; diff --git a/src/core/public/plugins/plugins_service.test.ts b/src/core/public/plugins/plugins_service.test.ts index c95cb42869ad..eedcd5290ca4 100644 --- a/src/core/public/plugins/plugins_service.test.ts +++ b/src/core/public/plugins/plugins_service.test.ts @@ -113,7 +113,7 @@ describe('PluginsService', () => { ...mockSetupDeps, application: expect.any(Object), getStartServices: expect.any(Function), - injectedMetadata: pick(mockSetupDeps.injectedMetadata, 'getInjectedVar'), + injectedMetadata: pick(mockSetupDeps.injectedMetadata, 'getInjectedVar', 'getBranding'), }; mockStartDeps = { application: applicationServiceMock.createInternalStartContract(), @@ -132,7 +132,7 @@ describe('PluginsService', () => { ...mockStartDeps, application: expect.any(Object), chrome: omit(mockStartDeps.chrome, 'getComponent'), - injectedMetadata: pick(mockStartDeps.injectedMetadata, 'getInjectedVar'), + injectedMetadata: pick(mockStartDeps.injectedMetadata, 'getInjectedVar', 'getBranding'), }; // Reset these for each test. diff --git a/src/core/server/opensearch_dashboards_config.ts b/src/core/server/opensearch_dashboards_config.ts index 7e3b64a7b219..a4628e19fb3e 100644 --- a/src/core/server/opensearch_dashboards_config.ts +++ b/src/core/server/opensearch_dashboards_config.ts @@ -54,9 +54,12 @@ export const config = { autocompleteTimeout: schema.duration({ defaultValue: 1000 }), branding: schema.object({ logoUrl: schema.string({ - defaultValue: - 'https://opensearch.org/assets/brand/SVG/Logo/opensearch_dashboards_logo_darkmode.svg', + defaultValue: '/', }), + smallLogoUrl: schema.string({ + defaultValue: '/', + }), + title: schema.string({ defaultValue: 'OpenSearch Dashboards' }), }), }), deprecations, diff --git a/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap b/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap index e934b3f9af6f..762269e81086 100644 --- a/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap +++ b/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap @@ -6,7 +6,7 @@ Object { "basePath": "/mock-server-basepath", "branch": Any, "branding": Object { - "logoUrl": "https://opensearch.org/assets/brand/SVG/Logo/opensearch_dashboards_logo_darkmode.svg", + "title": "OpenSearch Dashboards", }, "buildNumber": Any, "csp": Object { @@ -52,7 +52,7 @@ Object { "basePath": "/mock-server-basepath", "branch": Any, "branding": Object { - "logoUrl": "https://opensearch.org/assets/brand/SVG/Logo/opensearch_dashboards_logo_darkmode.svg", + "title": "OpenSearch Dashboards", }, "buildNumber": Any, "csp": Object { @@ -98,7 +98,7 @@ Object { "basePath": "/mock-server-basepath", "branch": Any, "branding": Object { - "logoUrl": "https://opensearch.org/assets/brand/SVG/Logo/opensearch_dashboards_logo_darkmode.svg", + "title": "OpenSearch Dashboards", }, "buildNumber": Any, "csp": Object { @@ -148,7 +148,7 @@ Object { "basePath": "", "branch": Any, "branding": Object { - "logoUrl": "https://opensearch.org/assets/brand/SVG/Logo/opensearch_dashboards_logo_darkmode.svg", + "title": "OpenSearch Dashboards", }, "buildNumber": Any, "csp": Object { @@ -194,7 +194,7 @@ Object { "basePath": "/mock-server-basepath", "branch": Any, "branding": Object { - "logoUrl": "https://opensearch.org/assets/brand/SVG/Logo/opensearch_dashboards_logo_darkmode.svg", + "title": "OpenSearch Dashboards", }, "buildNumber": Any, "csp": Object { diff --git a/src/core/server/rendering/rendering_service.test.ts b/src/core/server/rendering/rendering_service.test.ts index 5c5c091de7c4..05f755fc7d7c 100644 --- a/src/core/server/rendering/rendering_service.test.ts +++ b/src/core/server/rendering/rendering_service.test.ts @@ -136,42 +136,28 @@ describe('RenderingService', () => { }); }); }); - describe('checkUrlvalid()', () => { - it('URL is valid', async () => { - jest.mock('axios', () => ({ - async get() { - return { - status: 200, - }; - }, - })); + describe('checkUrlValid()', () => { + it('SVG URL is valid', async () => { const result = await service.checkUrlValid( - 'https://opensearch.org/assets/brand/SVG/Logo/opensearch_dashboards_logo_darkmode.svg' - ); - expect(result).toEqual( - 'https://opensearch.org/assets/brand/SVG/Logo/opensearch_dashboards_logo_darkmode.svg' + 'https://opensearch.org/assets/brand/SVG/Mark/opensearch_mark_default.svg', + 'config' ); + expect(result).toEqual(true); }); - it('URL does not contain jpeg, jpg, gif, or png', async () => { + it('PNG URL is valid', async () => { const result = await service.checkUrlValid( - 'https://opensearch.org/assets/brand/SVG/Logo/opensearch_dashboards_logo_darkmode' - ); - expect(result).toEqual( - 'https://opensearch.org/assets/brand/SVG/Logo/opensearch_dashboards_logo_darkmode.svg' + 'https://opensearch.org/assets/brand/PNG/Mark/opensearch_mark_default.png', + 'config' ); + expect(result).toEqual(true); + }); + it('URL does not contain svg, or png', async () => { + const result = await service.checkUrlValid('https://validUrl', 'config'); + expect(result).toEqual(false); }); it('URL is invalid', async () => { - jest.mock('axios', () => ({ - async get() { - return { - status: 404, - }; - }, - })); - const result = await service.checkUrlValid('http://notfound'); - expect(result).toEqual( - 'https://opensearch.org/assets/brand/SVG/Logo/opensearch_dashboards_logo_darkmode.svg' - ); + const result = await service.checkUrlValid('http://notfound.svg', 'config'); + expect(result).toEqual(false); }); }); }); diff --git a/src/core/server/rendering/rendering_service.tsx b/src/core/server/rendering/rendering_service.tsx index 13be829d0b7c..7c746c50cbaa 100644 --- a/src/core/server/rendering/rendering_service.tsx +++ b/src/core/server/rendering/rendering_service.tsx @@ -63,7 +63,14 @@ export class RenderingService { .pipe(first()) .toPromise(); - const validLogoUrl = await this.checkUrlValid(opensearchDashboardsConfig.branding.logoUrl); + const isLogoUrlValid = await this.checkUrlValid( + opensearchDashboardsConfig.branding.logoUrl, + 'logoUrl' + ); + const isSmallLogoUrlValid = await this.checkUrlValid( + opensearchDashboardsConfig.branding.smallLogoUrl, + 'smallLogoUrl' + ); return { render: async ( @@ -114,7 +121,11 @@ export class RenderingService { uiSettings: settings, }, branding: { - logoUrl: validLogoUrl, + logoUrl: isLogoUrlValid ? opensearchDashboardsConfig.branding.logoUrl : undefined, + smallLogoUrl: isSmallLogoUrlValid + ? opensearchDashboardsConfig.branding.smallLogoUrl + : undefined, + title: opensearchDashboardsConfig.branding.title, }, }, }; @@ -132,22 +143,18 @@ export class RenderingService { return ((await browserConfig?.pipe(take(1)).toPromise()) ?? {}) as Record; } - public checkUrlValid = async (url: string): Promise => { + public checkUrlValid = async (url: string, configName?: string): Promise => { if (url.match(/\.(png|svg)$/) === null) { - this.logger - .get('branding') - .error('Invalid URL for logo. Rendering default OpenSearch Dashboard Logo.'); - return 'https://opensearch.org/assets/brand/SVG/Logo/opensearch_dashboards_logo_darkmode.svg'; + this.logger.get('branding').warn(configName + ' config is not found or invalid.'); + return false; } return await Axios.get(url, { adapter: AxiosHttpAdapter }) .then(() => { - return url; + return true; }) .catch(() => { - this.logger - .get('branding') - .error('Invalid URL for logo. Rendering default OpenSearch Dashboard Logo.'); - return 'https://opensearch.org/assets/brand/SVG/Logo/opensearch_dashboards_logo_darkmode.svg'; + this.logger.get('branding').warn(configName + ' config is not found or invalid'); + return false; }); }; } diff --git a/src/core/server/rendering/types.ts b/src/core/server/rendering/types.ts index 72a45661ed4a..ae3038360516 100644 --- a/src/core/server/rendering/types.ts +++ b/src/core/server/rendering/types.ts @@ -75,7 +75,9 @@ export interface RenderingMetadata { }; }; branding: { - logoUrl: string; + logoUrl?: string; + smallLogoUrl?: string; + title: string; }; }; } diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index 3f21233ad9c9..846e397d08df 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -234,9 +234,9 @@ export default () => // TODO Also allow units here like in opensearch config once this is moved to the new platform autocompleteTimeout: Joi.number().integer().min(1).default(1000), branding: Joi.object({ - logoUrl: Joi.string().default( - 'https://opensearch.org/assets/brand/SVG/Logo/opensearch_dashboards_logo_darkmode.svg' - ), + logoUrl: Joi.string().default('/'), + smallLogoUrl: Joi.string().default('/'), + title: Joi.string().default('OpenSearch Dashboards'), }), }).default(), diff --git a/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap b/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap index 6b59eca2f25a..2b522815e339 100644 --- a/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap +++ b/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap @@ -790,6 +790,7 @@ exports[`home welcome should show the normal home page if welcome screen is disa exports[`home welcome should show the welcome screen if enabled, and there are no index patterns defined 1`] = ` diff --git a/src/plugins/home/public/application/components/__snapshots__/welcome.test.tsx.snap b/src/plugins/home/public/application/components/__snapshots__/welcome.test.tsx.snap index b52becae745a..bb331c752bad 100644 --- a/src/plugins/home/public/application/components/__snapshots__/welcome.test.tsx.snap +++ b/src/plugins/home/public/application/components/__snapshots__/welcome.test.tsx.snap @@ -1,6 +1,138 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`should render a Welcome screen with customized branding 1`] = ` + +
+
+
+ +
+ logo +
+ +

+ +

+
+ +
+
+
+ + + + + + +
+
+
+`; + exports[`should render a Welcome screen with no telemetry disclaimer 1`] = ` + +
+
+
+ +
+ logo +
+ +

+ +

+
+ +
+
+
+ + + + + + +
+
+
+`; + +exports[`should render a Welcome screen with the default OpenSearch Dashboards branding 1`] = `

@@ -75,21 +209,26 @@ exports[`should render a Welcome screen with the telemetry disclaimer when optIn - - - +

@@ -173,21 +312,26 @@ exports[`should render a Welcome screen with the telemetry disclaimer when optIn - - - +

diff --git a/src/plugins/home/public/application/components/_welcome.scss b/src/plugins/home/public/application/components/_welcome.scss index 8cb2539247b2..0c5a7d7553d9 100644 --- a/src/plugins/home/public/application/components/_welcome.scss +++ b/src/plugins/home/public/application/components/_welcome.scss @@ -14,6 +14,16 @@ @include euiBottomShadowMedium; } +.homWelcome__customLogoContainer { + height: 80px; + padding: 10px 10px 10px 10px; +} + +.homWelcome__customLogo { + height: 100%; + max-width: 100%; +} + .homWelcome__footerAction { margin-right: $euiSizeS; } diff --git a/src/plugins/home/public/application/components/home.js b/src/plugins/home/public/application/components/home.js index 52259cd6a6f0..0ee8713cfb17 100644 --- a/src/plugins/home/public/application/components/home.js +++ b/src/plugins/home/public/application/components/home.js @@ -201,6 +201,7 @@ export class Home extends Component { onSkip={this.skipWelcome} urlBasePath={this.props.urlBasePath} telemetry={this.props.telemetry} + branding={getServices().injectedMetadata.getBranding()} /> ); } @@ -216,7 +217,6 @@ export class Home extends Component { return this.renderWelcome(); } } - return this.renderNormal(); } } diff --git a/src/plugins/home/public/application/components/home.test.js b/src/plugins/home/public/application/components/home.test.js index 5c0de0365d6c..072e21eff9bd 100644 --- a/src/plugins/home/public/application/components/home.test.js +++ b/src/plugins/home/public/application/components/home.test.js @@ -45,6 +45,9 @@ jest.mock('../opensearch_dashboards_services', () => ({ chrome: { setBreadcrumbs: () => {}, }, + injectedMetadata: { + getBranding: () => ({}), + }, }), })); diff --git a/src/plugins/home/public/application/components/welcome.test.tsx b/src/plugins/home/public/application/components/welcome.test.tsx index d047746ca9cf..982b378217eb 100644 --- a/src/plugins/home/public/application/components/welcome.test.tsx +++ b/src/plugins/home/public/application/components/welcome.test.tsx @@ -49,10 +49,18 @@ test('should render a Welcome screen with the telemetry disclaimer', () => { expect(component).toMatchSnapshot(); }); */ + +const branding = { + smallLogoUrl: '/', + title: 'OpenSearch Dashboards', +}; + test('should render a Welcome screen with the telemetry disclaimer when optIn is true', () => { const telemetry = telemetryPluginMock.createStartContract(); telemetry.telemetryService.getIsOptedIn = jest.fn().mockReturnValue(true); - const component = shallow( {}} telemetry={telemetry} />); + const component = shallow( + {}} telemetry={telemetry} branding={branding} /> + ); expect(component).toMatchSnapshot(); }); @@ -60,13 +68,15 @@ test('should render a Welcome screen with the telemetry disclaimer when optIn is test('should render a Welcome screen with the telemetry disclaimer when optIn is false', () => { const telemetry = telemetryPluginMock.createStartContract(); telemetry.telemetryService.getIsOptedIn = jest.fn().mockReturnValue(false); - const component = shallow( {}} telemetry={telemetry} />); + const component = shallow( + {}} telemetry={telemetry} branding={branding} /> + ); expect(component).toMatchSnapshot(); }); test('should render a Welcome screen with no telemetry disclaimer', () => { - const component = shallow( {}} />); + const component = shallow( {}} branding={branding} />); expect(component).toMatchSnapshot(); }); @@ -75,7 +85,28 @@ test('fires opt-in seen when mounted', () => { const telemetry = telemetryPluginMock.createStartContract(); const mockSetOptedInNoticeSeen = jest.fn(); telemetry.telemetryNotifications.setOptedInNoticeSeen = mockSetOptedInNoticeSeen; - shallow( {}} telemetry={telemetry} />); + shallow( {}} telemetry={telemetry} branding={branding} />); expect(mockSetOptedInNoticeSeen).toHaveBeenCalled(); }); + +test('should render a Welcome screen with the default OpenSearch Dashboards branding', () => { + const defaultBranding = { + title: 'OpenSearch Dashboards', + }; + const component = shallow( + {}} branding={defaultBranding} /> + ); + expect(component).toMatchSnapshot(); +}); + +test('should render a Welcome screen with customized branding', () => { + const customBranding = { + smallLogoUrl: '/custom', + title: 'custom title', + }; + const component = shallow( + {}} branding={customBranding} /> + ); + expect(component).toMatchSnapshot(); +}); diff --git a/src/plugins/home/public/application/components/welcome.tsx b/src/plugins/home/public/application/components/welcome.tsx index 336414be130f..77d265c0862b 100644 --- a/src/plugins/home/public/application/components/welcome.tsx +++ b/src/plugins/home/public/application/components/welcome.tsx @@ -58,6 +58,10 @@ interface Props { urlBasePath: string; onSkip: () => void; telemetry?: TelemetryPluginStart; + branding: { + smallLogoUrl?: string; + title: string; + }; } /** @@ -140,20 +144,48 @@ export class Welcome extends React.Component { } }; + private renderBrandingEnabledOrDisabledLogo = () => { + if (!this.props.branding.smallLogoUrl) { + return ( + + + + ); + } else { + return ( +
+ logo +
+ ); + } + }; + render() { - const { urlBasePath, telemetry } = this.props; + const { urlBasePath, telemetry, branding } = this.props; return (
- - - - + {this.renderBrandingEnabledOrDisabledLogo()} +

- +

diff --git a/src/plugins/home/public/application/opensearch_dashboards_services.ts b/src/plugins/home/public/application/opensearch_dashboards_services.ts index a5b9fe616a24..7a7dc60a3d1d 100644 --- a/src/plugins/home/public/application/opensearch_dashboards_services.ts +++ b/src/plugins/home/public/application/opensearch_dashboards_services.ts @@ -68,6 +68,14 @@ export interface HomeOpenSearchDashboardsServices { environmentService: EnvironmentService; telemetry?: TelemetryPluginStart; tutorialService: TutorialService; + injectedMetadata: { + getInjectedVar: (name: string, defaultValue?: any) => unknown; + getBranding: () => { + logoUrl?: string; + smallLogoUrl?: string; + title: string; + }; + }; } let services: HomeOpenSearchDashboardsServices | null = null; diff --git a/src/plugins/home/public/plugin.ts b/src/plugins/home/public/plugin.ts index eef92ea8ec99..c0ef1c2b56ff 100644 --- a/src/plugins/home/public/plugin.ts +++ b/src/plugins/home/public/plugin.ts @@ -119,6 +119,7 @@ export class HomePublicPlugin homeConfig: this.initializerContext.config.get(), tutorialService: this.tutorialService, featureCatalogue: this.featuresCatalogueRegistry, + injectedMetadata: coreStart.injectedMetadata, }); coreStart.chrome.docTitle.change( i18n.translate('home.pageTitle', { defaultMessage: 'Home' }) diff --git a/test/common/config.js b/test/common/config.js index be5baaa5d055..05432914a95b 100644 --- a/test/common/config.js +++ b/test/common/config.js @@ -60,7 +60,7 @@ export default function () { `--opensearch.hosts=${formatUrl(servers.opensearch)}`, `--opensearch.username=${opensearchDashboardsServerTestUser.username}`, `--opensearch.password=${opensearchDashboardsServerTestUser.password}`, - `--home.disableWelcomeScreen=true`, + `--home.disableWelcomeScreen=false`, // Needed for async search functional tests to introduce a delay `--data.search.aggs.shardDelay.enabled=true`, //`--security.showInsecureClusterWarning=false`, @@ -76,6 +76,8 @@ export default function () { // `--newsfeed.service.pathTemplate=/api/_newsfeed-FTS-external-service-simulators/opensearch-dashboards/v{VERSION}.json`, // Custom branding config `--opensearchDashboards.branding.logoUrl=https://opensearch.org/assets/brand/SVG/Logo/opensearch_logo_darkmode.svg`, + `--opensearchDashboards.branding.smallLogoUrl=https://opensearch.org/assets/brand/SVG/Logo/opensearch_logo_darkmode.svg`, + `--opensearchDashboards.branding.title=OpenSearch`, ], }, services, diff --git a/test/functional/apps/visualize/_custom_branding.js b/test/functional/apps/visualize/_custom_branding.js index b783a3e5acab..1263914dbb36 100644 --- a/test/functional/apps/visualize/_custom_branding.js +++ b/test/functional/apps/visualize/_custom_branding.js @@ -30,29 +30,82 @@ * GitHub history for details. */ import expect from '@osd/expect'; +import { UI_SETTINGS } from '../../../../src/plugins/data/common'; export default function ({ getService, getPageObjects }) { const browser = getService('browser'); const globalNav = getService('globalNav'); + const opensearchArchiver = getService('opensearchArchiver'); + const opensearchDashboardsServer = getService('opensearchDashboardsServer'); const PageObjects = getPageObjects(['common', 'home', 'header']); + const testSubjects = getService('testSubjects'); - describe('OpenSearch Dashboards branding configuration', function customLogo() { - this.tags('includeFirefox'); - const expectedUrl = 'https://opensearch.org/assets/brand/SVG/Logo/opensearch_logo_darkmode.svg'; - before(async function () { - await PageObjects.common.navigateToApp('home'); - }); + describe('OpenSearch Dashboards branding configuration', function customHomeBranding() { + describe('should render welcome page', async () => { + this.tags('includeFirefox'); + const expectedWelcomeLogo = + 'https://opensearch.org/assets/brand/SVG/Logo/opensearch_logo_darkmode.svg'; + const expectedWelcomeMessage = 'Welcome to OpenSearch'; + + //unloading any pre-existing settings so the welcome page will appear + before(async function () { + await opensearchArchiver.unload('logstash_functional'); + await opensearchArchiver.unload('long_window_logstash'); + await opensearchArchiver.unload('visualize'); + await PageObjects.common.navigateToApp('home'); + }); + + //loading the settings again for + after(async function () { + await browser.setWindowSize(1280, 800); + await opensearchArchiver.loadIfNeeded('logstash_functional'); + await opensearchArchiver.loadIfNeeded('long_window_logstash'); + await opensearchArchiver.load('visualize'); + await opensearchDashboardsServer.uiSettings.replace({ + defaultIndex: 'logstash-*', + [UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN]: '0,0.[000]b', + }); + }); + + it('with customized logo', async () => { + await testSubjects.existOrFail('welcomeCustomLogo'); + const actualLabel = await testSubjects.getAttribute( + 'welcomeCustomLogo', + 'data-test-image-url' + ); + expect(actualLabel.toUpperCase()).to.equal(expectedWelcomeLogo.toUpperCase()); + }); - it('should show customized logo in Navbar on the main page', async () => { - await globalNav.logoExistsOrFail(expectedUrl); + it('with customized title', async () => { + await testSubjects.existOrFail('welcomeCustomTitle'); + const actualLabel = await testSubjects.getAttribute( + 'welcomeCustomTitle', + 'data-test-title-message' + ); + expect(actualLabel.toUpperCase()).to.equal(expectedWelcomeMessage.toUpperCase()); + }); }); - it('should show a customized logo that can take to home page', async () => { - await PageObjects.common.navigateToApp('settings'); - await globalNav.clickLogo(); - await PageObjects.header.waitUntilLoadingHasFinished(); - const url = await browser.getCurrentUrl(); - expect(url.includes('/app/home')).to.be(true); + describe('should render home page', async () => { + this.tags('includeFirefox'); + const expectedUrl = + 'https://opensearch.org/assets/brand/SVG/Logo/opensearch_logo_darkmode.svg'; + + before(async function () { + await PageObjects.common.navigateToApp('home'); + }); + + it('with customized logo in Navbar', async () => { + await globalNav.logoExistsOrFail(expectedUrl); + }); + + it('with customized logo that can take back to home page', async () => { + await PageObjects.common.navigateToApp('settings'); + await globalNav.clickLogo(); + await PageObjects.header.waitUntilLoadingHasFinished(); + const url = await browser.getCurrentUrl(); + expect(url.includes('/app/home')).to.be(true); + }); }); }); } diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index d6a018c96b50..57ff47e81cf7 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -68,8 +68,9 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo // Disable the welcome screen. This is relevant for environments // which don't allow to use the yml setting, e.g. cloud production. // It is done here so it applies to logins but also to a login re-use. - await browser.setLocalStorageItem('home:welcome:show', 'false'); + // Update: Enable the welcome screen for functional tests on the welcome screen. + await browser.setLocalStorageItem('home:welcome:show', 'true'); let currentUrl = await browser.getCurrentUrl(); log.debug(`currentUrl = ${currentUrl}\n appUrl = ${appUrl}`); await testSubjects.find('opensearchDashboardsChrome', 6 * defaultFindTimeout); // 60 sec waiting