Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(menu): dropdown position with translucent status bar #4495

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion example/metro.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ const defaultConfig = getDefaultConfig(__dirname);

const modules = [
'@expo/vector-icons',
'expo-constants',
...Object.keys(pak.peerDependencies),
'@react-navigation/native',
];
Expand Down
29 changes: 23 additions & 6 deletions example/src/Examples/MenuExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const MenuExample = ({ navigation }: Props) => {
x: nativeEvent.pageX,
y: nativeEvent.pageY,
});
setVisible({ menu3: true });
setVisible({ menu4: true });
};

React.useLayoutEffect(() => {
Expand Down Expand Up @@ -127,9 +127,25 @@ const MenuExample = ({ navigation }: Props) => {
)}
</Menu>
</View>
<View style={styles.alignCenter}>
<Menu
visible={_getVisible('menu3')}
onDismiss={_toggleMenu('menu3')}
anchor={
<Button mode="outlined" onPress={_toggleMenu('menu3')}>
Menu with anchorPosition bottom
</Button>
}
anchorPosition="bottom"
>
<Menu.Item onPress={() => {}} title="One" />
<Menu.Item onPress={() => {}} title="Two" />
<Menu.Item onPress={() => {}} title="Three" />
</Menu>
</View>
<Menu
visible={_getVisible('menu3')}
onDismiss={_toggleMenu('menu3')}
visible={_getVisible('menu4')}
onDismiss={_toggleMenu('menu4')}
anchor={contextualMenuCoord}
>
<Menu.Item onPress={() => {}} title="Item 1" />
Expand All @@ -149,10 +165,10 @@ const MenuExample = ({ navigation }: Props) => {

<View style={styles.bottomMenu}>
<Menu
visible={_getVisible('menu4')}
onDismiss={_toggleMenu('menu4')}
visible={_getVisible('menu5')}
onDismiss={_toggleMenu('menu5')}
anchor={
<Button mode="outlined" onPress={_toggleMenu('menu4')}>
<Button mode="outlined" onPress={_toggleMenu('menu5')}>
Menu at bottom
</Button>
}
Expand Down Expand Up @@ -181,6 +197,7 @@ const styles = StyleSheet.create({
},
alignCenter: {
alignItems: 'center',
marginVertical: 8,
},
md3Divider: {
marginVertical: 8,
Expand Down
627 changes: 29 additions & 598 deletions example/yarn.lock

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@
"eslint": "8.31.0",
"eslint-plugin-flowtype": "^8.0.3",
"eslint-plugin-local-rules": "^1.3.2",
"expo-constants": "^9.3.5",
"glob": "^7.1.3",
"husky": "^1.3.1",
"jest": "^29.2.1",
Expand Down
16 changes: 8 additions & 8 deletions src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export type Props = $Omit<React.ComponentProps<typeof Surface>, 'mode'> & {
* export default MyComponent;
* ```
*/
const CardComponent = (
const Card = (
{
elevation: cardElevation = 1,
delayLongPress,
Expand Down Expand Up @@ -330,24 +330,24 @@ const CardComponent = (
);
};

const Component = forwardRef(CardComponent);
const Component = forwardRef(Card);
Component.displayName = 'Card';

const Card = Component as typeof Component & {
const CardComponent = Component as typeof Component & {
Content: typeof CardContent;
Actions: typeof CardActions;
Cover: typeof CardCover;
Title: typeof CardTitle;
};

// @component ./CardContent.tsx
Card.Content = CardContent;
CardComponent.Content = CardContent;
// @component ./CardActions.tsx
Card.Actions = CardActions;
CardComponent.Actions = CardActions;
// @component ./CardCover.tsx
Card.Cover = CardCover;
CardComponent.Cover = CardCover;
// @component ./CardTitle.tsx
Card.Title = CardTitle;
CardComponent.Title = CardTitle;

const styles = StyleSheet.create({
innerContainer: {
Expand All @@ -365,4 +365,4 @@ const styles = StyleSheet.create({
},
});

export default Card;
export default CardComponent;
33 changes: 22 additions & 11 deletions src/components/Menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ import {
Pressable,
} from 'react-native';

import {
withSafeAreaInsets,
WithSafeAreaInsetsProps,
} from 'react-native-safe-area-context';

import MenuItem from './MenuItem';
import { APPROX_STATUSBAR_HEIGHT } from '../../constants';
import { withInternalTheme } from '../../core/theming';
import type { $Omit, InternalTheme, MD3Elevation } from '../../types';
import { ElevationLevels } from '../../types';
Expand All @@ -30,7 +34,7 @@ import { BackHandler } from '../../utils/BackHandler/BackHandler';
import Portal from '../Portal/Portal';
import Surface from '../Surface';

export type Props = {
export type Props = WithSafeAreaInsetsProps & {
/**
* Whether the Menu is currently visible.
*/
Expand All @@ -45,10 +49,9 @@ export type Props = {
*/
anchorPosition?: 'top' | 'bottom';
/**
* Extra margin to add at the top of the menu to account for translucent status bar on Android.
* If you are using Expo, we assume translucent status bar and set a height for status bar automatically.
* Pass `0` or a custom value to and customize it.
* Extra margin to add at the top of the menu to handle specific cases with the status bar on Android.
* This is automatically handled on iOS.
* Pass `0` or a custom value to customize it.
*/
statusBarHeight?: number;
/**
Expand Down Expand Up @@ -171,11 +174,7 @@ const DEFAULT_MODE = 'elevated';
* wrapping is not necessary if you use Paper's `Modal` instead.
*/
class Menu extends React.Component<Props, State> {
// @component ./MenuItem.tsx
static Item = MenuItem;

static defaultProps = {
statusBarHeight: APPROX_STATUSBAR_HEIGHT,
overlayAccessibilityLabel: 'Close menu',
testID: 'menu',
};
Expand Down Expand Up @@ -436,6 +435,7 @@ class Menu extends React.Component<Props, State> {
overlayAccessibilityLabel,
keyboardShouldPersistTaps,
testID,
insets,
} = this.props;

const {
Expand All @@ -455,7 +455,7 @@ class Menu extends React.Component<Props, State> {

// I don't know why but on Android measure function is wrong by 24
const additionalVerticalValue = Platform.select({
android: statusBarHeight,
android: statusBarHeight ?? insets.top,
default: 0,
});

Expand Down Expand Up @@ -706,4 +706,15 @@ const styles = StyleSheet.create({
},
});

export default withInternalTheme(Menu);
const Component = withInternalTheme(withSafeAreaInsets(Menu));
Component.displayName = 'Menu';

const MenuComponent = Component as typeof Component & {
Item: typeof MenuItem;
};

// we need to attach MenuItem here instead of a static property otherwise it will be lost after using withSafeAreaInsets
// @component ./MenuItem.tsx
MenuComponent.Item = MenuItem;

export default MenuComponent;
27 changes: 16 additions & 11 deletions src/components/__tests__/DataTable.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import * as React from 'react';

import { render } from '@testing-library/react-native';
import mockSafeAreaContext from 'react-native-safe-area-context/jest/mock';

import Checkbox from '../Checkbox';
import DataTable from '../DataTable/DataTable';

jest.mock('react-native-safe-area-context', () => mockSafeAreaContext);

describe('DataTable.Header', () => {
it('renders data table header', () => {
const tree = render(
Expand Down Expand Up @@ -133,17 +136,19 @@ describe('DataTable.Pagination', () => {

it('renders data table pagination with options select', () => {
const { getByLabelText, toJSON } = render(
<DataTable.Pagination
page={3}
numberOfPages={15}
onPageChange={() => {}}
label="11-20 of 150"
showFastPaginationControls
numberOfItemsPerPageList={[2, 4, 6]}
numberOfItemsPerPage={2}
onItemsPerPageChange={() => {}}
selectPageDropdownLabel={'Rows per page'}
/>
<mockSafeAreaContext.SafeAreaProvider>
<DataTable.Pagination
page={3}
numberOfPages={15}
onPageChange={() => {}}
label="11-20 of 150"
showFastPaginationControls
numberOfItemsPerPageList={[2, 4, 6]}
numberOfItemsPerPage={2}
onItemsPerPageChange={() => {}}
selectPageDropdownLabel={'Rows per page'}
/>
</mockSafeAreaContext.SafeAreaProvider>
);

expect(getByLabelText('Options Select')).toBeTruthy();
Expand Down
Loading
Loading