1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-08-13 13:48:59 +02:00

fix: add accessible descriptions to the dropdowns

This commit is contained in:
Thomas Heartman 2024-05-22 13:22:37 +02:00
parent 9bebd88707
commit 4b238ca39e
No known key found for this signature in database
GPG Key ID: BD1F880DAED1EE78
3 changed files with 55 additions and 20 deletions

View File

@ -161,6 +161,25 @@ export const NewProjectForm: React.FC<FormProps> = ({
const stickinessOptions = useStickinessOptions(projectStickiness);
const selectionButtonData = {
environments: {
icon: <EnvironmentsIcon />,
text: `Each feature flag can have a separate configuration per environment. This setting configures which environments your project should start with.`,
},
stickiness: {
icon: <StickinessIcon />,
text: 'Stickiness is used to guarantee that your users see the same result when using a gradual rollout. Default stickiness allows you to choose which field is used by default in this project.',
},
mode: {
icon: <ProjectModeIcon />,
text: 'Mode defines who should be allowed to interact and see your project. Private mode hides the project from anyone except the project owner and members.',
},
changeRequests: {
icon: <ChangeRequestIcon />,
text: 'Change requests can be configured per environment and require changes to go through an approval process before being applied.',
},
};
return (
<StyledForm
onSubmit={(submitEvent) => {
@ -200,6 +219,7 @@ export const NewProjectForm: React.FC<FormProps> = ({
<OptionButtons>
<MultiselectList
description={selectionButtonData.environments.text}
selectedOptions={projectEnvironments}
options={activeEnvironments.map((env) => ({
label: env.name,
@ -218,15 +238,13 @@ export const NewProjectForm: React.FC<FormProps> = ({
placeholder: 'Select project environments',
}}
onOpen={() =>
overrideDocumentation({
icon: <EnvironmentsIcon />,
text: `Each feature flag can have a separate configuration per environment. This setting configures which environments your project should start with.`,
})
overrideDocumentation(selectionButtonData.environments)
}
onClose={clearDocumentationOverride}
/>
<SingleSelectList
description={selectionButtonData.stickiness.text}
options={stickinessOptions.map(({ key, ...rest }) => ({
value: key,
...rest,
@ -243,10 +261,7 @@ export const NewProjectForm: React.FC<FormProps> = ({
placeholder: 'Select default stickiness',
}}
onOpen={() =>
overrideDocumentation({
icon: <StickinessIcon />,
text: 'Stickiness is used to guarantee that your users see the same result when using a gradual rollout. Default stickiness allows you to choose which field is used by default in this project.',
})
overrideDocumentation(selectionButtonData.stickiness)
}
onClose={clearDocumentationOverride}
/>
@ -255,6 +270,7 @@ export const NewProjectForm: React.FC<FormProps> = ({
condition={isEnterprise()}
show={
<SingleSelectList
description={selectionButtonData.mode.text}
options={projectModeOptions}
onChange={(value: any) => {
setProjectMode(value);
@ -268,10 +284,7 @@ export const NewProjectForm: React.FC<FormProps> = ({
placeholder: 'Select project mode',
}}
onOpen={() =>
overrideDocumentation({
icon: <ProjectModeIcon />,
text: 'Mode defines who should be allowed to interact and see your project. Private mode hides the project from anyone except the project owner and members.',
})
overrideDocumentation(selectionButtonData.mode)
}
onClose={clearDocumentationOverride}
/>
@ -281,6 +294,9 @@ export const NewProjectForm: React.FC<FormProps> = ({
condition={isEnterprise()}
show={
<TableSelect
description={
selectionButtonData.changeRequests.text
}
disabled={projectEnvironments.size === 0}
activeEnvironments={activeEnvironments
.filter((env) =>
@ -314,10 +330,9 @@ export const NewProjectForm: React.FC<FormProps> = ({
projectChangeRequestConfiguration
}
onOpen={() =>
overrideDocumentation({
icon: <ChangeRequestIcon />,
text: 'Change requests can be configured per environment and require changes to go through an approval process before being applied.',
})
overrideDocumentation(
selectionButtonData.changeRequests,
)
}
onClose={clearDocumentationOverride}
/>

View File

@ -39,6 +39,11 @@ const visuallyHiddenStyles = {
whiteSpace: 'nowrap',
};
export const HiddenDescription = styled('p')(() => ({
...visuallyHiddenStyles,
position: 'absolute',
}));
export const StyledDropdownSearch = styled(TextField, {
shouldForwardProp: (prop) => prop !== 'hideLabel',
})<{ hideLabel?: boolean }>(({ theme, hideLabel }) => ({

View File

@ -1,4 +1,5 @@
import Search from '@mui/icons-material/Search';
import { v4 as uuidv4 } from 'uuid';
import { Box, Button, InputAdornment, List, ListItemText } from '@mui/material';
import { type FC, type ReactNode, useRef, useState, useMemo } from 'react';
import {
@ -8,6 +9,7 @@ import {
StyledPopover,
StyledDropdownSearch,
TableSearchInput,
HiddenDescription,
} from './SelectionButton.styles';
import { ChangeRequestTable } from './ChangeRequestTable';
@ -81,6 +83,7 @@ type CombinedSelectProps = {
multiselect?: { selectedOptions: Set<string> };
onOpen?: () => void;
onClose?: () => void;
description: string; // visually hidden, for assistive tech
};
const CombinedSelect: FC<CombinedSelectProps> = ({
@ -91,10 +94,12 @@ const CombinedSelect: FC<CombinedSelectProps> = ({
multiselect,
onOpen = () => {},
onClose = () => {},
description,
}) => {
const ref = useRef<HTMLDivElement>(null);
const [anchorEl, setAnchorEl] = useState<HTMLDivElement | null>();
const [searchText, setSearchText] = useState('');
const descriptionId = uuidv4();
const open = () => {
setSearchText('');
@ -151,7 +156,10 @@ const CombinedSelect: FC<CombinedSelectProps> = ({
horizontal: 'left',
}}
>
<StyledDropdown>
<HiddenDescription id={descriptionId}>
{description}
</HiddenDescription>
<StyledDropdown aria-describedby={descriptionId}>
<StyledDropdownSearch
variant='outlined'
size='small'
@ -181,6 +189,7 @@ const CombinedSelect: FC<CombinedSelectProps> = ({
return (
<StyledListItem
aria-describedby={labelId}
key={option.value}
dense
disablePadding
@ -229,7 +238,7 @@ const CombinedSelect: FC<CombinedSelectProps> = ({
type MultiselectListProps = Pick<
CombinedSelectProps,
'options' | 'button' | 'search' | 'onOpen' | 'onClose'
'options' | 'button' | 'search' | 'onOpen' | 'onClose' | 'description'
> & {
selectedOptions: Set<string>;
onChange: (values: Set<string>) => void;
@ -265,7 +274,13 @@ export const MultiselectList: FC<MultiselectListProps> = ({
type SingleSelectListProps = Pick<
CombinedSelectProps,
'options' | 'button' | 'search' | 'onChange' | 'onOpen' | 'onClose'
| 'options'
| 'button'
| 'search'
| 'onChange'
| 'onOpen'
| 'onClose'
| 'description'
>;
export const SingleSelectList: FC<SingleSelectListProps> = (props) => {
@ -274,7 +289,7 @@ export const SingleSelectList: FC<SingleSelectListProps> = (props) => {
type TableSelectProps = Pick<
CombinedSelectProps,
'button' | 'search' | 'onOpen' | 'onClose'
'button' | 'search' | 'onOpen' | 'onClose' | 'description'
> & {
updateProjectChangeRequestConfiguration: {
disableChangeRequests: (env: string) => void;