Skip to content

Commit

Permalink
✨ TopAppBarMenu - add custom menu
Browse files Browse the repository at this point in the history
  • Loading branch information
Adrien Castagliola committed Feb 26, 2024
1 parent a1342ad commit acb5f8e
Show file tree
Hide file tree
Showing 10 changed files with 552 additions and 85 deletions.
55 changes: 51 additions & 4 deletions Storybook/components/TopAppBar/TopAppBar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@
import type { Meta, StoryObj } from '@storybook/react-native';
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { Headline, TopAppBar } from 'smartway-react-native-ui';
import {
Headline,
TopAppBar,
} from '../../../src/components/topAppBar/TopAppBar';

import TopAppBarMenuItem from '../../../src/components/topAppBar/TopAppBarMenuItem';

import type { Title } from 'src/components/topAppBar/TopAppBar';

const asString = { value: 'menu' };
const asButton = { value: 'menu', onPress: () => {} };
const asComponent = { value: <Headline size="h1">Headline H1</Headline> };
const asComponent = { value: <Headline size='h1'>Headline H1</Headline> };

type ComponentProps = React.ComponentProps<typeof TopAppBar> & {
withBackButton?: boolean;
Expand All @@ -28,10 +34,14 @@ export default {
control: { type: 'radio' },
options: ['small', 'medium', 'large', 'center-aligned'],
},
withTitleAs: { control: { type: 'radio' }, options: ['string', 'button', 'component'] },
withTitleAs: {
control: { type: 'radio' },
options: ['string', 'button', 'component'],
},
withBackButton: { type: 'boolean' },
onBack: { action: 'onBack' },
onPressIcon: { action: 'onPressIcon' },
onMenuItemPress: { action: 'onMenuItemPress' },
},

decorators: [
Expand Down Expand Up @@ -60,9 +70,46 @@ export const Default: Story = {
size={args.size}
onBack={args.withBackButton ? args.onBack : undefined}
title={titleComponent}
icon={{ name: 'dots-vertical', onPress: args.onPressIcon }}
/>
);
},
};
export const WithMenu = (args) => {
let titleComponent: Title = asString;
if (args.withTitleAs === 'button') titleComponent = asButton;
if (args.withTitleAs === 'component') titleComponent = asComponent;

const handlePress = (menuItem, hideMenu) => () => {
hideMenu();
args.onMenuItemPress(menuItem);
};

const menu = {
items: [
{
title: 'Ne plus surveiller',
id: 'noLongerMonitor',
},
],
renderItem: (menuItem, { hideMenu }) => {
return (
<TopAppBarMenuItem
id={menuItem.id}
title={menuItem.title}
onPress={handlePress(menuItem, hideMenu)}
/>
);
},
};

return (
<TopAppBar
size={args.size}
onBack={args.withBackButton ? args.onBack : undefined}
title={titleComponent}
menu={menu}
/>
);
};

