From 51f4c2d372983cac4ff85bc9cba52b6a2d8c7437 Mon Sep 17 00:00:00 2001 From: "Qingyang(Abby) Hu" Date: Mon, 20 Sep 2021 22:02:17 -0700 Subject: [PATCH] Add favicon configuration (#801) Added a configuration on favicon inside opensearchDashboards.branding in the yml file. If user inputs a valid URL, we gurantee basic browser favicon customization, while remaining places show the default browser/device favicon icon. If user does not provide a valid URL for favicon, the opensearch favicon icon will be used. Signed-off-by: Abby Hu --- config/opensearch_dashboards.yml | 2 +- .../injected_metadata_service.ts | 4 +- .../server/opensearch_dashboards_config.ts | 2 +- .../rendering_service.test.ts.snap | 5 - .../server/rendering/rendering_service.tsx | 15 +- src/core/server/rendering/types.ts | 4 +- .../__snapshots__/template.test.tsx.snap | 360 ++++++++++++++++-- .../server/rendering/views/template.test.tsx | 34 +- src/core/server/rendering/views/template.tsx | 37 +- src/legacy/server/config/schema.js | 2 +- 10 files changed, 412 insertions(+), 53 deletions(-) diff --git a/config/opensearch_dashboards.yml b/config/opensearch_dashboards.yml index bfc2b05c6db3..23f84ae71535 100644 --- a/config/opensearch_dashboards.yml +++ b/config/opensearch_dashboards.yml @@ -161,5 +161,5 @@ # loadingLogo: # defaultUrl: "" # darkModeUrl: "" - # favicon: "" + # faviconUrl: "" # applicationTitle: "" diff --git a/src/core/public/injected_metadata/injected_metadata_service.ts b/src/core/public/injected_metadata/injected_metadata_service.ts index 681d7e8d496c..ae1895a0d632 100644 --- a/src/core/public/injected_metadata/injected_metadata_service.ts +++ b/src/core/public/injected_metadata/injected_metadata_service.ts @@ -90,7 +90,7 @@ export interface InjectedMetadataParams { defaultUrl?: string; darkModeUrl?: string; }; - favicon?: string; + faviconUrl?: string; applicationTitle?: string; }; }; @@ -211,7 +211,7 @@ export interface InjectedMetadataSetup { defaultUrl?: string; darkModeUrl?: string; }; - favicon?: string; + faviconUrl?: string; applicationTitle?: string; }; } diff --git a/src/core/server/opensearch_dashboards_config.ts b/src/core/server/opensearch_dashboards_config.ts index 2f01239ccbc4..e59747048395 100644 --- a/src/core/server/opensearch_dashboards_config.ts +++ b/src/core/server/opensearch_dashboards_config.ts @@ -77,7 +77,7 @@ export const config = { defaultValue: '/', }), }), - favicon: schema.string({ + faviconUrl: schema.string({ defaultValue: '/', }), applicationTitle: schema.string({ 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 715e6e846989..0cdb5cd2e95b 100644 --- a/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap +++ b/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap @@ -8,7 +8,6 @@ Object { "branding": Object { "applicationTitle": "OpenSearch Dashboards", "darkMode": false, - "favicon": "", "loadingLogo": Object {}, "logo": Object {}, "mark": Object {}, @@ -59,7 +58,6 @@ Object { "branding": Object { "applicationTitle": "OpenSearch Dashboards", "darkMode": false, - "favicon": "", "loadingLogo": Object {}, "logo": Object {}, "mark": Object {}, @@ -110,7 +108,6 @@ Object { "branding": Object { "applicationTitle": "OpenSearch Dashboards", "darkMode": true, - "favicon": "", "loadingLogo": Object {}, "logo": Object {}, "mark": Object {}, @@ -165,7 +162,6 @@ Object { "branding": Object { "applicationTitle": "OpenSearch Dashboards", "darkMode": false, - "favicon": "", "loadingLogo": Object {}, "logo": Object {}, "mark": Object {}, @@ -216,7 +212,6 @@ Object { "branding": Object { "applicationTitle": "OpenSearch Dashboards", "darkMode": false, - "favicon": "", "loadingLogo": Object {}, "logo": Object {}, "mark": Object {}, diff --git a/src/core/server/rendering/rendering_service.tsx b/src/core/server/rendering/rendering_service.tsx index c9aea75e1c9f..0858956dc2a4 100644 --- a/src/core/server/rendering/rendering_service.tsx +++ b/src/core/server/rendering/rendering_service.tsx @@ -135,7 +135,7 @@ export class RenderingService { defaultUrl: brandingAssignment.loadingLogoDefault, darkModeUrl: brandingAssignment.loadingLogoDarkmode, }, - favicon: '', + faviconUrl: brandingAssignment.favicon, applicationTitle: brandingAssignment.applicationTitle, }, }, @@ -223,6 +223,9 @@ export class RenderingService { loadingLogoDarkmode = undefined; } + // assign favicon based on brandingValidation function result + const favicon = brandingValidation.isFaviconValid ? branding.faviconUrl : undefined; + // assign applition title based on brandingValidation function result const applicationTitle = brandingValidation.isTitleValid ? branding.applicationTitle @@ -235,6 +238,7 @@ export class RenderingService { markDarkmode, loadingLogoDefault, loadingLogoDarkmode, + favicon, applicationTitle, }; @@ -276,6 +280,8 @@ export class RenderingService { ? await this.checkUrlValid(branding.loadingLogo.darkModeUrl, 'loadingLogo darkMode') : false; + const isFaviconValid = await this.checkUrlValid(branding.faviconUrl, 'favicon'); + const isTitleValid = this.checkTitleValid(branding.applicationTitle, 'applicationTitle'); const brandingValidation: BrandingValidation = { @@ -285,6 +291,7 @@ export class RenderingService { isMarkDarkmodeValid, isLoadingLogoDefaultValid, isLoadingLogoDarkmodeValid, + isFaviconValid, isTitleValid, }; @@ -307,7 +314,7 @@ export class RenderingService { */ public checkUrlValid = async (url: string, configName?: string): Promise => { if (url.match(/\.(png|svg|gif|PNG|SVG|GIF)$/) === null) { - this.logger.get('branding').warn(configName + ' config is not found or invalid.'); + this.logger.get('branding').info(configName + ' config is not found or invalid.'); return false; } return await Axios.get(url, { adapter: AxiosHttpAdapter }) @@ -315,7 +322,7 @@ export class RenderingService { return true; }) .catch(() => { - this.logger.get('branding').warn(configName + ' config is not found or invalid'); + this.logger.get('branding').info(configName + ' config is not found or invalid'); return false; }); }; @@ -332,7 +339,7 @@ export class RenderingService { if (!title || title.length > 36) { this.logger .get('branding') - .warn( + .info( configName + ' config is not found or invalid. Title length should be between 1 to 36 characters.' ); diff --git a/src/core/server/rendering/types.ts b/src/core/server/rendering/types.ts index 1e4ad563eaf0..d0442e22d05c 100644 --- a/src/core/server/rendering/types.ts +++ b/src/core/server/rendering/types.ts @@ -88,7 +88,7 @@ export interface RenderingMetadata { defaultUrl?: string; darkModeUrl?: string; }; - favicon?: string; + faviconUrl?: string; applicationTitle?: string; }; }; @@ -148,6 +148,7 @@ export interface BrandingValidation { isMarkDarkmodeValid: boolean; isLoadingLogoDefaultValid: boolean; isLoadingLogoDarkmodeValid: boolean; + isFaviconValid: boolean; isTitleValid: boolean; } @@ -163,5 +164,6 @@ export interface BrandingAssignment { markDarkmode?: string; loadingLogoDefault?: string; loadingLogoDarkmode?: string; + favicon?: string; applicationTitle?: string; } diff --git a/src/core/server/rendering/views/__snapshots__/template.test.tsx.snap b/src/core/server/rendering/views/__snapshots__/template.test.tsx.snap index df44408819c3..12cea1e9b430 100644 --- a/src/core/server/rendering/views/__snapshots__/template.test.tsx.snap +++ b/src/core/server/rendering/views/__snapshots__/template.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Loading logo in dark mode rendered using loading logo dark mode URL 1`] = ` +exports[`Loading page logo in dark mode rendered using loading logo dark mode URL 1`] = ` Array [ , - - OpenSearch - , + , null, <link href="[object Object]/ui/favicons/apple-touch-icon.png" @@ -140,7 +138,7 @@ Array [ ] `; -exports[`Loading logo in dark mode rendered using loading logo default mode URL 1`] = ` +exports[`Loading page logo in dark mode rendered using loading logo default mode URL 1`] = ` Array [ <meta charset="utf-8" @@ -153,9 +151,7 @@ Array [ content="width=device-width" name="viewport" />, - <title> - OpenSearch - , + , null, <link href="[object Object]/ui/favicons/apple-touch-icon.png" @@ -280,7 +276,7 @@ Array [ ] `; -exports[`Loading logo in dark mode rendered using mark dark mode URL with loading bar 1`] = ` +exports[`Loading page logo in dark mode rendered using mark dark mode URL with loading bar 1`] = ` Array [ <meta charset="utf-8" @@ -293,9 +289,7 @@ Array [ content="width=device-width" name="viewport" />, - <title> - OpenSearch - , + , null, <link href="[object Object]/ui/favicons/apple-touch-icon.png" @@ -423,7 +417,7 @@ Array [ ] `; -exports[`Loading logo in dark mode rendered using mark default mode URL with loading bar 1`] = ` +exports[`Loading page logo in dark mode rendered using mark default mode URL with loading bar 1`] = ` Array [ <meta charset="utf-8" @@ -436,9 +430,7 @@ Array [ content="width=device-width" name="viewport" />, - <title> - OpenSearch - , + , null, <link href="[object Object]/ui/favicons/apple-touch-icon.png" @@ -566,7 +558,7 @@ Array [ ] `; -exports[`Loading logo in dark mode renders using original opensearch loading spinner 1`] = ` +exports[`Loading page logo in dark mode renders using original opensearch loading spinner 1`] = ` Array [ <meta charset="utf-8" @@ -579,9 +571,7 @@ Array [ content="width=device-width" name="viewport" />, - <title> - OpenSearch - , + , null, <link href="[object Object]/ui/favicons/apple-touch-icon.png" @@ -727,7 +717,7 @@ Array [ ] `; -exports[`Loading logo in default mode rendered using loading logo default mode URL 1`] = ` +exports[`Loading page logo in default mode rendered using loading logo default mode URL 1`] = ` Array [ <meta charset="utf-8" @@ -741,7 +731,7 @@ Array [ name="viewport" />, <title> - OpenSearch + custom title , null, , - OpenSearch + custom title , null, , - OpenSearch + custom title , null, , ] `; + +exports[`Loading page render favicon using a valid URL 1`] = ` +Array [ + , + , + , + , + null, + <link + href="/customFavicon" + rel="apple-touch-icon" + sizes="180x180" + />, + <link + href="/customFavicon" + rel="icon" + sizes="32x32" + type="image/png" + />, + <link + href="/customFavicon" + rel="icon" + sizes="16x16" + type="image/png" + />, + <link + href="" + rel="manifest" + />, + <link + color="#e8488b" + href="/customFavicon" + rel="mask-icon" + />, + <link + href="/customFavicon" + rel="shortcut icon" + />, + <meta + content="" + name="msapplication-config" + />, + <meta + content="#ffffff" + name="theme-color" + />, + null, + <meta + name="add-styles-here" + />, + <meta + name="add-scripts-here" + />, + <osd-csp + data="{\\"strictCsp\\":true}" + />, + <osd-injected-metadata + data="{\\"version\\":\\"opensearchDashboardsVersion\\",\\"buildNumber\\":1,\\"basePath\\":\\"\\",\\"serverBasePath\\":\\"\\",\\"env\\":{\\"packageInfo\\":{\\"version\\":\\"\\",\\"branch\\":\\"\\",\\"buildNum\\":1,\\"buildSha\\":\\"\\",\\"dist\\":true},\\"mode\\":{\\"name\\":\\"production\\",\\"dev\\":true,\\"prod\\":false}},\\"anonymousStatusPage\\":false,\\"i18n\\":{\\"translationsUrl\\":\\"\\"},\\"csp\\":{\\"warnLegacyBrowsers\\":true},\\"uiPlugins\\":[],\\"legacyMetadata\\":{\\"uiSettings\\":{\\"defaults\\":{\\"legacyInjectedUiSettingDefaults\\":true},\\"user\\":{}}},\\"branding\\":{\\"darkMode\\":false,\\"logo\\":{},\\"mark\\":{},\\"loadingLogo\\":{},\\"faviconUrl\\":\\"/customFavicon\\",\\"title\\":\\"custom title\\"}}" + />, + <div + class="osdWelcomeView" + data-test-subj="osdLoadingMessage" + id="osd_loading_message" + style="display:none" + > + <div + class="osdLoaderWrap" + data-test-subj="loadingLogo" + > + <svg + fill="none" + viewBox="0 0 90 90" + xmlns="http://www.w3.org/2000/svg" + > + <g> + <path + d="M75.7374 37.5C74.4878 37.5 73.4748 38.513 73.4748 39.7626C73.4748 58.3813 58.3813 73.4748 39.7626 73.4748C38.513 73.4748 37.5 74.4878 37.5 75.7374C37.5 76.987 38.513 78 39.7626 78C60.8805 78 78 60.8805 78 39.7626C78 38.513 76.987 37.5 75.7374 37.5Z" + fill="#005EB8" + /> + <animateTransform + attributeName="transform" + dur="1.5s" + from="0 40 40" + keyTimes="0; .3; .7; 1" + repeatCount="indefinite" + to="359.9 40 40" + type="rotate" + values="0 40 40; 15 40 40; 340 40 40; 359.9 40 40" + /> + </g> + <path + d="M62.0814 52C64.2572 48.4505 66.3615 43.7178 65.9475 37.0921C65.0899 23.3673 52.6589 12.9554 40.9206 14.0837C36.3253 14.5255 31.6068 18.2712 32.026 24.9805C32.2082 27.8961 33.6352 29.6169 35.9544 30.9399C38.1618 32.1992 40.9978 32.9969 44.2128 33.9011C48.0962 34.9934 52.6009 36.2203 56.0631 38.7717C60.2125 41.8296 63.0491 45.3743 62.0814 52Z" + fill="#003B5C" + /> + <path + d="M17.9186 28C15.7428 31.5495 13.6385 36.2822 14.0525 42.9079C14.9101 56.6327 27.3411 67.0446 39.0794 65.9163C43.6747 65.4745 48.3932 61.7288 47.974 55.0195C47.7918 52.1039 46.3647 50.3831 44.0456 49.0601C41.8382 47.8008 39.0022 47.0031 35.7872 46.0989C31.9038 45.0066 27.3991 43.7797 23.9369 41.2283C19.7875 38.1704 16.9509 34.6257 17.9186 28Z" + fill="#005EB8" + /> + </svg> + <div + class="osdWelcomeText" + data-error-message="" + /> + </div> + </div>, + <div + class="osdWelcomeView" + id="osd_legacy_browser_error" + style="display:none" + > + <svg + fill="none" + height="64" + viewBox="0 0 64 64" + width="64" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M61.7374 23.5C60.4878 23.5 59.4748 24.513 59.4748 25.7626C59.4748 44.3813 44.3813 59.4748 25.7626 59.4748C24.513 59.4748 23.5 60.4878 23.5 61.7374C23.5 62.987 24.513 64 25.7626 64C46.8805 64 64 46.8805 64 25.7626C64 24.513 62.987 23.5 61.7374 23.5Z" + fill="#005EB8" + /> + <path + d="M48.0814 38C50.2572 34.4505 52.3615 29.7178 51.9475 23.0921C51.0899 9.36725 38.6589 -1.04463 26.9206 0.0837327C22.3253 0.525465 17.6068 4.2712 18.026 10.9805C18.2082 13.8961 19.6352 15.6169 21.9544 16.9399C24.1618 18.1992 26.9978 18.9969 30.2128 19.9011C34.0962 20.9934 38.6009 22.2203 42.063 24.7717C46.2125 27.8295 49.0491 31.3743 48.0814 38Z" + fill="#003B5C" + /> + <path + d="M3.91861 14C1.74276 17.5495 -0.361506 22.2822 0.0524931 28.9079C0.910072 42.6327 13.3411 53.0446 25.0794 51.9163C29.6747 51.4745 34.3932 47.7288 33.974 41.0195C33.7918 38.1039 32.3647 36.3831 30.0456 35.0601C27.8382 33.8008 25.0022 33.0031 21.7872 32.0989C17.9038 31.0066 13.3991 29.7797 9.93694 27.2283C5.78746 24.1704 2.95092 20.6257 3.91861 14Z" + fill="#005EB8" + /> + </svg> + <h2 + class="osdWelcomeTitle" + /> + <div + class="osdWelcomeText" + /> + </div>, + <script> + + // Since this is an unsafe inline script, this code will not run + // in browsers that support content security policy(CSP). This is + // intentional as we check for the existence of __osdCspNotEnforced__ in + // bootstrap. + window.__osdCspNotEnforced__ = true; + + </script>, + <script + src="[object Object]/bootstrap.js" + />, +] +`; + +exports[`Loading page render favicon using an invalid URL 1`] = ` +Array [ + <meta + charset="utf-8" + />, + <meta + content="IE=edge,chrome=1" + http-equiv="X-UA-Compatible" + />, + <meta + content="width=device-width" + name="viewport" + />, + <title />, + null, + <link + href="[object Object]/ui/favicons/apple-touch-icon.png" + rel="apple-touch-icon" + sizes="180x180" + />, + <link + href="[object Object]/ui/favicons/favicon-32x32.png" + rel="icon" + sizes="32x32" + type="image/png" + />, + <link + href="[object Object]/ui/favicons/favicon-16x16.png" + rel="icon" + sizes="16x16" + type="image/png" + />, + <link + href="[object Object]/ui/favicons/manifest.json" + rel="manifest" + />, + <link + color="#e8488b" + href="[object Object]/ui/favicons/safari-pinned-tab.svg" + rel="mask-icon" + />, + <link + href="[object Object]/ui/favicons/favicon.ico" + rel="shortcut icon" + />, + <meta + content="[object Object]/ui/favicons/browserconfig.xml" + name="msapplication-config" + />, + <meta + content="#ffffff" + name="theme-color" + />, + null, + <meta + name="add-styles-here" + />, + <meta + name="add-scripts-here" + />, + <osd-csp + data="{\\"strictCsp\\":true}" + />, + <osd-injected-metadata + data="{\\"version\\":\\"opensearchDashboardsVersion\\",\\"buildNumber\\":1,\\"basePath\\":\\"\\",\\"serverBasePath\\":\\"\\",\\"env\\":{\\"packageInfo\\":{\\"version\\":\\"\\",\\"branch\\":\\"\\",\\"buildNum\\":1,\\"buildSha\\":\\"\\",\\"dist\\":true},\\"mode\\":{\\"name\\":\\"production\\",\\"dev\\":true,\\"prod\\":false}},\\"anonymousStatusPage\\":false,\\"i18n\\":{\\"translationsUrl\\":\\"\\"},\\"csp\\":{\\"warnLegacyBrowsers\\":true},\\"uiPlugins\\":[],\\"legacyMetadata\\":{\\"uiSettings\\":{\\"defaults\\":{\\"legacyInjectedUiSettingDefaults\\":true},\\"user\\":{}}},\\"branding\\":{\\"darkMode\\":false,\\"logo\\":{},\\"mark\\":{},\\"loadingLogo\\":{},\\"title\\":\\"custom title\\"}}" + />, + <div + class="osdWelcomeView" + data-test-subj="osdLoadingMessage" + id="osd_loading_message" + style="display:none" + > + <div + class="osdLoaderWrap" + data-test-subj="loadingLogo" + > + <svg + fill="none" + viewBox="0 0 90 90" + xmlns="http://www.w3.org/2000/svg" + > + <g> + <path + d="M75.7374 37.5C74.4878 37.5 73.4748 38.513 73.4748 39.7626C73.4748 58.3813 58.3813 73.4748 39.7626 73.4748C38.513 73.4748 37.5 74.4878 37.5 75.7374C37.5 76.987 38.513 78 39.7626 78C60.8805 78 78 60.8805 78 39.7626C78 38.513 76.987 37.5 75.7374 37.5Z" + fill="#005EB8" + /> + <animateTransform + attributeName="transform" + dur="1.5s" + from="0 40 40" + keyTimes="0; .3; .7; 1" + repeatCount="indefinite" + to="359.9 40 40" + type="rotate" + values="0 40 40; 15 40 40; 340 40 40; 359.9 40 40" + /> + </g> + <path + d="M62.0814 52C64.2572 48.4505 66.3615 43.7178 65.9475 37.0921C65.0899 23.3673 52.6589 12.9554 40.9206 14.0837C36.3253 14.5255 31.6068 18.2712 32.026 24.9805C32.2082 27.8961 33.6352 29.6169 35.9544 30.9399C38.1618 32.1992 40.9978 32.9969 44.2128 33.9011C48.0962 34.9934 52.6009 36.2203 56.0631 38.7717C60.2125 41.8296 63.0491 45.3743 62.0814 52Z" + fill="#003B5C" + /> + <path + d="M17.9186 28C15.7428 31.5495 13.6385 36.2822 14.0525 42.9079C14.9101 56.6327 27.3411 67.0446 39.0794 65.9163C43.6747 65.4745 48.3932 61.7288 47.974 55.0195C47.7918 52.1039 46.3647 50.3831 44.0456 49.0601C41.8382 47.8008 39.0022 47.0031 35.7872 46.0989C31.9038 45.0066 27.3991 43.7797 23.9369 41.2283C19.7875 38.1704 16.9509 34.6257 17.9186 28Z" + fill="#005EB8" + /> + </svg> + <div + class="osdWelcomeText" + data-error-message="" + /> + </div> + </div>, + <div + class="osdWelcomeView" + id="osd_legacy_browser_error" + style="display:none" + > + <svg + fill="none" + height="64" + viewBox="0 0 64 64" + width="64" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M61.7374 23.5C60.4878 23.5 59.4748 24.513 59.4748 25.7626C59.4748 44.3813 44.3813 59.4748 25.7626 59.4748C24.513 59.4748 23.5 60.4878 23.5 61.7374C23.5 62.987 24.513 64 25.7626 64C46.8805 64 64 46.8805 64 25.7626C64 24.513 62.987 23.5 61.7374 23.5Z" + fill="#005EB8" + /> + <path + d="M48.0814 38C50.2572 34.4505 52.3615 29.7178 51.9475 23.0921C51.0899 9.36725 38.6589 -1.04463 26.9206 0.0837327C22.3253 0.525465 17.6068 4.2712 18.026 10.9805C18.2082 13.8961 19.6352 15.6169 21.9544 16.9399C24.1618 18.1992 26.9978 18.9969 30.2128 19.9011C34.0962 20.9934 38.6009 22.2203 42.063 24.7717C46.2125 27.8295 49.0491 31.3743 48.0814 38Z" + fill="#003B5C" + /> + <path + d="M3.91861 14C1.74276 17.5495 -0.361506 22.2822 0.0524931 28.9079C0.910072 42.6327 13.3411 53.0446 25.0794 51.9163C29.6747 51.4745 34.3932 47.7288 33.974 41.0195C33.7918 38.1039 32.3647 36.3831 30.0456 35.0601C27.8382 33.8008 25.0022 33.0031 21.7872 32.0989C17.9038 31.0066 13.3991 29.7797 9.93694 27.2283C5.78746 24.1704 2.95092 20.6257 3.91861 14Z" + fill="#005EB8" + /> + </svg> + <h2 + class="osdWelcomeTitle" + /> + <div + class="osdWelcomeText" + /> + </div>, + <script> + + // Since this is an unsafe inline script, this code will not run + // in browsers that support content security policy(CSP). This is + // intentional as we check for the existence of __osdCspNotEnforced__ in + // bootstrap. + window.__osdCspNotEnforced__ = true; + + </script>, + <script + src="[object Object]/bootstrap.js" + />, +] +`; diff --git a/src/core/server/rendering/views/template.test.tsx b/src/core/server/rendering/views/template.test.tsx index 0f70bc690c4f..fd75258816ff 100644 --- a/src/core/server/rendering/views/template.test.tsx +++ b/src/core/server/rendering/views/template.test.tsx @@ -83,8 +83,8 @@ function mockProps() { }; } -describe('Loading logo ', () => { - describe('in default mode ', () => { +describe('Loading page ', () => { + describe('logo in default mode ', () => { it('rendered using loading logo default mode URL', () => { const branding = { darkMode: false, @@ -125,7 +125,7 @@ describe('Loading logo ', () => { }); }); - describe('in dark mode ', () => { + describe('logo in dark mode ', () => { it('rendered using loading logo dark mode URL', () => { const branding = { darkMode: false, @@ -191,4 +191,32 @@ describe('Loading logo ', () => { expect(component).toMatchSnapshot(); }); }); + describe('render favicon ', () => { + it('using a valid URL', () => { + const branding = { + darkMode: false, + logo: {}, + mark: {}, + loadingLogo: {}, + faviconUrl: '/customFavicon', + title: 'custom title', + }; + injectedMetadata.getBranding.mockReturnValue(branding); + const component = renderWithIntl(<Template metadata={mockProps()} />); + expect(component).toMatchSnapshot(); + }); + + it('using an invalid URL', () => { + const branding = { + darkMode: false, + logo: {}, + mark: {}, + loadingLogo: {}, + title: 'custom title', + }; + injectedMetadata.getBranding.mockReturnValue(branding); + const component = renderWithIntl(<Template metadata={mockProps()} />); + expect(component).toMatchSnapshot(); + }); + }); }); diff --git a/src/core/server/rendering/views/template.tsx b/src/core/server/rendering/views/template.tsx index dd7834b672d2..5c9f80f92b83 100644 --- a/src/core/server/rendering/views/template.tsx +++ b/src/core/server/rendering/views/template.tsx @@ -100,6 +100,7 @@ export const Template: FunctionComponent<Props> = ({ const loadingLogoDarkMode = injectedMetadata.branding.loadingLogo.darkModeUrl; const markDefault = injectedMetadata.branding.mark.defaultUrl; const markDarkMode = injectedMetadata.branding.mark.darkModeUrl; + const favicon = injectedMetadata.branding.faviconUrl; const applicationTitle = injectedMetadata.branding.applicationTitle; /** @@ -177,37 +178,55 @@ export const Template: FunctionComponent<Props> = ({ <meta charSet="utf-8" /> <meta httpEquiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta name="viewport" content="width=device-width" /> - <title>OpenSearch + {applicationTitle} - {/* Favicons (generated from https://realfavicongenerator.net/) */} + {/** + * Favicons (generated from https://realfavicongenerator.net/) + * + * For user customized favicon using yml file: + * If user inputs a valid URL, we gurantee basic favicon customization, such as + * browser favicon(Chrome, Firefox, Safari, and Edge), apple touch icon, safari + * pinned icon. (For Safari browser favicon, we recommend input a png image URL, + * svg image URL might not work) + * + * we do not guarantee other advanced favicon customization such as + * windows tile icon, Andriod device favicon etc. However, the opensearch favicon + * will not be shown at those places and the default browser/device icon will be shown instead. + * + * If user inputs a invalid URL, original opensearch favicon will be used. + */} + - + - + - + diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index fec206ac570a..84a5e7cd5481 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -246,7 +246,7 @@ export default () => defaultUrl: Joi.any().default('/'), darkModeUrl: Joi.any().default('/'), }), - favicon: Joi.any().default('/'), + faviconUrl: Joi.any().default('/'), applicationTitle: Joi.any().default(''), }), }).default(),