Git Sync UI a11y finding fixes (#112751)
* ProgressBar: progressbar nodes must have accsible name fix * BrowseActions: Bulk move and delete drawwer a11y fix * FolderActionsButton: Move and delete drawer a11y fix * ConfigForm: a11y fix missing id * GettingStarted: Skip img alt since its decorative * JobContent: heading a11y fix * StatusBadge: add displayOnly prop to avoid cursor pointer display when its not necessary * RepositoryTypeCards: Card missing discernible text * i18n * input id fix
This commit is contained in:
@@ -2,7 +2,7 @@ import { useState } from 'react';
|
||||
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { config, reportInteraction } from '@grafana/runtime';
|
||||
import { Button, Drawer, Stack } from '@grafana/ui';
|
||||
import { Button, Drawer, Stack, Text } from '@grafana/ui';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { ManagerKind } from 'app/features/apiserver/types';
|
||||
import { BulkDeleteProvisionedResource } from 'app/features/provisioning/components/BulkActions/BulkDeleteProvisionedResource';
|
||||
@@ -152,7 +152,12 @@ export function BrowseActions({ folderDTO }: Props) {
|
||||
{/* bulk delete */}
|
||||
{showBulkDeleteProvisionedResource && (
|
||||
<Drawer
|
||||
title={t('browse-dashboards.action.bulk-delete-provisioned-resources', 'Bulk Delete Provisioned Resources')}
|
||||
title={
|
||||
// Heading levels should only increase by one (a11y)
|
||||
<Text variant="h3" element="h2">
|
||||
{t('browse-dashboards.action.bulk-delete-provisioned-resources', 'Bulk Delete Provisioned Resources')}
|
||||
</Text>
|
||||
}
|
||||
onClose={() => setShowBulkDeleteProvisionedResource(false)}
|
||||
size="md"
|
||||
>
|
||||
@@ -169,7 +174,12 @@ export function BrowseActions({ folderDTO }: Props) {
|
||||
{/* bulk move */}
|
||||
{showBulkMoveProvisionedResource && (
|
||||
<Drawer
|
||||
title={t('browse-dashboards.action.bulk-move-provisioned-resources', 'Bulk Move Provisioned Resources')}
|
||||
title={
|
||||
// Heading levels should only increase by one (a11y)
|
||||
<Text variant="h3" element="h2">
|
||||
{t('browse-dashboards.action.bulk-move-provisioned-resources', 'Bulk Move Provisioned Resources')}
|
||||
</Text>
|
||||
}
|
||||
onClose={() => setShowBulkMoveProvisionedResource(false)}
|
||||
size="md"
|
||||
>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useState } from 'react';
|
||||
import { AppEvents } from '@grafana/data';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { locationService, reportInteraction } from '@grafana/runtime';
|
||||
import { Button, Drawer, Dropdown, Icon, Menu, MenuItem } from '@grafana/ui';
|
||||
import { Button, Drawer, Dropdown, Icon, Menu, MenuItem, Text } from '@grafana/ui';
|
||||
import { Permissions } from 'app/core/components/AccessControl/Permissions';
|
||||
import { appEvents } from 'app/core/core';
|
||||
import { RepoType } from 'app/features/provisioning/Wizard/types';
|
||||
@@ -180,7 +180,11 @@ export function FolderActionsButton({ folder, repoType, isReadOnlyRepo }: Props)
|
||||
)}
|
||||
{showDeleteProvisionedFolderDrawer && (
|
||||
<Drawer
|
||||
title={t('browse-dashboards.action.delete-provisioned-folder', 'Delete provisioned folder')}
|
||||
title={
|
||||
<Text variant="h3" element="h2">
|
||||
{t('browse-dashboards.action.delete-provisioned-folder', 'Delete provisioned folder')}
|
||||
</Text>
|
||||
}
|
||||
subtitle={folder.title}
|
||||
onClose={() => setShowDeleteProvisionedFolderDrawer(false)}
|
||||
>
|
||||
@@ -192,7 +196,11 @@ export function FolderActionsButton({ folder, repoType, isReadOnlyRepo }: Props)
|
||||
)}
|
||||
{showMoveProvisionedFolderDrawer && (
|
||||
<Drawer
|
||||
title={t('browse-dashboards.action.move-provisioned-folder', 'Move provisioned folder')}
|
||||
title={
|
||||
<Text variant="h3" element="h2">
|
||||
{t('browse-dashboards.action.move-provisioned-folder', 'Move provisioned folder')}
|
||||
</Text>
|
||||
}
|
||||
subtitle={folder.title}
|
||||
onClose={() => setShowMoveProvisionedFolderDrawer(false)}
|
||||
>
|
||||
|
||||
@@ -162,7 +162,7 @@ export function ConfigForm({ data }: ConfigFormProps) {
|
||||
<FormPrompt onDiscard={reset} confirmRedirect={isDirty} />
|
||||
<Stack direction="column" gap={2}>
|
||||
<Field noMargin label={t('provisioning.config-form.label-repository-type', 'Repository type')}>
|
||||
<Input value={getRepositoryTypeConfig(type)?.label || type} disabled />
|
||||
<Input id="repository-type" value={getRepositoryTypeConfig(type)?.label || type} disabled />
|
||||
</Field>
|
||||
<Field
|
||||
noMargin
|
||||
@@ -274,7 +274,7 @@ export function ConfigForm({ data }: ConfigFormProps) {
|
||||
/>
|
||||
</Field>
|
||||
<Field noMargin label={gitFields.pathConfig.label} description={gitFields.pathConfig.description}>
|
||||
<Input {...register('path')} />
|
||||
<Input id="repository-path" {...register('path')} />
|
||||
</Field>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -153,7 +153,8 @@ export default function GettingStarted({ items }: Props) {
|
||||
<Stack direction="column" gap={6} wrap="wrap">
|
||||
<Stack gap={10} alignItems="center">
|
||||
<div className={styles.imageContainer}>
|
||||
<img src={provisioningSvg} className={styles.image} alt={'Grafana provisioning'} />
|
||||
{/* decorative img, use empty str to skip alt*/}
|
||||
<img src={provisioningSvg} className={styles.image} alt="" />
|
||||
</div>
|
||||
<FeaturesList
|
||||
hasRequiredFeatures={hasRequiredFeatures}
|
||||
|
||||
@@ -79,7 +79,7 @@ export function JobContent({ jobType, job, isFinishedJob = false, onStatusChange
|
||||
<Stack direction="column" alignItems="center">
|
||||
<Stack direction="row" alignItems="center" justifyContent="center" gap={2}>
|
||||
<Spinner size={24} />
|
||||
<Text element="h5" color="secondary">
|
||||
<Text element="h3" variant="h5" color="secondary">
|
||||
{message ?? state ?? t('provisioning.job-status.starting', 'Starting...')}
|
||||
</Text>
|
||||
</Stack>
|
||||
|
||||
@@ -27,7 +27,7 @@ export function RepositoryActions({ repository }: RepositoryActionsProps) {
|
||||
return (
|
||||
<Stack wrap="wrap">
|
||||
{isReadOnlyRepo && <Badge color="darkgrey" text={t('folder-repo.read-only-badge', 'Read only')} />}
|
||||
<StatusBadge repo={repository} />
|
||||
<StatusBadge repo={repository} displayOnly />
|
||||
{repoHref && (
|
||||
<Button variant="secondary" icon={providerIcon} onClick={() => window.open(repoHref, '_blank')}>
|
||||
<Trans i18nKey="provisioning.repository-actions.source-code">Source code</Trans>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { css } from '@emotion/css';
|
||||
import { useRef, useEffect } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
|
||||
interface ProgressBarProps {
|
||||
@@ -24,7 +25,14 @@ const ProgressBar = ({ progress, topBottomSpacing }: ProgressBarProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div
|
||||
className={styles.container}
|
||||
aria-label={t('provisioning.shared.progress-bar.aria-label', 'Progress Bar')}
|
||||
role="progressbar"
|
||||
aria-valuenow={progress}
|
||||
aria-valuemin={0}
|
||||
aria-valuemax={100}
|
||||
>
|
||||
<div className={shouldAnimate ? styles.fillerAnimated : styles.filler} style={{ width: `${progress}%` }} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -27,15 +27,17 @@ export function RepositoryTypeCards() {
|
||||
<Stack direction="row" gap={1} wrap>
|
||||
{gitProviders.map((config) => (
|
||||
<Card key={config.type} href={`${CONNECT_URL}/${config.type}`} className={styles.card} noMargin>
|
||||
<Stack gap={2} alignItems="center">
|
||||
<RepoIcon type={config.type} />
|
||||
<Trans
|
||||
i18nKey="provisioning.repository-type-cards.configure-with-provider"
|
||||
values={{ provider: config.label }}
|
||||
>
|
||||
Configure with {'{{ provider }}'}
|
||||
</Trans>
|
||||
</Stack>
|
||||
<Card.Heading>
|
||||
<Stack gap={2} alignItems="center">
|
||||
<RepoIcon type={config.type} />
|
||||
<Trans
|
||||
i18nKey="provisioning.repository-type-cards.configure-with-provider"
|
||||
values={{ provider: config.label }}
|
||||
>
|
||||
Configure with {'{{ provider }}'}
|
||||
</Trans>
|
||||
</Stack>
|
||||
</Card.Heading>
|
||||
</Card>
|
||||
))}
|
||||
</Stack>
|
||||
@@ -53,21 +55,23 @@ export function RepositoryTypeCards() {
|
||||
<Stack direction="row" gap={1} wrap>
|
||||
{otherProviders.map((config) => (
|
||||
<Card key={config.type} href={`${CONNECT_URL}/${config.type}`} className={styles.card} noMargin>
|
||||
<Stack gap={2} alignItems="center">
|
||||
<RepoIcon type={config.type} />
|
||||
{config.type === 'local' ? (
|
||||
<Trans i18nKey="provisioning.repository-type-cards.configure-file">
|
||||
Configure file provisioning
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans
|
||||
i18nKey="provisioning.repository-type-cards.configure-with-provider"
|
||||
values={{ provider: config.label }}
|
||||
>
|
||||
Configure with {'{{ provider }}'}
|
||||
</Trans>
|
||||
)}
|
||||
</Stack>
|
||||
<Card.Heading>
|
||||
<Stack gap={2} alignItems="center">
|
||||
<RepoIcon type={config.type} />
|
||||
{config.type === 'local' ? (
|
||||
<Trans i18nKey="provisioning.repository-type-cards.configure-file">
|
||||
Configure file provisioning
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans
|
||||
i18nKey="provisioning.repository-type-cards.configure-with-provider"
|
||||
values={{ provider: config.label }}
|
||||
>
|
||||
Configure with {'{{ provider }}'}
|
||||
</Trans>
|
||||
)}
|
||||
</Stack>
|
||||
</Card.Heading>
|
||||
</Card>
|
||||
))}
|
||||
</Stack>
|
||||
|
||||
@@ -7,9 +7,13 @@ import { PROVISIONING_URL } from '../constants';
|
||||
|
||||
interface StatusBadgeProps {
|
||||
repo?: Repository;
|
||||
displayOnly?: boolean; // if true, disable click action and cursor will be default
|
||||
}
|
||||
|
||||
export function StatusBadge({ repo }: StatusBadgeProps) {
|
||||
/**
|
||||
* @description Displays a status badge for the given provisioned repository.
|
||||
*/
|
||||
export function StatusBadge({ repo, displayOnly = false }: StatusBadgeProps) {
|
||||
if (!repo) {
|
||||
return null;
|
||||
}
|
||||
@@ -86,10 +90,12 @@ export function StatusBadge({ repo }: StatusBadgeProps) {
|
||||
color={color}
|
||||
icon={icon}
|
||||
text={text}
|
||||
style={{ cursor: 'pointer' }}
|
||||
style={{ cursor: displayOnly ? 'default' : 'pointer' }}
|
||||
tooltip={tooltip}
|
||||
onClick={() => {
|
||||
locationService.push(`${PROVISIONING_URL}/${repo.metadata?.name}/?tab=overview`);
|
||||
if (!displayOnly) {
|
||||
locationService.push(`${PROVISIONING_URL}/${repo.metadata?.name}/?tab=overview`);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -11810,6 +11810,11 @@
|
||||
"next": "Next",
|
||||
"previous": "Previous"
|
||||
},
|
||||
"shared": {
|
||||
"progress-bar": {
|
||||
"aria-label": "Progress Bar"
|
||||
}
|
||||
},
|
||||
"sidebar-item": {
|
||||
"label-completed-step": "Completed step",
|
||||
"label-current-step": "Current step",
|
||||
|
||||
Reference in New Issue
Block a user