-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
fix(modal): make Modal accessible and honour its dismissal props #5125
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
base: main
Are you sure you want to change the base?
Changes from all commits
6952ce0
9704537
cea8535
e2fc8c0
4a39245
e28935c
69b6a33
f3bb008
c0b1669
2f05043
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { Button, Portal, Dialog, Palette } from 'react-native-paper'; | ||
|
|
||
| import { TextComponent } from './DialogTextComponent'; | ||
|
|
||
| const DialogWithUndismissableBackButton = ({ | ||
| visible, | ||
| close, | ||
| }: { | ||
| visible: boolean; | ||
| close: () => void; | ||
| }) => ( | ||
| <Portal> | ||
| <Dialog onDismiss={close} visible={visible} dismissableBackButton={false}> | ||
| <Dialog.Title>Alert</Dialog.Title> | ||
| <Dialog.Content> | ||
| <TextComponent> | ||
| This dialog can be dismissed by tapping outside, however the hardware | ||
| back button will not close it! | ||
| </TextComponent> | ||
| </Dialog.Content> | ||
| <Dialog.Actions> | ||
| <Button textColor={Palette.tertiary50} disabled> | ||
| Disagree | ||
| </Button> | ||
| <Button onPress={close}>Agree</Button> | ||
| </Dialog.Actions> | ||
| </Dialog> | ||
| </Portal> | ||
| ); | ||
|
|
||
| export default DialogWithUndismissableBackButton; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,6 +36,10 @@ export type Props = { | |
| * Accessibility label for the overlay. This is read by the screen reader when the user taps outside the modal. | ||
| */ | ||
| overlayAccessibilityLabel?: string; | ||
| /** | ||
| * Accessible name for the modal. | ||
| */ | ||
| 'aria-label'?: string; | ||
| /** | ||
| * testID for the overlay that is displayed behind the modal content. | ||
| */ | ||
|
|
@@ -89,7 +93,7 @@ const AnimatedPressable = Animated.createAnimatedComponent(Pressable); | |
| /** | ||
| * The Modal component is a simple way to present content above an enclosing view. | ||
| * To render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component. | ||
| * Note that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal. | ||
| * Give the modal an accessible name with `aria-label`. | ||
| * | ||
| * ## Usage | ||
| * ```js | ||
|
|
@@ -110,6 +114,7 @@ const AnimatedPressable = Animated.createAnimatedComponent(Pressable); | |
| * <Modal | ||
| * visible={visible} | ||
| * onDismiss={hideModal} | ||
| * aria-label="Example modal" | ||
| * contentBackgroundColor="white" | ||
| * contentContainerStyle={containerStyle} | ||
| * > | ||
|
|
@@ -131,6 +136,7 @@ function Modal({ | |
| dismissableBackButton = dismissable, | ||
| visible = false, | ||
| overlayAccessibilityLabel = 'Close modal', | ||
| 'aria-label': ariaLabel, | ||
| overlayTestID, | ||
| onDismiss = () => {}, | ||
| children, | ||
|
|
@@ -182,7 +188,7 @@ function Modal({ | |
| } | ||
|
|
||
| const onHardwareBackPress = () => { | ||
| if (dismissable || dismissableBackButton) { | ||
| if (dismissableBackButton) { | ||
| onDismissCallback(); | ||
| } | ||
|
|
||
|
|
@@ -196,7 +202,7 @@ function Modal({ | |
| ); | ||
|
|
||
| return () => subscription.remove(); | ||
| }, [dismissable, dismissableBackButton, onDismissCallback, visible]); | ||
| }, [dismissableBackButton, onDismissCallback, visible]); | ||
|
|
||
| const transitionTimingFunction = cubicBezier(1 / 3, 1, 2 / 3, 1); | ||
|
|
||
|
|
@@ -227,20 +233,20 @@ function Modal({ | |
| return ( | ||
| <Animated.View | ||
| pointerEvents={visible ? 'auto' : 'none'} | ||
| aria-modal | ||
| aria-live="polite" | ||
| style={StyleSheet.absoluteFill} | ||
| onAccessibilityEscape={onDismissCallback} | ||
| onAccessibilityEscape={dismissable ? onDismissCallback : undefined} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Given the ticket calls this out as the hard, VoiceOver/TalkBack/keyboard/switch-control-verified part of the work, will there be another follow-up PR for this, or should it be included here? @satya164 what do You think about it?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There will be a follow-up PR with initial focus / restore focus. |
||
| testID={testID} | ||
| > | ||
| <AnimatedPressable | ||
| aria-label={overlayAccessibilityLabel} | ||
| role="button" | ||
| disabled={!dismissable} | ||
| onPress={dismissable ? onDismissCallback : undefined} | ||
| importantForAccessibility="no" | ||
| style={[styles.backdrop, backdropStyle, backdropTransitionStyle]} | ||
| testID={overlayTestID} | ||
| importantForAccessibility={dismissable ? 'auto' : 'no'} | ||
| accessible={dismissable} | ||
| /> | ||
| <View | ||
| style={[ | ||
|
|
@@ -251,6 +257,9 @@ function Modal({ | |
| pointerEvents="box-none" | ||
| > | ||
| <Surface | ||
| role="dialog" | ||
| aria-modal | ||
| aria-label={ariaLabel} | ||
| theme={theme} | ||
| backgroundColor={contentBackgroundColor} | ||
| borderRadius={contentBorderRadius} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with
overlayAccessibilityLabelremoved, how can user customize aria label for the overlay or access it in tests (there is test id, but better practice is to use a11y, same way your users will)?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@satya164 please check my comment above. It will not be needed if we move forward with not making it accessible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Restored the label