Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add accessible descriptions to the dropdowns #7112

Merged
merged 2 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,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) => {
Expand Down Expand Up @@ -207,6 +226,7 @@ export const NewProjectForm: React.FC<FormProps> = ({

<OptionButtons>
<MultiselectList
description={selectionButtonData.environments.text}
selectedOptions={projectEnvironments}
options={activeEnvironments.map((env) => ({
label: env.name,
Expand All @@ -225,15 +245,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,
Expand All @@ -250,10 +268,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}
/>
Expand All @@ -262,6 +277,7 @@ export const NewProjectForm: React.FC<FormProps> = ({
condition={isEnterprise()}
show={
<SingleSelectList
description={selectionButtonData.mode.text}
options={projectModeOptions}
onChange={(value: any) => {
setProjectMode(value);
Expand All @@ -275,10 +291,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}
/>
Expand All @@ -288,6 +301,9 @@ export const NewProjectForm: React.FC<FormProps> = ({
condition={isEnterprise()}
show={
<TableSelect
description={
selectionButtonData.changeRequests.text
}
disabled={projectEnvironments.size === 0}
activeEnvironments={activeEnvironments
.filter((env) =>
Expand Down Expand Up @@ -321,10 +337,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}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,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 }) => ({
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -8,6 +9,7 @@ import {
StyledPopover,
StyledDropdownSearch,
TableSearchInput,
HiddenDescription,
} from './SelectionButton.styles';
import { ChangeRequestTable } from './ChangeRequestTable';

Expand Down Expand Up @@ -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> = ({
Expand All @@ -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 [recentlyClosed, setRecentlyClosed] = useState(false);

const open = () => {
Expand Down Expand Up @@ -156,7 +161,10 @@ const CombinedSelect: FC<CombinedSelectProps> = ({
horizontal: 'left',
}}
>
<StyledDropdown>
<HiddenDescription id={descriptionId}>
{description}
</HiddenDescription>
<StyledDropdown aria-describedby={descriptionId}>
<StyledDropdownSearch
variant='outlined'
size='small'
Expand Down Expand Up @@ -186,6 +194,7 @@ const CombinedSelect: FC<CombinedSelectProps> = ({

return (
<StyledListItem
aria-describedby={labelId}
key={option.value}
dense
disablePadding
Expand Down Expand Up @@ -234,7 +243,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;
Expand Down Expand Up @@ -270,7 +279,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) => {
Expand All @@ -279,7 +294,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;
Expand Down
Loading