Dashboards: Hide error notifications in kiosk mode on dashboards (#112390)
Hide error notifications in kiosk mode on dashboards Suppress error alerts when dashboard is viewed in kiosk mode Kiosk mode is typically used for TV displays without interaction Other notification types (success, warning, info) remain visible --------- Co-authored-by: Tom Ratcliffe <tom.ratcliffe@grafana.com>
This commit is contained in:
co-authored by
Tom Ratcliffe
parent
7d88b0130c
commit
e51ec6a827
@@ -0,0 +1,123 @@
|
||||
import { act, getWrapper, render, screen } from 'test/test-utils';
|
||||
|
||||
import { AppEvents } from '@grafana/data';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { KioskMode } from 'app/types/dashboard';
|
||||
|
||||
import { AppChromeService } from '../AppChrome/AppChromeService';
|
||||
|
||||
import { AppNotificationList } from './AppNotificationList';
|
||||
|
||||
const renderWithContext = (kioskMode?: KioskMode, pathname = '/') => {
|
||||
const chromeService = new AppChromeService();
|
||||
if (kioskMode) {
|
||||
chromeService.update({ kioskMode });
|
||||
}
|
||||
|
||||
const wrapper = getWrapper({
|
||||
renderWithRouter: true,
|
||||
historyOptions: { initialEntries: [pathname] },
|
||||
grafanaContext: {
|
||||
chrome: chromeService,
|
||||
},
|
||||
});
|
||||
const view = render(<AppNotificationList />, { wrapper });
|
||||
|
||||
return view;
|
||||
};
|
||||
|
||||
const expectedErrorMessage = 'Test error';
|
||||
const expectedSuccessMessage = 'Test success';
|
||||
const expectedWarningMessage = 'Test warning';
|
||||
const expectedInfoMessage = 'Test info';
|
||||
|
||||
const sendTestNotification = async (type: (typeof AppEvents)[keyof typeof AppEvents], message: string) => {
|
||||
return act(async () => {
|
||||
appEvents.publish({ type: type.name, payload: [message] });
|
||||
});
|
||||
};
|
||||
|
||||
describe('AppNotificationList', () => {
|
||||
describe('Error notifications', () => {
|
||||
it('should show error notifications when not in kiosk mode', async () => {
|
||||
renderWithContext(undefined, '/d/test-dashboard');
|
||||
await sendTestNotification(AppEvents.alertError, expectedErrorMessage);
|
||||
|
||||
expect(await screen.findByText(expectedErrorMessage)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should hide error notifications in kiosk mode on dashboard page', async () => {
|
||||
renderWithContext(KioskMode.Full, '/d/test-dashboard');
|
||||
await sendTestNotification(AppEvents.alertError, expectedErrorMessage);
|
||||
|
||||
expect(screen.queryByText(expectedErrorMessage)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show error notifications in kiosk mode on non-dashboard pages', async () => {
|
||||
renderWithContext(KioskMode.Full, '/alerting');
|
||||
await sendTestNotification(AppEvents.alertError, expectedErrorMessage);
|
||||
|
||||
expect(await screen.findByText(expectedErrorMessage)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should hide error notifications in kiosk mode on home dashboard', async () => {
|
||||
renderWithContext(KioskMode.Full, '/d/');
|
||||
await sendTestNotification(AppEvents.alertError, expectedErrorMessage);
|
||||
|
||||
expect(screen.queryByText(expectedErrorMessage)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show error notifications in kiosk mode on root page (not dashboard route)', async () => {
|
||||
renderWithContext(KioskMode.Full, '/');
|
||||
await sendTestNotification(AppEvents.alertError, expectedErrorMessage);
|
||||
|
||||
expect(await screen.findByText(expectedErrorMessage)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Other notification types', () => {
|
||||
it('should always show success notifications in kiosk mode on dashboard', async () => {
|
||||
renderWithContext(KioskMode.Full, '/d/test-dashboard');
|
||||
await sendTestNotification(AppEvents.alertSuccess, expectedSuccessMessage);
|
||||
|
||||
expect(await screen.findByText(expectedSuccessMessage)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should always show warning notifications in kiosk mode on dashboard', async () => {
|
||||
renderWithContext(KioskMode.Full, '/d/test-dashboard');
|
||||
await sendTestNotification(AppEvents.alertWarning, expectedWarningMessage);
|
||||
|
||||
expect(await screen.findByText(expectedWarningMessage)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should always show info notifications in kiosk mode on dashboard', async () => {
|
||||
renderWithContext(KioskMode.Full, '/d/test-dashboard');
|
||||
await sendTestNotification(AppEvents.alertInfo, expectedInfoMessage);
|
||||
|
||||
expect(await screen.findByText(expectedInfoMessage)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge cases', () => {
|
||||
it('should show error on dashboard page with uid and slug', async () => {
|
||||
renderWithContext(undefined, '/d/test-uid/test-slug');
|
||||
await sendTestNotification(AppEvents.alertError, expectedErrorMessage);
|
||||
|
||||
expect(await screen.findByText(expectedErrorMessage)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should hide error in kiosk mode on dashboard page with uid and slug', async () => {
|
||||
renderWithContext(KioskMode.Full, '/d/test-uid/test-slug');
|
||||
await sendTestNotification(AppEvents.alertError, expectedErrorMessage);
|
||||
|
||||
expect(screen.queryByText(expectedErrorMessage)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show error on legacy dashboard route', async () => {
|
||||
renderWithContext(KioskMode.Full, '/dashboard/db/test-dashboard');
|
||||
await sendTestNotification(AppEvents.alertError, expectedErrorMessage);
|
||||
|
||||
expect(await screen.findByText(expectedErrorMessage)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,10 +1,12 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { AppEvents, GrafanaTheme2 } from '@grafana/data';
|
||||
import { AlertErrorPayload, AppEvents, GrafanaTheme2 } from '@grafana/data';
|
||||
import { useStyles2, Stack } from '@grafana/ui';
|
||||
import { notifyApp, hideAppNotification } from 'app/core/actions';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||
import { selectVisible } from 'app/core/reducers/appNotification';
|
||||
import { useSelector, useDispatch } from 'app/types/store';
|
||||
|
||||
@@ -21,13 +23,28 @@ export function AppNotificationList() {
|
||||
const appNotifications = useSelector((state) => selectVisible(state.appNotifications));
|
||||
const dispatch = useDispatch();
|
||||
const styles = useStyles2(getStyles);
|
||||
const { chrome } = useGrafana();
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
// Suppress error notifications in kiosk mode on dashboards.
|
||||
// Kiosk mode is typically used for TV displays which are non-interactive.
|
||||
// Backend errors like "Failed to fetch" cannot be dismissed and would remain visible,
|
||||
// degrading the viewing experience. Other notification types (success, warning, info)
|
||||
// are still shown as they indicate successful operations or important information.
|
||||
const handleErrorAlert = (payload: AlertErrorPayload) => {
|
||||
const isKioskDashboard = chrome.state.getValue().kioskMode && location.pathname.startsWith('/d/');
|
||||
|
||||
if (!isKioskDashboard) {
|
||||
dispatch(notifyApp(createErrorNotification(...payload)));
|
||||
}
|
||||
};
|
||||
|
||||
appEvents.on(AppEvents.alertWarning, (payload) => dispatch(notifyApp(createWarningNotification(...payload))));
|
||||
appEvents.on(AppEvents.alertSuccess, (payload) => dispatch(notifyApp(createSuccessNotification(...payload))));
|
||||
appEvents.on(AppEvents.alertError, (payload) => dispatch(notifyApp(createErrorNotification(...payload))));
|
||||
appEvents.on(AppEvents.alertError, handleErrorAlert);
|
||||
appEvents.on(AppEvents.alertInfo, (payload) => dispatch(notifyApp(createInfoNotification(...payload))));
|
||||
}, [dispatch]);
|
||||
}, [dispatch, chrome, location.pathname]);
|
||||
|
||||
const onClearAppNotification = (id: string) => {
|
||||
dispatch(hideAppNotification(id));
|
||||
|
||||
Reference in New Issue
Block a user