mattermost-mobile/app/components/banner/Banner.test.tsx
Rahim Rahman 5887c3ab46
feat(MM-65625): floating banner (#9162)
* feat: implement floating banner system

- Add FloatingBanner component with gesture support and keyboard awareness
- Implement BannerManager singleton for banner lifecycle management
- Create floating banner screen with SafeAreaProvider integration
- Add comprehensive banner configuration types and positioning
- Update Banner component to use modern gesture handling
- Enhance BannerItem with improved typography and spacing (40px height)
- Add ConnectionBanner improvements with better sizing
- Remove ConnectionBanner from channel list (moved to floating system)
- Update screens constants (remove FLOATING_BANNER - handled as overlay)
- Add i18n support for limited network connection message

The system provides:
- Auto-hide functionality with customizable duration
- Position-aware rendering (top/bottom with keyboard adjustment)
- Tablet-specific offset handling
- Swipe-to-dismiss with configurable thresholds
- Custom component support alongside default banner items
- Comprehensive test coverage with device-specific scenarios

* docs: add floating banner system documentation and cleanup

- Add comprehensive floating-banner.md with architecture diagrams
- Remove incompatible connection_banner/index.ts file
- Update device.ts hooks for better keyboard handling
- Simplify screens/index.tsx floating banner registration
- Update test/setup.ts to remove deprecated keyboard mocks
- Clean up keyboard height logic and ESLint issues

The documentation covers:
- System architecture and component relationships
- API reference and usage patterns
- Performance considerations and best practices
- Integration points and troubleshooting guide
- Comprehensive testing strategy

All tests now pass with the updated setup.

* fix issue with translation file

* some self cleanup.

* renamed index.tsx => Banner.tsx

* creaete meaningful tests for Banner component and all the hooks.

* fix tests

* cleanup based on initial review by AI

* dismissible was set to true, changing to what was configured.

* making title and message optional

* addressed some comments in PR

* more fixes based on PR review.

* added future enhancement

* dismissOverlay will be awaited
* delay dismissing overlay so we don't have to show a new one all the time

* make the banner stackable

* Fix issue with last banner dismissal delayed by 2s

* update floating-banner test

* clean-up based on review by @enahum

* fix failing test

* fix failiing tests

* rename confusing var

* fixed issue with swipe not working on android

* fix issue w/ android not registering touch events behind the overlay

* fix failing test

* animate the banner moving up when bottom banner first appear.

* removed unused functions and update tests

* add useMemo and useCallback

* update jsdoc to say dismissable is default true

* fix failing test
2025-10-12 09:33:51 -06:00

113 lines
3.2 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {render, screen} from '@testing-library/react-native';
import React from 'react';
import {Text} from 'react-native';
import Banner from './Banner';
import {useBanner} from './hooks/useBanner';
jest.mock('./hooks/useBanner');
jest.mock('react-native-reanimated', () => {
const {View} = require('react-native');
return {
__esModule: true,
default: {View},
};
});
jest.mock('react-native-gesture-handler', () => {
const ReactLib = require('react');
return {
GestureDetector: ({children}: {children: React.ReactNode}) => {
return ReactLib.createElement('View', {testID: 'gesture-detector'}, children);
},
};
});
describe('Banner', () => {
const mockUseBanner = jest.mocked(useBanner);
beforeEach(() => {
jest.clearAllMocks();
mockUseBanner.mockReturnValue({
animatedStyle: {
opacity: 1,
transform: [{translateY: 0}, {translateX: 0}],
},
swipeGesture: {} as unknown as ReturnType<typeof useBanner>['swipeGesture'],
});
});
it('renders children and animated view', () => {
render(
<Banner>
<Text testID='banner-content'>{'Test Content'}</Text>
</Banner>,
);
expect(screen.getByTestId('banner-content')).toBeTruthy();
expect(screen.getByTestId('banner-animated-view')).toBeTruthy();
});
it('does not use GestureDetector when not dismissible', () => {
render(
<Banner dismissible={false}>
<Text testID='banner-content'>{'Test Content'}</Text>
</Banner>,
);
expect(screen.queryByTestId('gesture-detector')).toBeNull();
expect(screen.getByTestId('banner-content')).toBeTruthy();
});
it('uses GestureDetector when dismissible', () => {
render(
<Banner dismissible={true}>
<Text testID='banner-content'>{'Test Content'}</Text>
</Banner>,
);
expect(screen.getByTestId('gesture-detector')).toBeTruthy();
expect(screen.getByTestId('banner-content')).toBeTruthy();
});
it('passes correct props to useBanner hook', () => {
const onDismiss = jest.fn();
render(
<Banner
animationDuration={300}
dismissible={true}
onDismiss={onDismiss}
swipeThreshold={150}
>
<Text testID='banner-content'>{'Test Content'}</Text>
</Banner>,
);
expect(mockUseBanner).toHaveBeenCalledWith({
animationDuration: 300,
dismissible: true,
swipeThreshold: 150,
onDismiss,
});
});
it('uses default prop values when not provided', () => {
render(
<Banner>
<Text testID='banner-content'>{'Test Content'}</Text>
</Banner>,
);
expect(mockUseBanner).toHaveBeenCalledWith({
animationDuration: 250,
dismissible: false,
swipeThreshold: 100,
onDismiss: undefined,
});
});
});