diff --git a/packages/grafana-ui/src/components/Spinner/Spinner.tsx b/packages/grafana-ui/src/components/Spinner/Spinner.tsx
index 6867bdde0c2..a4d45805f29 100644
--- a/packages/grafana-ui/src/components/Spinner/Spinner.tsx
+++ b/packages/grafana-ui/src/components/Spinner/Spinner.tsx
@@ -6,6 +6,7 @@ import { GrafanaTheme2 } from '@grafana/data';
import { useStyles2 } from '../../themes';
import { IconSize, isIconSize } from '../../types';
+import { t } from '../../utils/i18n';
import { Icon } from '../Icon/Icon';
import { getIconRoot, getIconSubDir } from '../Icon/utils';
@@ -38,6 +39,8 @@ export const Spinner = ({
const styles = useStyles2(getStyles);
const deprecatedStyles = useStyles2(getDeprecatedStyles, size);
+ const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
+ const iconName = prefersReducedMotion ? 'hourglass' : 'spinner';
// this entire if statement is handling the deprecated size prop
// TODO remove once we fully remove the deprecated type
@@ -80,7 +83,17 @@ export const Spinner = ({
className
)}
>
-
+
);
};
diff --git a/public/app/core/components/AppChrome/OrganizationSwitcher/OrganizationSwitcher.test.tsx b/public/app/core/components/AppChrome/OrganizationSwitcher/OrganizationSwitcher.test.tsx
index 74b3af55698..20a5eb1db1c 100644
--- a/public/app/core/components/AppChrome/OrganizationSwitcher/OrganizationSwitcher.test.tsx
+++ b/public/app/core/components/AppChrome/OrganizationSwitcher/OrganizationSwitcher.test.tsx
@@ -30,11 +30,14 @@ const renderWithProvider = ({ initialState }: { initialState?: Partial {
beforeEach(() => {
- (window.matchMedia as jest.Mock).mockImplementation(() => ({
- addEventListener: jest.fn(),
- removeEventListener: jest.fn(),
- matches: true,
- }));
+ jest.spyOn(window, 'matchMedia').mockImplementation(
+ () =>
+ ({
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ matches: true,
+ }) as unknown as MediaQueryList
+ );
});
it('should only render if more than one organisations', () => {
@@ -80,11 +83,14 @@ describe('OrganisationSwitcher', () => {
});
it('should render a picker in mobile screen', () => {
- (window.matchMedia as jest.Mock).mockImplementation(() => ({
- addEventListener: jest.fn(),
- removeEventListener: jest.fn(),
- matches: false,
- }));
+ jest.spyOn(window, 'matchMedia').mockImplementation(
+ () =>
+ ({
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ matches: false,
+ }) as unknown as MediaQueryList
+ );
renderWithProvider({
initialState: {
organization: {
diff --git a/public/app/core/components/AppChrome/TopBar/TopSearchBarSection.test.tsx b/public/app/core/components/AppChrome/TopBar/TopSearchBarSection.test.tsx
index c0888649c4b..d883b0a32c8 100644
--- a/public/app/core/components/AppChrome/TopBar/TopSearchBarSection.test.tsx
+++ b/public/app/core/components/AppChrome/TopBar/TopSearchBarSection.test.tsx
@@ -14,11 +14,14 @@ const renderComponent = (options?: { props: TopSearchBarSectionProps }) => {
describe('TopSearchBarSection', () => {
it('should use a wrapper on non mobile screen', () => {
- (window.matchMedia as jest.Mock).mockImplementation(() => ({
- addEventListener: jest.fn(),
- removeEventListener: jest.fn(),
- matches: true,
- }));
+ jest.spyOn(window, 'matchMedia').mockImplementation(
+ () =>
+ ({
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ matches: true,
+ }) as unknown as MediaQueryList
+ );
const component = renderComponent();
@@ -27,11 +30,14 @@ describe('TopSearchBarSection', () => {
});
it('should not use a wrapper on mobile screen', () => {
- (window.matchMedia as jest.Mock).mockImplementation(() => ({
- addEventListener: jest.fn(),
- removeEventListener: jest.fn(),
- matches: false,
- }));
+ jest.spyOn(window, 'matchMedia').mockImplementation(
+ () =>
+ ({
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ matches: false,
+ }) as unknown as MediaQueryList
+ );
const component = renderComponent();
diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json
index dcba7b50053..76d9d9fe01a 100644
--- a/public/locales/en-US/grafana.json
+++ b/public/locales/en-US/grafana.json
@@ -618,6 +618,9 @@
"select": {
"no-options-label": "No options found",
"placeholder": "Choose"
+ },
+ "spinner": {
+ "aria-label": "Loading"
}
},
"graph": {
diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json
index deb470d072b..7142d64ccf6 100644
--- a/public/locales/pseudo-LOCALE/grafana.json
+++ b/public/locales/pseudo-LOCALE/grafana.json
@@ -618,6 +618,9 @@
"select": {
"no-options-label": "Ńő őpŧįőʼnş ƒőūʼnđ",
"placeholder": "Cĥőőşę"
+ },
+ "spinner": {
+ "aria-label": "Ŀőäđįʼnģ"
}
},
"graph": {
diff --git a/public/test/jest-setup.ts b/public/test/jest-setup.ts
index 4e77c25a542..4d34bb4a7fd 100644
--- a/public/test/jest-setup.ts
+++ b/public/test/jest-setup.ts
@@ -34,19 +34,15 @@ global.grafanaBootData = {
navTree: [],
};
-// https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom
-Object.defineProperty(global, 'matchMedia', {
- writable: true,
- value: jest.fn().mockImplementation((query) => ({
- matches: false,
- media: query,
- onchange: null,
- addListener: jest.fn(), // deprecated
- removeListener: jest.fn(), // deprecated
- addEventListener: jest.fn(),
- removeEventListener: jest.fn(),
- dispatchEvent: jest.fn(),
- })),
+window.matchMedia = (query) => ({
+ matches: false,
+ media: query,
+ onchange: null,
+ addListener: jest.fn(), // Deprecated
+ removeListener: jest.fn(), // Deprecated
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ dispatchEvent: jest.fn(),
});
angular.module('grafana', ['ngRoute']);