diff --git a/app/screens/bottom_sheet/index.tsx b/app/screens/bottom_sheet/index.tsx index 89dbdbfee..49b7bb244 100644 --- a/app/screens/bottom_sheet/index.tsx +++ b/app/screens/bottom_sheet/index.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import BottomSheetM, {BottomSheetBackdrop, type BottomSheetBackdropProps, type BottomSheetFooterProps} from '@gorhom/bottom-sheet'; +import BottomSheetM, {BottomSheetBackdrop, type BottomSheetBackdropProps} from '@gorhom/bottom-sheet'; import React, {type ReactNode, useCallback, useEffect, useMemo, useRef} from 'react'; import {DeviceEventEmitter, type Handle, InteractionManager, Keyboard, type StyleProp, View, type ViewStyle} from 'react-native'; @@ -27,7 +27,7 @@ type Props = { componentId: AvailableScreens; contentStyle?: StyleProp; initialSnapIndex?: number; - footerComponent?: React.FC; + footerComponent?: React.FC; renderContent: () => ReactNode; snapPoints?: Array; testID?: string; @@ -188,10 +188,12 @@ const BottomSheet = ({ ); if (isTablet) { + const FooterComponent = footerComponent; return ( <> {renderContainerContent()} + {FooterComponent && ()} ); } diff --git a/app/screens/emoji_picker/index.tsx b/app/screens/emoji_picker/index.tsx index d6423e4ca..5a7ec52d2 100644 --- a/app/screens/emoji_picker/index.tsx +++ b/app/screens/emoji_picker/index.tsx @@ -5,6 +5,7 @@ import React, {useCallback} from 'react'; import {DeviceEventEmitter, StyleSheet} from 'react-native'; import {Events} from '@constants'; +import {useIsTablet} from '@hooks/device'; import BottomSheet from '@screens/bottom_sheet'; import Picker from './picker'; @@ -25,6 +26,8 @@ const style = StyleSheet.create({ }); const EmojiPickerScreen = ({closeButtonId, componentId, onEmojiPress}: Props) => { + const isTablet = useIsTablet(); + const handleEmojiPress = useCallback((emoji: string) => { onEmojiPress(emoji); DeviceEventEmitter.emit(Events.CLOSE_BOTTOM_SHEET); @@ -46,7 +49,7 @@ const EmojiPickerScreen = ({closeButtonId, componentId, onEmojiPress}: Props) => componentId={componentId} contentStyle={style.contentStyle} initialSnapIndex={1} - footerComponent={PickerFooter} + footerComponent={isTablet ? undefined : PickerFooter} testID='post_options' /> ); diff --git a/app/screens/post_priority_picker/footer.tsx b/app/screens/post_priority_picker/footer.tsx index 509ff2b2b..512ce30f5 100644 --- a/app/screens/post_priority_picker/footer.tsx +++ b/app/screens/post_priority_picker/footer.tsx @@ -61,34 +61,41 @@ const PostPriorityPickerFooter = ({onCancel, onSubmit, ...props}: Props) => { const style = getStyleSheet(theme); const isTablet = useIsTablet(); + const footer = ( + + + + + + + + + ); + + if (isTablet) { + return footer; + } return ( - - - - - - - - + {footer} ); }; diff --git a/app/screens/post_priority_picker/post_priority_picker.test.tsx b/app/screens/post_priority_picker/post_priority_picker.test.tsx new file mode 100644 index 000000000..00d30d2a6 --- /dev/null +++ b/app/screens/post_priority_picker/post_priority_picker.test.tsx @@ -0,0 +1,42 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {screen} from '@testing-library/react-native'; +import React, {type ComponentProps} from 'react'; + +import {useIsTablet} from '@hooks/device'; +import {renderWithIntl} from '@test/intl-test-helper'; + +import PostPriorityPicker from './post_priority_picker'; + +jest.mock('@hooks/device'); +const mockedIsTablet = jest.mocked(useIsTablet); + +function getBaseProps(): ComponentProps { + return { + closeButtonId: '', + componentId: 'BottomSheet', + isPersistenNotificationsEnabled: true, + isPostAcknowledgementEnabled: true, + persistentNotificationInterval: 0, + postPriority: {priority: ''}, + updatePostPriority: jest.fn(), + }; +} +describe('post_priority_picker', () => { + it('correctly shows the apply and cancel buttons on mobile', async () => { + mockedIsTablet.mockReturnValue(false); + const props = getBaseProps(); + renderWithIntl(); + expect(await screen.findByText('Apply')).toBeVisible(); + expect(await screen.findByText('Cancel')).toBeVisible(); + }); + + it('correctly shows the apply and cancel buttons on tablet', async () => { + mockedIsTablet.mockReturnValue(true); + const props = getBaseProps(); + renderWithIntl(); + expect(await screen.findByText('Apply')).toBeVisible(); + expect(await screen.findByText('Cancel')).toBeVisible(); + }); +}); diff --git a/test/setup.ts b/test/setup.ts index eb39d8ba6..89aad0e2f 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -5,12 +5,14 @@ import {setGenerator} from '@nozbe/watermelondb/utils/common/randomId'; import * as ReactNative from 'react-native'; -import 'react-native-gesture-handler/jestSetup'; import mockSafeAreaContext from 'react-native-safe-area-context/jest/mock'; import {v4 as uuidv4} from 'uuid'; import type {ReadDirItem, StatResult} from 'react-native-fs'; +import 'react-native-gesture-handler/jestSetup'; +import '@testing-library/react-native/extend-expect'; + // @ts-expect-error Promise does not exists in global global.Promise = jest.requireActual('promise'); @@ -248,12 +250,12 @@ jest.mock('../node_modules/react-native/Libraries/EventEmitter/NativeEventEmitte jest.mock('react-native-device-info', () => { return { - getVersion: () => '0.0.0', - getBuildNumber: () => '0', - getModel: () => 'iPhone X', - hasNotch: () => true, - isTablet: () => false, - getApplicationName: () => 'Mattermost', + getVersion: jest.fn(() => '0.0.0'), + getBuildNumber: jest.fn(() => '0'), + getModel: jest.fn(() => 'iPhone X'), + hasNotch: jest.fn(() => true), + isTablet: jest.fn(() => false), + getApplicationName: jest.fn(() => 'Mattermost'), }; }); @@ -389,6 +391,14 @@ jest.mock('react-native-safe-area-context', () => mockSafeAreaContext); jest.mock('react-native-reanimated', () => require('react-native-reanimated/mock')); jest.mock('react-native-permissions', () => require('react-native-permissions/mock')); +jest.mock('react-native-haptic-feedback', () => { + const RNHF = jest.requireActual('react-native-haptic-feedback'); + return { + ...RNHF, + trigger: () => '', + }; +}); + declare const global: {requestAnimationFrame: (callback: any) => void}; global.requestAnimationFrame = (callback) => { setTimeout(callback, 0);