From cdca1518d2d2ee5d725517a8d8206b0cfa3656d0 Mon Sep 17 00:00:00 2001 From: Juan Cabanas Date: Fri, 13 Oct 2023 13:18:05 -0300 Subject: [PATCH] PublicDashboards: Title logo and footer redesign (#74769) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --------- Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> --- .betterer.results | 5 -- .../configure-custom-branding/index.md | 8 ++- pkg/api/dtos/frontend_settings.go | 34 ++++----- .../PublicDashboardsFooter.tsx | 37 ++++++++++ .../usePublicDashboardConfig.tsx | 55 +++++++++++++++ .../PublicDashboardsFooter.tsx | 54 -------------- .../containers/PublicDashboardPage.tsx | 10 ++- public/img/grafana_text_logo_dark.svg | 70 +++++++++++++++++++ public/img/grafana_text_logo_light.svg | 20 ++++++ 9 files changed, 213 insertions(+), 80 deletions(-) create mode 100644 public/app/features/dashboard/components/PublicDashboard/PublicDashboardsFooter.tsx create mode 100644 public/app/features/dashboard/components/PublicDashboard/usePublicDashboardConfig.tsx delete mode 100644 public/app/features/dashboard/components/PublicDashboardFooter/PublicDashboardsFooter.tsx create mode 100644 public/img/grafana_text_logo_dark.svg create mode 100644 public/img/grafana_text_logo_light.svg diff --git a/.betterer.results b/.betterer.results index f99801e80ad..bda1b17be1e 100644 --- a/.betterer.results +++ b/.betterer.results @@ -3251,11 +3251,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "4"], [0, 0, 0, "Unexpected any. Specify a different type.", "5"] ], - "public/app/features/dashboard/components/PublicDashboardFooter/PublicDashboardsFooter.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"] - ], "public/app/features/dashboard/components/PublicDashboardNotAvailable/PublicDashboardNotAvailable.tsx:5381": [ [0, 0, 0, "Styles should be written using objects.", "0"], [0, 0, 0, "Styles should be written using objects.", "1"], diff --git a/docs/sources/setup-grafana/configure-grafana/configure-custom-branding/index.md b/docs/sources/setup-grafana/configure-grafana/configure-custom-branding/index.md index ff0634252eb..f85ebfd30ee 100644 --- a/docs/sources/setup-grafana/configure-grafana/configure-custom-branding/index.md +++ b/docs/sources/setup-grafana/configure-grafana/configure-custom-branding/index.md @@ -120,18 +120,22 @@ To customize the footer of a public dashboard, add the following section to the ```ini [white_labeling.public_dashboards] -# Hides the footer for the public dashboards if set to "true". If +# Hides the footer for the public dashboards if set to `true`. # example: footer_hide = "true" ;footer_hide = # Set to text shown in the footer ;footer_text = -# Set to complete url to override public dashboard footer logo +# Set to complete url to override public dashboard footer logo. Default is `grafana-logo` and will display the Grafana logo. +# An empty value will hide the footer logo. ;footer_logo = # Set to link for the footer ;footer_link = + +# Set to `true` to hide the Grafana logo next to the title +;header_logo_hide = ``` If you specify `footer_hide` to `true`, all the other values are ignored because the footer will not be shown. diff --git a/pkg/api/dtos/frontend_settings.go b/pkg/api/dtos/frontend_settings.go index 529ddd08cef..35d6ee53689 100644 --- a/pkg/api/dtos/frontend_settings.go +++ b/pkg/api/dtos/frontend_settings.go @@ -93,27 +93,27 @@ type FrontendSettingsFooterConfigItemDTO struct { } // Enterprise-only -type FrontendSettingsPublicDashboardFooterConfigDTO struct { - Hide bool `json:"hide"` - Text string `json:"text"` - Logo string `json:"logo"` - Link string `json:"link"` +type FrontendSettingsPublicDashboardConfigDTO struct { + FooterHide bool `json:"footerHide"` + FooterText string `json:"footerText"` + FooterLogo string `json:"footerLogo"` + FooterLink string `json:"footerLink"` + HeaderLogoHide bool `json:"headerLogoHide"` } // Enterprise-only type FrontendSettingsWhitelabelingDTO struct { - Links []FrontendSettingsFooterConfigItemDTO `json:"links"` - LoginTitle string `json:"loginTitle"` - - AppTitle *string `json:"appTitle,omitempty"` - LoginLogo *string `json:"loginLogo,omitempty"` - MenuLogo *string `json:"menuLogo,omitempty"` - LoginBackground *string `json:"loginBackground,omitempty"` - LoginSubtitle *string `json:"loginSubtitle,omitempty"` - LoginBoxBackground *string `json:"loginBoxBackground,omitempty"` - LoadingLogo *string `json:"loadingLogo,omitempty"` - HideEdition *bool `json:"hideEdition,omitempty"` - PublicDashboardFooter *FrontendSettingsPublicDashboardFooterConfigDTO `json:"publicDashboardFooter,omitempty"` // PR TODO: type this properly + Links []FrontendSettingsFooterConfigItemDTO `json:"links"` + LoginTitle string `json:"loginTitle"` + AppTitle *string `json:"appTitle,omitempty"` + LoginLogo *string `json:"loginLogo,omitempty"` + MenuLogo *string `json:"menuLogo,omitempty"` + LoginBackground *string `json:"loginBackground,omitempty"` + LoginSubtitle *string `json:"loginSubtitle,omitempty"` + LoginBoxBackground *string `json:"loginBoxBackground,omitempty"` + LoadingLogo *string `json:"loadingLogo,omitempty"` + HideEdition *bool `json:"hideEdition,omitempty"` + PublicDashboard *FrontendSettingsPublicDashboardConfigDTO `json:"publicDashboard,omitempty"` } type FrontendSettingsSqlConnectionLimitsDTO struct { diff --git a/public/app/features/dashboard/components/PublicDashboard/PublicDashboardsFooter.tsx b/public/app/features/dashboard/components/PublicDashboard/PublicDashboardsFooter.tsx new file mode 100644 index 00000000000..c2b9f72dd9f --- /dev/null +++ b/public/app/features/dashboard/components/PublicDashboard/PublicDashboardsFooter.tsx @@ -0,0 +1,37 @@ +import { css } from '@emotion/css'; +import React from 'react'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { useStyles2 } from '@grafana/ui'; + +import { useGetPublicDashboardConfig } from './usePublicDashboardConfig'; + +export const PublicDashboardFooter = function () { + const styles = useStyles2(getStyles); + const conf = useGetPublicDashboardConfig(); + + return conf.footerHide ? null : ( +
+ + {conf.footerText} + +
+ ); +}; + +const getStyles = (theme: GrafanaTheme2) => ({ + footer: css({ + display: 'flex', + justifyContent: 'end', + height: '30px', + padding: theme.spacing(0, 2, 0, 1), + }), + link: css({ + display: 'flex', + alignItems: 'center', + }), + logoImg: css({ + height: '16px', + marginLeft: theme.spacing(0.5), + }), +}); diff --git a/public/app/features/dashboard/components/PublicDashboard/usePublicDashboardConfig.tsx b/public/app/features/dashboard/components/PublicDashboard/usePublicDashboardConfig.tsx new file mode 100644 index 00000000000..eff429de19a --- /dev/null +++ b/public/app/features/dashboard/components/PublicDashboard/usePublicDashboardConfig.tsx @@ -0,0 +1,55 @@ +import { css } from '@emotion/css'; +import React from 'react'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { useStyles2, useTheme2 } from '@grafana/ui'; + +const FOOTER_URL = 'https://grafana.com/?src=grafananet&cnt=public-dashboards'; +const GRAFANA_LOGO_LIGHT_URL = 'public/img/grafana_text_logo_light.svg'; +const GRAFANA_LOGO_DARK_URL = 'public/img/grafana_text_logo_dark.svg'; +const GRAFANA_LOGO_DEFAULT_VALUE = 'grafana-logo'; + +export interface PublicDashboardCfg { + footerHide: boolean; + footerText: React.ReactNode; + footerLogo: string; + footerLink: string; + headerLogoHide: boolean; +} +const useGetConfig = (cfg?: PublicDashboardCfg) => { + const theme = useTheme2(); + const styles = useStyles2(getStyles); + + const { footerHide, footerText, footerLink, footerLogo, headerLogoHide } = cfg || { + footerHide: false, + footerText: 'Powered by', + footerLogo: GRAFANA_LOGO_DEFAULT_VALUE, + footerLink: FOOTER_URL, + headerLogoHide: false, + }; + + return { + footerHide, + footerText: {footerText}, + footerLogo: + footerLogo === GRAFANA_LOGO_DEFAULT_VALUE + ? theme.isDark + ? GRAFANA_LOGO_LIGHT_URL + : GRAFANA_LOGO_DARK_URL + : footerLogo, + footerLink, + headerLogoHide, + }; +}; +export let useGetPublicDashboardConfig = (): PublicDashboardCfg => useGetConfig(); + +export function setPublicDashboardConfigFn(cfg: PublicDashboardCfg) { + useGetPublicDashboardConfig = (): PublicDashboardCfg => useGetConfig(cfg); +} + +const getStyles = (theme: GrafanaTheme2) => ({ + text: css({ + color: theme.colors.text.secondary, + fontSize: theme.typography.body.fontSize, + }), +}); diff --git a/public/app/features/dashboard/components/PublicDashboardFooter/PublicDashboardsFooter.tsx b/public/app/features/dashboard/components/PublicDashboardFooter/PublicDashboardsFooter.tsx deleted file mode 100644 index 4b65096307d..00000000000 --- a/public/app/features/dashboard/components/PublicDashboardFooter/PublicDashboardsFooter.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { css } from '@emotion/css'; -import React from 'react'; - -import { GrafanaTheme2 } from '@grafana/data'; -import { useStyles2 } from '@grafana/ui'; - -export interface PublicDashboardFooterCfg { - hide: boolean; - text: string; - logo: string; - link: string; -} - -export const PublicDashboardFooter = function () { - const styles = useStyles2(getStyles); - const conf = getPublicDashboardFooterConfig(); - - return conf.hide ? null : ( -
- - {conf.text} - -
- ); -}; - -export function setPublicDashboardFooterConfigFn(fn: typeof getPublicDashboardFooterConfig) { - getPublicDashboardFooterConfig = fn; -} -export let getPublicDashboardFooterConfig = (): PublicDashboardFooterCfg => ({ - hide: false, - text: 'powered by Grafana', - logo: 'public/img/grafana_icon.svg', - link: 'https://grafana.com/', -}); - -const getStyles = (theme: GrafanaTheme2) => ({ - footer: css` - display: flex; - justify-content: end; - height: 30px; - padding: ${theme.spacing(0, 2, 0, 1)}; - `, - link: css` - display: flex; - gap: 4px; - justify-content: end; - align-items: center; - `, - logoImg: css` - height: 100%; - padding: ${theme.spacing(0.25, 0, 0.5, 0)}; - `, -}); diff --git a/public/app/features/dashboard/containers/PublicDashboardPage.tsx b/public/app/features/dashboard/containers/PublicDashboardPage.tsx index 861f7b6dd70..a4af9556c90 100644 --- a/public/app/features/dashboard/containers/PublicDashboardPage.tsx +++ b/public/app/features/dashboard/containers/PublicDashboardPage.tsx @@ -14,7 +14,8 @@ import { useSelector, useDispatch } from 'app/types'; import { DashNavTimeControls } from '../components/DashNav/DashNavTimeControls'; import { DashboardFailed } from '../components/DashboardLoading/DashboardFailed'; import { DashboardLoading } from '../components/DashboardLoading/DashboardLoading'; -import { PublicDashboardFooter } from '../components/PublicDashboardFooter/PublicDashboardsFooter'; +import { PublicDashboardFooter } from '../components/PublicDashboard/PublicDashboardsFooter'; +import { useGetPublicDashboardConfig } from '../components/PublicDashboard/usePublicDashboardConfig'; import { PublicDashboardNotAvailable } from '../components/PublicDashboardNotAvailable/PublicDashboardNotAvailable'; import { DashboardGrid } from '../dashgrid/DashboardGrid'; import { getTimeSrv } from '../services/TimeSrv'; @@ -37,13 +38,18 @@ const selectors = e2eSelectors.pages.PublicDashboard; const Toolbar = ({ dashboard }: { dashboard: DashboardModel }) => { const dispatch = useDispatch(); + const conf = useGetPublicDashboardConfig(); const onChangeTimeZone = (timeZone: TimeZone) => { dispatch(updateTimeZoneForSession(timeZone)); }; return ( - + {!dashboard.timepicker.hidden && ( )} diff --git a/public/img/grafana_text_logo_dark.svg b/public/img/grafana_text_logo_dark.svg new file mode 100644 index 00000000000..68d56d355d9 --- /dev/null +++ b/public/img/grafana_text_logo_dark.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + diff --git a/public/img/grafana_text_logo_light.svg b/public/img/grafana_text_logo_light.svg new file mode 100644 index 00000000000..1dd0ec6a841 --- /dev/null +++ b/public/img/grafana_text_logo_light.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + +