[APP-737] Accessible native dropdown menu (#988)
* fix comments * add zeego package * get basic native dropdown working * add separator and icon components * refined native dropdown component * add android build properties to app.json * move `PostDropdownBtn` to its own component * fix selectors issue * move `PostDropdownBtn` to its own component * fix hitslop * fix post dropdown hitslop * fix android dropdown icons * move `UserAvatar.tsx` to native dropdown * use native dropdown in `ProfileHeader.tsx` * use native dropdown in `PostThreadItem.tsx` * use native dropdown in `UserBanner.tsx` * use native dropdown in `CustomFeed.tsx` * replace `testId` with `testID` (which is what is used everywhere) * move `Settings.tsx` to use native dropdown * create jest mocks for zeego * create jest mock for `zeego/dropdown-menu` * web styles for native dropdown * remove example native dropdown * adjust web styles * fix propagation * fix pressable in `Settings.tsx` * animate dropdown on web * add keyboard nav and hover styles * add hitslop to constants * add comments to NativeDropdown component * temporarily removed android icons * add testID to PostDropdownBtn * add testID back to all NativeDropdown button implementations * add postDropdownBtn testID * add testID to dropdown items * remove testID from dropdown menu item * refactor home-screen tests for native dropdown * refactor profile-screen tests for native dropdown * refactor thread-muting tests for native dropdown * refactor thread-screen tests for native dropdown * fix dropdown color for post dropdown button * remove icons from android dropdown menu * fix `create-account.test.ts` * fix `invite-codes.test.ts`
This commit is contained in:
parent
eec300d772
commit
3b8b562268
30 changed files with 1093 additions and 342 deletions
|
@ -14,14 +14,10 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
|||
import {Text} from '../text/Text'
|
||||
import {Button, ButtonType} from './Button'
|
||||
import {colors} from 'lib/styles'
|
||||
import {toShareUrl} from 'lib/strings/url-helpers'
|
||||
import {useStores} from 'state/index'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {shareUrl} from 'lib/sharing'
|
||||
import {HITSLOP_10} from 'lib/constants'
|
||||
|
||||
const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10}
|
||||
const ESTIMATED_BTN_HEIGHT = 50
|
||||
const ESTIMATED_SEP_HEIGHT = 16
|
||||
const ESTIMATED_HEADING_HEIGHT = 60
|
||||
|
@ -140,7 +136,7 @@ export function DropdownButton({
|
|||
testID={testID}
|
||||
style={style}
|
||||
onPress={onPress}
|
||||
hitSlop={HITSLOP}
|
||||
hitSlop={HITSLOP_10}
|
||||
ref={ref1}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={accessibilityLabel || `Opens ${numItems} options`}
|
||||
|
@ -163,112 +159,6 @@ export function DropdownButton({
|
|||
)
|
||||
}
|
||||
|
||||
export function PostDropdownBtn({
|
||||
testID,
|
||||
style,
|
||||
children,
|
||||
itemUri,
|
||||
itemCid,
|
||||
itemHref,
|
||||
isAuthor,
|
||||
isThreadMuted,
|
||||
onCopyPostText,
|
||||
onOpenTranslate,
|
||||
onToggleThreadMute,
|
||||
onDeletePost,
|
||||
}: {
|
||||
testID?: string
|
||||
style?: StyleProp<ViewStyle>
|
||||
children?: React.ReactNode
|
||||
itemUri: string
|
||||
itemCid: string
|
||||
itemHref: string
|
||||
itemTitle: string
|
||||
isAuthor: boolean
|
||||
isThreadMuted: boolean
|
||||
onCopyPostText: () => void
|
||||
onOpenTranslate: () => void
|
||||
onToggleThreadMute: () => void
|
||||
onDeletePost: () => void
|
||||
}) {
|
||||
const store = useStores()
|
||||
|
||||
const dropdownItems: DropdownItem[] = [
|
||||
{
|
||||
testID: 'postDropdownTranslateBtn',
|
||||
icon: 'language',
|
||||
label: 'Translate...',
|
||||
onPress() {
|
||||
onOpenTranslate()
|
||||
},
|
||||
},
|
||||
{
|
||||
testID: 'postDropdownCopyTextBtn',
|
||||
icon: ['far', 'paste'],
|
||||
label: 'Copy post text',
|
||||
onPress() {
|
||||
onCopyPostText()
|
||||
},
|
||||
},
|
||||
{
|
||||
testID: 'postDropdownShareBtn',
|
||||
icon: 'share',
|
||||
label: 'Share...',
|
||||
onPress() {
|
||||
const url = toShareUrl(itemHref)
|
||||
shareUrl(url)
|
||||
},
|
||||
},
|
||||
{sep: true},
|
||||
{
|
||||
testID: 'postDropdownMuteThreadBtn',
|
||||
icon: 'comment-slash',
|
||||
label: isThreadMuted ? 'Unmute thread' : 'Mute thread',
|
||||
onPress() {
|
||||
onToggleThreadMute()
|
||||
},
|
||||
},
|
||||
{sep: true},
|
||||
!isAuthor && {
|
||||
testID: 'postDropdownReportBtn',
|
||||
icon: 'circle-exclamation',
|
||||
label: 'Report post',
|
||||
onPress() {
|
||||
store.shell.openModal({
|
||||
name: 'report-post',
|
||||
postUri: itemUri,
|
||||
postCid: itemCid,
|
||||
})
|
||||
},
|
||||
},
|
||||
isAuthor && {
|
||||
testID: 'postDropdownDeleteBtn',
|
||||
icon: ['far', 'trash-can'],
|
||||
label: 'Delete post',
|
||||
onPress() {
|
||||
store.shell.openModal({
|
||||
name: 'confirm',
|
||||
title: 'Delete this post?',
|
||||
message: 'Are you sure? This can not be undone.',
|
||||
onPressConfirm: onDeletePost,
|
||||
})
|
||||
},
|
||||
},
|
||||
].filter(Boolean) as DropdownItem[]
|
||||
|
||||
return (
|
||||
<DropdownButton
|
||||
testID={testID}
|
||||
style={style}
|
||||
items={dropdownItems}
|
||||
menuWidth={isWeb ? 220 : 200}
|
||||
accessibilityLabel="Additional post actions"
|
||||
accessibilityHint="">
|
||||
{children}
|
||||
</DropdownButton>
|
||||
)
|
||||
}
|
||||
|
||||
function createDropdownMenu(
|
||||
x: number,
|
||||
y: number,
|
||||
|
@ -324,15 +214,16 @@ const DropdownItems = ({
|
|||
|
||||
const numItems = items.filter(isBtn).length
|
||||
|
||||
// TODO: Refactor dropdown components to:
|
||||
// - (On web, if not handled by React Native) use semantic <select />
|
||||
// and <option /> elements for keyboard navigation out of the box
|
||||
// - (On mobile) be buttons by default, accept `label` and `nativeID`
|
||||
// props, and always have an explicit label
|
||||
return (
|
||||
<>
|
||||
{/* This TouchableWithoutFeedback renders the background so if the user clicks outside, the dropdown closes */}
|
||||
<TouchableWithoutFeedback
|
||||
onPress={onOuterPress}
|
||||
// TODO: Refactor dropdown components to:
|
||||
// - (On web, if not handled by React Native) use semantic <select />
|
||||
// and <option /> elements for keyboard navigation out of the box
|
||||
// - (On mobile) be buttons by default, accept `label` and `nativeID`
|
||||
// props, and always have an explicit label
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Toggle dropdown"
|
||||
accessibilityHint="">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue