diff --git a/app/component-library/components-temp/HeaderCenter/getHeaderCenterNavbarOptions.test.tsx b/app/component-library/components-temp/HeaderCenter/getHeaderCenterNavbarOptions.test.tsx new file mode 100644 index 000000000000..924722f43458 --- /dev/null +++ b/app/component-library/components-temp/HeaderCenter/getHeaderCenterNavbarOptions.test.tsx @@ -0,0 +1,115 @@ +// Third party dependencies. +import React from 'react'; +import { render, fireEvent } from '@testing-library/react-native'; + +// Internal dependencies. +import getHeaderCenterNavbarOptions from './getHeaderCenterNavbarOptions'; + +const TITLE_TEST_ID = 'header-center-title'; +const BACK_BUTTON_TEST_ID = 'header-center-back-button'; +const CLOSE_BUTTON_TEST_ID = 'header-center-close-button'; + +describe('getHeaderCenterNavbarOptions', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('return value', () => { + it('returns object with header function', () => { + const options = getHeaderCenterNavbarOptions({ title: 'Test' }); + + expect(options).toHaveProperty('header'); + expect(typeof options.header).toBe('function'); + }); + + it('returns React element when header function is called', () => { + const options = getHeaderCenterNavbarOptions({ title: 'Test' }); + + const headerElement = options.header(); + + expect(React.isValidElement(headerElement)).toBe(true); + }); + }); + + describe('rendering', () => { + it('renders HeaderCenter with title', () => { + const options = getHeaderCenterNavbarOptions({ + title: 'Settings', + titleProps: { testID: TITLE_TEST_ID }, + }); + const Header = options.header; + const { getByTestId } = render(
); + + expect(getByTestId(TITLE_TEST_ID)).toBeOnTheScreen(); + }); + + it('renders HeaderCenter with back button', () => { + const onBack = jest.fn(); + const options = getHeaderCenterNavbarOptions({ + title: 'Settings', + onBack, + backButtonProps: { testID: BACK_BUTTON_TEST_ID }, + }); + const Header = options.header; + const { getByTestId } = render(
); + + expect(getByTestId(BACK_BUTTON_TEST_ID)).toBeOnTheScreen(); + }); + + it('renders HeaderCenter with close button', () => { + const onClose = jest.fn(); + const options = getHeaderCenterNavbarOptions({ + title: 'Settings', + onClose, + closeButtonProps: { testID: CLOSE_BUTTON_TEST_ID }, + }); + const Header = options.header; + const { getByTestId } = render(
); + + expect(getByTestId(CLOSE_BUTTON_TEST_ID)).toBeOnTheScreen(); + }); + }); + + describe('props forwarding', () => { + it('forwards onBack callback to HeaderCenter', () => { + const onBack = jest.fn(); + const options = getHeaderCenterNavbarOptions({ + title: 'Settings', + onBack, + backButtonProps: { testID: BACK_BUTTON_TEST_ID }, + }); + const Header = options.header; + const { getByTestId } = render(
); + + fireEvent.press(getByTestId(BACK_BUTTON_TEST_ID)); + + expect(onBack).toHaveBeenCalledTimes(1); + }); + + it('forwards onClose callback to HeaderCenter', () => { + const onClose = jest.fn(); + const options = getHeaderCenterNavbarOptions({ + title: 'Settings', + onClose, + closeButtonProps: { testID: CLOSE_BUTTON_TEST_ID }, + }); + const Header = options.header; + const { getByTestId } = render(
); + + fireEvent.press(getByTestId(CLOSE_BUTTON_TEST_ID)); + + expect(onClose).toHaveBeenCalledTimes(1); + }); + + it('forwards testID to HeaderCenter container', () => { + const options = getHeaderCenterNavbarOptions({ + title: 'Settings', + testID: 'custom-header', + }); + const Header = options.header; + const { getByTestId } = render(
); + + expect(getByTestId('custom-header')).toBeOnTheScreen(); + }); + }); +}); diff --git a/app/component-library/components-temp/HeaderCenter/getHeaderCenterNavbarOptions.tsx b/app/component-library/components-temp/HeaderCenter/getHeaderCenterNavbarOptions.tsx new file mode 100644 index 000000000000..5df40dcd7f9d --- /dev/null +++ b/app/component-library/components-temp/HeaderCenter/getHeaderCenterNavbarOptions.tsx @@ -0,0 +1,32 @@ +// Third party dependencies. +import React from 'react'; + +// Internal dependencies. +import HeaderCenter from './HeaderCenter'; +import { HeaderCenterProps } from './HeaderCenter.types'; + +/** + * Returns React Navigation screen options with a HeaderCenter component. + * + * @example + * ```tsx + * const options = getHeaderCenterNavbarOptions({ + * title: 'Settings', + * onBack: () => navigation.goBack(), + * onClose: () => navigation.pop(), + * includesTopInset: true, + * }); + * + * + * ``` + * + * @param options - Props to pass to the HeaderCenter component. + * @returns React Navigation screen options object with header property. + */ +const getHeaderCenterNavbarOptions = ( + options: HeaderCenterProps, +): { header: () => React.ReactElement } => ({ + header: () => , +}); + +export default getHeaderCenterNavbarOptions; diff --git a/app/component-library/components-temp/HeaderCenter/index.ts b/app/component-library/components-temp/HeaderCenter/index.ts index dc7c94a28e5f..f6d9784b349b 100644 --- a/app/component-library/components-temp/HeaderCenter/index.ts +++ b/app/component-library/components-temp/HeaderCenter/index.ts @@ -1,2 +1,3 @@ export { default } from './HeaderCenter'; +export { default as getHeaderCenterNavbarOptions } from './getHeaderCenterNavbarOptions'; export type { HeaderCenterProps } from './HeaderCenter.types'; diff --git a/app/component-library/components-temp/HeaderWithTitleLeft/getHeaderWithTitleLeftNavbarOptions.test.tsx b/app/component-library/components-temp/HeaderWithTitleLeft/getHeaderWithTitleLeftNavbarOptions.test.tsx new file mode 100644 index 000000000000..4337a2346174 --- /dev/null +++ b/app/component-library/components-temp/HeaderWithTitleLeft/getHeaderWithTitleLeftNavbarOptions.test.tsx @@ -0,0 +1,99 @@ +// Third party dependencies. +import React from 'react'; +import { render, fireEvent } from '@testing-library/react-native'; + +// Internal dependencies. +import getHeaderWithTitleLeftNavbarOptions from './getHeaderWithTitleLeftNavbarOptions'; + +const CONTAINER_TEST_ID = 'header-with-title-left-container'; +const BACK_BUTTON_TEST_ID = 'header-with-title-left-back-button'; + +describe('getHeaderWithTitleLeftNavbarOptions', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('return value', () => { + it('returns object with header function', () => { + const options = getHeaderWithTitleLeftNavbarOptions({}); + + expect(options).toHaveProperty('header'); + expect(typeof options.header).toBe('function'); + }); + + it('returns React element when header function is called', () => { + const options = getHeaderWithTitleLeftNavbarOptions({}); + + const headerElement = options.header(); + + expect(React.isValidElement(headerElement)).toBe(true); + }); + }); + + describe('rendering', () => { + it('renders HeaderWithTitleLeft with titleLeftProps', () => { + const options = getHeaderWithTitleLeftNavbarOptions({ + testID: CONTAINER_TEST_ID, + titleLeftProps: { + title: 'NFT Name', + topLabel: 'Collection', + }, + }); + const Header = options.header; + const { getByTestId, getByText } = render(
); + + expect(getByTestId(CONTAINER_TEST_ID)).toBeOnTheScreen(); + expect(getByText('NFT Name')).toBeOnTheScreen(); + expect(getByText('Collection')).toBeOnTheScreen(); + }); + + it('renders HeaderWithTitleLeft with back button', () => { + const onBack = jest.fn(); + const options = getHeaderWithTitleLeftNavbarOptions({ + onBack, + backButtonProps: { testID: BACK_BUTTON_TEST_ID }, + }); + const Header = options.header; + const { getByTestId } = render(
); + + expect(getByTestId(BACK_BUTTON_TEST_ID)).toBeOnTheScreen(); + }); + }); + + describe('props forwarding', () => { + it('forwards onBack callback to HeaderWithTitleLeft', () => { + const onBack = jest.fn(); + const options = getHeaderWithTitleLeftNavbarOptions({ + onBack, + backButtonProps: { testID: BACK_BUTTON_TEST_ID }, + }); + const Header = options.header; + const { getByTestId } = render(
); + + fireEvent.press(getByTestId(BACK_BUTTON_TEST_ID)); + + expect(onBack).toHaveBeenCalledTimes(1); + }); + + it('forwards testID to HeaderWithTitleLeft container', () => { + const options = getHeaderWithTitleLeftNavbarOptions({ + testID: 'custom-header', + }); + const Header = options.header; + const { getByTestId } = render(
); + + expect(getByTestId('custom-header')).toBeOnTheScreen(); + }); + + it('forwards titleSectionTestID to title section', () => { + const options = getHeaderWithTitleLeftNavbarOptions({ + titleLeftProps: { title: 'Title' }, + titleSectionTestID: 'title-section', + }); + const Header = options.header; + const { getByTestId } = render(
); + + expect(getByTestId('title-section')).toBeOnTheScreen(); + }); + }); +}); diff --git a/app/component-library/components-temp/HeaderWithTitleLeft/getHeaderWithTitleLeftNavbarOptions.tsx b/app/component-library/components-temp/HeaderWithTitleLeft/getHeaderWithTitleLeftNavbarOptions.tsx new file mode 100644 index 000000000000..1dc2c033474b --- /dev/null +++ b/app/component-library/components-temp/HeaderWithTitleLeft/getHeaderWithTitleLeftNavbarOptions.tsx @@ -0,0 +1,34 @@ +// Third party dependencies. +import React from 'react'; + +// Internal dependencies. +import HeaderWithTitleLeft from './HeaderWithTitleLeft'; +import { HeaderWithTitleLeftProps } from './HeaderWithTitleLeft.types'; + +/** + * Returns React Navigation screen options with a HeaderWithTitleLeft component. + * + * @example + * ```tsx + * const options = getHeaderWithTitleLeftNavbarOptions({ + * onBack: () => navigation.goBack(), + * titleLeftProps: { + * title: 'NFT Name', + * topLabel: 'Collection', + * }, + * includesTopInset: true, + * }); + * + * + * ``` + * + * @param options - Props to pass to the HeaderWithTitleLeft component. + * @returns React Navigation screen options object with header property. + */ +const getHeaderWithTitleLeftNavbarOptions = ( + options: HeaderWithTitleLeftProps, +): { header: () => React.ReactElement } => ({ + header: () => , +}); + +export default getHeaderWithTitleLeftNavbarOptions; diff --git a/app/component-library/components-temp/HeaderWithTitleLeft/index.ts b/app/component-library/components-temp/HeaderWithTitleLeft/index.ts index 6a5de15f41fa..4ab676451502 100644 --- a/app/component-library/components-temp/HeaderWithTitleLeft/index.ts +++ b/app/component-library/components-temp/HeaderWithTitleLeft/index.ts @@ -1,2 +1,3 @@ export { default } from './HeaderWithTitleLeft'; +export { default as getHeaderWithTitleLeftNavbarOptions } from './getHeaderWithTitleLeftNavbarOptions'; export type { HeaderWithTitleLeftProps } from './HeaderWithTitleLeft.types'; diff --git a/app/component-library/components-temp/HeaderWithTitleLeftScrollable/getHeaderWithTitleLeftScrollableNavbarOptions.test.tsx b/app/component-library/components-temp/HeaderWithTitleLeftScrollable/getHeaderWithTitleLeftScrollableNavbarOptions.test.tsx new file mode 100644 index 000000000000..34d61ee5a497 --- /dev/null +++ b/app/component-library/components-temp/HeaderWithTitleLeftScrollable/getHeaderWithTitleLeftScrollableNavbarOptions.test.tsx @@ -0,0 +1,167 @@ +// Third party dependencies. +import React from 'react'; +import { render, fireEvent } from '@testing-library/react-native'; +import { useSharedValue, SharedValue } from 'react-native-reanimated'; + +// Internal dependencies. +import getHeaderWithTitleLeftScrollableNavbarOptions from './getHeaderWithTitleLeftScrollableNavbarOptions'; + +// Mock react-native-reanimated +jest.mock('react-native-reanimated', () => ({ + useSharedValue: jest.fn((initial) => ({ value: initial })), + useAnimatedStyle: jest.fn(() => ({})), + useDerivedValue: jest.fn((fn) => ({ value: fn() })), + withTiming: jest.fn((value) => value), + interpolate: jest.fn((_value, _inputRange, outputRange) => outputRange[0]), + Extrapolation: { CLAMP: 'clamp' }, + default: { + View: 'Animated.View', + }, +})); + +const CONTAINER_TEST_ID = 'header-scrollable-container'; +const BACK_BUTTON_TEST_ID = 'header-scrollable-back-button'; + +// Test wrapper component that provides scrollY +const TestWrapper: React.FC<{ + children: (scrollYValue: SharedValue) => React.ReactNode; +}> = ({ children }) => { + const scrollYValue = useSharedValue(0); + return <>{children(scrollYValue)}; +}; + +describe('getHeaderWithTitleLeftScrollableNavbarOptions', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('return value', () => { + it('returns object with header function', () => { + const mockScrollY = { value: 0 }; + const options = getHeaderWithTitleLeftScrollableNavbarOptions({ + title: 'Send', + scrollY: mockScrollY as SharedValue, + }); + + expect(options).toHaveProperty('header'); + expect(typeof options.header).toBe('function'); + }); + + it('returns React element when header function is called', () => { + const mockScrollY = { value: 0 }; + const options = getHeaderWithTitleLeftScrollableNavbarOptions({ + title: 'Send', + scrollY: mockScrollY as SharedValue, + }); + + const headerElement = options.header(); + + expect(React.isValidElement(headerElement)).toBe(true); + }); + }); + + describe('rendering', () => { + it('renders HeaderWithTitleLeftScrollable with title', () => { + const { getAllByText, getByTestId } = render( + + {(scrollYValue) => { + const options = getHeaderWithTitleLeftScrollableNavbarOptions({ + title: 'Send', + scrollY: scrollYValue, + testID: CONTAINER_TEST_ID, + }); + const Header = options.header; + return
; + }} + , + ); + + expect(getByTestId(CONTAINER_TEST_ID)).toBeOnTheScreen(); + expect(getAllByText('Send').length).toBeGreaterThan(0); + }); + + it('renders HeaderWithTitleLeftScrollable with back button', () => { + const onBack = jest.fn(); + const { getByTestId } = render( + + {(scrollYValue) => { + const options = getHeaderWithTitleLeftScrollableNavbarOptions({ + title: 'Send', + scrollY: scrollYValue, + onBack, + backButtonProps: { testID: BACK_BUTTON_TEST_ID }, + }); + const Header = options.header; + return
; + }} + , + ); + + expect(getByTestId(BACK_BUTTON_TEST_ID)).toBeOnTheScreen(); + }); + + it('renders HeaderWithTitleLeftScrollable with titleLeftProps', () => { + const { getByText } = render( + + {(scrollYValue) => { + const options = getHeaderWithTitleLeftScrollableNavbarOptions({ + title: 'Send', + scrollY: scrollYValue, + titleLeftProps: { + topLabel: 'Sending', + bottomLabel: 'To 0x123...', + }, + }); + const Header = options.header; + return
; + }} + , + ); + + expect(getByText('Sending')).toBeOnTheScreen(); + expect(getByText('To 0x123...')).toBeOnTheScreen(); + }); + }); + + describe('props forwarding', () => { + it('forwards onBack callback to HeaderWithTitleLeftScrollable', () => { + const onBack = jest.fn(); + const { getByTestId } = render( + + {(scrollYValue) => { + const options = getHeaderWithTitleLeftScrollableNavbarOptions({ + title: 'Send', + scrollY: scrollYValue, + onBack, + backButtonProps: { testID: BACK_BUTTON_TEST_ID }, + }); + const Header = options.header; + return
; + }} + , + ); + + fireEvent.press(getByTestId(BACK_BUTTON_TEST_ID)); + + expect(onBack).toHaveBeenCalledTimes(1); + }); + + it('forwards testID to HeaderWithTitleLeftScrollable container', () => { + const { getByTestId } = render( + + {(scrollYValue) => { + const options = getHeaderWithTitleLeftScrollableNavbarOptions({ + title: 'Send', + scrollY: scrollYValue, + testID: 'custom-scrollable-header', + }); + const Header = options.header; + return
; + }} + , + ); + + expect(getByTestId('custom-scrollable-header')).toBeOnTheScreen(); + }); + }); +}); diff --git a/app/component-library/components-temp/HeaderWithTitleLeftScrollable/getHeaderWithTitleLeftScrollableNavbarOptions.tsx b/app/component-library/components-temp/HeaderWithTitleLeftScrollable/getHeaderWithTitleLeftScrollableNavbarOptions.tsx new file mode 100644 index 000000000000..50d1f7f6d3b0 --- /dev/null +++ b/app/component-library/components-temp/HeaderWithTitleLeftScrollable/getHeaderWithTitleLeftScrollableNavbarOptions.tsx @@ -0,0 +1,38 @@ +// Third party dependencies. +import React from 'react'; + +// Internal dependencies. +import HeaderWithTitleLeftScrollable from './HeaderWithTitleLeftScrollable'; +import { HeaderWithTitleLeftScrollableProps } from './HeaderWithTitleLeftScrollable.types'; + +/** + * Returns React Navigation screen options with a HeaderWithTitleLeftScrollable component. + * + * @example + * ```tsx + * const { scrollY, onScroll, expandedHeight, setExpandedHeight } = useHeaderWithTitleLeftScrollable(); + * + * const options = getHeaderWithTitleLeftScrollableNavbarOptions({ + * title: 'Send', + * scrollY, + * onBack: () => navigation.goBack(), + * onExpandedHeightChange: setExpandedHeight, + * titleLeftProps: { + * title: '$4.42', + * topLabel: 'Send', + * }, + * }); + * + * + * ``` + * + * @param options - Props to pass to the HeaderWithTitleLeftScrollable component. + * @returns React Navigation screen options object with header property. + */ +const getHeaderWithTitleLeftScrollableNavbarOptions = ( + options: HeaderWithTitleLeftScrollableProps, +): { header: () => React.ReactElement } => ({ + header: () => , +}); + +export default getHeaderWithTitleLeftScrollableNavbarOptions; diff --git a/app/component-library/components-temp/HeaderWithTitleLeftScrollable/index.ts b/app/component-library/components-temp/HeaderWithTitleLeftScrollable/index.ts index 3277b02bcb72..faf40fcc1343 100644 --- a/app/component-library/components-temp/HeaderWithTitleLeftScrollable/index.ts +++ b/app/component-library/components-temp/HeaderWithTitleLeftScrollable/index.ts @@ -1,5 +1,6 @@ export { default } from './HeaderWithTitleLeftScrollable'; export { default as useHeaderWithTitleLeftScrollable } from './useHeaderWithTitleLeftScrollable'; +export { default as getHeaderWithTitleLeftScrollableNavbarOptions } from './getHeaderWithTitleLeftScrollableNavbarOptions'; export type { HeaderWithTitleLeftScrollableProps, UseHeaderWithTitleLeftScrollableOptions,