Repository: janis-commerce/ui-native
Pull request #59: Appsrn 371 desarrollo de base detail y product detail
Author: login: GonzaFran
Labels: name: documentation
description: Improvements or additions to documentation
name: enhancement
description: New feature or request
Created: 2025-04-28T20:47:01Z | Merged: 2025-04-29T18:45:26Z

LINK DE TICKET: *

https://janiscommerce.atlassian.net/browse/APPSRN-366

DESCRIPCIÓN DEL REQUERIMIENTO: *

Actualmente la APP de Orders (Modulo de Picking) tiene una limitación para mostrar los nombres de los productos en el detalle del mismo, y esa es que aquellos nombres que superan los 52 caracteres son finalizados con “…” por lo que no se muestra en su totalidad.

En algunos casos esto puede representar un problema ya que hay productos que, si bien son similares, la diferencia entre ellos viene detallada en el nombre (medidas o colores). Estos detalles agregan una mayor extensión al nombre del producto, lo que provoca que, en la mayoría de casos, la app no termine de comunicar el nombre correcto al picker.
Necesidad

Para estos casos necesitamos tener la posibilidad de mirar el nombre completo, y para ello se propone contar con un modal que permita visualizar tanto la imagen del producto a mayor escala, como su nombre completo sin puntos suspensivos, tal cuál presenta el diseño 


DESCRIPCIÓN DE LA SOLUCIÓN: *

Se agregaron 2 nuevos componentes:
 El primero es el componente `BaseDetail`, que es una molecula  "base" que sirve para envolver y representar un children dentro de un componente Modal o un componente SwipeUp. Para definir qué componente utilizar en cada caso el `BaseDetail` espera recibir una prop `componentType` que condicionará el tipo de componente representado como wrapper, y que sólo puede ser de tipo 'swipe' o 'modal'. 

El segundo es el componente `ProductDetail` que utiliza internamente el `BaseDetail` para representar, en un modal o un swipe, información asociada a un producto.
La información que espera recibir este componente es:
- la imagen del producto
- su refId
- su marca
- su nombre

Ambos componentes están preparados para recibir una propiedad `ref` que permita acceder a los métodos correspondientes de los componentes swipeUp y modal desde fuera de los mismos.

DATOS EXTRA A TENER EN CUENTA:
Se agregaron componentes secundarios que son utilizados por ambos componentes principales para evitar tener componentes muy extensos y agrupar varias definiciones de tipos, estilos e interfaces en el mismo archivo.
Estos componentes son BaseDetailModal, BaseDetailSwipe y ProductInfo:
 - El primero renderiza el componente `Modal` anidando un componente `scrollView`, lo que permitirá scrollear el children recibido como argumento en `BaseDetail`. Este componente será utilizado por `BaseDetail` cuando el valor recibido para `componentType` sea `modal`

 - El segundo es similar al primero, pero en vez de un `scrollView` renderiza el componente `SwipeUp` anidando el componente `SwipeUpScrollView`. Este cambio de componentes scrolleables se debe a que, para poder scrollear dentro del swipe, es necesario contar con un componente que sea adecuado y que permita responder al gesto del scroll. Este componente se renderizará cuando el valor de `componentType` para `BaseDetail` sea `swipe`

- El tercero es utilizado por `ProductDetail` para representar los valores recibidos como argumentos para las propiedades `image`, `productName`, `brand` y `refId`
 
CÓMO SE PUEDE PROBAR? *

Para probarlo alcanza con levantar storybook android y/o web,

