From 78ffc0bc230f02063d997a3b3ae8cdb7b89810a6 Mon Sep 17 00:00:00 2001 From: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com> Date: Mon, 11 Sep 2023 12:44:15 -0700 Subject: [PATCH] Mobile: Improve accessibility of side menu (#8839) --- .../components/side-menu-content.tsx | 18 +++++++++++++++++- packages/app-mobile/root.tsx | 7 +++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/app-mobile/components/side-menu-content.tsx b/packages/app-mobile/components/side-menu-content.tsx index 282b0b2c0..52e160a82 100644 --- a/packages/app-mobile/components/side-menu-content.tsx +++ b/packages/app-mobile/components/side-menu-content.tsx @@ -23,6 +23,7 @@ Icon.loadFont().catch((error: any) => { console.info(error); }); interface Props { syncStarted: boolean; themeId: number; + sideMenuVisible: boolean; // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied dispatch: Function; collapsedFolderIds: string[]; @@ -491,15 +492,29 @@ const SideMenuContentComponent = (props: Props) => { items = items.concat(folderItems); } + const isHidden = !props.sideMenuVisible; + const style = { flex: 1, borderRightWidth: 1, borderRightColor: theme.dividerColor, backgroundColor: theme.backgroundColor, + + // Have the UI reflect whether the View is hidden to the screen reader. + // This way, there will be visual feedback if isHidden is incorrect. + opacity: isHidden ? 0.5 : undefined, }; + // Note: iOS uses accessibilityElementsHidden and Android uses importantForAccessibility + // to hide elements from the screenreader. + return ( - + {items} @@ -520,6 +535,7 @@ export default connect((state: AppState) => { notesParentType: state.notesParentType, locale: state.settings.locale, themeId: state.settings.theme, + sideMenuVisible: state.showSideMenu, // Don't do the opacity animation as it means re-rendering the list multiple times // opacity: state.sideMenuOpenPercent, collapsedFolderIds: state.collapsedFolderIds, diff --git a/packages/app-mobile/root.tsx b/packages/app-mobile/root.tsx index 64fc2061d..aa6aa5a4e 100644 --- a/packages/app-mobile/root.tsx +++ b/packages/app-mobile/root.tsx @@ -22,14 +22,14 @@ import handleShared from './utils/shareHandler'; import uuid from '@joplin/lib/uuid'; import { loadKeychainServiceAndSettings } from '@joplin/lib/services/SettingUtils'; import KeychainServiceDriverMobile from '@joplin/lib/services/keychain/KeychainServiceDriver.mobile'; -import { setLocale } from '@joplin/lib/locale'; +import { _, setLocale } from '@joplin/lib/locale'; import SyncTargetJoplinServer from '@joplin/lib/SyncTargetJoplinServer'; import SyncTargetJoplinCloud from '@joplin/lib/SyncTargetJoplinCloud'; import SyncTargetOneDrive from '@joplin/lib/SyncTargetOneDrive'; import initProfile from '@joplin/lib/services/profileConfig/initProfile'; const VersionInfo = require('react-native-version-info').default; const { Keyboard, BackHandler, Animated, View, StatusBar, Platform, Dimensions } = require('react-native'); -import { AppState as RNAppState, EmitterSubscription, Linking, NativeEventSubscription, Appearance } from 'react-native'; +import { AppState as RNAppState, EmitterSubscription, Linking, NativeEventSubscription, Appearance, AccessibilityInfo } from 'react-native'; import getResponsiveValue from './components/getResponsiveValue'; import NetInfo from '@react-native-community/netinfo'; const DropdownAlert = require('react-native-dropdownalert').default; @@ -984,6 +984,9 @@ class AppComponent extends React.Component { this.props.dispatch({ type: isOpen ? 'SIDE_MENU_OPEN' : 'SIDE_MENU_CLOSE', }); + AccessibilityInfo.announceForAccessibility( + isOpen ? _('Side menu opened') : _('Side menu closed') + ); } private getSideMenuWidth = () => {