Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

M4 update #67

Merged
merged 14 commits into from
Nov 8, 2024
17 changes: 10 additions & 7 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
{
"env": {
"jest/globals": true
},
"extends": ["airbnb"],
"env": {},
"extends": ["airbnb", "plugin:jest/recommended"],
"globals": {
"page": true,
"document": true
},
"parser": "babel-eslint",
"plugins": ["jest"],
"parser": "@babel/eslint-parser",
"plugins": ["babel", "jest", "react", "react-hooks"],
"rules": {
"import/prefer-default-export": "off",
"import/no-extraneous-dependencies": "off",
"no-console": "warn",
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
"react/jsx-fragments": "off",
"react/jsx-props-no-spreading": "off",
"react/prefer-stateless-function": "off",
"react/function-component-definition": "off"
}
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
/umd
npm-debug.log*
package-lock.json
.cache
dist
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![npm package][npm-badge]][npm]
[![Coveralls][coveralls-badge]][coveralls]

`mirador-dl-plugin` is a Mirador 3 plugin that adds manifest-provided download links (e.g. `rendering`) to the window options menu. A [live demo](https://mirador-download-plugin.netlify.app/) with several institutions' manifests is available for testing.
`mirador-dl-plugin` is a Mirador 4 plugin that adds manifest-provided download links (e.g. `rendering`) to the window options menu. A [live demo](https://mirador-download-plugin.netlify.app/) with several institutions' manifests is available for testing.

![download option in menu](https://user-images.githubusercontent.com/5402927/87057974-5e665a80-c1bc-11ea-8f10-7b783bdc972f.png)

Expand All @@ -22,7 +22,7 @@

## Installation

`mirador-dl-plugin` requires an instance of Mirador 3. See the [Mirador wiki](https://github.com/ProjectMirador/mirador/wiki) for examples of embedding Mirador within an application and additional information about plugins. See the [live demo's index.js](https://github.com/ProjectMirador/mirador-dl-plugin/blob/master/demo/src/index.js) for an example of importing and configuring `mirador-dl-plugin`.
`mirador-dl-plugin` requires an instance of Mirador 4. See the [Mirador wiki](https://github.com/ProjectMirador/mirador/wiki) for examples of embedding Mirador within an application and additional information about plugins. See the [live demo's index.js](https://github.com/ProjectMirador/mirador-dl-plugin/blob/master/demo/src/index.js) for an example of importing and configuring `mirador-dl-plugin`.

## Configuration

Expand Down
232 changes: 109 additions & 123 deletions __tests__/CanvasDownloadLinks.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React from 'react';
import { shallow } from 'enzyme';
import Link from '@material-ui/core/Link';
import Typography from '@material-ui/core/Typography';
import { OSDReferences } from 'mirador/dist/es/src/plugins/OSDReferences';
import CanvasDownloadLinks from '../src/CanvasDownloadLinks';
import RenderingDownloadLink from '../src/RenderingDownloadLink';
import { render, screen } from './test-utils';

/**
* Helper function to render the CanvasDownloadLinks component with custom props.
*/
function createWrapper(props) {
return shallow(
return render(
<CanvasDownloadLinks
canvasId="abc123"
canvasLabel="My Canvas Label"
Expand All @@ -22,10 +22,9 @@ function createWrapper(props) {
}

describe('CanvasDownloadLinks', () => {
let wrapper;
const canvas = {
id: 'abc123',
getCanonicalImageUri: width => (
getCanonicalImageUri: (width) => (
width
? `http://example.com/iiif/abc123/full/${width},/0/default.jpg`
: 'http://example.com/iiif/abc123/full/4000,/0/default.jpg'
Expand All @@ -40,181 +39,168 @@ describe('CanvasDownloadLinks', () => {
},
],
};
const viewport = {
getBounds: () => ({
x: 0, y: 0, width: 4000, height: 1000,
}),
};
const zoomedInViewport = {
getBounds: () => ({
x: 0, y: 0, width: 2000, height: 500,
}),
};

const zoomedOutViewport = {
getBounds: () => ({
x: 0, y: 0, width: 6000, height: 1000,
}),
};
let currentBoundsSpy;

const zoomedIntoNonImageSpaceViewport = {
getBounds: () => ({
x: -100, y: 100, width: 2000, height: 500,
}),
};
beforeEach(() => {
currentBoundsSpy = jest.spyOn(CanvasDownloadLinks.prototype, 'currentBounds');
});

beforeAll(() => {
OSDReferences.set('wid123', {
current: { viewport },
});
OSDReferences.set('zoomedInWindow', {
current: { viewport: zoomedInViewport },
});
OSDReferences.set('zoomedOutWindow', {
current: { viewport: zoomedOutViewport },
});
OSDReferences.set('zoomedIntoNonImageSpaceWindow', {
current: { viewport: zoomedIntoNonImageSpaceViewport },
});
afterEach(() => {
currentBoundsSpy.mockRestore();
});

it('renders canvas label in an h3 typography', () => {
wrapper = createWrapper({ canvas });
expect(
wrapper.find(Typography)
.find({ variant: 'h3' })
.props().children,
).toEqual('My Canvas Label');
it('renders the canvas label as an h3 heading', () => {
createWrapper({ canvas });

const headingElement = screen.getByText('My Canvas Label');
expect(headingElement).toBeInTheDocument();
expect(headingElement.tagName).toBe('H3');
});

it('renders canvas level renderings', () => {
wrapper = createWrapper({ canvas });
expect(
wrapper.find(RenderingDownloadLink).length,
).toEqual(1);
describe('Canvas Renderings', () => {
it('includes a canvas-level rendering as a download link', () => {
createWrapper({ canvas });

const downloadLink = screen.getByRole('link', { name: /Whole image \(4000 x 1000px\)/i });
expect(downloadLink).toBeInTheDocument();
});
});

describe('Zoomed region link', () => {
describe('Zoomed Region Links', () => {
const infoResponse = {
json: { width: 4000, height: 1000 },
};

it('it does not render a link when the viewer is zoomed out/at the entire image', () => {
wrapper = createWrapper({ canvas, infoResponse, windowId: 'zoomedOutWindow' });
expect(wrapper.find(Link).length).toBe(2);
it('does not render a zoom link when viewer is zoomed out to full image', () => {
currentBoundsSpy.mockImplementation(() => ({
x: 0, y: 0, width: 6000, height: 1000,
}));

createWrapper({ canvas, infoResponse, windowId: 'zoomedOutWindow' });

wrapper = createWrapper({ canvas, infoResponse, windowId: 'wid123' });
expect(wrapper.find(Link).length).toBe(2);
const zoomedLink = screen.queryByText('Zoomed region (6000 x 1000px)');
expect(zoomedLink).not.toBeInTheDocument();
});

it('does not render a link when the viewer is zoomed into non-image space (e.g. a reponse the image server cannot handle)', () => {
wrapper = createWrapper({ canvas, infoResponse, windowId: 'zoomedIntoNonImageSpaceWindow' });
it('does not render a zoom link when zoomed into an area outside of the image bounds', () => {
currentBoundsSpy.mockImplementation(() => ({
x: -100, y: 100, width: 2000, height: 500,
}));

expect(wrapper.find(Link).length).toBe(2);
createWrapper({ canvas, infoResponse, windowId: 'zoomedIntoNonImageSpaceWindow' });

const zoomedLink = screen.queryByText('Zoomed region (2000 x 500px)');
expect(zoomedLink).not.toBeInTheDocument();
});

it('is present when the viewer is zoomed into the image', () => {
wrapper = createWrapper({ canvas, infoResponse, windowId: 'zoomedInWindow' });
it('renders a zoomed region link when zoomed into a valid area of the image', () => {
currentBoundsSpy.mockImplementation(() => ({
x: 0, y: 0, width: 2000, height: 500,
}));

createWrapper({ canvas, infoResponse, windowId: 'zoomedInWindow' });

expect(wrapper.find(Link).length).toBe(3);
expect(
wrapper
.find(Link)
.find({ href: 'http://example.com/iiif/abc123/0,0,2000,500/full/0/default.jpg?download=true' })
.props().children,
).toEqual('Zoomed region (2000 x 500px)');
const zoomedLink = screen.queryByText('Zoomed region (2000 x 500px)');
expect(zoomedLink).toBeInTheDocument();
});

it('is not present when the window is in book or gallery view (only single view)', () => {
wrapper = createWrapper({
it('does not render a zoomed region link in non-single view types (e.g., book, gallery views)', () => {
currentBoundsSpy.mockImplementation(() => ({
x: 0, y: 0, width: 2000, height: 500,
}));

createWrapper({
canvas, infoResponse, viewType: 'book', windowId: 'zoomedInWindow',
});
const zoomedLink = screen.queryByText('Zoomed region (2000 x 500px)');
expect(zoomedLink).not.toBeInTheDocument();

expect(wrapper.find(Link).length).toBe(2);

wrapper = createWrapper({
createWrapper({
canvas, infoResponse, viewType: 'gallery', windowId: 'zoomedInWindow',
});

expect(wrapper.find(Link).length).toBe(2);
const zoomedLinkGallery = screen.queryByText('Zoomed region (2000 x 500px)');
expect(zoomedLinkGallery).not.toBeInTheDocument();
});

describe('when the zoom link is set to be restricted', () => {
it('has just the whole image link from the sizes and does not present a zoomed region link', () => {
wrapper = createWrapper({
describe('Download Link Size Restrictions', () => {
it('renders only a single download link based on the restricted sizes', () => {
createWrapper({
canvas,
infoResponse: {
json: {
width: 4000,
height: 1000,
sizes: [{
width: 400,
height: 100,
}],
sizes: [{ width: 400, height: 100 }],
},
},
restrictDownloadOnSizeDefinition: true,
windowId: 'zoomedInWindow',
});

expect(wrapper.find(Link).length).toBe(1);
expect(wrapper.find(Link).props().children).toEqual('Whole image (400 x 100px)');
const downloadLink = screen.getByRole('link', { name: /Whole image \(400 x 100px\)/i });
expect(screen.getAllByRole('link')).toHaveLength(2); // Should only show small-size version and link to PDF.
expect(downloadLink).toBeInTheDocument();
});
gerdesque marked this conversation as resolved.
Show resolved Hide resolved
});
});

describe('when there is are sizes defined in the infoResponse', () => {
describe('When Defined Sizes Are Present in infoResponse', () => {
const sizes = [
{ width: 4000, height: 1000 },
{ width: 2000, height: 500 },
{ width: 1000, height: 250 },
];
it('uses those sizes for links in the download dialog', () => {
wrapper = createWrapper({ canvas, infoResponse: { json: { sizes } } });

// console.log(wrapper.debug());
expect(wrapper.find(Link).at(0).props().children).toEqual('Whole image (4000 x 1000px)');
expect(wrapper.find(Link).at(1).props().children).toEqual('Whole image (2000 x 500px)');
expect(wrapper.find(Link).at(2).props().children).toEqual('Whole image (1000 x 250px)');
const viewport = {
getBounds: () => ({
x: 0, y: 0, width: 4000, height: 1000,
}),
};
OSDReferences.set('wid123', {
current: { viewport },
});
it('renders download links for all specified sizes in the dialog', () => {
createWrapper({ canvas, infoResponse: { json: { sizes } } });

const link1 = screen.getByRole('link', { name: /Whole image \(4000 x 1000px\)/i });
const link2 = screen.getByRole('link', { name: /Whole image \(2000 x 500px\)/i });
const link3 = screen.getByRole('link', { name: /Whole image \(1000 x 250px\)/i });

expect(link1).toBeInTheDocument();
expect(link2).toBeInTheDocument();
expect(link3).toBeInTheDocument();
});
});

describe('when there are no defined sizes', () => {
it('renders a link to the whole image', () => {
wrapper = createWrapper({ canvas });
expect(
wrapper
.find(Link)
.find({ href: 'http://example.com/iiif/abc123/full/full/0/default.jpg?download=true' })
.props()
.children,
).toEqual('Whole image (4000 x 1000px)');
describe('When No Sizes Are Defined in infoResponse', () => {
it('renders a single link to the full-size image', () => {
createWrapper({ canvas });

const link = screen.getByRole('link', { name: /Whole image \(4000 x 1000px\)/i });
expect(link).toBeInTheDocument();
expect(link).toHaveAttribute('href', 'http://example.com/iiif/abc123/full/full/0/default.jpg?download=true');
});

describe('when the image is > 1000px wide', () => {
it('renders a link to a small image (1000px wide), and calculates the correct height', () => {
wrapper = createWrapper({ canvas });
expect(wrapper.find(Link).length).toEqual(2);
expect(
wrapper
.find(Link)
.find({ href: 'http://example.com/iiif/abc123/full/1000,/0/default.jpg?download=true' })
.length,
).toEqual(1);
expect(
wrapper
.find(Link)
.find({ href: 'http://example.com/iiif/abc123/full/1000,/0/default.jpg?download=true' })
.props().children,
).toEqual('Whole image (1000 x 250px)');
describe('For Images Wider Than 1000px', () => {
it('renders links for both full-size and 1000px wide versions', () => {
createWrapper({ canvas });

const link1 = screen.getByRole('link', { name: /Whole image \(4000 x 1000px\)/i });
expect(link1).toHaveAttribute('href', 'http://example.com/iiif/abc123/full/full/0/default.jpg?download=true');

const link2 = screen.getByRole('link', { name: /Whole image \(1000 x 250px\)/i });
expect(link2).toHaveAttribute('href', 'http://example.com/iiif/abc123/full/1000,/0/default.jpg?download=true');
});
});

describe('when the image is < 1000px wide', () => {
it('does not render a link to a small image', () => {
describe('For Images Less Than 1000px Wide', () => {
it('does not render a smaller version link if image is under 1000px wide', () => {
canvas.getWidth = () => 999;
wrapper = createWrapper({ canvas });
expect(wrapper.find(Link).length).toEqual(1); // Does not include the 2nd link
createWrapper({ canvas });

const links = screen.getAllByRole('link');
expect(links).toHaveLength(2); // Should only show full-size version and link to PDF.
});
});
});
Expand Down
Loading
Loading