From 97397fb8f9db61a7c25b4c9a9aef91fec0e110eb Mon Sep 17 00:00:00 2001 From: "Grot (@grafanabot)" <43478413+grafanabot@users.noreply.github.com> Date: Wed, 24 Nov 2021 12:34:24 -0500 Subject: [PATCH] Azure Monitor: Fastpass Fixes -- Add trap focus for modals in grafana/ui and other small a11y fixes for Azure Monitor. (#41449) (#42248) (cherry picked from commit fc8d93e231fe329f055f1a6e4300e89df896b78d) Co-authored-by: Sarah Zinger --- .../grafana-ui/src/components/Modal/Modal.tsx | 19 +- .../components/AzureCredentialsForm.test.tsx | 60 +- .../components/AzureCredentialsForm.tsx | 14 +- .../AzureCredentialsForm.test.tsx.snap | 626 ------------------ 4 files changed, 43 insertions(+), 676 deletions(-) delete mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/__snapshots__/AzureCredentialsForm.test.tsx.snap diff --git a/packages/grafana-ui/src/components/Modal/Modal.tsx b/packages/grafana-ui/src/components/Modal/Modal.tsx index c92377aca91..3f9308e4daf 100644 --- a/packages/grafana-ui/src/components/Modal/Modal.tsx +++ b/packages/grafana-ui/src/components/Modal/Modal.tsx @@ -7,6 +7,7 @@ import { getModalStyles } from './getModalStyles'; import { ModalHeader } from './ModalHeader'; import { IconButton } from '../IconButton/IconButton'; import { HorizontalGroup } from '../Layout/Layout'; +import { FocusScope } from '@react-aria/focus'; export interface Props { /** @deprecated no longer used */ @@ -75,16 +76,18 @@ export function Modal(props: PropsWithChildren) { className={styles.modalBackdrop} onClick={onClickBackdrop || (closeOnBackdropClick ? onDismiss : undefined)} /> -
-
- {typeof title === 'string' && } - {typeof title !== 'string' && title} -
- + +
+
+ {typeof title === 'string' && } + {typeof title !== 'string' && title} +
+ +
+
{children}
-
{children}
-
+ ); } diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AzureCredentialsForm.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AzureCredentialsForm.test.tsx index 75fe8b0c85f..3afa70a9763 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AzureCredentialsForm.test.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AzureCredentialsForm.test.tsx @@ -1,8 +1,6 @@ import React from 'react'; -import { shallow } from 'enzyme'; +import { render, screen, waitFor } from '@testing-library/react'; import AzureCredentialsForm, { Props } from './AzureCredentialsForm'; -import { LegacyForms, Button } from '@grafana/ui'; -const { Input, Select } = LegacyForms; const setup = (propsFunc?: (props: Props) => Props) => { let props: Props = { @@ -22,24 +20,25 @@ const setup = (propsFunc?: (props: Props) => Props) => { { value: 'chinaazuremonitor', label: 'Azure China' }, ], onCredentialsChange: jest.fn(), - getSubscriptions: jest.fn(), + getSubscriptions: jest.fn(() => Promise.resolve([])), }; if (propsFunc) { props = propsFunc(props); } - return shallow(); + return render(); }; describe('Render', () => { it('should render component', () => { - const wrapper = setup(); - expect(wrapper).toMatchSnapshot(); + setup(); + + expect(screen.getByText('Azure Cloud')).toBeInTheDocument(); }); - it('should disable azure monitor secret input', () => { - const wrapper = setup((props) => ({ + it('should disable azure monitor secret input', async () => { + setup((props) => ({ ...props, credentials: { authType: 'clientsecret', @@ -49,11 +48,12 @@ describe('Render', () => { clientSecret: Symbol(), }, })); - expect(wrapper).toMatchSnapshot(); + await waitFor(() => screen.getByTestId('client-secret')); + expect(screen.getByTestId('client-secret')).toBeDisabled(); }); - it('should enable azure monitor load subscriptions button', () => { - const wrapper = setup((props) => ({ + it('should enable azure monitor load subscriptions button', async () => { + setup((props) => ({ ...props, credentials: { authType: 'clientsecret', @@ -63,46 +63,34 @@ describe('Render', () => { clientSecret: 'e7f3f661-a933-4b3f-8176-51c4f982ec48', }, })); - expect(wrapper).toMatchSnapshot(); + await waitFor(() => expect(screen.getByText('Load Subscriptions')).toBeInTheDocument()); }); describe('when disabled', () => { - it('should disable inputs', () => { - const wrapper = setup((props) => ({ + it('should disable inputs', async () => { + setup((props) => ({ ...props, disabled: true, })); - const inputs = wrapper.find(Input); - expect(inputs.length).toBeGreaterThan(1); - inputs.forEach((input) => { - expect(input.prop('disabled')).toBe(true); - }); + + await waitFor(() => screen.getByLabelText('Azure Cloud')); + expect(screen.getByLabelText('Azure Cloud')).toBeDisabled(); }); - it('should remove buttons', () => { - const wrapper = setup((props) => ({ + it('should remove buttons', async () => { + setup((props) => ({ ...props, disabled: true, })); - expect(wrapper.find(Button).exists()).toBe(false); + await waitFor(() => expect(screen.queryByText('Load Subscriptions')).not.toBeInTheDocument()); }); - it('should disable cloud selector', () => { - const wrapper = setup((props) => ({ - ...props, - disabled: true, - })); - const selects = wrapper.find(Select); - selects.forEach((s) => expect(s.prop('isDisabled')).toBe(true)); - }); - - it('should render a children component', () => { - const wrapper = setup((props) => ({ + it('should render children components', () => { + setup((props) => ({ ...props, children: , })); - const button = wrapper.find('button'); - expect(button.text()).toBe('click me'); + expect(screen.getByText('click me')).toBeInTheDocument(); }); }); }); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AzureCredentialsForm.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AzureCredentialsForm.tsx index 57cc9517c9e..2d7429cbdc0 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AzureCredentialsForm.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AzureCredentialsForm.tsx @@ -1,9 +1,9 @@ import React, { ChangeEvent, FunctionComponent, useEffect, useReducer, useState } from 'react'; import { SelectableValue } from '@grafana/data'; -import { InlineFormLabel, LegacyForms, Button } from '@grafana/ui'; +import { InlineFormLabel, LegacyForms, Button, Select } from '@grafana/ui'; import { AzureAuthType, AzureCredentials } from '../types'; import { isCredentialsComplete } from '../credentials'; -const { Select, Input } = LegacyForms; +const { Input } = LegacyForms; export interface Props { managedIdentityEnabled: boolean; @@ -162,7 +162,7 @@ export const AzureCredentialsForm: FunctionComponent = (props: Props) => value={authTypeOptions.find((opt) => opt.value === credentials.authType)} options={authTypeOptions} onChange={onAuthTypeChange} - isDisabled={disabled} + disabled={disabled} />
@@ -176,12 +176,13 @@ export const AzureCredentialsForm: FunctionComponent = (props: Props) => Azure Cloud +
@@ -254,6 +255,7 @@ export const AzureCredentialsForm: FunctionComponent = (props: Props) => Default Subscription
-
-
-
-
- - Directory (tenant) ID - -
- -
-
-
-
-
- - Application (client) ID - -
- -
-
-
-
-
- - Client Secret - - -
-
-
- -
-
-
-
-
- - Default Subscription - -
- -
-
-
-
- - Directory (tenant) ID - -
- -
-
-
-
-
- - Application (client) ID - -
- -
-
-
-
-
- - Client Secret - -
- -
-
-
-
-
- - Default Subscription - -
- -
-
-
-
- - Directory (tenant) ID - -
- -
-
-
-
-
- - Application (client) ID - -
- -
-
-
-
-
- - Client Secret - -
- -
-
-
-
-
- - Default Subscription - -
-