fix: [Connectors > Create a Connector][SCREEN READER] Form elements must announce their accessible labels (#192450)

Closes: https://github.com/elastic/search-team/issues/8216

## Description
The Choose a name for your connector view has a custom `EuiSuperSelect`
dropdown that does not announce the label text "Connector type" when it
takes keyboard focus. This is especially problematic to screen reader
users because they may not know what the purpose of this dropdown is
without further effort, or at all.

## What was changed?: 

Here's the corrected version of the text with grammar improvements:

## What was changed?

1. The `EditConnector` and `EditDescription` components were updated to
utilize `EuiFormRow`. Using `EuiFormRow` helps resolve the a11y issue
mentioned in the description.
2. The `EditDescription` component was slightly refactored to make it
more EUI-friendly:
- The edit button was aligned to the same level as the description
title.
- To provide users with more context, the Save/Cancel buttons were moved
into `EuiFormRow`. These elements are now associated with the edited
component.
3. `EuiForm` was removed from the `EditConnector` and `EditDescription`
components and added to the parent component, `EditConnector`.

## Screen: 


https://github.com/user-attachments/assets/57392f4c-7c21-45ee-ae3a-eb9a64ec80fd
This commit is contained in:
Alexey Antonov 2024-10-01 15:58:53 +03:00 committed by GitHub
parent a5caba8839
commit 1ba78275f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 59 additions and 57 deletions

View file

@ -12,6 +12,7 @@ import {
EuiFlexItem,
EuiPageTemplate,
EuiPanel,
EuiForm,
EuiPopover,
EuiSpacer,
EuiText,
@ -158,9 +159,11 @@ export const EditConnector: React.FC = () => {
<EuiPageTemplate.Section>
<EuiFlexGroup direction="row">
<EuiFlexItem grow={1}>
<EditServiceType connector={connector} />
<EuiSpacer />
<EditDescription connector={connector} />
<EuiForm>
<EditServiceType connector={connector} />
<EuiSpacer />
<EditDescription connector={connector} />
</EuiForm>
</EuiFlexItem>
<EuiFlexItem grow={2}>
<EuiPanel hasBorder hasShadow={false}>

View file

@ -12,12 +12,11 @@ import {
EuiFlexItem,
EuiFlexGroup,
EuiFieldText,
EuiForm,
EuiButton,
EuiSpacer,
EuiFormRow,
EuiText,
EuiButtonEmpty,
EuiLink,
} from '@elastic/eui';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Connector } from '@kbn/search-connectors';
@ -56,43 +55,48 @@ export const EditDescription: React.FC<EditDescriptionProps> = ({ connector }) =
});
return (
<EuiFlexGroup direction="row">
<EuiForm>
<EuiFlexItem grow={false}>
<EuiFormRow
helpText={i18n.translate('xpack.serverlessSearch.connectors.descriptionHelpText', {
defaultMessage: 'Optional description for your connector.',
})}
label={i18n.translate('xpack.serverlessSearch.connectors.descriptionLabel', {
defaultMessage: 'Description',
})}
labelAppend={
<EuiButtonEmpty
data-test-subj="serverlessSearchEditDescriptionButton"
size="xs"
onClick={() => setIsEditing(true)}
>
{EDIT_LABEL}
</EuiButtonEmpty>
}
<EuiFormRow
helpText={
!isEditing &&
i18n.translate('xpack.serverlessSearch.connectors.descriptionHelpText', {
defaultMessage: 'Optional description for your connector.',
})
}
label={i18n.translate('xpack.serverlessSearch.connectors.descriptionLabel', {
defaultMessage: 'Description',
})}
labelAppend={
<EuiText size="xs">
<EuiLink
data-test-subj="serverlessSearchEditDescriptionButton"
onClick={() => setIsEditing(true)}
role="button"
>
{isEditing ? (
<EuiFieldText
data-test-subj="serverlessSearchEditDescriptionFieldText"
onChange={(event) => setNewDescription(event.target.value)}
value={newDescription || ''}
/>
) : (
<EuiText size="s" data-test-subj="serverlessSearchConnectorDescription">
{connector.description}
</EuiText>
)}
</EuiFormRow>
{EDIT_LABEL}
</EuiLink>
</EuiText>
}
fullWidth
>
<EuiFlexGroup direction="column" gutterSize="xs">
<EuiFlexItem>
{isEditing ? (
<EuiFieldText
data-test-subj="serverlessSearchEditDescriptionFieldText"
onChange={(event) => setNewDescription(event.target.value)}
value={newDescription || ''}
fullWidth
/>
) : (
<EuiText size="s" data-test-subj="serverlessSearchConnectorDescription">
{connector.description}
</EuiText>
)}
</EuiFlexItem>
{isEditing && (
<>
<EuiFlexItem>
<EuiSpacer size="s" />
<EuiFlexGroup direction="row" justifyContent="center" alignItems="center">
<EuiFlexGroup direction="row" justifyContent="flexStart" gutterSize="s">
<EuiFlexItem
grow={false}
css={css`
@ -130,9 +134,9 @@ export const EditDescription: React.FC<EditDescriptionProps> = ({ connector }) =
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</>
</EuiFlexItem>
)}
</EuiForm>
</EuiFlexGroup>
</EuiFlexGroup>
</EuiFormRow>
);
};

View file

@ -7,14 +7,7 @@
import { i18n } from '@kbn/i18n';
import React from 'react';
import {
EuiFlexItem,
EuiFlexGroup,
EuiForm,
EuiFormLabel,
EuiIcon,
EuiSuperSelect,
} from '@elastic/eui';
import { EuiFlexItem, EuiFlexGroup, EuiIcon, EuiFormRow, EuiSuperSelect } from '@elastic/eui';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Connector } from '@kbn/search-connectors';
import { useKibanaServices } from '../../hooks/use_kibana';
@ -69,12 +62,13 @@ export const EditServiceType: React.FC<EditServiceTypeProps> = ({ connector }) =
});
return (
<EuiForm>
<EuiFormLabel data-test-subj="serverlessSearchEditConnectorTypeLabel">
{i18n.translate('xpack.serverlessSearch.connectors.serviceTypeLabel', {
defaultMessage: 'Connector type',
})}
</EuiFormLabel>
<EuiFormRow
label={i18n.translate('xpack.serverlessSearch.connectors.serviceTypeLabel', {
defaultMessage: 'Connector type',
})}
data-test-subj="serverlessSearchEditConnectorType"
fullWidth
>
<EuiSuperSelect
// We only want to allow people to set the service type once to avoid weird conflicts
disabled={Boolean(connector.service_type)}
@ -83,7 +77,8 @@ export const EditServiceType: React.FC<EditServiceTypeProps> = ({ connector }) =
onChange={(event) => mutate(event)}
options={options}
valueOfSelected={connector.service_type || undefined}
fullWidth
/>
</EuiForm>
</EuiFormRow>
);
};

View file

@ -50,7 +50,7 @@ export function SvlSearchConnectorsPageProvider({ getService }: FtrProviderConte
expect(await testSubjects.getVisibleText('serverlessSearchConnectorName')).to.be(name);
},
async editType(type: string) {
await testSubjects.existOrFail('serverlessSearchEditConnectorTypeLabel');
await testSubjects.existOrFail('serverlessSearchEditConnectorType');
await testSubjects.existOrFail('serverlessSearchEditConnectorTypeChoices');
await testSubjects.click('serverlessSearchEditConnectorTypeChoices');
await testSubjects.exists('serverlessSearchConnectorServiceType-zoom');