Fix priority footer on tablets (#7966) (#8001)

(cherry picked from commit b966e1f356)

Co-authored-by: Daniel Espino García <larkox@gmail.com>
This commit is contained in:
Mattermost Build 2024-06-10 18:31:46 +03:00 committed by GitHub
parent fc06c0e189
commit 272895d37f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 100 additions and 36 deletions

View file

@ -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<ViewStyle>;
initialSnapIndex?: number;
footerComponent?: React.FC<BottomSheetFooterProps>;
footerComponent?: React.FC<unknown>;
renderContent: () => ReactNode;
snapPoints?: Array<string | number>;
testID?: string;
@ -188,10 +188,12 @@ const BottomSheet = ({
);
if (isTablet) {
const FooterComponent = footerComponent;
return (
<>
<View style={styles.separator}/>
{renderContainerContent()}
{FooterComponent && (<FooterComponent/>)}
</>
);
}

View file

@ -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'
/>
);

View file

@ -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>
);
};

View file

@ -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();
});
});

View file

@ -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);