Default.parameters = { noSafeArea: false };
2 changes: 1 addition & 1 deletion jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const jestConfig: JestConfigWithTsJest = {
testMatch: ['**/?(*.)test.(ts|tsx)'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
transformIgnorePatterns: [
'node_modules/(?!(@react-native|react-native|react-native-drop-shadow|@gorhom/bottom-sheet|react-native-reanimated)/)',
'node_modules/(?!(@react-native|react-native|react-native-drop-shadow|@gorhom/bottom-sheet|react-native-reanimated|react-native-paper)/)',
],
moduleDirectories: ['node_modules', 'src'],
setupFilesAfterEnv: ['./jest.setup.ts'],
Expand Down
157 changes: 157 additions & 0 deletions src/__tests__/components/TopAppBar.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import React from 'react';
import { TopAppBar } from '../../components/topAppBar/TopAppBar';
import {
render,
screen,
setupFakeTimer,
userEvent,
waitForElementToBeRemoved,
} from '../../shared/testUtils';
import { Text } from 'react-native';
import TopAppBarMenuItem from '../../components/topAppBar/TopAppBarMenuItem';

const topBarTitle = 'Menu';

describe('TopAppBar mounting with a simple title', () => {
it('displays a title', () => {
const title = {
value: topBarTitle,
};

render(<TopAppBar title={title} />);

expect(screen.getByText(topBarTitle)).toBeOnTheScreen();
});

it.todo("testing menu title 'as string' on press");
});

describe('TopAppBar mounting with a title by passing a custom component', () => {
it('displays a title', () => {
const title = {
value: <Text>{topBarTitle}</Text>,
};

render(<TopAppBar title={title} />);

expect(screen.getByText(topBarTitle)).toBeOnTheScreen();
});

it.todo("testing menu title 'as component' on press");
});

describe('TopAppBar mounting with a go back button', () => {
it('triggers `goBack` event when user press the go back button', async () => {
setupFakeTimer();

const user = userEvent.setup();

const mockOnGoBack = jest.fn();

const title = {
value: topBarTitle,
};

render(<TopAppBar title={title} onBack={mockOnGoBack} />);

expect(mockOnGoBack).not.toHaveBeenCalled();

await user.press(screen.getByLabelText(/back/i));

expect(mockOnGoBack).toHaveBeenCalled();
});
});

describe('TopAppBar mounting with a menu', () => {
const noLongerMonitorMenuItem = {
id: 'noLongerMonitor' as const,
title: 'Ne plus surveiller',
};
const title = {
value: topBarTitle,
};
let mockOnMenuItemPress: jest.Mock;

beforeEach(() => {
mockOnMenuItemPress = jest.fn();

const handlePress =
(menuItem: typeof noLongerMonitorMenuItem, hideMenu: () => void) =>
() => {
hideMenu();
mockOnMenuItemPress(menuItem);
};

render(
<TopAppBar
title={title}
menu={{
items: [noLongerMonitorMenuItem],
renderItem: (menuItem, { hideMenu }) => {
return (
<TopAppBarMenuItem
id={menuItem.id}
title={menuItem.title}
onPress={handlePress(menuItem, hideMenu)}
/>
);
},
}}
/>,
);
});

it('displays action menu button', () => {
expect(screen.getByLabelText(/menu/i)).toBeOnTheScreen();
});

it('displays menu items when user press the action menu button', async () => {
setupFakeTimer();

const user = userEvent.setup();

expect(
screen.queryByText(noLongerMonitorMenuItem.title),
).not.toBeOnTheScreen();

await user.press(screen.getByLabelText(/menu/i));

expect(
screen.getByText(noLongerMonitorMenuItem.title),
).toBeOnTheScreen();
});

it('retreives menu item data when user press a menu item', async () => {
setupFakeTimer();

const user = userEvent.setup();

await user.press(screen.getByLabelText(/menu/i));

expect(mockOnMenuItemPress).not.toHaveBeenCalled();

await user.press(screen.getByText(noLongerMonitorMenuItem.title));

expect(mockOnMenuItemPress).toHaveBeenCalledWith(
noLongerMonitorMenuItem,
);
});

it('hides menu when user press a menu item', async () => {
setupFakeTimer();

const user = userEvent.setup();

await user.press(screen.getByLabelText(/menu/i));

expect(
screen.getByText(noLongerMonitorMenuItem.title),
).toBeOnTheScreen();

await user.press(screen.getByText(noLongerMonitorMenuItem.title));

await waitForElementToBeRemoved(() =>
screen.queryByText(noLongerMonitorMenuItem.title),
);
});
});
53 changes: 53 additions & 0 deletions src/__tests__/components/TopAppBarMenuItem.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { ComponentProps } from 'react';
import {
setupFakeTimer,
render,
screen,
userEvent,
} from '../../shared/testUtils';

import TopAppBarMenuItem from '../../components/topAppBar/TopAppBarMenuItem';

describe('TopAppBarMenuItem mounting', () => {
const title = 'Ne plus surveiller';
const id = 'noLongerMonitor';
const onPress = jest.fn();

it('displays a menu item', () => {
render(<TopAppBarMenuItem id={id} onPress={onPress} title={title} />);

expect(screen.getByText(title)).toBeOnTheScreen();
});

it('triggers onPress event when user press the menu item', async () => {
setupFakeTimer();

const user = userEvent.setup();

render(<TopAppBarMenuItem id={id} onPress={onPress} title={title} />);

await user.press(screen.getByText(title));

expect(onPress).toHaveBeenCalledTimes(1);
});
});

describe('TopAppBarMenuItem failing to mount', () => {
const title = 'Ne plus surveiller';
const notExpectedId = '__not_expected_id__' as ComponentProps<
typeof TopAppBarMenuItem
>['id'];
const onPress = jest.fn();

it('throw an error if menu item id is not handled', () => {
expect(() =>
render(
<TopAppBarMenuItem
id={notExpectedId}
onPress={onPress}
title={title}
/>,
),
).toThrowError();
});
});
Loading

0 comments on commit acb5f8e

Please sign in to comment.