Compare commits
1 Commits
sriram/SQL
...
feature/ki
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13eb993304 |
@@ -1840,14 +1840,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"public/app/features/dashboard-scene/pages/DashboardScenePage.tsx": {
|
||||
"@typescript-eslint/consistent-type-assertions": {
|
||||
"count": 2
|
||||
},
|
||||
"@typescript-eslint/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx": {
|
||||
"@typescript-eslint/consistent-type-assertions": {
|
||||
"count": 1
|
||||
|
||||
@@ -174,6 +174,56 @@ describe('DashboardScenePage', () => {
|
||||
expect(await screen.findByText('Content B')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows Powered by footer in kiosk mode', async () => {
|
||||
setup({ routeProps: { queryParams: { kiosk: true } } });
|
||||
|
||||
await waitForDashboardToRender();
|
||||
|
||||
expect(await screen.findByTestId(selectors.pages.PublicDashboard.footer)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('uses kiosk dashboard CTA url', async () => {
|
||||
setup({ routeProps: { queryParams: { kiosk: true } } });
|
||||
|
||||
await waitForDashboardToRender();
|
||||
|
||||
const footer = await screen.findByTestId(selectors.pages.PublicDashboard.footer);
|
||||
const link = footer.querySelector('a');
|
||||
expect(link).toHaveAttribute('href', 'https://grafana.com/?src=grafananet&cnt=kiosk-dashboard');
|
||||
});
|
||||
|
||||
it('hides Powered by footer in kiosk mode when hideLogo is present', async () => {
|
||||
setup({ routeProps: { queryParams: { kiosk: true, hideLogo: true } } });
|
||||
|
||||
await waitForDashboardToRender();
|
||||
|
||||
expect(screen.queryByTestId(selectors.pages.PublicDashboard.footer)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('hides Powered by footer in kiosk mode when hideLogo=1', async () => {
|
||||
setup({ routeProps: { queryParams: { kiosk: true, hideLogo: '1' } } });
|
||||
|
||||
await waitForDashboardToRender();
|
||||
|
||||
expect(screen.queryByTestId(selectors.pages.PublicDashboard.footer)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows Powered by footer in kiosk mode when hideLogo=0', async () => {
|
||||
setup({ routeProps: { queryParams: { kiosk: true, hideLogo: '0' } } });
|
||||
|
||||
await waitForDashboardToRender();
|
||||
|
||||
expect(await screen.findByTestId(selectors.pages.PublicDashboard.footer)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows Powered by footer in kiosk mode when hideLogo=false', async () => {
|
||||
setup({ routeProps: { queryParams: { kiosk: true, hideLogo: 'false' } } });
|
||||
|
||||
await waitForDashboardToRender();
|
||||
|
||||
expect(await screen.findByTestId(selectors.pages.PublicDashboard.footer)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('routeReloadCounter should trigger reload', async () => {
|
||||
const { rerender, props } = setup();
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Box } from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import PageLoader from 'app/core/components/PageLoader/PageLoader';
|
||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||
import { PublicDashboardFooter } from 'app/features/dashboard/components/PublicDashboard/PublicDashboardsFooter';
|
||||
import { DashboardPageError } from 'app/features/dashboard/containers/DashboardPageError';
|
||||
import { DashboardPageRouteParams, DashboardPageRouteSearchParams } from 'app/features/dashboard/containers/types';
|
||||
import { getDashboardSceneProfiler } from 'app/features/dashboard/services/DashboardProfiler';
|
||||
@@ -21,6 +22,25 @@ import { preserveDashboardSceneStateInLocalStorage } from '../utils/dashboardSes
|
||||
|
||||
import { getDashboardScenePageStateManager } from './DashboardScenePageStateManager';
|
||||
|
||||
const KIOSK_DASHBOARD_FOOTER_URL = 'https://grafana.com/?src=grafananet&cnt=kiosk-dashboard';
|
||||
|
||||
function shouldHideDashboardKioskFooter(hideLogo?: string | true): boolean {
|
||||
if (hideLogo === undefined || hideLogo === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hideLogo === true || hideLogo === '1') {
|
||||
return true;
|
||||
}
|
||||
|
||||
const normalized = String(hideLogo).trim().toLowerCase();
|
||||
if (normalized === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return normalized !== 'false' && normalized !== '0';
|
||||
}
|
||||
|
||||
export interface Props
|
||||
extends Omit<GrafanaRouteComponentProps<DashboardPageRouteParams, DashboardPageRouteSearchParams>, 'match'> {}
|
||||
|
||||
@@ -33,7 +53,32 @@ export function DashboardScenePage({ route, queryParams, location }: Props) {
|
||||
const stateManager = getDashboardScenePageStateManager();
|
||||
const { dashboard, isLoading, loadError } = stateManager.useState();
|
||||
// After scene migration is complete and we get rid of old dashboard we should refactor dashboardWatcher so this route reload is not need
|
||||
const routeReloadCounter = (location.state as any)?.routeReloadCounter;
|
||||
const routeReloadCounter = (() => {
|
||||
const state = location.state;
|
||||
if (state && typeof state === 'object') {
|
||||
const value = Reflect.get(state, 'routeReloadCounter');
|
||||
return typeof value === 'number' ? value : undefined;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
})();
|
||||
|
||||
const dashboardRoute = (() => {
|
||||
switch (route.routeName) {
|
||||
case DashboardRoutes.Home:
|
||||
case DashboardRoutes.New:
|
||||
case DashboardRoutes.Template:
|
||||
case DashboardRoutes.Normal:
|
||||
case DashboardRoutes.Provisioning:
|
||||
case DashboardRoutes.Scripted:
|
||||
case DashboardRoutes.Public:
|
||||
case DashboardRoutes.Embedded:
|
||||
case DashboardRoutes.Report:
|
||||
return route.routeName;
|
||||
default:
|
||||
return DashboardRoutes.Normal;
|
||||
}
|
||||
})();
|
||||
const prevParams = useRef<Params<string>>(params);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -44,7 +89,7 @@ export function DashboardScenePage({ route, queryParams, location }: Props) {
|
||||
uid: (route.routeName === DashboardRoutes.Provisioning ? path : uid) ?? '',
|
||||
type,
|
||||
slug,
|
||||
route: route.routeName as DashboardRoutes,
|
||||
route: dashboardRoute,
|
||||
urlFolderUid: queryParams.folderUid,
|
||||
});
|
||||
}
|
||||
@@ -106,12 +151,18 @@ export function DashboardScenePage({ route, queryParams, location }: Props) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const showKioskFooter = queryParams.kiosk === '1' || queryParams.kiosk === true;
|
||||
const hideKioskFooter = shouldHideDashboardKioskFooter(queryParams.hideLogo);
|
||||
|
||||
return (
|
||||
<UrlSyncContextProvider scene={dashboard} updateUrlOnInit={true} createBrowserHistorySteps={true}>
|
||||
<DashboardPreviewBanner queryParams={queryParams} route={route.routeName} slug={slug} path={path} />
|
||||
<DashboardConversionWarningBanner dashboard={dashboard} />
|
||||
<dashboard.Component model={dashboard} key={dashboard.state.key} />
|
||||
<DashboardPrompt dashboard={dashboard} />
|
||||
{showKioskFooter && !hideKioskFooter && (
|
||||
<PublicDashboardFooter paddingX={2} useMinHeight linkUrl={KIOSK_DASHBOARD_FOOTER_URL} />
|
||||
)}
|
||||
</UrlSyncContextProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,52 @@
|
||||
import { css } from '@emotion/css';
|
||||
import type { CSSProperties } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { useStyles2, useTheme2 } from '@grafana/ui';
|
||||
|
||||
import { useGetPublicDashboardConfig } from './usePublicDashboardConfig';
|
||||
|
||||
const selectors = e2eSelectors.pages.PublicDashboard;
|
||||
|
||||
export const PublicDashboardFooter = function () {
|
||||
export interface PublicDashboardFooterProps {
|
||||
/**
|
||||
* Applies horizontal padding to the footer container.
|
||||
* Useful when rendering the footer in layouts that don't already have page padding (e.g. kiosk mode).
|
||||
*/
|
||||
paddingX?: number;
|
||||
/**
|
||||
* When true, avoids clipping in containers with `overflow: hidden` by not relying on a fixed height.
|
||||
*/
|
||||
useMinHeight?: boolean;
|
||||
/**
|
||||
* Overrides the CTA link URL.
|
||||
* Useful when reusing the footer outside public dashboards.
|
||||
*/
|
||||
linkUrl?: string;
|
||||
}
|
||||
|
||||
export const PublicDashboardFooter = function ({ paddingX, useMinHeight, linkUrl }: PublicDashboardFooterProps) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const theme = useTheme2();
|
||||
const conf = useGetPublicDashboardConfig();
|
||||
|
||||
const footerStyle: CSSProperties = {};
|
||||
if (paddingX !== undefined) {
|
||||
footerStyle.boxSizing = 'border-box';
|
||||
footerStyle.paddingLeft = theme.spacing(paddingX);
|
||||
footerStyle.paddingRight = theme.spacing(paddingX);
|
||||
}
|
||||
|
||||
if (useMinHeight) {
|
||||
footerStyle.height = 'auto';
|
||||
footerStyle.minHeight = '30px';
|
||||
footerStyle.alignItems = 'center';
|
||||
}
|
||||
|
||||
return conf.footerHide ? null : (
|
||||
<div className={styles.footer} data-testid={selectors.footer}>
|
||||
<a className={styles.link} href={conf.footerLink} target="_blank" rel="noreferrer noopener">
|
||||
<div className={styles.footer} data-testid={selectors.footer} style={footerStyle}>
|
||||
<a className={styles.link} href={linkUrl ?? conf.footerLink} target="_blank" rel="noreferrer noopener">
|
||||
{conf.footerText} <img className={styles.logoImg} alt="" src={conf.footerLogo} />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -18,6 +18,7 @@ export type DashboardPageRouteSearchParams = {
|
||||
to?: string;
|
||||
refresh?: string;
|
||||
kiosk?: string | true;
|
||||
hideLogo?: string | true;
|
||||
scenes?: boolean;
|
||||
shareView?: string;
|
||||
ref?: string; // used for repo preview
|
||||
|
||||
Reference in New Issue
Block a user