Skip to content

Commit

Permalink
Fix priority footer on tablets (#7966) (#8001)
Browse files Browse the repository at this point in the history
(cherry picked from commit b966e1f)

Co-authored-by: Daniel Espino García <[email protected]>
  • Loading branch information
mattermost-build and larkox authored Jun 10, 2024
1 parent fc06c0e commit 272895d
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 36 deletions.
6 changes: 4 additions & 2 deletions app/screens/bottom_sheet/index.tsx
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -27,7 +27,7 @@ type Props = {
componentId: AvailableScreens;
contentStyle?: StyleProp<ViewStyle>;
initialSnapIndex?: number;
footerComponent?: React.FC<BottomSheetFooterProps>;
footerComponent?: React.FC<unknown>;
renderContent: () => ReactNode;
snapPoints?: Array<string | number>;
testID?: string;
Expand Down Expand Up @@ -188,10 +188,12 @@ const BottomSheet = ({
);

if (isTablet) {
const FooterComponent = footerComponent;
return (
<>
<View style={styles.separator}/>
{renderContainerContent()}
{FooterComponent && (<FooterComponent/>)}
</>
);
}
Expand Down
5 changes: 4 additions & 1 deletion app/screens/emoji_picker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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);
Expand All @@ -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'
/>
);
Expand Down
59 changes: 33 additions & 26 deletions app/screens/post_priority_picker/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,34 +61,41 @@ const PostPriorityPickerFooter = ({onCancel, onSubmit, ...props}: Props) => {
const style = getStyleSheet(theme);
const isTablet = useIsTablet();

const footer = (
<View
style={[style.container, {
paddingBottom: FOOTER_PADDING + Platform.select({ios: (isTablet ? FOOTER_PADDING_BOTTOM_TABLET_ADJUST : 0), default: 0}),
}]}
>
<TouchableOpacity
onPress={onCancel}
style={style.cancelButton}
>
<FormattedText
id='post_priority.picker.cancel'
defaultMessage='Cancel'
style={style.cancelButtonText}
/>
</TouchableOpacity>
<TouchableOpacity
onPress={onSubmit}
style={style.applyButton}
>
<FormattedText
id='post_priority.picker.apply'
defaultMessage='Apply'
style={style.applyButtonText}
/>
</TouchableOpacity>
</View>
);

if (isTablet) {
return footer;
}
return (
<BottomSheetFooter {...props}>
<View
style={[style.container, {
paddingBottom: FOOTER_PADDING + Platform.select({ios: (isTablet ? FOOTER_PADDING_BOTTOM_TABLET_ADJUST : 0), default: 0}),
}]}
>
<TouchableOpacity
onPress={onCancel}
style={style.cancelButton}
>
<FormattedText
id='post_priority.picker.cancel'
defaultMessage='Cancel'
style={style.cancelButtonText}
/>
</TouchableOpacity>
<TouchableOpacity
onPress={onSubmit}
style={style.applyButton}
>
<FormattedText
id='post_priority.picker.apply'
defaultMessage='Apply'
style={style.applyButtonText}
/>
</TouchableOpacity>
</View>
{footer}
</BottomSheetFooter>
);
};
Expand Down
42 changes: 42 additions & 0 deletions app/screens/post_priority_picker/post_priority_picker.test.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof PostPriorityPicker> {
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(<PostPriorityPicker {...props}/>);
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(<PostPriorityPicker {...props}/>);
expect(await screen.findByText('Apply')).toBeVisible();
expect(await screen.findByText('Cancel')).toBeVisible();
});
});
24 changes: 17 additions & 7 deletions test/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down Expand Up @@ -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'),
};
});

Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 272895d

Please sign in to comment.