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

Improve Image and Presentation API compatibility #68

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 35 additions & 18 deletions __tests__/CanvasDownloadLinks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ function createWrapper(props) {
canvasId="abc123"
canvasLabel="My Canvas Label"
classes={{}}
infoResponse={{}}
restrictDownloadOnSizeDefinition={false}
viewType="single"
windowId="wid123"
Expand Down Expand Up @@ -40,6 +39,18 @@ describe('CanvasDownloadLinks', () => {
],
};

const infoResponse = {
json: {
'@context': 'http://iiif.io/api/image/2/context.json',
'@id': 'http://example.com/iiif/abc123/',
width: 4000,
height: 1000,
profile: [
'http://iiif.io/api/image/2/level1.json',
],
},
};

let currentBoundsSpy;

beforeEach(() => {
Expand All @@ -51,7 +62,7 @@ describe('CanvasDownloadLinks', () => {
});

it('renders the canvas label as an h3 heading', () => {
createWrapper({ canvas });
createWrapper({ canvas, infoResponse });

const headingElement = screen.getByText('My Canvas Label');
expect(headingElement).toBeInTheDocument();
Expand All @@ -60,18 +71,14 @@ describe('CanvasDownloadLinks', () => {

describe('Canvas Renderings', () => {
it('includes a canvas-level rendering as a download link', () => {
createWrapper({ canvas });
createWrapper({ canvas, infoResponse });

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

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

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,
Expand Down Expand Up @@ -129,8 +136,7 @@ describe('CanvasDownloadLinks', () => {
canvas,
infoResponse: {
json: {
width: 4000,
height: 1000,
...infoResponse.json,
sizes: [{ width: 400, height: 100 }],
},
},
Expand All @@ -146,11 +152,16 @@ describe('CanvasDownloadLinks', () => {
});

describe('When Defined Sizes Are Present in infoResponse', () => {
const sizes = [
{ width: 4000, height: 1000 },
{ width: 2000, height: 500 },
{ width: 1000, height: 250 },
];
const infoResponseWithSizes = {
json: {
...infoResponse.json,
sizes: [
{ width: 4000, height: 1000 },
{ width: 2000, height: 500 },
{ width: 1000, height: 250 },
],
},
};

const viewport = {
getBounds: () => ({
Expand All @@ -161,7 +172,7 @@ describe('CanvasDownloadLinks', () => {
current: { viewport },
});
it('renders download links for all specified sizes in the dialog', () => {
createWrapper({ canvas, infoResponse: { json: { sizes } } });
createWrapper({ canvas, infoResponse: infoResponseWithSizes });

const link1 = screen.getByRole('link', { name: /Whole image \(4000 x 1000px\)/i });
const link2 = screen.getByRole('link', { name: /Whole image \(2000 x 500px\)/i });
Expand All @@ -175,7 +186,7 @@ describe('CanvasDownloadLinks', () => {

describe('When No Sizes Are Defined in infoResponse', () => {
it('renders a single link to the full-size image', () => {
createWrapper({ canvas });
createWrapper({ canvas, infoResponse });

const link = screen.getByRole('link', { name: /Whole image \(4000 x 1000px\)/i });
expect(link).toBeInTheDocument();
Expand All @@ -184,7 +195,7 @@ describe('CanvasDownloadLinks', () => {

describe('For Images Wider Than 1000px', () => {
it('renders links for both full-size and 1000px wide versions', () => {
createWrapper({ canvas });
createWrapper({ canvas, infoResponse });

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');
Expand All @@ -197,7 +208,13 @@ describe('CanvasDownloadLinks', () => {
describe('For Images Less Than 1000px Wide', () => {
it('does not render a smaller version link if image is under 1000px wide', () => {
canvas.getWidth = () => 999;
createWrapper({ canvas });
const smallInfoResponse = {
json: {
...infoResponse.json,
width: 999,
},
};
createWrapper({ canvas, infoResponse: smallInfoResponse });

const links = screen.getAllByRole('link');
expect(links).toHaveLength(2); // Should only show full-size version and link to PDF.
Expand Down
29 changes: 23 additions & 6 deletions __tests__/MiradorDownloadDialog.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function createWrapper(props) {
closeDialog={() => {}}
containerId="container-123"
infoResponse={() => ({})}
manifest={{ getSequences: () => [] }}
manifest={{ getRenderings: () => undefined, getSequences: () => [] }}
open
viewType="single"
windowId="wid123"
Expand Down Expand Up @@ -58,22 +58,39 @@ describe('Dialog', () => {
describe('ManifestDownloadLinks', () => {
it('does not render when there are no manifest renderings', () => {
createWrapper();
const manifestLinks = screen.queryByText('ManifestDownloadLinks');
expect(manifestLinks).not.toBeInTheDocument();
const manifestLinksHeading = screen.queryByText('Other download options');
expect(manifestLinksHeading).not.toBeInTheDocument();
});

it('renders when the manifest contains renderings', () => {
const rendering = { id: '', getLabel: () => ({ getValue: () => 'ManifestDownloadLinks' }), getFormat: () => {} };
it('renders when the default sequence contains renderings', () => {
const rendering = { id: '', getLabel: () => ({ getValue: () => 'Rendering from sequence' }), getFormat: () => {} };
createWrapper({
manifest: {
getRenderings: () => undefined,
getSequences: () => [
{
getRenderings: () => [rendering],
},
],
},
});
const manifestLinks = screen.queryByText('ManifestDownloadLinks');
const manifestLinksHeading = screen.queryByText('Other download options');
expect(manifestLinksHeading).toBeInTheDocument();
const manifestLinks = screen.queryByText('Rendering from sequence');
expect(manifestLinks).toBeInTheDocument();
});

it('renders when the manifest contains renderings', () => {
const rendering = { id: '', getLabel: () => ({ getValue: () => 'Rendering from manifest' }), getFormat: () => {} };
createWrapper({
manifest: {
getRenderings: () => [rendering],
getSequences: () => undefined,
},
});
const manifestLinksHeading = screen.queryByText('Other download options');
expect(manifestLinksHeading).toBeInTheDocument();
const manifestLinks = screen.queryByText('Rendering from manifest');
expect(manifestLinks).toBeInTheDocument();
});
});
Expand Down
56 changes: 29 additions & 27 deletions src/CanvasDownloadLinks.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Link from '@mui/material/Link';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import RenderingDownloadLink from './RenderingDownloadLink';
import { calculateHeightForWidth, createCanonicalImageUrl } from './iiifImageFunctions';

/**
* CanvasDownloadLinks ~
Expand All @@ -20,50 +21,50 @@ export default class CanvasDownloadLinks extends Component {
}

fullImageLabel() {
const { canvas } = this.props;

return `Whole image (${canvas.getWidth()} x ${canvas.getHeight()}px)`;
const { infoResponse } = this.props;
const imageInfo = infoResponse && infoResponse.json;
return imageInfo && `Whole image (${imageInfo.width} x ${imageInfo.height}px)`;
}

smallImageLabel() {
const { canvas } = this.props;
const { infoResponse } = this.props;
const imageInfo = infoResponse && infoResponse.json;

return `Whole image (1000 x ${Math.floor(
(1000 * canvas.getHeight()) / canvas.getWidth(),
(1000 * imageInfo.height) / imageInfo.width,
)}px)`;
}

zoomedImageUrl() {
const { canvas } = this.props;
const { infoResponse } = this.props;
const imageInfo = infoResponse && infoResponse.json;
const bounds = this.currentBounds();
const boundsUrl = canvas
.getCanonicalImageUri()
.replace(
/\/full\/.*\/0\//,
`/${bounds.x},${bounds.y},${bounds.width},${bounds.height}/full/0/`,
);

return `${boundsUrl}?download=true`;
const boundsUrl = createCanonicalImageUrl(
imageInfo,
`${bounds.x},${bounds.y},${bounds.width},${bounds.height}`,
bounds.width,
bounds.height,
);
return imageInfo && `${boundsUrl}?download=true`;
}

imageUrlForSize(size) {
const { canvas } = this.props;

return `${canvas.getCanonicalImageUri(size.width)}?download=true`;
const { infoResponse } = this.props;
const imageInfo = infoResponse && infoResponse.json;
return imageInfo && `${createCanonicalImageUrl(imageInfo, 'full', size.width, size.height)}?download=true`;
}

fullImageUrl() {
const { canvas } = this.props;

return `${canvas
.getCanonicalImageUri()
.replace(/\/full\/.*\/0\//, '/full/full/0/')}?download=true`;
const { infoResponse } = this.props;
const imageInfo = infoResponse && infoResponse.json;
return imageInfo && `${createCanonicalImageUrl(imageInfo, 'full', imageInfo.width, imageInfo.height)}?download=true`;
}

thousandPixelWideImage() {
const { canvas } = this.props;

return `${canvas.getCanonicalImageUri('1000')}?download=true`;
const { infoResponse } = this.props;
const imageInfo = infoResponse && infoResponse.json;
const height = calculateHeightForWidth(imageInfo, 1000);
return imageInfo && `${createCanonicalImageUrl(imageInfo, 'full', 1000, height)}?download=true`;
}

osdViewport() {
Expand Down Expand Up @@ -142,9 +143,10 @@ export default class CanvasDownloadLinks extends Component {
}

thousandPixelWideLink() {
const { canvas } = this.props;
const { infoResponse } = this.props;
const imageInfo = infoResponse && infoResponse.json;

if (canvas.getWidth() < 1000) return '';
if (!imageInfo || imageInfo.width < 1000) return '';

return (
<ListItem disableGutters divider key={this.thousandPixelWideImage()}>
Expand Down
13 changes: 5 additions & 8 deletions src/MiradorDownloadDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,12 @@ const mapStateToProps = (state, { windowId }) => ({
export class MiradorDownloadDialog extends Component {
renderings() {
const { manifest } = this.props;
if (
!(
manifest
const manifestRenderings = (manifest && manifest.getRenderings()) || [];
const sequenceRenderings = (manifest
&& manifest.getSequences()
&& manifest.getSequences()[0]
&& manifest.getSequences()[0].getRenderings()
)
) return [];

return manifest.getSequences()[0].getRenderings();
&& manifest.getSequences()[0].getRenderings()) || [];
return [...manifestRenderings, ...sequenceRenderings];
}

/**
Expand Down Expand Up @@ -126,6 +122,7 @@ MiradorDownloadDialog.propTypes = {
infoResponse: PropTypes.func.isRequired,
manifest: PropTypes.shape({
getSequences: PropTypes.func,
getRenderings: PropTypes.func,
}),
open: PropTypes.bool,
restrictDownloadOnSizeDefinition: PropTypes.bool,
Expand Down
Loading
Loading