Page: Use bouncing logo instead of loading spinner for fallback (#79028)

* use bouncing logo instead of loading spinner for page loader

* translate loading text

* update unit test

* extract bouncing logo out into component
This commit is contained in:
Ashley Harrison 2023-12-15 13:35:42 +00:00 committed by GitHub
parent 7451f41967
commit 09445e0ecc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 133 additions and 10 deletions

View File

@ -0,0 +1,95 @@
import { css, keyframes } from '@emotion/css';
import React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { useStyles2 } from '@grafana/ui';
import { t } from '../../internationalization';
export function BouncingLoader() {
const styles = useStyles2(getStyles);
return (
<div
className={styles.container}
aria-live="polite"
role="status"
aria-label={t('bouncing-loader.label', 'Loading')}
>
<div className={styles.bounce}>
<img alt="" src="public/img/grafana_icon.svg" className={styles.logo} />
</div>
</div>
);
}
const fadeIn = keyframes({
'0%': {
opacity: 0,
animationTimingFunction: 'cubic-bezier(0, 0, 0.5, 1)',
},
'100%': {
opacity: 1,
},
});
const bounce = keyframes({
'from, to': {
transform: 'translateY(0px)',
animationTimingFunction: 'cubic-bezier(0.3, 0, 0.1, 1)',
},
'50%': {
transform: 'translateY(-50px)',
animationTimingFunction: 'cubic-bezier(0.9, 0, 0.7, 1)',
},
});
const squash = keyframes({
'0%': {
transform: 'scaleX(1.3) scaleY(0.8)',
animationTimingFunction: 'cubic-bezier(0.3, 0, 0.1, 1)',
},
'15%': {
transform: 'scaleX(0.75) scaleY(1.25)',
animationTimingFunction: 'cubic-bezier(0, 0, 0.7, 0.75)',
},
'55%': {
transform: 'scaleX(1.05) scaleY(0.95)',
animationTimingFunction: 'cubic-bezier(0.9, 0, 1, 1)',
},
'95%': {
transform: 'scaleX(0.75) scaleY(1.25)',
animationTimingFunction: 'cubic-bezier(0, 0, 0, 1)',
},
'100%': {
transform: 'scaleX(1.3) scaleY(0.8)',
animationTimingFunction: 'cubic-bezier(0, 0, 0.7, 1)',
},
});
const getStyles = (theme: GrafanaTheme2) => ({
container: css({
opacity: 0,
animationName: fadeIn,
animationIterationCount: 1,
animationDuration: '0.9s',
animationDelay: '0.5s',
animationFillMode: 'forwards',
}),
bounce: css({
textAlign: 'center',
animationName: bounce,
animationDuration: '0.9s',
animationIterationCount: 'infinite',
}),
logo: css({
display: 'inline-block',
animationName: squash,
animationDuration: '0.9s',
animationIterationCount: 'infinite',
width: '60px',
height: '60px',
}),
});

View File

@ -53,7 +53,7 @@ describe('GrafanaRoute', () => {
setup({ route: { component: PageComponent, path: '' } });
expect(await screen.findByText('Loading...')).toBeInTheDocument();
expect(await screen.findByLabelText('Loading')).toBeInTheDocument();
});
it('Shows error on page error', async () => {

View File

@ -1,19 +1,27 @@
import { css } from '@emotion/css';
import { css, cx } from '@emotion/css';
import React from 'react';
import { LoadingPlaceholder, useStyles2 } from '@grafana/ui';
import { GrafanaTheme2 } from '@grafana/data';
import { config } from '@grafana/runtime';
import { useStyles2 } from '@grafana/ui';
import { BouncingLoader } from '../components/BouncingLoader/BouncingLoader';
export function GrafanaRouteLoading() {
const styles = useStyles2(getStyles);
return (
<div className={styles.loadingPage}>
<LoadingPlaceholder text={'Loading...'} />
<div
className={cx(styles.loadingPage, {
[styles.loadingPageDockedNav]: config.featureToggles.dockedMegaMenu,
})}
>
<BouncingLoader />
</div>
);
}
const getStyles = () => ({
const getStyles = (theme: GrafanaTheme2) => ({
loadingPage: css({
height: '100%',
flexDrection: 'column',
@ -21,4 +29,7 @@ const getStyles = () => ({
justifyContent: 'center',
alignItems: 'center',
}),
loadingPageDockedNav: css({
backgroundColor: theme.colors.background.primary,
}),
});

View File

@ -25,6 +25,9 @@
"user": "Nutzer"
}
},
"bouncing-loader": {
"label": ""
},
"browse-dashboards": {
"action": {
"cancel-button": "Abbrechen",

View File

@ -25,6 +25,9 @@
"user": "User"
}
},
"bouncing-loader": {
"label": "Loading"
},
"browse-dashboards": {
"action": {
"cancel-button": "Cancel",

View File

@ -25,6 +25,9 @@
"user": "Usuario"
}
},
"bouncing-loader": {
"label": ""
},
"browse-dashboards": {
"action": {
"cancel-button": "Cancelar",

View File

@ -25,6 +25,9 @@
"user": "Utilisateur"
}
},
"bouncing-loader": {
"label": ""
},
"browse-dashboards": {
"action": {
"cancel-button": "Annuler",

View File

@ -25,6 +25,9 @@
"user": "Ůşęř"
}
},
"bouncing-loader": {
"label": "Ŀőäđįʼnģ"
},
"browse-dashboards": {
"action": {
"cancel-button": "Cäʼnčęľ",

View File

@ -25,6 +25,9 @@
"user": "用户"
}
},
"bouncing-loader": {
"label": ""
},
"browse-dashboards": {
"action": {
"cancel-button": "取消",

View File

@ -50,7 +50,7 @@
animation-name: preloader-fade-in;
animation-iteration-count: 1;
animation-duration: 0.9s;
animation-delay: 1.35s;
animation-delay: 0.5s;
animation-fill-mode: forwards;
}
@ -81,7 +81,7 @@
opacity: 0;
animation-name: preloader-fade-in;
animation-duration: 0.9s;
animation-delay: 1.8s;
animation-delay: 0.5s;
animation-fill-mode: forwards;
}
@ -172,10 +172,9 @@
<div class="preloader">
<div class="preloader__enter">
<div class="preloader__bounce">
<div class="preloader__logo"></div>
<div class="preloader__logo" aria-live="polite" role="status" aria-label="Loading Grafana"></div>
</div>
</div>
<div class="preloader__text">Loading Grafana</div>
<div class="preloader__text preloader__text--fail">
<p>
<strong>If you're seeing this Grafana has failed to load its application files</strong>