Skip to content

Commit

Permalink
Merge pull request #1145 from ral-facilities/custom-light-box
Browse files Browse the repository at this point in the history
Gallery View and image info dialog #1081 #1099 #1083 #1082
  • Loading branch information
joshuadkitenge authored Dec 10, 2024
2 parents b540dba + b72d8e1 commit 718d659
Show file tree
Hide file tree
Showing 21 changed files with 4,588 additions and 23 deletions.
194 changes: 194 additions & 0 deletions cypress/e2e/with_mock_data/items.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,200 @@ describe('Items', () => {

cy.findByText('Upload failed').should('exist');
});

it('falls back to placeholder thumbnail', () => {
cy.findByText('5YUQDDjKpz2z').click();
cy.findByText(
'High-resolution cameras for beam characterization. 1'
).should('exist');

cy.findByText('Gallery').click();
cy.findByAltText('The image cannot be loaded').should('exist');
});

it('displays and hides filters, applies and clears name filter on gallery view', () => {
cy.findByText('5YUQDDjKpz2z').click();
cy.findByText(
'High-resolution cameras for beam characterization. 1'
).should('exist');

cy.findByText('Gallery').click();
cy.findAllByText('stfc-logo-blue-text.png').should('have.length', 8);
cy.findByText('Show Filters').click();
cy.findByRole('button', { name: 'Clear Filters' }).should('be.disabled');
cy.findByLabelText('Filter by File name').type('logo1.png');
cy.findByAltText('test').should('not.exist');
cy.findByRole('button', { name: 'Clear Filters' }).click();
cy.findAllByText('stfc-logo-blue-text.png').should('have.length', 8);
cy.findByText('Hide Filters').click();
cy.findByText('Show Filters').should('exist');
});

it('opens information dialog from card view', () => {
cy.findByText('5YUQDDjKpz2z').click();
cy.findByText(
'High-resolution cameras for beam characterization. 1'
).should('exist');

cy.findByText('Gallery').click();

cy.findAllByLabelText('Card Actions').first().click();
cy.findAllByText('Information').last().click();

cy.findByRole('dialog').within(() => {
cy.findByText('Image Information').should('exist');
cy.findByText('stfc-logo-blue-text').should('exist');
cy.findByText('stfc-logo-blue-text.png').should('exist');
cy.findByText('test').should('exist');
cy.findByText('No').should('exist');

cy.findByRole('button', { name: 'Close' }).click();
});

cy.findByText('Image information').should('not.exist');
});

it('opens full-size image when thumbnail is clicked and navigates to the next image', () => {
cy.findByText('5YUQDDjKpz2z').click();
cy.findByText(
'High-resolution cameras for beam characterization. 1'
).should('exist');

cy.findByText('Gallery').click();

cy.findAllByAltText('test').first().click();
cy.findByTestId('galleryLightBox').within(() => {
cy.findByText('File name: stfc-logo-blue-text.png').should('exist');
cy.findByText('Title: stfc-logo-blue-text').should('exist');
cy.findByText('test').should('exist');

cy.findByAltText('test').should('exist');

cy.findByAltText('test')
.should('have.attr', 'src')
.and(
'include',
'http://localhost:3000/images/stfc-logo-blue-text.png?text=1'
);

cy.findByLabelText('Next').click();

cy.findByText('File name: logo1.png').should('exist');
cy.findByText('Title: logo1').should('exist');
cy.findByText('test').should('exist');

cy.findByAltText('test').should('exist');

cy.findByAltText('test')
.should('have.attr', 'src')
.and('include', 'http://localhost:3000/logo192.png?text=2');
cy.findByLabelText('Close').click();
});

cy.findByTestId('galleryLightBox').should('not.exist');
});

it('opens corrupted image, and navigates back to previous image (invalid url)', () => {
cy.findByText('5YUQDDjKpz2z').click();
cy.findByText(
'High-resolution cameras for beam characterization. 1'
).should('exist');

cy.findByText('Gallery').click();

cy.findByAltText('The image cannot be loaded').click();
cy.findByTestId('galleryLightBox').within(() => {
cy.findByText('File name: stfc-logo-blue-text.png').should('exist');
cy.findByText('Title: stfc-logo-blue-text').should('exist');
cy.findByText('No description available').should('exist');

cy.findByText('The image cannot be loaded').should('exist');

cy.findByLabelText('Previous').click();

cy.findByText('File name: logo1.png').should('exist');
cy.findByText('Title: logo1').should('exist');
cy.findByText('test').should('exist');

cy.findByAltText('test').should('exist');

cy.findByAltText('test')
.should('have.attr', 'src')
.and('include', 'http://localhost:3000/logo192.png?text=2');
cy.findByLabelText('Close').click();
});

cy.findByTestId('galleryLightBox').should('not.exist');
});

it('opens corrupted image (network error)', () => {
cy.findByText('5YUQDDjKpz2z').click();
cy.findByText(
'High-resolution cameras for beam characterization. 1'
).should('exist');

cy.findByText('Gallery').click();

cy.findAllByAltText('test').eq(3).click();
cy.findByTestId('galleryLightBox').within(() => {
cy.findByText('The image cannot be loaded', { timeout: 10000 }).should(
'exist'
);

cy.findByText('File name: stfc-logo-blue-text.png').should('exist');
cy.findByText('Title: stfc-logo-blue-text').should('exist');
cy.findByText('test').should('exist');

cy.findByLabelText('Close').click();
});

cy.findByTestId('galleryLightBox').should('not.exist');
});

