mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	fix(a11y): Fix keyboard navigation issues with sidebar (#8769)
This PR fixes a number of keyboard accessibility issues with the feedback sidebar. They are (in no particular order): 1. The radio inputs don't have a focus style for `focus-visible` (when keyboard focused). 2. There's two close buttons there for some reason? One is invisible, but you can tab to it? 3. The sidebar doesn't trap focus, so you can tab out of the modal and continue tabbing through the main page (with the modal still open) 4. The sidebar doesn't steal focus. When you open it, your focus remains on the button you used to open it. So if you want to navigate to it, you have to go through the entire page (behind the modal) to get to it. 5. The sidebar can't be closed by 'escape'. The fixes are: 1. Apply the same styles when focus visible as when hover 2. Wrap the component in the `BaseModal` component 3. Wrap the component in the `BaseModal` component 4. Wrap the component in the `BaseModal` component 5. Wrap the component in the `BaseModal` component (see a theme here?) Additionally, because the base modal has its own `open` state, I removed the wrapping conditionally render, reducing nesting by one stop. Most of the changes in the file are just whitespace changes.  I considered also applying an auto-focus to the first input in the sidebar, but our linter doesn't like it. Additionally MDN lists the following [accessibility concerns](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus#accessibility_concerns) > Automatically focusing a form control can confuse visually-impaired people using screen-reading technology and people with cognitive impairments. When autofocus is assigned, screen-readers "teleport" their user to the form control without warning them beforehand. > > Use careful consideration for accessibility when applying the autofocus attribute. Automatically focusing on a control can cause the page to scroll on load. The focus can also cause dynamic keyboards to display on some touch devices. While a screen reader will announce the label of the form control receiving focus, the screen reader will not announce anything before the label, and the sighted user on a small device will equally miss the context created by the preceding content. So I'll leave it off.
This commit is contained in:
		
							parent
							
								
									b4d19862d7
								
							
						
					
					
						commit
						046573174b
					
				| @ -22,6 +22,7 @@ import type { FeedbackData, FeedbackMode } from './FeedbackContext'; | ||||
| import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; | ||||
| import { useUiFlag } from 'hooks/useUiFlag'; | ||||
| import useUserType from './useUserType'; | ||||
| import { BaseModal } from 'component/common/SidebarModal/SidebarModal'; | ||||
| 
 | ||||
| export const ParentContainer = styled('div')(({ theme }) => ({ | ||||
|     position: 'relative', | ||||
| @ -150,7 +151,7 @@ const StyledScoreValue = styled('label')(({ theme }) => ({ | ||||
|         background: theme.palette.primary.main, | ||||
|         color: theme.palette.primary.contrastText, | ||||
|     }, | ||||
|     '& input:hover + span': { | ||||
|     '& input:is(:hover, :focus) + span': { | ||||
|         outline: '2px solid', | ||||
|         outlineOffset: 2, | ||||
|         outlineColor: theme.palette.primary.main, | ||||
| @ -278,192 +279,184 @@ export const FeedbackComponent = ({ | ||||
|     }; | ||||
| 
 | ||||
|     return ( | ||||
|         <ConditionallyRender | ||||
|             condition={showFeedback} | ||||
|             show={ | ||||
|                 <ParentContainer> | ||||
|                     <ClickAwayListener onClickAway={() => closeFeedback()}> | ||||
|                         <StyledContainer> | ||||
|                             <Tooltip title='Close' arrow> | ||||
|                                 <StyledCloseButton | ||||
|                                     onClick={closeFeedback} | ||||
|                                     size='large' | ||||
|                                 > | ||||
|                                     <CloseIcon /> | ||||
|                                 </StyledCloseButton> | ||||
|                             </Tooltip> | ||||
|                             <StyledContent> | ||||
|                                 <StyledTitle> | ||||
|                                     Help us improve Unleash | ||||
|                                 </StyledTitle> | ||||
|                                 <StyledForm onSubmit={onSubmission}> | ||||
|                                     <input | ||||
|                                         type='hidden' | ||||
|                                         name='category' | ||||
|                                         value={feedbackData.category} | ||||
|                                     /> | ||||
|                                     <input | ||||
|                                         type='hidden' | ||||
|                                         name='userType' | ||||
|                                         value={userType} | ||||
|                                     /> | ||||
|                                     <FormTitle>{feedbackData.title}</FormTitle> | ||||
|                                     <StyledScoreContainer> | ||||
|                                         <StyledScoreInput> | ||||
|                                             {[1, 2, 3, 4, 5, 6, 7].map( | ||||
|                                                 (score) => ( | ||||
|                                                     <StyledScoreValue | ||||
|                                                         key={score} | ||||
|                                                     > | ||||
|                                                         <input | ||||
|                                                             type='radio' | ||||
|                                                             name='difficultyScore' | ||||
|                                                             value={score} | ||||
|                                                             onChange={ | ||||
|                                                                 onScoreChange | ||||
|                                                             } | ||||
|                                                         /> | ||||
|                                                         <span>{score}</span> | ||||
|                                                     </StyledScoreValue> | ||||
|                                                 ), | ||||
|                                             )} | ||||
|                                         </StyledScoreInput> | ||||
|                                         <ScoreHelpContainer> | ||||
|                                             <StyledScoreHelp> | ||||
|                                                 Very difficult | ||||
|                                             </StyledScoreHelp> | ||||
|                                             <StyledScoreHelp> | ||||
|                                                 Very easy | ||||
|                                             </StyledScoreHelp> | ||||
|                                         </ScoreHelpContainer> | ||||
|                                     </StyledScoreContainer> | ||||
|         <BaseModal open={showFeedback} onClose={closeFeedback} label='Feedback'> | ||||
|             <ParentContainer> | ||||
|                 <ClickAwayListener onClickAway={() => closeFeedback()}> | ||||
|                     <StyledContainer> | ||||
|                         <Tooltip title='Close' arrow> | ||||
|                             <StyledCloseButton | ||||
|                                 onClick={closeFeedback} | ||||
|                                 size='large' | ||||
|                             > | ||||
|                                 <CloseIcon /> | ||||
|                             </StyledCloseButton> | ||||
|                         </Tooltip> | ||||
|                         <StyledContent> | ||||
|                             <StyledTitle>Help us improve Unleash</StyledTitle> | ||||
|                             <StyledForm onSubmit={onSubmission}> | ||||
|                                 <input | ||||
|                                     type='hidden' | ||||
|                                     name='category' | ||||
|                                     value={feedbackData.category} | ||||
|                                 /> | ||||
|                                 <input | ||||
|                                     type='hidden' | ||||
|                                     name='userType' | ||||
|                                     value={userType} | ||||
|                                 /> | ||||
|                                 <FormTitle>{feedbackData.title}</FormTitle> | ||||
|                                 <StyledScoreContainer> | ||||
|                                     <StyledScoreInput> | ||||
|                                         {[1, 2, 3, 4, 5, 6, 7].map((score) => ( | ||||
|                                             <StyledScoreValue key={score}> | ||||
|                                                 <input | ||||
|                                                     type='radio' | ||||
|                                                     name='difficultyScore' | ||||
|                                                     value={score} | ||||
|                                                     onChange={onScoreChange} | ||||
|                                                 /> | ||||
|                                                 <span>{score}</span> | ||||
|                                             </StyledScoreValue> | ||||
|                                         ))} | ||||
|                                     </StyledScoreInput> | ||||
|                                     <ScoreHelpContainer> | ||||
|                                         <StyledScoreHelp> | ||||
|                                             Very difficult | ||||
|                                         </StyledScoreHelp> | ||||
|                                         <StyledScoreHelp> | ||||
|                                             Very easy | ||||
|                                         </StyledScoreHelp> | ||||
|                                     </ScoreHelpContainer> | ||||
|                                 </StyledScoreContainer> | ||||
| 
 | ||||
|                                     {feedbackComments !== false && | ||||
|                                     feedbackComments.enabled && | ||||
|                                     feedbackComments.name === | ||||
|                                         'withoutComments' ? ( | ||||
|                                         <> | ||||
|                                             <Box> | ||||
|                                                 <TextField | ||||
|                                                     placeholder='Your answer here' | ||||
|                                                     style={{ width: '100%' }} | ||||
|                                                     name='positive' | ||||
|                                                     hidden | ||||
|                                                     value={ | ||||
|                                                         feedbackComments.name | ||||
|                                                     } | ||||
|                                                     multiline | ||||
|                                                     rows={3} | ||||
|                                                     variant='outlined' | ||||
|                                                     size='small' | ||||
|                                                     InputLabelProps={{ | ||||
|                                                         style: { | ||||
|                                                             fontSize: | ||||
|                                                                 theme.fontSizes | ||||
|                                                                     .smallBody, | ||||
|                                                         }, | ||||
|                                                     }} | ||||
|                                                 /> | ||||
|                                             </Box> | ||||
|                                             <Box> | ||||
|                                                 <TextField | ||||
|                                                     placeholder='Your answer here' | ||||
|                                                     style={{ width: '100%' }} | ||||
|                                                     multiline | ||||
|                                                     name='areasForImprovement' | ||||
|                                                     rows={3} | ||||
|                                                     InputLabelProps={{ | ||||
|                                                         style: { | ||||
|                                                             fontSize: | ||||
|                                                                 theme.fontSizes | ||||
|                                                                     .smallBody, | ||||
|                                                         }, | ||||
|                                                     }} | ||||
|                                                     variant='outlined' | ||||
|                                                     size='small' | ||||
|                                                     hidden | ||||
|                                                 /> | ||||
|                                             </Box> | ||||
|                                         </> | ||||
|                                     ) : ( | ||||
|                                         <> | ||||
|                                             <Box> | ||||
|                                                 <FormSubTitle> | ||||
|                                                     {feedbackData.positiveLabel} | ||||
|                                                 </FormSubTitle> | ||||
|                                                 <TextField | ||||
|                                                     placeholder='Your answer here' | ||||
|                                                     style={{ width: '100%' }} | ||||
|                                                     name='positive' | ||||
|                                                     multiline | ||||
|                                                     rows={3} | ||||
|                                                     variant='outlined' | ||||
|                                                     size='small' | ||||
|                                                     InputLabelProps={{ | ||||
|                                                         style: { | ||||
|                                                             fontSize: | ||||
|                                                                 theme.fontSizes | ||||
|                                                                     .smallBody, | ||||
|                                                         }, | ||||
|                                                     }} | ||||
|                                                 /> | ||||
|                                             </Box> | ||||
|                                             <Box> | ||||
|                                                 <FormSubTitle> | ||||
|                                                     { | ||||
|                                                         feedbackData.areasForImprovementsLabel | ||||
|                                                     } | ||||
|                                                 </FormSubTitle> | ||||
|                                                 <TextField | ||||
|                                                     placeholder='Your answer here' | ||||
|                                                     style={{ width: '100%' }} | ||||
|                                                     multiline | ||||
|                                                     name='areasForImprovement' | ||||
|                                                     rows={3} | ||||
|                                                     InputLabelProps={{ | ||||
|                                                         style: { | ||||
|                                                             fontSize: | ||||
|                                                                 theme.fontSizes | ||||
|                                                                     .smallBody, | ||||
|                                                         }, | ||||
|                                                     }} | ||||
|                                                     variant='outlined' | ||||
|                                                     size='small' | ||||
|                                                 /> | ||||
|                                             </Box> | ||||
|                                         </> | ||||
|                                     )} | ||||
|                                 {feedbackComments !== false && | ||||
|                                 feedbackComments.enabled && | ||||
|                                 feedbackComments.name === 'withoutComments' ? ( | ||||
|                                     <> | ||||
|                                         <Box> | ||||
|                                             <TextField | ||||
|                                                 placeholder='Your answer here' | ||||
|                                                 style={{ | ||||
|                                                     width: '100%', | ||||
|                                                 }} | ||||
|                                                 name='positive' | ||||
|                                                 hidden | ||||
|                                                 value={feedbackComments.name} | ||||
|                                                 multiline | ||||
|                                                 rows={3} | ||||
|                                                 variant='outlined' | ||||
|                                                 size='small' | ||||
|                                                 InputLabelProps={{ | ||||
|                                                     style: { | ||||
|                                                         fontSize: | ||||
|                                                             theme.fontSizes | ||||
|                                                                 .smallBody, | ||||
|                                                     }, | ||||
|                                                 }} | ||||
|                                             /> | ||||
|                                         </Box> | ||||
|                                         <Box> | ||||
|                                             <TextField | ||||
|                                                 placeholder='Your answer here' | ||||
|                                                 style={{ | ||||
|                                                     width: '100%', | ||||
|                                                 }} | ||||
|                                                 multiline | ||||
|                                                 name='areasForImprovement' | ||||
|                                                 rows={3} | ||||
|                                                 InputLabelProps={{ | ||||
|                                                     style: { | ||||
|                                                         fontSize: | ||||
|                                                             theme.fontSizes | ||||
|                                                                 .smallBody, | ||||
|                                                     }, | ||||
|                                                 }} | ||||
|                                                 variant='outlined' | ||||
|                                                 size='small' | ||||
|                                                 hidden | ||||
|                                             /> | ||||
|                                         </Box> | ||||
|                                     </> | ||||
|                                 ) : ( | ||||
|                                     <> | ||||
|                                         <Box> | ||||
|                                             <FormSubTitle> | ||||
|                                                 {feedbackData.positiveLabel} | ||||
|                                             </FormSubTitle> | ||||
|                                             <TextField | ||||
|                                                 placeholder='Your answer here' | ||||
|                                                 style={{ | ||||
|                                                     width: '100%', | ||||
|                                                 }} | ||||
|                                                 name='positive' | ||||
|                                                 multiline | ||||
|                                                 rows={3} | ||||
|                                                 variant='outlined' | ||||
|                                                 size='small' | ||||
|                                                 InputLabelProps={{ | ||||
|                                                     style: { | ||||
|                                                         fontSize: | ||||
|                                                             theme.fontSizes | ||||
|                                                                 .smallBody, | ||||
|                                                     }, | ||||
|                                                 }} | ||||
|                                             /> | ||||
|                                         </Box> | ||||
|                                         <Box> | ||||
|                                             <FormSubTitle> | ||||
|                                                 { | ||||
|                                                     feedbackData.areasForImprovementsLabel | ||||
|                                                 } | ||||
|                                             </FormSubTitle> | ||||
|                                             <TextField | ||||
|                                                 placeholder='Your answer here' | ||||
|                                                 style={{ | ||||
|                                                     width: '100%', | ||||
|                                                 }} | ||||
|                                                 multiline | ||||
|                                                 name='areasForImprovement' | ||||
|                                                 rows={3} | ||||
|                                                 InputLabelProps={{ | ||||
|                                                     style: { | ||||
|                                                         fontSize: | ||||
|                                                             theme.fontSizes | ||||
|                                                                 .smallBody, | ||||
|                                                     }, | ||||
|                                                 }} | ||||
|                                                 variant='outlined' | ||||
|                                                 size='small' | ||||
|                                             /> | ||||
|                                         </Box> | ||||
|                                     </> | ||||
|                                 )} | ||||
| 
 | ||||
|                                     <StyledButtonContainer> | ||||
|                                         <StyledButton | ||||
|                                             disabled={!selectedScore} | ||||
|                                             variant='contained' | ||||
|                                             color='primary' | ||||
|                                             type='submit' | ||||
|                                         > | ||||
|                                             Send Feedback | ||||
|                                         </StyledButton> | ||||
|                                         <ConditionallyRender | ||||
|                                             condition={ | ||||
|                                                 feedbackMode === 'manual' | ||||
|                                             } | ||||
|                                             show={ | ||||
|                                                 <StyledButton | ||||
|                                                     variant='outlined' | ||||
|                                                     color='primary' | ||||
|                                                     onClick={dontAskAgain} | ||||
|                                                 > | ||||
|                                                     Don't ask me again | ||||
|                                                 </StyledButton> | ||||
|                                             } | ||||
|                                         /> | ||||
|                                     </StyledButtonContainer> | ||||
|                                 </StyledForm> | ||||
|                             </StyledContent> | ||||
|                         </StyledContainer> | ||||
|                     </ClickAwayListener> | ||||
|                 </ParentContainer> | ||||
|             } | ||||
|         /> | ||||
|                                 <StyledButtonContainer> | ||||
|                                     <StyledButton | ||||
|                                         disabled={!selectedScore} | ||||
|                                         variant='contained' | ||||
|                                         color='primary' | ||||
|                                         type='submit' | ||||
|                                     > | ||||
|                                         Send Feedback | ||||
|                                     </StyledButton> | ||||
|                                     <ConditionallyRender | ||||
|                                         condition={feedbackMode === 'manual'} | ||||
|                                         show={ | ||||
|                                             <StyledButton | ||||
|                                                 variant='outlined' | ||||
|                                                 color='primary' | ||||
|                                                 onClick={dontAskAgain} | ||||
|                                             > | ||||
|                                                 Don't ask me again | ||||
|                                             </StyledButton> | ||||
|                                         } | ||||
|                                     /> | ||||
|                                 </StyledButtonContainer> | ||||
|                             </StyledForm> | ||||
|                         </StyledContent> | ||||
|                     </StyledContainer> | ||||
|                 </ClickAwayListener> | ||||
|             </ParentContainer> | ||||
|         </BaseModal> | ||||
|     ); | ||||
| }; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user