mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
A11y: Fix fastpass issues for /org/ pages (#39902)
* A11y: Fix fastpass issues for /org/ pages See #39429
This commit is contained in:
parent
54afe20b44
commit
816d70a7e5
@ -80,7 +80,7 @@ export function Modal(props: PropsWithChildren<Props>) {
|
||||
{typeof title === 'string' && <DefaultModalHeader {...props} title={title} />}
|
||||
{typeof title !== 'string' && title}
|
||||
<div className={styles.modalHeaderClose}>
|
||||
<IconButton surface="header" name="times" size="xl" onClick={onDismiss} />
|
||||
<IconButton aria-label="Close dialogue" surface="header" name="times" size="xl" onClick={onDismiss} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={cx(styles.modalContent, contentClassName)}>{children}</div>
|
||||
|
@ -1,11 +1,15 @@
|
||||
import React, { ChangeEvent, FC, FormEvent, useEffect, useState } from 'react';
|
||||
import { EventsWithValidation, InlineFormLabel, LegacyForms, ValidationEvents, Button } from '@grafana/ui';
|
||||
import { EventsWithValidation, LegacyForms, ValidationEvents, Button, Select, InlineField } from '@grafana/ui';
|
||||
import { NewApiKey, OrgRole } from '../../types';
|
||||
import { rangeUtil } from '@grafana/data';
|
||||
import { rangeUtil, SelectableValue } from '@grafana/data';
|
||||
import { SlideDown } from '../../core/components/Animations/SlideDown';
|
||||
import { CloseButton } from 'app/core/components/CloseButton/CloseButton';
|
||||
|
||||
const { Input } = LegacyForms;
|
||||
const ROLE_OPTIONS: Array<SelectableValue<OrgRole>> = Object.keys(OrgRole).map((role) => ({
|
||||
label: role,
|
||||
value: role as OrgRole,
|
||||
}));
|
||||
|
||||
interface Props {
|
||||
show: boolean;
|
||||
@ -56,8 +60,8 @@ export const ApiKeysForm: FC<Props> = ({ show, onClose, onKeyAdded }) => {
|
||||
const onNameChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
setName(event.currentTarget.value);
|
||||
};
|
||||
const onRoleChange = (event: ChangeEvent<HTMLSelectElement>) => {
|
||||
setRole(event.currentTarget.value as OrgRole);
|
||||
const onRoleChange = (role: SelectableValue<OrgRole>) => {
|
||||
setRole(role.value!);
|
||||
};
|
||||
const onSecondsToLiveChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
setSecondsToLive(event.currentTarget.value);
|
||||
@ -75,29 +79,27 @@ export const ApiKeysForm: FC<Props> = ({ show, onClose, onKeyAdded }) => {
|
||||
<Input type="text" className="gf-form-input" value={name} placeholder="Name" onChange={onNameChange} />
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<span className="gf-form-label">Role</span>
|
||||
<span className="gf-form-select-wrapper">
|
||||
<select className="gf-form-input gf-size-auto" value={role} onChange={onRoleChange}>
|
||||
{Object.keys(OrgRole).map((role) => {
|
||||
return (
|
||||
<option key={role} label={role} value={role}>
|
||||
{role}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</span>
|
||||
<InlineField label="Role">
|
||||
<Select
|
||||
inputId="role-select"
|
||||
value={role}
|
||||
onChange={onRoleChange}
|
||||
options={ROLE_OPTIONS}
|
||||
menuShouldPortal
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
<div className="gf-form max-width-21">
|
||||
<InlineFormLabel tooltip={tooltipText}>Time to live</InlineFormLabel>
|
||||
<Input
|
||||
type="text"
|
||||
className="gf-form-input"
|
||||
placeholder="1d"
|
||||
validationEvents={timeRangeValidationEvents}
|
||||
value={secondsToLive}
|
||||
onChange={onSecondsToLiveChange}
|
||||
/>
|
||||
<InlineField tooltip={tooltipText} label="Time to live">
|
||||
<Input
|
||||
id="time-to-live-input"
|
||||
type="text"
|
||||
placeholder="1d"
|
||||
validationEvents={timeRangeValidationEvents}
|
||||
value={secondsToLive}
|
||||
onChange={onSecondsToLiveChange}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<Button>Add</Button>
|
||||
|
@ -124,8 +124,8 @@ describe('ApiKeysPage', () => {
|
||||
deleteApiKeyMock.mockClear();
|
||||
expect(within(firstRow).getByRole('cell', { name: /cancel delete/i })).toBeInTheDocument();
|
||||
userEvent.click(within(firstRow).getByRole('cell', { name: /cancel delete/i }));
|
||||
expect(within(firstRow).getByRole('button', { name: /delete/i })).toBeInTheDocument();
|
||||
userEvent.click(within(firstRow).getByRole('button', { name: /delete/i }));
|
||||
expect(within(firstRow).getByRole('button', { name: /delete$/i })).toBeInTheDocument();
|
||||
userEvent.click(within(firstRow).getByRole('button', { name: /delete$/i }));
|
||||
expect(deleteApiKeyMock).toHaveBeenCalledTimes(1);
|
||||
expect(deleteApiKeyMock).toHaveBeenCalledWith(1, false);
|
||||
|
||||
@ -134,8 +134,8 @@ describe('ApiKeysPage', () => {
|
||||
deleteApiKeyMock.mockClear();
|
||||
expect(within(secondRow).getByRole('cell', { name: /cancel delete/i })).toBeInTheDocument();
|
||||
userEvent.click(within(secondRow).getByRole('cell', { name: /cancel delete/i }));
|
||||
expect(within(secondRow).getByRole('button', { name: /delete/i })).toBeInTheDocument();
|
||||
userEvent.click(within(secondRow).getByRole('button', { name: /delete/i }));
|
||||
expect(within(secondRow).getByRole('button', { name: /delete$/i })).toBeInTheDocument();
|
||||
userEvent.click(within(secondRow).getByRole('button', { name: /delete$/i }));
|
||||
expect(deleteApiKeyMock).toHaveBeenCalledTimes(1);
|
||||
expect(deleteApiKeyMock).toHaveBeenCalledWith(2, true);
|
||||
});
|
||||
@ -194,7 +194,6 @@ function toggleShowExpired() {
|
||||
async function addAndVerifyApiKey(addApiKeyMock: jest.Mock, includeExpired: boolean) {
|
||||
expect(screen.getByRole('heading', { name: /add api key/i })).toBeInTheDocument();
|
||||
expect(screen.getByPlaceholderText(/name/i)).toBeInTheDocument();
|
||||
expect(screen.getByRole('combobox')).toBeInTheDocument();
|
||||
expect(screen.getByPlaceholderText(/1d/i)).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /^add$/i })).toBeInTheDocument();
|
||||
|
||||
|
@ -30,7 +30,7 @@ export const ApiKeysTable: FC<Props> = ({ apiKeys, timeZone, onDelete }) => {
|
||||
<td>{key.role}</td>
|
||||
<td>{formatDate(key.expiration, timeZone)}</td>
|
||||
<td>
|
||||
<DeleteButton size="sm" onConfirm={() => onDelete(key)} />
|
||||
<DeleteButton aria-label="Delete API key" size="sm" onConfirm={() => onDelete(key)} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
@ -16,7 +16,7 @@ const OrgProfile: FC<Props> = ({ onSubmit, orgName }) => {
|
||||
{({ register }) => (
|
||||
<FieldSet label="Organization profile">
|
||||
<Field label="Organization name">
|
||||
<Input type="text" {...register('orgName', { required: true })} />
|
||||
<Input id="org-name-input" type="text" {...register('orgName', { required: true })} />
|
||||
</Field>
|
||||
|
||||
<Button type="submit">Update organization name</Button>
|
||||
|
@ -72,7 +72,7 @@ export const UserInviteForm: FC<Props> = ({}) => {
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Send invite email">
|
||||
<Switch {...register('sendEmail')} />
|
||||
<Switch id="send-email-switch" {...register('sendEmail')} />
|
||||
</Field>
|
||||
<HorizontalGroup>
|
||||
<Button type="submit">Submit</Button>
|
||||
|
Loading…
Reference in New Issue
Block a user