it('opens information dialog in lightbox', () => {
cy.findByText('5YUQDDjKpz2z').click();
cy.findByText(
'High-resolution cameras for beam characterization. 1'
).should('exist');

cy.findByText('Gallery').click();

cy.findAllByAltText('test').first().click();
cy.findByTestId('galleryLightBox').within(() => {
cy.findByText('File name: stfc-logo-blue-text.png').should('exist');
cy.findByText('Title: stfc-logo-blue-text').should('exist');
cy.findByText('test').should('exist');

cy.findByAltText('test').should('exist');

cy.findByAltText('test')
.should('have.attr', 'src')
.and(
'include',
'http://localhost:3000/images/stfc-logo-blue-text.png?text=1'
);

cy.findByLabelText('Image Actions').click();
});

cy.findAllByText('Information').last().click();

cy.findByRole('dialog', { timeout: 10000 }).should('exist');

cy.findByRole('dialog').within(() => {
cy.findByText('Image Information').should('exist');
});

cy.findByRole('dialog').within(() => {
cy.findByRole('button', { name: 'Close' }).click();
});

cy.findByRole('dialog').should('not.exist');

cy.findByLabelText('Close').click();

cy.findByTestId('galleryLightBox').should('not.exist');
});
});

it('delete an item', () => {
Expand Down
6 changes: 5 additions & 1 deletion src/api/api.types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,14 @@ export interface ImagePost {
description?: string | null;
}

export interface Image
export interface APIImage
extends Required<Omit<ImagePost, 'upload_file'>>,
CreatedModifiedMixin {
id: string;
primary: boolean;
thumbnail_base64: string;
}

export interface APIImageWithURL extends APIImage {
url: string;
}
65 changes: 65 additions & 0 deletions src/api/images.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { renderHook, waitFor } from '@testing-library/react';
import ImagesJSON from '../mocks/Images.json';
import { hooksWrapperWithProviders } from '../testUtils';
import { useGetImage, useGetImages } from './images';

describe('images api functions', () => {
afterEach(() => {
vi.clearAllMocks();
});

describe('useGetImages', () => {
it('sends request to fetch image data and returns successful response', async () => {
const { result } = renderHook(() => useGetImages('1'), {
wrapper: hooksWrapperWithProviders(),
});

await waitFor(() => {
expect(result.current.isSuccess).toBeTruthy();
});

expect(result.current.data?.length).toEqual(20);
});

it('sends request to fetch primary image data and returns successful response', async () => {
const { result } = renderHook(() => useGetImages('1', true), {
wrapper: hooksWrapperWithProviders(),
});

await waitFor(() => {
expect(result.current.isSuccess).toBeTruthy();
});

expect(result.current.data?.length).toEqual(1);
});

it('sends request to fetch primary image data and returns successful empty list response', async () => {
const { result } = renderHook(() => useGetImages('90', true), {
wrapper: hooksWrapperWithProviders(),
});

await waitFor(() => {
expect(result.current.isSuccess).toBeTruthy();
});

expect(result.current.data?.length).toEqual(0);
});
});

describe('useGetImage', () => {
it('sends request to fetch image data and returns successful response', async () => {
const { result } = renderHook(() => useGetImage('1'), {
wrapper: hooksWrapperWithProviders(),
});

await waitFor(() => {
expect(result.current.isSuccess).toBeTruthy();
});

expect(result.current.data).toEqual({
...ImagesJSON[1],
url: 'http://localhost:3000/images/stfc-logo-blue-text.png?text=1',
});
});
});
});
45 changes: 45 additions & 0 deletions src/api/images.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useQuery, UseQueryResult } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { storageApi } from './api';
import { APIImage, APIImageWithURL } from './api.types';

export const getImage = async (id: string): Promise<APIImageWithURL> => {
return storageApi.get(`/images/${id}`).then((response) => {
return response.data;
});
};

export const useGetImage = (
id: string
): UseQueryResult<APIImageWithURL, AxiosError> => {
return useQuery({
queryKey: ['Image', id],
queryFn: () => getImage(id),
});
};

const getImages = async (
entityId: string,
primary?: boolean
): Promise<APIImage[]> => {
const queryParams = new URLSearchParams();
queryParams.append('entity_id', entityId);

if (primary !== undefined) queryParams.append('primary', String(primary));
return storageApi
.get(`/images`, {
params: queryParams,
})
.then((response) => response.data);
};

export const useGetImages = (
entityId?: string,
primary?: boolean
): UseQueryResult<APIImage[], AxiosError> => {
return useQuery({
queryKey: ['Images', entityId, primary],
queryFn: () => getImages(entityId ?? '', primary),
enabled: !!entityId,
});
};
1 change: 0 additions & 1 deletion src/common/actionMenu.component.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import '@testing-library/jest-dom';
import { screen, waitFor } from '@testing-library/react';
import userEvent, { UserEvent } from '@testing-library/user-event';
import { vi } from 'vitest';
Expand Down
Loading

0 comments on commit 718d659

Please sign in to comment.