diff --git a/package.json b/package.json index e85120c..39de291 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@observation.org/react-native-components", - "version": "1.31.0", + "version": "1.32.0", "main": "src/index.ts", "repository": "git@github.com:observation/react-native-components.git", "author": "Observation.org", diff --git a/src/components/Lightbox.tsx b/src/components/Lightbox.tsx index 0b96da6..d0db34d 100644 --- a/src/components/Lightbox.tsx +++ b/src/components/Lightbox.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useState } from 'react' import { SafeAreaView, StyleSheet, Text, TextStyle, TouchableOpacity, View } from 'react-native' import ImageView from '@observation.org/react-native-image-viewing' @@ -10,34 +10,40 @@ import font from '../styles/font' import textStyle from '../styles/text' import theme from '../styles/theme' +const hitSlop = { top: 16, left: 16, bottom: 16, right: 16 } + const getLightboxHeaderComponent = (numberOfPages: number, onClose: () => void) => - ({ imageIndex }: { imageIndex: number }) => { - const hitSlop = { top: 16, left: 16, bottom: 16, right: 16 } - return ( - - - - - - - - onClose()} hitSlop={hitSlop}> - - - + ({ imageIndex }: { imageIndex: number }) => ( + + + + + - - ) - } + + onClose()} hitSlop={hitSlop}> + + + + + + ) const getLightboxFooterComponent = - (title?: string, description?: string, content?: React.ReactNode, style?: LightboxStyle) => () => ( + ( + title?: string, + description?: string, + content?: React.ReactNode, + style?: LightboxStyle, + onPressDelete?: () => void, + ) => + () => ( {title && ( @@ -51,6 +57,15 @@ const getLightboxFooterComponent = )} {content && {content}} + {onPressDelete && ( + + + + + + + + )} ) @@ -62,6 +77,7 @@ type LightboxStyle = { type Props = { index?: number onClose: () => void + onDelete?: (imageIndex: number) => void photos: string[] title?: string description?: string @@ -69,17 +85,25 @@ type Props = { style?: LightboxStyle } -const Lightbox = ({ index, onClose, photos, title, description, content, style }: Props) => ( - ({ uri: photo }))} - imageIndex={index ?? 0} - visible={index !== undefined} - swipeToCloseEnabled={false} - onRequestClose={onClose} - HeaderComponent={getLightboxHeaderComponent(photos.length, onClose)} - FooterComponent={getLightboxFooterComponent(title, description, content, style)} - /> -) +const Lightbox = ({ index, onClose, onDelete, photos, title, description, content, style }: Props) => { + const initialImageIndex = index ?? 0 + const [currentImageIndex, setCurrentImageIndex] = useState() + + const onPressDelete = onDelete ? () => onDelete(currentImageIndex ?? initialImageIndex) : undefined + + return ( + ({ uri: photo }))} + imageIndex={initialImageIndex} + visible={index !== undefined} + swipeToCloseEnabled={false} + onImageIndexChange={setCurrentImageIndex} + onRequestClose={onClose} + HeaderComponent={getLightboxHeaderComponent(photos.length, onClose)} + FooterComponent={getLightboxFooterComponent(title, description, content, style, onPressDelete)} + /> + ) +} export default Lightbox @@ -123,4 +147,13 @@ const styles = StyleSheet.create({ ...textStyle.body, color: theme.color.white, }, + buttonsContainer: { + flexDirection: 'row', + marginVertical: theme.margin.large, + marginHorizontal: theme.margin.common, + }, + buttonContainer: { + flex: 0.5, + alignItems: 'center', + }, }) diff --git a/src/components/__tests__/Lightbox.test.tsx b/src/components/__tests__/Lightbox.test.tsx index 407573b..1d58f26 100644 --- a/src/components/__tests__/Lightbox.test.tsx +++ b/src/components/__tests__/Lightbox.test.tsx @@ -15,6 +15,15 @@ const content = Jan de Vogelaar let onClose: () => void +let mockOnImageIndexChange = jest.fn() +jest.mock('@observation.org/react-native-image-viewing', () => { + const actualModule = jest.requireActual('@observation.org/react-native-image-viewing') + return (props: any) => { + mockOnImageIndexChange = props.onImageIndexChange as any + return actualModule.default(props) + } +}) + describe('Lightbox', () => { beforeEach(() => { onClose = jest.fn() @@ -59,6 +68,15 @@ describe('Lightbox', () => { expect(toJSON()).toMatchSnapshot() }) + + test('With a delete button', () => { + const { toJSON, queryByTestId } = render( + {}} />, + ) + + expect(queryByTestId('delete-photo')).not.toBeNull() + expect(toJSON()).toMatchSnapshot() + }) }) describe('Interaction', () => { @@ -70,7 +88,34 @@ describe('Lightbox', () => { await fireEvent.press(getByTestId('close-lightbox')) // THEN - expect(onClose).toBeCalled() + expect(onClose).toHaveBeenCalled() + }) + + test('Press delete button calls onDelete', async () => { + // GIVEN + const onDelete = jest.fn() + const { getByTestId } = render() + + // WHEN + await fireEvent.press(getByTestId('delete-photo')) + + // THEN + expect(onDelete).toHaveBeenCalledWith(0) + }) + + test('When swiping to the second photo and pressing the delete button, onDelete is called with the second photo', async () => { + // GIVEN + jest.mock('@observation.org/react-native-image-viewing', () => 'ImageCarousel') + + const onDelete = jest.fn() + const { getByTestId } = render() + + // WHEN + mockOnImageIndexChange(1) + await fireEvent.press(getByTestId('delete-photo')) + + // THEN + expect(onDelete).toHaveBeenCalledWith(1) }) }) }) diff --git a/src/components/__tests__/__snapshots__/Lightbox.test.tsx.snap b/src/components/__tests__/__snapshots__/Lightbox.test.tsx.snap index 4beea4c..e9939df 100644 --- a/src/components/__tests__/__snapshots__/Lightbox.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Lightbox.test.tsx.snap @@ -1598,3 +1598,434 @@ exports[`Lightbox Rendering Second photo 1`] = ` `; + +exports[`Lightbox Rendering With a delete button 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`;