SCREENSHOTS:
![productDetailPicking](https://github.com/user-attachments/assets/ab28e8f0-631b-46e4-8c69-a7181ba95c8a)
![productDetailPickingSwipe](https://github.com/user-attachments/assets/d99e7587-9a8c-4f3a-bc64-bb38a4baf5f2)


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Introduced `ProductDetail`, a flexible component for displaying product information in modal or swipe-up styles.
  - Added `BaseDetail`, `BaseDetailModal`, and `BaseDetailSwipe` components for customizable detail presentation.
  - Included `ProductInfo` component for structured display of product brand, name, reference ID, and image.
  - Exported `BaseDetail` and `ProductDetail` for external use.
  - Added Storybook stories for both modal and swipe-up variants of `ProductDetail`.

- **Tests**
  - Added comprehensive tests for `BaseDetail`, `BaseDetailModal`, `BaseDetailSwipe`, `ProductDetail`, and `ProductInfo` components.

- **Refactor**
  - Renamed and exported modal method interface for improved clarity and consistency.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/src/components/atoms/Modal/index.tsx b/src/components/atoms/Modal/index.tsx
index 5f2812c..3ac1486 100644
--- a/src/components/atoms/Modal/index.tsx
+++ b/src/components/atoms/Modal/index.tsx
@@ -20,7 +20,7 @@ export interface UIModalProps extends NativeModalProps {
 	canClose?: boolean;
 }
 
-interface RefProps {
+export interface ModalMethods {
 	open?: () => void;
 	close?: () => void;
 }
@@ -74,7 +74,7 @@ const styles = StyleSheet.create({
 	},
 });
 
-const Modal = forwardRef<RefProps, UIModalProps>(
+const Modal = forwardRef<ModalMethods, UIModalProps>(
 	(
 		{
 			children = null,
diff --git a/src/components/molecules/BaseDetail/components/BaseDetailModal/index.test.tsx b/src/components/molecules/BaseDetail/components/BaseDetailModal/index.test.tsx
new file mode 100644
index 0000000..e176b49
--- /dev/null
+++ b/src/components/molecules/BaseDetail/components/BaseDetailModal/index.test.tsx
@@ -0,0 +1,18 @@
+import React from 'react';
+import BaseDetailModal from './index';
+import {create} from 'react-test-renderer';
+import {View, Text} from 'react-native';
+
+describe('BaseDetailModal', () => {
+	it('should return modal with nested children', () => {
+		const BaseDetailModalComp = create(
+			<BaseDetailModal componentType="modal">
+				<View>
+					<Text>Modal component</Text>
+				</View>
+			</BaseDetailModal>
+		).root;
+
+		expect(BaseDetailModalComp).toBeTruthy();
+	});
+});
diff --git a/src/components/molecules/BaseDetail/components/BaseDetailModal/index.tsx b/src/components/molecules/BaseDetail/components/BaseDetailModal/index.tsx
new file mode 100644
index 0000000..545d4ee
--- /dev/null
+++ b/src/components/molecules/BaseDetail/components/BaseDetailModal/index.tsx
@@ -0,0 +1,18 @@
+import React, {forwardRef} from 'react';
+import {BaseModalProps} from '../../types';
+import Modal, {ModalMethods} from 'atoms/Modal';
+import {ScrollView} from 'react-native';
+
+const BaseDetailModal = forwardRef<ModalMethods, BaseModalProps>((props, ref) => {
+	const {children, scrollProps = {}, ...rest} = props;
+
+	return (
+		<Modal ref={ref} {...rest}>
+			<ScrollView showsVerticalScrollIndicator={false} {...scrollProps}>
+				{children}
+			</ScrollView>
+		</Modal>
+	);
+});
+
+export default BaseDetailModal;
diff --git a/src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.test.tsx b/src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.test.tsx
new file mode 100644
index 0000000..9bc7dba
--- /dev/null
+++ b/src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.test.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import BaseDetailSwipe from './index';
+import {create} from 'react-test-renderer';
+import {SwipeUpScrollView} from 'atoms/SwipeUp/childComponents';
+
+describe('BaseDetailSwipe', () => {
+	it('should return Scrolleable Swipe with nested children', () => {
+		const BaseDetailSwipeComp = create(
+			<BaseDetailSwipe componentType="swipe">{null}</BaseDetailSwipe>
+		).root;
+
+		const ScrolleableComp = BaseDetailSwipeComp.findByType(SwipeUpScrollView);
+
+		expect(ScrolleableComp).toBeTruthy();
+		expect(BaseDetailSwipeComp).toBeTruthy();
+	});
+});
diff --git a/src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.tsx b/src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.tsx
new file mode 100644
index 0000000..e34ed86
--- /dev/null
+++ b/src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.tsx
@@ -0,0 +1,19 @@
+import React, {forwardRef} from 'react';
+import SwipeUp from 'atoms/SwipeUp';
+import {BaseSwipeUpProps} from 'molecules/BaseDetail/types';
+import {SwipeUpScrollView} from 'atoms/SwipeUp/childComponents';
+import BottomSheet from '@gorhom/bottom-sheet/lib/typescript/components/bottomSheet/BottomSheet';
+
+const BaseDetailSwipe = forwardRef<BottomSheet, BaseSwipeUpProps>((props, ref) => {
+	const {children, scrollProps = {}, ...rest} = props;
+
+	return (
+		<SwipeUp ref={ref} {...rest}>
+			<SwipeUpScrollView showsVerticalScrollIndicator={false} {...scrollProps}>
+				{children}
+			</SwipeUpScrollView>
+		</SwipeUp>
+	);
+});
+
+export default BaseDetailSwipe;
diff --git a/src/components/molecules/BaseDetail/components/index.tsx b/src/components/molecules/BaseDetail/components/index.tsx
new file mode 100644
index 0000000..6cfa3f0
--- /dev/null
+++ b/src/components/molecules/BaseDetail/components/index.tsx
@@ -0,0 +1,4 @@
+import BaseDetailModal from './BaseDetailModal';
+import BaseDetailSwipe from './BaseDetailSwipe';
+
+export {BaseDetailModal, BaseDetailSwipe};
diff --git a/src/components/molecules/BaseDetail/index.test.tsx b/src/components/molecules/BaseDetail/index.test.tsx
new file mode 100644
index 0000000..3bed1f0
--- /dev/null
+++ b/src/components/molecules/BaseDetail/index.test.tsx
@@ -0,0 +1,31 @@
+import React, {createRef} from 'react';
+import {create} from 'react-test-renderer';
+import BaseDetail, {BaseDetailMethods} from './index';
+import {BaseDetailModal, BaseDetailSwipe} from './components';
+
+describe('BaseDetail', () => {
+	it('should return BaseDetailSwipe when componentType is "swipe"', () => {
+		const BaseDetailComp = create(<BaseDetail componentType="swipe">{null}</BaseDetail>).root;
+
+		const BaseDetailSwipeComp = BaseDetailComp.findByType(BaseDetailSwipe);
+
+		expect(BaseDetailSwipeComp).toBeTruthy();
+	});
+
+	it('should return BaseDetailModal when componentType is "modal"', () => {
+		const ModalRef = createRef<BaseDetailMethods>();
+
+		const BaseDetailComp = create(
+			<BaseDetail componentType="modal" ref={ModalRef}>
+				{null}
+			</BaseDetail>
+		).root;
+
+		const BaseDetailModalComp = BaseDetailComp.findByType(BaseDetailModal);
+
+		expect(ModalRef.current).toBeTruthy();
+		expect(typeof ModalRef.current?.open).toBe('function');
+		expect(typeof ModalRef.current?.close).toBe('function');
+		expect(BaseDetailModalComp).toBeTruthy();
+	});
+});
diff --git a/src/components/molecules/BaseDetail/index.tsx b/src/components/molecules/BaseDetail/index.tsx
new file mode 100644
index 0000000..8e5f335
--- /dev/null
+++ b/src/components/molecules/BaseDetail/index.tsx
@@ -0,0 +1,18 @@
+import React, {forwardRef} from 'react';
+import {BaseModalProps, BaseSwipeUpProps} from './types';
+import {BaseDetailModal, BaseDetailSwipe} from './components';
+import {ModalMethods} from 'atoms/Modal';
+import {BottomSheetMethods} from '@gorhom/bottom-sheet/lib/typescript/types';
+
+type BaseDetailProps = BaseModalProps | BaseSwipeUpProps;
+export type BaseDetailMethods = ModalMethods & BottomSheetMethods;
+
+const BaseDetail = forwardRef<BaseDetailMethods, BaseDetailProps>((props, ref) => {
+	if (props.componentType === 'swipe') {
+		return <BaseDetailSwipe {...props} ref={ref} enablePanDownToClose index={-1} />;
+	}
+
+	return <BaseDetailModal {...props} fullScreen componentType="modal" ref={ref} />;
+});
+
+export default BaseDetail;
diff --git a/src/components/molecules/BaseDetail/types/index.ts b/src/components/molecules/BaseDetail/types/index.ts
new file mode 100644
index 0000000..41922b3
--- /dev/null
+++ b/src/components/molecules/BaseDetail/types/index.ts
@@ -0,0 +1,12 @@
+import {BottomSheetScrollViewProps} from '@gorhom/bottom-sheet/lib/typescript/components/bottomSheetScrollable/types';
+import {UIModalProps} from 'atoms/Modal';
+import {SwipeUpProps} from 'atoms/SwipeUp';
+import {ScrollViewProps} from 'react-native';
+
+export type ComponentType = 'modal' | 'swipe';
+export type BaseModalProps = {componentType: 'modal'} & UIModalProps & {
+		scrollProps?: ScrollViewProps;
+	};
+export type BaseSwipeUpProps = {componentType: 'swipe'} & SwipeUpProps & {
+		scrollProps?: BottomSheetScrollViewProps;
+	};
diff --git a/src/components/organisms/ProductDetail/components/ProductInfo/index.test.tsx b/src/components/organisms/ProductDetail/components/ProductInfo/index.test.tsx
new file mode 100644
index 0000000..660be08
--- /dev/null
+++ b/src/components/organisms/ProductDetail/components/ProductInfo/index.test.tsx
@@ -0,0 +1,50 @@
+import React from 'react';
+import {create} from 'react-test-renderer';
+import ProductInfo from './index';
+import Image from 'atoms/Image';
+import Typography from 'atoms/Typography';
+
+describe('ProductInfo', () => {
+	it('should render Image component when receive image argument', () => {
+		const ProductInfoComp = create(
+			<ProductInfo image="https://avatars.githubusercontent.com/u/49998302?s=200&v=4" />
+		).root;
+
+		const ImageComp = ProductInfoComp.findByType(Image);
+
+		expect(ImageComp).toBeTruthy();
+		expect(ImageComp.props.source).toEqual({
+			uri: 'https://avatars.githubusercontent.com/u/49998302?s=200&v=4',
+		});
+		expect(ProductInfoComp).toBeTruthy();
+	});
+
+	it('should render Typography component when receive productName argument', () => {
+		const ProductInfoComp = create(
+			<ProductInfo productName="Campera de Hombre H&G Softshell Estampado PRINT Gris Melange Talle XXL" />
+		).root;
+
+		const TypographyComp = ProductInfoComp.findByType(Typography);
+
+		expect(TypographyComp).toBeTruthy();
+		expect(ProductInfoComp).toBeTruthy();
+	});
+
+	it('should render Typography component when receive refId argument', () => {
+		const ProductInfoComp = create(<ProductInfo refId="4AR6353-010" />).root;
+
+		const TypographyComp = ProductInfoComp.findByType(Typography);
+
+		expect(TypographyComp).toBeTruthy();
+		expect(ProductInfoComp).toBeTruthy();
+	});
+
+	it('should render Typography component when receive brand argument', () => {
+		const ProductInfoComp = create(<ProductInfo brand="Janis" />).root;
+
+		const TypographyComp = ProductInfoComp.findByType(Typography);
+
+		expect(TypographyComp).toBeTruthy();
+		expect(ProductInfoComp).toBeTruthy();
+	});
+});
diff --git a/src/components/organisms/ProductDetail/components/ProductInfo/index.tsx b/src/components/organisms/ProductDetail/components/ProductInfo/index.tsx
new file mode 100644
index 0000000..fa5496f
--- /dev/null
+++ b/src/components/organisms/ProductDetail/components/ProductInfo/index.tsx
@@ -0,0 +1,71 @@
+import React from 'react';
+import Image from 'atoms/Image';
+import Typography from 'atoms/Typography';
+import {View, StyleSheet} from 'react-native';
+import {horizontalScale, moderateScale, verticalScale} from 'scale';
+import {palette} from 'theme/palette';
+
+export interface ProductProps {
+	brand?: string;
+	productName?: string;
+	refId?: string;
+	image?: string;
+}
+
+const styles = StyleSheet.create({
+	Wrapper: {
+		flex: 1,
+		alignItems: 'center',
+		paddingBottom: verticalScale(12),
+		marginHorizontal: horizontalScale(24),
+	},
+	productImage: {
+		height: moderateScale(312),
+		width: moderateScale(312),
+		marginTop: verticalScale(16),
+	},
+	productInfo: {
+		height: 'auto',
+		width: moderateScale(312),
+		marginTop: verticalScale(24),
+	},
+	productName: {
+		marginTop: verticalScale(16),
+	},
+	referenceId: {
+		marginTop: verticalScale(16),
+	},
+});
+
+const ProductInfo = ({brand, productName, refId, image}: ProductProps) => {
+	return (
+		<View style={styles.Wrapper}>
+			{!!image && <Image source={{uri: String(image)}} style={styles.productImage} />}
+			<View style={styles.productInfo}>
+				{!!brand && (
+					<Typography color={palette.primary.main} type="overline" size="large">
+						{String(brand)}
+					</Typography>
+				)}
+				{!!productName && (
+					<Typography
+						color={palette.black.main}
+						type="heading"
+						size="medium"
+						style={styles.productName}>
+						{String(productName)}
+					</Typography>
+				)}
+				{!!refId && (
+					<Typography
+						color={palette.black.main}
+						type="heading"
+						size="small"
+						style={styles.referenceId}>{`# ${String(refId)}`}</Typography>
+				)}
+			</View>
+		</View>
+	);
+};
+
+export default ProductInfo;
diff --git a/src/components/organisms/ProductDetail/index.test.tsx b/src/components/organisms/ProductDetail/index.test.tsx
new file mode 100644
index 0000000..f66d27f
--- /dev/null
+++ b/src/components/organisms/ProductDetail/index.test.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import ProductDetail from './index';
+import {create} from 'react-test-renderer';
+import BaseDetail from 'molecules/BaseDetail';
+import Modal from 'atoms/Modal';
+import SwipeUp from 'atoms/SwipeUp';
+
+describe('ProductDetail component', () => {
+	it('should render modal BaseDetail components', () => {
+		const ProductDetailComponent = create(
+			<ProductDetail
+				componentType="modal"
+				image="https://avatars.githubusercontent.com/u/49998302?s=200&v=4"
+				refId="4AR6353-010"
+				productName="Campera de Hombre H&G Softshell Estampado PRINT Gris Melange Talle XXL"
+				brand="H&G"
+			/>
+		).root;
+
+		const BaseDetailComp = ProductDetailComponent.findByType(BaseDetail);
+		const ModalComp = ProductDetailComponent.findByType(Modal);
+
+		expect(ModalComp).toBeTruthy();
+		expect(BaseDetailComp).toBeTruthy();
+	});
+
+	it('should render swipe BaseDetail components', () => {
+		const ProductDetailComponent = create(
+			<ProductDetail
+				componentType="swipe"
+				image="https://avatars.githubusercontent.com/u/49998302?s=200&v=4"
+				refId="4AR6353-010"
+				productName="Campera de Hombre H&G Softshell Estampado PRINT Gris Melange Talle XXL"
+				brand="H&G">
+				<></>
+			</ProductDetail>
+		).root;
+
+		const BaseDetailComp = ProductDetailComponent.findByType(BaseDetail);
+		const SwipeUpComp = ProductDetailComponent.findByType(SwipeUp);
+
+		expect(SwipeUpComp).toBeTruthy();
+		expect(BaseDetailComp).toBeTruthy();
+	});
+});
diff --git a/src/components/organisms/ProductDetail/index.tsx b/src/components/organisms/ProductDetail/index.tsx
new file mode 100644
index 0000000..68c111d
--- /dev/null
+++ b/src/components/organisms/ProductDetail/index.tsx
@@ -0,0 +1,22 @@
+import React, {forwardRef} from 'react';
+import {BaseModalProps, BaseSwipeUpProps} from 'molecules/BaseDetail/types';
+import BaseDetail from 'molecules/BaseDetail';
+import ProductInfo, {ProductProps} from './components/ProductInfo';
+import {ModalMethods} from 'atoms/Modal';
+import {BottomSheetMethods} from '@gorhom/bottom-sheet/lib/typescript/types';
+
+type ProductDetailProps = (ProductProps & BaseModalProps) | (ProductProps & BaseSwipeUpProps);
+
+type BaseDetailMethods = ModalMethods & BottomSheetMethods;
+
+const ProductDetail = forwardRef<BaseDetailMethods, ProductDetailProps>((props, ref) => {
+	const {brand, productName, refId, image, ...rest} = props;
+
+	return (
+		<BaseDetail ref={ref} {...rest}>
+			<ProductInfo image={image} refId={refId} productName={productName} brand={brand} />
+		</BaseDetail>
+	);
+});
+
+export default ProductDetail;
diff --git a/src/index.ts b/src/index.ts
index a3e65a3..a40cd2e 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -29,12 +29,14 @@ import SwipeList from 'molecules/SwipeList';
 import ItemSelectionButton from 'molecules/ItemSelectionButton';
 import MainCardList from 'molecules/MainCardList';
 import Input from 'molecules/Input';
+import BaseDetail from 'molecules/BaseDetail';
 
 // Organisms
 import LoadingFullScreen from 'organisms/LoadingFullScreen';
 import FullScreenMessage from 'organisms/FullScreenMessage';
 import SwipeItemSelectionList from 'organisms/SwipeItemSelectionList';
 import ErrorBoundary from 'organisms/ErrorBoundary';
+import ProductDetail from 'organisms/ProductDetail';
 
 // Misc
 import {palette} from 'theme/palette';
@@ -77,4 +79,6 @@ export {
 	Collapsible,
 	ErrorBoundary,
 	Modal,
+	BaseDetail,
+	ProductDetail,
 };
diff --git a/storybook/stories/ProductDetail/ProductDetail.stories.js b/storybook/stories/ProductDetail/ProductDetail.stories.js
new file mode 100644
index 0000000..5ddf741
--- /dev/null
+++ b/storybook/stories/ProductDetail/ProductDetail.stories.js
@@ -0,0 +1,95 @@
+import React, {useRef} from 'react';
+import ProductDetail from 'organisms/ProductDetail';
+import {View, Pressable, StyleSheet, Text} from 'react-native';
+import {GestureHandlerRootView} from 'react-native-gesture-handler';
+
+const styles = StyleSheet.create({
+	centeredView: {
+		flex: 1,
+		justifyContent: 'center',
+		alignItems: 'center',
+	},
+	modalView: {
+		marginVertical: 15,
+	},
+	button: {
+		borderRadius: 20,
+		padding: 10,
+		elevation: 2,
+	},
+	buttonOpen: {
+		backgroundColor: '#2196F3',
+	},
+	buttonClose: {
+		backgroundColor: '#2196F3',
+	},
+	textStyle: {
+		color: 'white',
+		fontWeight: 'bold',
+		textAlign: 'center',
+	},
+	modalText: {
+		marginBottom: 15,
+		textAlign: 'center',
+	},
+	fullScreenView: {
+		marginHorizontal: 10,
+		paddingBottom: 10,
+	},
+});
+
+const OpenDetail = ({...props}) => {
+	return (
+		<Pressable style={[styles.button, styles.buttonOpen]} {...props}>
+			<Text style={styles.textStyle}>Open Detail</Text>
+		</Pressable>
+	);
+};
+
+export const ModalDetail = ({...props}) => {
+	const ModalRef = useRef();
+
+	const handleOpenModal = () => {
+		ModalRef.current.open();
+	};
+
+	return (
+		<View style={styles.centeredView}>
+			<OpenDetail onPress={handleOpenModal} />
+			<ProductDetail componentType="modal" ref={ModalRef} showCloseButton {...props} />
+		</View>
+	);
+};
+
+export const SwipeDetail = ({...props}) => {
+	const SwipeRef = useRef();
+
+	const handleOpenModal = () => {
+		SwipeRef.current.expand();
+	};
+
+	return (
+		<GestureHandlerRootView style={StyleSheet.absoluteFill}>
+			<OpenDetail onPress={handleOpenModal} />
+			<ProductDetail componentType="swipe" ref={SwipeRef} {...props} />
+		</GestureHandlerRootView>
+	);
+};
+
+ModalDetail.args = {
+	image: 'https://avatars.githubusercontent.com/u/49998302?s=200&v=4',
+	refId: '4AR6353-010',
+	productName: 'Campera de Hombre H&G Softshell Estampado PRINT Gris Melange Talle XXL',
+	brand: 'H&G',
+};
+
+SwipeDetail.args = {
+	image: 'https://avatars.githubusercontent.com/u/49998302?s=200&v=4',
+	refId: '4AR6353-010',
+	productName: 'Campera de Hombre H&G Softshell Estampado PRINT Gris Melange Talle XXL',
+	brand: 'H&G',
+};
+
+export default {
+	title: 'Components/ProductDetail',
+};


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (21)</summary><blockquote>

<details>
<summary>src/components/molecules/BaseDetail/components/BaseDetailModal/index.test.tsx (1)</summary><blockquote>

`6-18`: **Basic rendering test implemented**

While the test confirms that the component renders without crashing, consider adding tests for:
1. Modal opening/closing functionality
2. Proper forwarding of props to underlying components
3. Scrolling behavior inside the modal


```javascript
// Example additional test
it('should forward ref with open/close methods', () => {
  const ref = React.createRef();
  create(
    <BaseDetailModal componentType="modal" ref={ref}>
      <View><Text>Test content</Text></View>
    </BaseDetailModal>
  );
  
  expect(ref.current).toBeTruthy();
  expect(typeof ref.current.open).toBe('function');
  expect(typeof ref.current.close).toBe('function');
});
```

</blockquote></details>
<details>
<summary>src/components/molecules/BaseDetail/index.test.tsx (2)</summary><blockquote>

`10-10`: **Remove unnecessary Fragment elements**

The empty fragments (`<></>`) are unnecessary and flagged by static analysis. Use `null` instead since you're just testing component rendering, not the children's behavior.


```diff
-			<BaseDetail componentType="swipe">
-				<></>
+			<BaseDetail componentType="swipe">
+				{null}

-			<BaseDetail componentType="modal">
-				<></>
+			<BaseDetail componentType="modal">
+				{null}
```


Also applies to: 22-22

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 10-10: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment


(lint/complexity/noUselessFragments)

</details>

</details>

---

`6-30`: **Consider testing ref forwarding and prop passing**

The current tests only check component type rendering. Consider adding tests for:
1. Forwarding refs to access modal/swipe methods
2. Proper passing of props to child components
3. Testing interaction with both modal and swipe variants


```javascript
// Example ref forwarding test
it('should forward ref with appropriate methods', () => {
  const ref = React.createRef();
  create(
    <BaseDetail componentType="modal" ref={ref}>
      <Text>Test</Text>
    </BaseDetail>
  );
  
  expect(ref.current).toBeTruthy();
  expect(typeof ref.current.open).toBe('function');
  expect(typeof ref.current.close).toBe('function');
});
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 10-10: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment


(lint/complexity/noUselessFragments)

---

[error] 22-22: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment


(lint/complexity/noUselessFragments)

</details>

</details>

</blockquote></details>
<details>
<summary>src/components/organisms/ProductDetail/index.test.tsx (2)</summary><blockquote>

`35-36`: **Remove unnecessary Fragment**

The empty Fragment `<></>` used as a child element is unnecessary here. Since it doesn't contain any content, you can either remove it entirely if you're just testing the structure, or add a meaningful child component for a more realistic test.


```diff
- <></>
+
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 35-35: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment


(lint/complexity/noUselessFragments)

</details>

</details>

---

`8-45`: **Enhance test coverage**

The current tests verify the basic component structure, but could be improved to ensure complete coverage:

1. Test props are properly forwarded to child components
2. Add a negative test case for invalid componentType values 
3. Verify handlers are properly connected (e.g., testing the ref functionality)



Example of testing prop forwarding:
```tsx
// Add to the first test
const imageProps = ModalComp.findByType(Image).props;
expect(imageProps.source).toEqual({uri: "https://avatars.githubusercontent.com/u/49998302?s=200&v=4"});
expect(ModalComp.props.fullScreen).toBe(true);
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 35-35: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment


(lint/complexity/noUselessFragments)

</details>

</details>

</blockquote></details>
<details>
<summary>src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.test.tsx (2)</summary><blockquote>

`10-10`: **Remove unnecessary Fragment**

The empty Fragment `<></>` is unnecessary and can be removed. If you need to test with children, consider using a more meaningful test component.


```diff
- <></>
+
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 10-10: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment


(lint/complexity/noUselessFragments)

</details>

</details>

---

`7-18`: **Enhance test coverage**

The test verifies basic component rendering but has several improvement opportunities:

1. Test that props are correctly passed to the SwipeUpScrollView component
2. Test with meaningful children to ensure they're rendered correctly
3. Verify the ref forwarding functionality



Example of improved test:
```tsx
it('should forward props to SwipeUpScrollView', () => {
  const customScrollProps = { showsVerticalScrollIndicator: true };
  const BaseDetailSwipeComp = create(
    <BaseDetailSwipe componentType="swipe" scrollProps={customScrollProps}>
      <Text>Test content</Text>
    </BaseDetailSwipe>
  ).root;

  const ScrollViewComp = BaseDetailSwipeComp.findByType(SwipeUpScrollView);
  expect(ScrollViewComp.props.showsVerticalScrollIndicator).toBe(true);
  expect(ScrollViewComp.props.children).toBeTruthy();
});
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 10-10: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment


(lint/complexity/noUselessFragments)

</details>

</details>

</blockquote></details>
<details>
<summary>src/components/molecules/BaseDetail/components/BaseDetailModal/index.tsx (2)</summary><blockquote>

`7-17`: **Add component documentation**

This component would benefit from JSDoc style documentation to explain its purpose, props, and usage. As a forwarded ref component that's part of a complex UI pattern, clear documentation will help other developers understand how to use it.



```diff
+/**
+ * BaseDetailModal - A modal component with scrollable content
+ * 
+ * @component
+ * @param {Object} props - Component props
+ * @param {React.ReactNode} props.children - Content to render inside the modal
+ * @param {Object} props.scrollProps - Props to pass to the ScrollView component
+ * @param {React.Ref<ModalMethods>} ref - Ref to access modal methods (open/close)
+ * @returns {React.ReactElement} A modal with scrollable content
+ */
const BaseDetailModal = forwardRef<ModalMethods, BaseModalProps>((props, ref) => {
  const {children, scrollProps = {}, ...rest} = props;

  return (
    <Modal ref={ref} {...rest}>
      <ScrollView showsVerticalScrollIndicator={false} {...scrollProps}>
        {children}
      </ScrollView>
    </Modal>
  );
});
+
+// Display name for debugging
+BaseDetailModal.displayName = 'BaseDetailModal';
```

---

`11-15`: **Consider handling content overflow**

The modal contains a ScrollView, but there's no handling for very large content that might impact performance. Consider adding props to control maximum height or using FlatList instead of ScrollView for large amounts of content.



You could add a prop for maxHeight:

```diff
const BaseDetailModal = forwardRef<ModalMethods, BaseModalProps>((props, ref) => {
-  const {children, scrollProps = {}, ...rest} = props;
+  const {children, scrollProps = {}, maxHeight, ...rest} = props;

  return (
    <Modal ref={ref} {...rest}>
-      <ScrollView showsVerticalScrollIndicator={false} {...scrollProps}>
+      <ScrollView 
+        showsVerticalScrollIndicator={false}
+        style={maxHeight ? { maxHeight } : undefined}
+        {...scrollProps}
+      >
        {children}
      </ScrollView>
    </Modal>
  );
});
```

</blockquote></details>
<details>
<summary>src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.tsx (2)</summary><blockquote>

`1-7`: **Check for unnecessary import duplication**

There's a duplication in your imports: `React` is imported twice - once directly (line 1) and once via `forwardRef` (line 4).


```diff
-import React from 'react';
+import React, {forwardRef} from 'react';
-import {forwardRef} from 'react';
```

---

`20-20`: **Add component display name for debugging**

Consider adding a display name to make the component easier to identify in React DevTools:


```diff
const BaseDetailSwipe = forwardRef<BottomSheet, BaseSwipeUpProps>((props, ref) => {
    // component implementation
});
+BaseDetailSwipe.displayName = 'BaseDetailSwipe';

export default BaseDetailSwipe;
```

</blockquote></details>
<details>
<summary>src/components/organisms/ProductDetail/components/ProductInfo/index.tsx (4)</summary><blockquote>

`1-8`: **Consider organizing imports more systematically**

The scaling utility imports could be consolidated and organized better.


```diff
import React from 'react';
import Image from 'atoms/Image';
import Typography from 'atoms/Typography';
import {View, StyleSheet} from 'react-native';
-import {horizontalScale, moderateScale} from 'scale';
-import {verticalScale} from 'scale';
+import {horizontalScale, verticalScale, moderateScale} from 'scale';
import {palette} from 'theme/palette';
```

---

`9-14`: **Add JSDoc comments to interface for better documentation**

Consider adding descriptive JSDoc comments to the ProductProps interface to improve documentation.


```diff
+/**
+ * Props for the ProductInfo component
+ * @property {string} [brand] - The product brand name
+ * @property {string} [productName] - The full product name
+ * @property {string} [refId] - The product reference ID
+ * @property {string} [image] - The product image URL
+ */
export interface ProductProps {
	brand?: string;
	productName?: string;
	refId?: string;
	image?: string;
}
```

---

`16-39`: **Consider extracting text labels to constants or providing them as optional props**

The styles implementation looks good, but the formatting of the reference ID with `#` is hardcoded in the render function.


This would make the component more flexible if formatting requirements change:

```diff
+const REF_ID_PREFIX = '# ';

const styles = StyleSheet.create({
    // styles remain the same
});
```

Then in the render:
```diff
-<Typography color={palette.black.main} type="heading" size="small" style={styles.referenceId}>{`# ${String(refId)}`}</Typography>
+<Typography color={palette.black.main} type="heading" size="small" style={styles.referenceId}>{`${REF_ID_PREFIX}${String(refId)}`}</Typography>
```

---

`41-70`: **Handle edge cases for optional props more gracefully**

The component already uses conditional rendering with `!!`, but using `String()` on potentially undefined values could be more robustly handled.


Consider the following improvements:

```diff
const ProductInfo = ({brand, productName, refId, image}: ProductProps) => {
	return (
		<View style={styles.Wrapper}>
-			{!!image && <Image source={{uri: String(image)}} style={styles.productImage} />}
+			{image && <Image source={{uri: image}} style={styles.productImage} />}
			<View style={styles.productInfo}>
-				{!!brand && (
+				{brand && (
					<Typography color={palette.primary.main} type="overline" size="large">
-						{String(brand)}
+						{brand}
					</Typography>
				)}
-				{!!productName && (
+				{productName && (
					<Typography
						color={palette.black.main}
						type="heading"
						size="medium"
						style={styles.productName}>
-						{String(productName)}
+						{productName}
					</Typography>
				)}
-				{!!refId && (
+				{refId && (
					<Typography
						color={palette.black.main}
						type="heading"
						size="small"
-						style={styles.referenceId}>{`# ${String(refId)}`}</Typography>
+						style={styles.referenceId}>{`# ${refId}`}</Typography>
				)}
			</View>
		</View>
	);
};
```

The `!!` operator is redundant since React already treats `undefined`, `null`, and `false` as falsy values for conditional rendering. The `String()` calls are also unnecessary since React already converts values to strings when rendering.

</blockquote></details>
<details>
<summary>src/components/organisms/ProductDetail/index.tsx (2)</summary><blockquote>

`1-8`: **Consider combining React imports**

Similar to the first file, there's a duplication in React imports.


```diff
-import React from 'react';
+import React, {forwardRef} from 'react';
import {BaseModalProps, BaseSwipeUpProps} from 'molecules/BaseDetail/types';
import BaseDetail from 'molecules/BaseDetail';
import ProductInfo, {ProductProps} from './components/ProductInfo';
-import {forwardRef} from 'react';
import {ModalMethods} from 'atoms/Modal';
import {BottomSheetMethods} from '@gorhom/bottom-sheet/lib/typescript/types';
```

---

`13-21`: **Add component display name and consider adding TypeScript type guard**

The component implementation is good, but it could benefit from a display name and more robust type handling.


```diff
const ProductDetail = forwardRef<BaseDetailMethods, ProductDetailProps>((props, ref) => {
	const {brand, productName, refId, image, ...rest} = props;

	return (
		<BaseDetail ref={ref} {...rest}>
			<ProductInfo image={image} refId={refId} productName={productName} brand={brand} />
		</BaseDetail>
	);
});
+ProductDetail.displayName = 'ProductDetail';

export default ProductDetail;
```

</blockquote></details>
<details>
<summary>src/components/molecules/BaseDetail/index.tsx (2)</summary><blockquote>

`1-6`: **Consider combining React imports**

Similar to the previous files, there's a duplication in React imports.


```diff
-import React from 'react';
+import React, {forwardRef} from 'react';
-import {forwardRef} from 'react';
import {BaseModalProps, BaseSwipeUpProps} from './types';
import {BaseDetailModal, BaseDetailSwipe} from './components';
import {ModalMethods} from 'atoms/Modal';
import {BottomSheetMethods} from '@gorhom/bottom-sheet/lib/typescript/types';
```

---

`11-17`: **Add explicit type checking and display name**

The component implementation is good, but could benefit from more explicit type handling and a display name for debugging.


```diff
const BaseDetail = forwardRef<BaseDetailMethods, BaseDetailProps>((props, ref) => {
+	// Explicit type checking for better type safety
	if (props.componentType === 'swipe') {
-		return <BaseDetailSwipe {...props} ref={ref} enablePanDownToClose index={-1} />;
+		// Since we know the component type is 'swipe', we can safely cast props
+		return <BaseDetailSwipe {...(props as BaseSwipeUpProps)} ref={ref} enablePanDownToClose index={-1} />;
	}

-	return <BaseDetailModal {...props} fullScreen componentType="modal" ref={ref} />;
+	// Default to modal if componentType is not 'swipe'
+	return <BaseDetailModal {...(props as BaseModalProps)} fullScreen componentType="modal" ref={ref} />;
});
+BaseDetail.displayName = 'BaseDetail';

export default BaseDetail;
```

This ensures that TypeScript correctly understands which props are being passed to which component, improving type safety.

</blockquote></details>
<details>
<summary>storybook/stories/ProductDetail/ProductDetail.stories.js (2)</summary><blockquote>

`67-69`: **Rename function for clarity**

The function name `handleOpenModal` doesn't match its purpose of expanding a swipe interface.

```diff
-const handleOpenModal = () => {
+const handleExpandSwipe = () => {
  SwipeRef.current.expand();
};
```

---

`1-95`: **Consider converting to TypeScript**

Since this component is built on TypeScript types defined in `src/components/molecules/BaseDetail/types/index.ts`, consider converting this story file to TypeScript (.tsx) for consistency and better type checking.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 6adfd8e2d94cf96118ef73eb3926341c4c115384 and b9722586071813472e6faac22dfd2a69e978add7.

</details>

<details>
<summary>📒 Files selected for processing (15)</summary>

* `src/components/atoms/Modal/index.tsx` (2 hunks)
* `src/components/molecules/BaseDetail/components/BaseDetailModal/index.test.tsx` (1 hunks)
* `src/components/molecules/BaseDetail/components/BaseDetailModal/index.tsx` (1 hunks)
* `src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.test.tsx` (1 hunks)
* `src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.tsx` (1 hunks)
* `src/components/molecules/BaseDetail/components/index.tsx` (1 hunks)
* `src/components/molecules/BaseDetail/index.test.tsx` (1 hunks)
* `src/components/molecules/BaseDetail/index.tsx` (1 hunks)
* `src/components/molecules/BaseDetail/types/index.ts` (1 hunks)
* `src/components/organisms/ProductDetail/components/ProductInfo/index.test.tsx` (1 hunks)
* `src/components/organisms/ProductDetail/components/ProductInfo/index.tsx` (1 hunks)
* `src/components/organisms/ProductDetail/index.test.tsx` (1 hunks)
* `src/components/organisms/ProductDetail/index.tsx` (1 hunks)
* `src/index.ts` (2 hunks)
* `storybook/stories/ProductDetail/ProductDetail.stories.js` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (6)</summary>

<details>
<summary>src/components/organisms/ProductDetail/index.tsx (3)</summary><blockquote>

<details>
<summary>src/components/organisms/ProductDetail/components/ProductInfo/index.tsx (1)</summary>

* `ProductProps` (9-14)

</details>
<details>
<summary>src/components/molecules/BaseDetail/types/index.ts (2)</summary>

* `BaseModalProps` (7-9)
* `BaseSwipeUpProps` (10-12)

</details>
<details>
<summary>src/components/atoms/Modal/index.tsx (1)</summary>

* `ModalMethods` (23-26)

</details>

</blockquote></details>
<details>
<summary>src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.tsx (3)</summary><blockquote>

<details>
<summary>src/components/molecules/BaseDetail/components/index.tsx (1)</summary>

* `BaseDetailSwipe` (4-4)

</details>
<details>
<summary>src/components/molecules/BaseDetail/types/index.ts (1)</summary>

* `BaseSwipeUpProps` (10-12)

</details>
<details>
<summary>src/index.ts (2)</summary>

* `SwipeUp` (59-59)
* `SwipeUpScrollView` (61-61)

</details>

</blockquote></details>
<details>
<summary>src/components/molecules/BaseDetail/components/BaseDetailModal/index.tsx (2)</summary><blockquote>

<details>
<summary>src/components/atoms/Modal/index.tsx (1)</summary>

* `ModalMethods` (23-26)

</details>
<details>
<summary>src/components/molecules/BaseDetail/types/index.ts (1)</summary>

* `BaseModalProps` (7-9)

</details>

</blockquote></details>
<details>
<summary>src/components/molecules/BaseDetail/index.tsx (4)</summary><blockquote>

<details>
<summary>src/components/molecules/BaseDetail/types/index.ts (2)</summary>

* `BaseModalProps` (7-9)
* `BaseSwipeUpProps` (10-12)

</details>
<details>
<summary>src/components/atoms/Modal/index.tsx (1)</summary>

* `ModalMethods` (23-26)

</details>
<details>
<summary>src/index.ts (1)</summary>

* `BaseDetail` (82-82)

</details>
<details>
<summary>src/components/molecules/BaseDetail/components/index.tsx (2)</summary>

* `BaseDetailSwipe` (4-4)
* `BaseDetailModal` (4-4)

</details>

</blockquote></details>
<details>
<summary>src/components/organisms/ProductDetail/components/ProductInfo/index.tsx (2)</summary><blockquote>

<details>
<summary>src/scale/index.ts (3)</summary>

* `verticalScale` (32-32)
* `horizontalScale` (31-31)
* `moderateScale` (33-33)

</details>
<details>
<summary>src/index.ts (3)</summary>

* `Image` (50-50)
* `Typography` (78-78)
* `palette` (55-55)

</details>

</blockquote></details>
<details>
<summary>src/components/molecules/BaseDetail/types/index.ts (2)</summary><blockquote>

<details>
<summary>src/components/atoms/Modal/index.tsx (1)</summary>

* `UIModalProps` (15-21)

</details>
<details>
<summary>src/components/atoms/SwipeUp/index.tsx (1)</summary>

* `SwipeUpProps` (5-9)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.test.tsx</summary>

[error] 10-10: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment


(lint/complexity/noUselessFragments)

</details>
<details>
<summary>src/components/organisms/ProductDetail/index.test.tsx</summary>

[error] 35-35: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment


(lint/complexity/noUselessFragments)

</details>
<details>
<summary>src/components/molecules/BaseDetail/index.test.tsx</summary>

[error] 10-10: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment


(lint/complexity/noUselessFragments)

---

[error] 22-22: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment


(lint/complexity/noUselessFragments)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (13)</summary><blockquote>

<details>
<summary>src/components/atoms/Modal/index.tsx (2)</summary>

`23-26`: **Good refactoring: Improved interface name for better semantic clarity**

Renaming `RefProps` to `ModalMethods` and exporting it is a good improvement. The new name more clearly communicates the purpose of this interface, making the code more self-documenting.

---

`77-77`: **Consistent update of forwardRef type parameter**

Good job updating the forwardRef generic type parameter to match the renamed interface.

</details>
<details>
<summary>src/index.ts (3)</summary>

`32-32`: **Import correctly placed in molecules section**

The BaseDetail import is properly placed in the molecules section.

---

`39-39`: **Import correctly placed in organisms section**

The ProductDetail import is properly placed in the organisms section.

---

`82-83`: **New components properly exported**

Both components are correctly exported, making them available for use throughout the application.

</details>
<details>
<summary>src/components/molecules/BaseDetail/components/index.tsx (1)</summary>

`1-4`: **Clean barrel export file structure**

Good use of a barrel file to consolidate and re-export related components. This pattern keeps imports clean in consumer code.

</details>
<details>
<summary>src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.tsx (1)</summary>

`8-18`: **Looks good, component implementation is clean and well-structured**

The component correctly implements a scrollable bottom sheet using forwardRef to expose the BottomSheet methods. The props destructuring and defaults are handled properly.

</details>
<details>
<summary>src/components/organisms/ProductDetail/index.tsx (1)</summary>

`9-11`: **The type definitions look solid**

The union type approach for props and combining method types is a good design choice, making the component flexible while maintaining type safety.

</details>
<details>
<summary>src/components/molecules/BaseDetail/index.tsx (1)</summary>

`8-9`: **Type definitions look good**

Clear and concise type definitions for props and methods.

</details>
<details>
<summary>src/components/molecules/BaseDetail/types/index.ts (1)</summary>

`1-12`: **Well-structured type definitions for the BaseDetail component system**

The type definitions are clear, well-organized, and properly support the component architecture described in the PR objectives. The discriminated union pattern with `componentType` provides good type safety, and the optional scroll props for both variants give flexibility for customization.

</details>
<details>
<summary>storybook/stories/ProductDetail/ProductDetail.stories.js (3)</summary>

`41-47`: **LGTM: Clean reusable button component**

The `OpenDetail` component is a good abstraction that reduces code duplication between the two stories.

---

`72-75`: **Good inclusion of GestureHandlerRootView**

Properly wrapping the swipe component in `GestureHandlerRootView` ensures gesture handling works correctly.

---

`79-91`: **LGTM: Consistent props between story variants**

Using the same test data for both story variants ensures consistent demonstration of the component.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-28T20:50:08Z
user: login: pablonortiz
body: Lo estuve probando y lo vi bien!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T21:57:58Z
user: login: christian97dd
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-29T18:43:32Z

Review comments:
diff_hunk: @@ -0,0 +1,30 @@
+import React from 'react';
+import {create} from 'react-test-renderer';
+import BaseDetail from './index';
+import {BaseDetailModal, BaseDetailSwipe} from './components';
+
+describe('BaseDetail', () => {
+	it('should return BaseDetailSwipe when componentType is "swipe"', () => {
+		const BaseDetailComp = create(
+			<BaseDetail componentType="swipe">
+				<></>
+			</BaseDetail>
+		).root;
+
+		const BaseDetailSwipeComp = BaseDetailComp.findByType(BaseDetailSwipe);
+
+		expect(BaseDetailSwipeComp).toBeTruthy();
+	});
+
+	it('should return BaseDetailModal when componentType is "swipe"', () => {
path: src/components/molecules/BaseDetail/index.test.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix test description to match test implementation**

There's a typo in the test description. It says "when componentType is 'swipe'" but it's actually testing "when componentType is 'modal'".


```diff
-	it('should return BaseDetailModal when componentType is "swipe"', () => {
+	it('should return BaseDetailModal when componentType is "modal"', () => {
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
	it('should return BaseDetailModal when componentType is "modal"', () => {
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-28T20:50:07Z
author_association: NONE
diff_hunk: @@ -0,0 +1,47 @@
+import React from 'react';
+import {create} from 'react-test-renderer';
+import ProductInfo from './index';
+import Image from 'atoms/Image';
+import Typography from 'atoms/Typography';
+
+describe('ProductInfo', () => {
+	it('should render Image component when receive image argument', () => {
+		const ProductInfoComp = create(
+			<ProductInfo image="https://avatars.githubusercontent.com/u/49998302?s=200&v=4" />
+		).root;
+
+		const ImageComp = ProductInfoComp.findByType(Image);
+
+		expect(ImageComp).toBeTruthy();
+		expect(ProductInfoComp).toBeTruthy();
+	});
+
+	it('should render Typography component when receive productName argument', () => {
+		const ProductInfoComp = create(
+			<ProductInfo productName="Campera de Hombre H&G Softshell Estampado PRINT Gris Melange Talle XXL" />
+		).root;
+
+		const TypographyComp = ProductInfoComp.findByType(Typography);
+
+		expect(TypographyComp).toBeTruthy();
+		expect(ProductInfoComp).toBeTruthy();
+	});
+
+	it('should render Typography component when receive refId argument', () => {
+		const ProductInfoComp = create(<ProductInfo refId="4AR6353-010" />).root;
+
+		const TypographyComp = ProductInfoComp.findByType(Typography);
+
+		expect(TypographyComp).toBeTruthy();
+		expect(ProductInfoComp).toBeTruthy();
+	});
+
+	it('should render Typography component when receive brand argument', () => {
+		const ProductInfoComp = create(<ProductInfo brand="Janis" />).root;
+
+		const TypographyComp = ProductInfoComp.findByType(Typography);
+
+		expect(TypographyComp).toBeTruthy();
+		expect(ProductInfoComp).toBeTruthy();
+	});
path: src/components/organisms/ProductDetail/components/ProductInfo/index.test.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Improve test coverage for ProductInfo component**

The current tests verify that each prop renders the appropriate component type, but they don't test:

1. Multiple props together (real-world usage)
2. The actual text content of Typography components
3. That props are correctly passed to child components (e.g., Image source)



Consider adding a comprehensive test that verifies all props together:

```tsx
it('should render all product information correctly when all props are provided', () => {
  const props = {
    image: "https://example.com/image.jpg",
    productName: "Test Product",
    refId: "TEST-123",
    brand: "Test Brand"
  };
  
  const component = create(<ProductInfo {...props} />).root;
  
  // Verify image
  const imageComp = component.findByType(Image);
  expect(imageComp.props.source).toEqual({uri: props.image});
  
  // Verify text elements - you'll need to add testID or use appropriate queries
  const textElements = component.findAllByType(Typography);
  expect(textElements.length).toBeGreaterThanOrEqual(3); // At least product name, refId, and brand
  
  // Find specific Typography components - adjust according to actual implementation
  const productNameElement = textElements.find(el => el.props.text === props.productName);
  const refIdElement = textElements.find(el => el.props.text?.includes(props.refId));
  const brandElement = textElements.find(el => el.props.text?.includes(props.brand));
  
  expect(productNameElement).toBeTruthy();
  expect(refIdElement).toBeTruthy();
  expect(brandElement).toBeTruthy();
});
```

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-28T20:50:07Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-28T20:47:08Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

<details>
<summary>📝 Walkthrough</summary>

## Walkthrough

This change introduces a new modular detail component system for displaying product information in either a modal or swipe-up (bottom sheet) interface. It adds new components (`BaseDetail`, `BaseDetailModal`, `BaseDetailSwipe`, and `ProductDetail`) with associated types, tests, and Storybook stories. The modal ref interface is renamed and exported as `ModalMethods`. The new components are integrated into the main export, and comprehensive tests and stories are provided for both detail presentation variants and the product info display.

## Changes

| File(s)                                                                                                  | Change Summary |
|----------------------------------------------------------------------------------------------------------|---------------|
| src/components/atoms/Modal/index.tsx                                                                     | Renamed and exported the modal ref interface as `ModalMethods`; updated `forwardRef` type parameter accordingly. |
| src/components/molecules/BaseDetail/components/BaseDetailModal/index.tsx                                 | Added `BaseDetailModal` component using `forwardRef`, rendering a scrollable modal with customizable behavior. |
| src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.tsx                                 | Added `BaseDetailSwipe` component using `forwardRef`, rendering a swipe-up interface with scrollable content. |
| src/components/molecules/BaseDetail/components/index.tsx                                                 | Added index file re-exporting `BaseDetailModal` and `BaseDetailSwipe`. |
| src/components/molecules/BaseDetail/types/index.ts                                                       | Added type definitions for modal and swipe-up detail props. |
| src/components/molecules/BaseDetail/index.tsx                                                            | Added `BaseDetail` component that conditionally renders `BaseDetailModal` or `BaseDetailSwipe` based on props. |
| src/components/organisms/ProductDetail/components/ProductInfo/index.tsx                                  | Added `ProductInfo` component to display product image, name, brand, and reference ID. |
| src/components/organisms/ProductDetail/index.tsx                                                         | Added `ProductDetail` component combining product info with modal/swipe-up presentation, forwarding ref methods. |
| src/index.ts                                                                                             | Added `BaseDetail` and `ProductDetail` to the main export list. |
| src/components/molecules/BaseDetail/components/BaseDetailModal/index.test.tsx                            | Added test for `BaseDetailModal` rendering with children. |
| src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.test.tsx                            | Added test for `BaseDetailSwipe` rendering and presence of `SwipeUpScrollView`. |
| src/components/molecules/BaseDetail/index.test.tsx                                                       | Added tests for `BaseDetail` rendering correct subcomponent based on `componentType`. |
| src/components/organisms/ProductDetail/components/ProductInfo/index.test.tsx                             | Added tests for `ProductInfo` rendering child components based on props. |
| src/components/organisms/ProductDetail/index.test.tsx                                                    | Added tests for `ProductDetail` rendering with modal and swipe-up variants. |
| storybook/stories/ProductDetail/ProductDetail.stories.js                                                 | Added Storybook stories for `ProductDetail` in modal and swipe-up modes, with interactive controls and styling. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Parent
    participant ProductDetail
    participant BaseDetail
    participant BaseDetailModal
    participant BaseDetailSwipe
    participant ProductInfo

    Parent->>ProductDetail: Render with props (brand, productName, refId, image, componentType)
    ProductDetail->>BaseDetail: Pass detail props and ref
    alt componentType == "modal"
        BaseDetail->>BaseDetailModal: Render with props, ref
        BaseDetailModal->>ProductInfo: Render as child
    else componentType == "swipe"
        BaseDetail->>BaseDetailSwipe: Render with props, ref
        BaseDetailSwipe->>ProductInfo: Render as child
    end
```

## Possibly related PRs

- janis-commerce/ui-native#57: Renames and exports the interface for the Modal component's ref methods and updates the forwardRef typing, directly modifying the same Modal component and its ref interface.

## Poem

> In a modal or a swipe, details now appear,  
> Product info shining bright, the UI crystal clear.  
> With tests and stories written, our code is well in sight,  
> Components come together, everything feels right.  
> 🐇✨  
> So hop along, dear dev, and see what’s new today—  
> The detail’s in the journey, in a modular display!

</details>

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between b9722586071813472e6faac22dfd2a69e978add7 and 0037eab8ef0fbcf9984baee11413ac05d24b48f4.

</details>

<details>
<summary>📒 Files selected for processing (8)</summary>

* `src/components/molecules/BaseDetail/components/BaseDetailModal/index.tsx` (1 hunks)
* `src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.test.tsx` (1 hunks)
* `src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.tsx` (1 hunks)
* `src/components/molecules/BaseDetail/index.test.tsx` (1 hunks)
* `src/components/molecules/BaseDetail/index.tsx` (1 hunks)
* `src/components/organisms/ProductDetail/components/ProductInfo/index.test.tsx` (1 hunks)
* `src/components/organisms/ProductDetail/components/ProductInfo/index.tsx` (1 hunks)
* `src/components/organisms/ProductDetail/index.tsx` (1 hunks)

</details>

<details>
<summary>✅ Files skipped from review due to trivial changes (1)</summary>

* src/components/organisms/ProductDetail/components/ProductInfo/index.test.tsx

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (7)</summary>

* src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.tsx
* src/components/molecules/BaseDetail/index.test.tsx
* src/components/molecules/BaseDetail/components/BaseDetailModal/index.tsx
* src/components/organisms/ProductDetail/components/ProductInfo/index.tsx
* src/components/molecules/BaseDetail/components/BaseDetailSwipe/index.test.tsx
* src/components/organisms/ProductDetail/index.tsx
* src/components/molecules/BaseDetail/index.tsx

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary>

* GitHub Check: Build
* GitHub Check: Build

</details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=janis-commerce/ui-native&utm_content=59):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: coveralls
created_at: 2025-04-28T21:39:59Z
body: ## Pull Request Test Coverage Report for [Build 14718389807](https://coveralls.io/builds/73422135)


### Details

* **17** of **17**   **(100.0%)**  changed or added relevant lines in **6** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage remained the same at **100.0%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/73422135/badge)](https://coveralls.io/builds/73422135) |
| :-- | --: |
| Change from base [Build 14716420813](https://coveralls.io/builds/73420203): |  0.0% |
| Covered Lines: | 779 |
| Relevant Lines: | 779 |

---
##### 💛  - [Coveralls](https://coveralls.io)

author_association: NONE