mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
7451f41967
commit
09445e0ecc
95
public/app/core/components/BouncingLoader/BouncingLoader.tsx
Normal file
95
public/app/core/components/BouncingLoader/BouncingLoader.tsx
Normal 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',
|
||||
}),
|
||||
});
|
@ -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 () => {
|
||||
|
@ -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,
|
||||
}),
|
||||
});
|
||||
|
@ -25,6 +25,9 @@
|
||||
"user": "Nutzer"
|
||||
}
|
||||
},
|
||||
"bouncing-loader": {
|
||||
"label": ""
|
||||
},
|
||||
"browse-dashboards": {
|
||||
"action": {
|
||||
"cancel-button": "Abbrechen",
|
||||
|
@ -25,6 +25,9 @@
|
||||
"user": "User"
|
||||
}
|
||||
},
|
||||
"bouncing-loader": {
|
||||
"label": "Loading"
|
||||
},
|
||||
"browse-dashboards": {
|
||||
"action": {
|
||||
"cancel-button": "Cancel",
|
||||
|
@ -25,6 +25,9 @@
|
||||
"user": "Usuario"
|
||||
}
|
||||
},
|
||||
"bouncing-loader": {
|
||||
"label": ""
|
||||
},
|
||||
"browse-dashboards": {
|
||||
"action": {
|
||||
"cancel-button": "Cancelar",
|
||||
|
@ -25,6 +25,9 @@
|
||||
"user": "Utilisateur"
|
||||
}
|
||||
},
|
||||
"bouncing-loader": {
|
||||
"label": ""
|
||||
},
|
||||
"browse-dashboards": {
|
||||
"action": {
|
||||
"cancel-button": "Annuler",
|
||||
|
@ -25,6 +25,9 @@
|
||||
"user": "Ůşęř"
|
||||
}
|
||||
},
|
||||
"bouncing-loader": {
|
||||
"label": "Ŀőäđįʼnģ"
|
||||
},
|
||||
"browse-dashboards": {
|
||||
"action": {
|
||||
"cancel-button": "Cäʼnčęľ",
|
||||
|
@ -25,6 +25,9 @@
|
||||
"user": "用户"
|
||||
}
|
||||
},
|
||||
"bouncing-loader": {
|
||||
"label": ""
|
||||
},
|
||||
"browse-dashboards": {
|
||||
"action": {
|
||||
"cancel-button": "取消",
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user