diff --git a/.eslintrc b/.eslintrc index 2dc55b0ec53755829dac743c83ed5d776d174676..2c700b50916e10e7084e5fef6bd06282ce5136bc 100644 --- a/.eslintrc +++ b/.eslintrc @@ -7,7 +7,6 @@ "page": true, "document": true }, - "parser": "babel-eslint", "plugins": ["jest"], "rules": { "import/prefer-default-export": "off", @@ -29,6 +28,8 @@ "natural": false }], "react/jsx-props-no-spreading": "off", + "react/function-component-definition": "off", + "default-param-last": "off", "arrow-parens": "off", "import/no-anonymous-default-export": "off", "max-len": ["error", { diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index fce1892d58ebbbaa6fe2d4159b2c0714c8e0e1c2..120b69a47a674634d7018a752b5af401d5681e83 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -12,13 +12,16 @@ on: jobs: build: runs-on: ubuntu-latest + strategy: + matrix: + node-version: [14.x, 16.x, 17.x] steps: - uses: actions/checkout@v2 - - name: Use Node.js 14.x + - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: - node-version: 14.x + node-version: ${{ matrix.node-version }} - run: npm install -g codecov - run: npm install - run: npm test diff --git a/__tests__/integration/mirador/companion_windows.test.js b/__tests__/integration/mirador/companion_windows.test.js index 97e6ca6fe473e490157b1c1dcd68843f5c175ca5..848a5f167c4cf9937f9de86707a2289193ba3e08 100644 --- a/__tests__/integration/mirador/companion_windows.test.js +++ b/__tests__/integration/mirador/companion_windows.test.js @@ -6,7 +6,7 @@ describe('Companion Windows', () => { await expect(page).toFill('#manifestURL', 'http://127.0.0.1:4488/__tests__/fixtures/version-2/001.json'); await expect(page).toClick('#fetchBtn'); await expect(page).toClick('[data-manifestid="http://127.0.0.1:4488/__tests__/fixtures/version-2/001.json"] button'); - await page.waitFor(300); + await page.waitForTimeout(300); await expect(page).toMatchElement('.mirador-window'); }); diff --git a/__tests__/integration/mirador/invalid-api-response.test.js b/__tests__/integration/mirador/invalid-api-response.test.js index 5495d99a8facc38bd7826f6314c4e4aced2f8522..d0edd9de16a0702f5c1f3c14ad784f12079a09d4 100644 --- a/__tests__/integration/mirador/invalid-api-response.test.js +++ b/__tests__/integration/mirador/invalid-api-response.test.js @@ -8,7 +8,7 @@ describe('Mirador Invalid API Response Handler Test', () => { await page.evaluate(() => { document.querySelector('.mirador-add-resource-button').click(); }); - await page.waitFor(50); + await page.waitForTimeout(50); await expect(page).toFill('#manifestURL', uri); await expect(page).toClick('#fetchBtn'); @@ -25,16 +25,12 @@ describe('Mirador Invalid API Response Handler Test', () => { it('renders an error message when a manifest cannot be loaded (and allows it to be dismissed)', async () => { await fetchManifest('http://127.0.0.1:4488/__tests__/fixtures/version-2/broken'); - await expect(page).toMatchElement( - 'p', { text: 'The resource cannot be added:', timeout: 2000 }, - ); - await expect(page).toMatchElement( - 'p', { text: 'http://127.0.0.1:4488/__tests__/fixtures/version-2/broken' }, - ); + await expect(page).toMatchElement('p', { text: 'The resource cannot be added:', timeout: 2000 }); + await expect(page).toMatchElement('p', { text: 'http://127.0.0.1:4488/__tests__/fixtures/version-2/broken' }); await expect(page).toClick('button', { text: 'Dismiss' }); - await page.waitFor(() => !document.querySelector('li[data-manifestid="http://127.0.0.1:4488/__tests__/fixtures/version-2/broken"]')); + await page.waitForFunction(() => !document.querySelector('li[data-manifestid="http://127.0.0.1:4488/__tests__/fixtures/version-2/broken"]')); await expect(page).not.toMatchElement( 'p', diff --git a/__tests__/integration/mirador/language_switching.test.js b/__tests__/integration/mirador/language_switching.test.js index e5f4b25f602bf2be43f9729ff7b9fe838fb7e705..58ceb88f9a60a753de1ad5837c4a29e32dff9e81 100644 --- a/__tests__/integration/mirador/language_switching.test.js +++ b/__tests__/integration/mirador/language_switching.test.js @@ -16,7 +16,7 @@ describe('Language Switching', () => { await expect(page).toMatchElement('[aria-label="Start Here"]'); await expect(page).not.toMatchElement('[aria-label="Hier starten"]'); await expect(page).toClick('li', { text: 'Deutsch' }); - await page.waitFor(1000); + await page.waitForTimeout(1000); await expect(page).not.toMatchElement('[aria-label="Start Here"]'); await expect(page).toMatchElement('[aria-label="Hier starten"]'); }); diff --git a/__tests__/integration/mirador/plugins/add.test.js b/__tests__/integration/mirador/plugins/add.test.js index 5e867d89e0c1592419952e60f85939014b7ccabe..878f81403c2f08704d4cdd2c40cf0da4deb35c01 100644 --- a/__tests__/integration/mirador/plugins/add.test.js +++ b/__tests__/integration/mirador/plugins/add.test.js @@ -2,7 +2,7 @@ describe('add two plugins to <WorkspaceControlPanelButtons>', () => { beforeAll(async () => { await page.goto('http://127.0.0.1:4488/__tests__/integration/mirador/plugins/add.html'); await expect(page).toMatchElement('.mirador-viewer'); - await page.waitFor(1000); + await page.waitForTimeout(1000); }); it('all add plugins will be added to <WorkspaceControlPanelButtons>', async () => { diff --git a/__tests__/integration/mirador/plugins/companionWindow.test.js b/__tests__/integration/mirador/plugins/companionWindow.test.js index d93f36e3abdf8c2bdd9645abd0ae94734b7f936d..71ad618bc9e7c3a981faab57aeda65a4885a7e70 100644 --- a/__tests__/integration/mirador/plugins/companionWindow.test.js +++ b/__tests__/integration/mirador/plugins/companionWindow.test.js @@ -2,13 +2,13 @@ describe('add plugins for companion windows', () => { beforeAll(async () => { await page.goto('http://127.0.0.1:4488/__tests__/integration/mirador/plugins/companionWindow.html'); await expect(page).toMatchElement('.mirador-viewer'); - await page.waitFor(1000); + await page.waitForTimeout(1000); }); it('added a plugin to the window sidebar and companion window', async () => { await expect(page).toClick('button[aria-label="Toggle sidebar"]'); - await page.waitFor(1000); + await page.waitForTimeout(1000); await expect(page).toMatchElement('.mirador-companion-window-left.mirador-window-sidebar-info-panel'); await expect(page).toMatchElement('#add-plugin-companion-window-button'); diff --git a/__tests__/integration/mirador/plugins/priority.test.js b/__tests__/integration/mirador/plugins/priority.test.js index 3ed451fb86ca96bc9af0fb14c9bd3c791e376991..6dfece29f863934b059ec503eb4c175340040e23 100644 --- a/__tests__/integration/mirador/plugins/priority.test.js +++ b/__tests__/integration/mirador/plugins/priority.test.js @@ -2,7 +2,7 @@ describe('try to apply 2 add plugins and 2 wrap plugins to <WorkspaceControlPane beforeAll(async () => { await page.goto('http://127.0.0.1:4488/__tests__/integration/mirador/plugins/priority.html'); await expect(page).toMatchElement('.mirador-viewer'); - await page.waitFor(1000); + await page.waitForTimeout(1000); }); it('only apply the first wrap plugin', async () => { diff --git a/__tests__/integration/mirador/plugins/state.test.js b/__tests__/integration/mirador/plugins/state.test.js index eafa59e8c29da51640d59e379458cf62d3264ee9..15e821ce441fbc9fc9cb3e5b1638ba894c326858 100644 --- a/__tests__/integration/mirador/plugins/state.test.js +++ b/__tests__/integration/mirador/plugins/state.test.js @@ -4,7 +4,7 @@ describe('how plugins relate to state', () => { beforeAll(async () => { await page.goto('http://127.0.0.1:4488/__tests__/integration/mirador/plugins/state.html'); await expect(page).toMatchElement('.mirador-viewer'); - await page.waitFor(1000); + await page.waitForTimeout(1000); }); it('plugin can read from state', async () => { diff --git a/__tests__/integration/mirador/plugins/validate.test.js b/__tests__/integration/mirador/plugins/validate.test.js index 311d2ec741f854d2a8d7f69f7bad3c805be604cf..d62a7c261cd39d43709a45aca81f4910ace16499 100644 --- a/__tests__/integration/mirador/plugins/validate.test.js +++ b/__tests__/integration/mirador/plugins/validate.test.js @@ -2,7 +2,7 @@ describe('pass valid and invalid plugins to <WorkspaceControlPanelButtons>', () beforeAll(async () => { await page.goto('http://127.0.0.1:4488/__tests__/integration/mirador/plugins/validate.html'); await expect(page).toMatchElement('.mirador-viewer'); - await page.waitFor(1000); + await page.waitForTimeout(1000); }); it('valid plugins will be applied <WorkspaceControlPanelButtons>', async () => { diff --git a/__tests__/integration/mirador/plugins/wrap.test.js b/__tests__/integration/mirador/plugins/wrap.test.js index 77865efa64a3aa67c3ead55313c4dc96a4982344..2e77a1842b5b1b270fd3c11fb9031ecca6b5e7d5 100644 --- a/__tests__/integration/mirador/plugins/wrap.test.js +++ b/__tests__/integration/mirador/plugins/wrap.test.js @@ -2,7 +2,7 @@ describe('wrap <WorkspaceControlPanelButtons> by a plugin', () => { beforeAll(async () => { await page.goto('http://127.0.0.1:4488/__tests__/integration/mirador/plugins/wrap.html'); await expect(page).toMatchElement('.mirador-viewer'); - await page.waitFor(1000); + await page.waitForTimeout(1000); }); it('wraps <WorkspaceControlPanelButtons>', async () => { diff --git a/__tests__/integration/mirador/thumbnail-navigation.test.js b/__tests__/integration/mirador/thumbnail-navigation.test.js index 6b2cf8051f93b17a8989a7ab2a34a1270d7dd9f0..35de953ccaa3b13e04ec8ab4fb17bc7a9faf381e 100644 --- a/__tests__/integration/mirador/thumbnail-navigation.test.js +++ b/__tests__/integration/mirador/thumbnail-navigation.test.js @@ -12,7 +12,7 @@ describe('Thumbnail navigation', () => { miradorInstance.store.getState().windows )); expect(Object.values(windows)[0].canvasId).toBe('https://iiif.harvardartmuseums.org/manifests/object/299843/canvas/canvas-47174892'); // test harness in index.html starts at 2 - await page.waitFor(1000); + await page.waitForTimeout(1000); await expect(page).toClick('.mirador-thumbnail-nav-canvas-1 img'); await expect(page).toMatchElement('.mirador-thumbnail-nav-canvas-1.mirador-current-canvas-grouping', { timeout: 1500 }); windows = await page.evaluate(() => ( diff --git a/__tests__/integration/mirador/window_actions.test.js b/__tests__/integration/mirador/window_actions.test.js index cc35d96dee1076da19efc6d340d1ff8e8795177b..8dd54ffba986ed101cc89dba80cb75823c64c40e 100644 --- a/__tests__/integration/mirador/window_actions.test.js +++ b/__tests__/integration/mirador/window_actions.test.js @@ -12,12 +12,12 @@ describe('Window actions', () => { await expect(page).toClick('[data-manifestid="http://127.0.0.1:4488/__tests__/fixtures/version-2/sn904cj3429.json"] button'); await expect(page).toMatchElement('.mirador-window'); - await page.waitFor(1000); + await page.waitForTimeout(1000); await expect(page).toClick('.mirador-window-close'); const numWindows = await page.evaluate(page => ( document.querySelectorAll('.mirador-window').length )); // only default configed windows found - await page.waitFor(1000); + await page.waitForTimeout(1000); await expect(numWindows).toBe(0); }); }); diff --git a/__tests__/src/components/AccessTokenSender.test.js b/__tests__/src/components/AccessTokenSender.test.js index 33d56e0ada4c3a5f6bb0036847941ea784418a63..d14f10a556cb8d994a2c4bc1a2999fadb1b45753 100644 --- a/__tests__/src/components/AccessTokenSender.test.js +++ b/__tests__/src/components/AccessTokenSender.test.js @@ -20,7 +20,7 @@ describe('AccessTokenSender', () => { it('renders nothing if there is no url', () => { wrapper = createWrapper({}); - expect(wrapper.matchesElement(<></>)).toBe(true); + expect(wrapper.isEmptyRender()).toBe(true); }); it('renders properly', () => { diff --git a/__tests__/src/components/LabelValueMetadata.test.js b/__tests__/src/components/LabelValueMetadata.test.js index 23c279ff140fa029e8c8b8c0adad4d2d6468bab3..2e3178bab9a6b4d222861dc2a0cc3004b40f32c1 100644 --- a/__tests__/src/components/LabelValueMetadata.test.js +++ b/__tests__/src/components/LabelValueMetadata.test.js @@ -59,7 +59,7 @@ describe('LabelValueMetadata', () => { it('renders an empty fragment instead of an empty dl', () => { expect(wrapper.find('dl').length).toEqual(0); - expect(wrapper.matchesElement(<></>)).toBe(true); + expect(wrapper.isEmptyRender()).toBe(true); }); }); diff --git a/__tests__/src/components/MiradorMenuButton.test.js b/__tests__/src/components/MiradorMenuButton.test.js index da0178315ea735140e548ba9a6ffa506183524d0..ad865d63dab0cd05901265415cc6d263618c5ea0 100644 --- a/__tests__/src/components/MiradorMenuButton.test.js +++ b/__tests__/src/components/MiradorMenuButton.test.js @@ -11,7 +11,7 @@ import { MiradorMenuButton } from '../../../src/components/MiradorMenuButton'; function createWrapper(props) { return shallow( <MiradorMenuButton aria-label="The Label" containerId="mirador" {...props}> - <>icon</> + icon </MiradorMenuButton>, ); } diff --git a/__tests__/src/components/NestedMenu.test.js b/__tests__/src/components/NestedMenu.test.js index f6c49ba3cccc8b3865a1aefef80d8e5b953effa9..6db2b464cfb440374d01469df7be36e53c43ddbb 100644 --- a/__tests__/src/components/NestedMenu.test.js +++ b/__tests__/src/components/NestedMenu.test.js @@ -13,11 +13,11 @@ import { NestedMenu } from '../../../src/components/NestedMenu'; function createWrapper(props) { return shallow( <NestedMenu - icon={<>GivenIcon</>} + icon="GivenIcon" label="GivenLabel" {...props} > - <>GivenChildren</> + GivenChildren </NestedMenu>, ); } diff --git a/__tests__/src/components/OpenSeadragonViewer.test.js b/__tests__/src/components/OpenSeadragonViewer.test.js index 7dfa0aae873b665d221d505927f71ee30034ec2b..1c957824c3ece5fbe987c8146b10bd258d1f0b2b 100644 --- a/__tests__/src/components/OpenSeadragonViewer.test.js +++ b/__tests__/src/components/OpenSeadragonViewer.test.js @@ -10,46 +10,52 @@ const canvases = Utils.parseManifest(fixture).getSequences()[0].getCanvases(); jest.mock('openseadragon'); +/** + * Helper function to create a shallow wrapper around OpenSeadragonViewer + */ +function createWrapper(props) { + return shallow( + <OpenSeadragonViewer + classes={{}} + infoResponses={[{ + id: 'a', + json: { + '@id': 'http://foo', + height: 200, + width: 100, + }, + }, { + id: 'b', + json: { + '@id': 'http://bar', + height: 201, + width: 150, + }, + }]} + nonTiledImages={[{ + getProperty: () => {}, + id: 'http://foo', + }]} + windowId="base" + config={{}} + updateViewport={jest.fn()} + t={k => k} + canvasWorld={new CanvasWorld(canvases)} + {...props} + > + <div className="foo" /> + <div className="bar" /> + </OpenSeadragonViewer>, + ); +} + describe('OpenSeadragonViewer', () => { let wrapper; let updateViewport; beforeEach(() => { OpenSeadragon.mockClear(); - - updateViewport = jest.fn(); - - wrapper = shallow( - <OpenSeadragonViewer - classes={{}} - infoResponses={[{ - id: 'a', - json: { - '@id': 'http://foo', - height: 200, - width: 100, - }, - }, { - id: 'b', - json: { - '@id': 'http://bar', - height: 201, - width: 150, - }, - }]} - nonTiledImages={[{ - getProperty: () => {}, - id: 'http://foo', - }]} - windowId="base" - config={{}} - updateViewport={updateViewport} - t={k => k} - canvasWorld={new CanvasWorld(canvases)} - > - <div className="foo" /> - <div className="bar" /> - </OpenSeadragonViewer>, - ); + wrapper = createWrapper({}); + updateViewport = wrapper.instance().props.updateViewport; }); it('renders the component', () => { expect(wrapper.find('.mirador-osd-container').length).toBe(1); @@ -70,10 +76,7 @@ describe('OpenSeadragonViewer', () => { expect(wrapper.instance().infoResponsesMatch([])).toBe(false); }); it('with an empty array', () => { - wrapper.instance().viewer = { - close: () => {}, - }; - wrapper.setProps({ infoResponses: [] }); + wrapper = createWrapper({ infoResponses: [] }); expect(wrapper.instance().infoResponsesMatch([])).toBe(true); }); it('when the @ids do match', () => { @@ -87,7 +90,7 @@ describe('OpenSeadragonViewer', () => { expect(wrapper.instance().infoResponsesMatch([{ id: 'a', json: { '@id': 'http://foo-degraded' } }])).toBe(false); }); it('when the id props match', () => { - wrapper.setProps({ + wrapper = createWrapper({ infoResponses: [{ id: 'a', json: { @@ -106,10 +109,7 @@ describe('OpenSeadragonViewer', () => { expect(wrapper.instance().nonTiledImagedMatch([])).toBe(false); }); it('with an empty array', () => { - wrapper.instance().viewer = { - close: () => {}, - }; - wrapper.setProps({ nonTiledImages: [] }); + wrapper = createWrapper({ nonTiledImages: [] }); expect(wrapper.instance().nonTiledImagedMatch([])).toBe(true); }); it('when the ids do match', () => { @@ -118,21 +118,17 @@ describe('OpenSeadragonViewer', () => { }); describe('addAllImageSources', () => { - it('calls addTileSource for every tileSources and then zoomsToWorld', () => { - wrapper.instance().viewer = { - close: () => {}, - }; - wrapper.setProps({ infoResponses: [1, 2, 3, 4] }); + it('calls addTileSource for every tileSources and then zoomsToWorld', async () => { + wrapper = createWrapper({ infoResponses: [1, 2, 3, 4] }); + wrapper.setState({ viewer: { viewport: { fitBounds: () => {} }, world: { getItemCount: () => 0 } } }); const mockAddTileSource = jest.fn(); wrapper.instance().addTileSource = mockAddTileSource; - wrapper.instance().addAllImageSources(); + await wrapper.instance().addAllImageSources(); expect(mockAddTileSource).toHaveBeenCalledTimes(4); }); - it('calls addNonTileSource for every nonTiledImage and then zoomsToWorld', () => { - wrapper.instance().viewer = { - close: () => {}, - }; - wrapper.setProps({ + + it('calls addNonTileSource for every nonTiledImage and then zoomsToWorld', async () => { + wrapper = createWrapper({ nonTiledImages: [ { getProperty: () => 'Image' }, { getProperty: () => 'Image' }, @@ -140,22 +136,18 @@ describe('OpenSeadragonViewer', () => { { getProperty: () => 'Image' }, ], }); + const instance = wrapper.instance(); const mockAddNonTiledImage = jest.fn(); wrapper.instance().addNonTiledImage = mockAddNonTiledImage; - wrapper.instance().addAllImageSources(); + await instance.addAllImageSources(); expect(mockAddNonTiledImage).toHaveBeenCalledTimes(4); }); }); describe('addTileSource', () => { - it('calls addTiledImage asynchronously on the OSD viewer', async () => { - wrapper.instance().addTileSource({}).then((event) => { - expect(event).toBe('event'); - }); - }); - it('when a viewer is not available, returns an unresolved Promise', () => { - expect(wrapper.instance().addTileSource({})).toEqual(expect.any(Promise)); - }); + it('when a viewer is not available, returns an unresolved Promise', () => ( + expect(wrapper.instance().addTileSource({})).rejects.toBeUndefined() + )); }); describe('addNonTiledImage', () => { @@ -189,17 +181,15 @@ describe('OpenSeadragonViewer', () => { layerIndexOfImageResource: i => 1 - i, layerOpacityOfImageResource: i => 0.5, }; - wrapper.setProps({ canvasWorld }); + wrapper = createWrapper({ canvasWorld }); wrapper.instance().loaded = true; - wrapper.setState({ - viewer: { - world: { - getItemAt: i => ({ setOpacity, source: { id: i } }), - getItemCount: () => 2, - setItemIndex, - }, + wrapper.instance().state.viewer = { + world: { + getItemAt: i => ({ setOpacity, source: { id: i } }), + getItemCount: () => 2, + setItemIndex, }, - }); + }; wrapper.instance().refreshTileProperties(); @@ -280,12 +270,8 @@ describe('OpenSeadragonViewer', () => { it('calls the OSD viewport panTo and zoomTo with the component state', () => { wrapper.instance().componentDidMount(); - expect(panTo).toHaveBeenCalledWith( - { x: 1, y: 0, zoom: 0.5 }, true, - ); - expect(zoomTo).toHaveBeenCalledWith( - 0.5, { x: 1, y: 0, zoom: 0.5 }, true, - ); + expect(panTo).toHaveBeenCalledWith({ x: 1, y: 0, zoom: 0.5 }, true); + expect(zoomTo).toHaveBeenCalledWith(0.5, { x: 1, y: 0, zoom: 0.5 }, true); }); it('adds animation-start/finish flag for rerendering performance', () => { @@ -340,12 +326,8 @@ describe('OpenSeadragonViewer', () => { }, }); - expect(panTo).toHaveBeenCalledWith( - expect.objectContaining({ x: 1, y: 0, zoom: 0.5 }), false, - ); - expect(zoomTo).toHaveBeenCalledWith( - 0.5, expect.objectContaining({ x: 1, y: 0, zoom: 0.5 }), false, - ); + expect(panTo).toHaveBeenCalledWith(expect.objectContaining({ x: 1, y: 0, zoom: 0.5 }), false); + expect(zoomTo).toHaveBeenCalledWith(0.5, expect.objectContaining({ x: 1, y: 0, zoom: 0.5 }), false); expect(setRotation).toHaveBeenCalledWith(90); expect(setFlip).toHaveBeenCalledWith(true); expect(forceRedraw).not.toHaveBeenCalled(); diff --git a/__tests__/src/components/ScrollTo.test.js b/__tests__/src/components/ScrollTo.test.js index b6c7953106d6fff824bbf86bd484f375a1f49c7c..ecb4a6d3f7de6891e958139b7ca8c0e95ebac435 100644 --- a/__tests__/src/components/ScrollTo.test.js +++ b/__tests__/src/components/ScrollTo.test.js @@ -10,7 +10,7 @@ function createWrapper(props) { scrollTo {...props} > - <>Child Prop</> + Child Prop </ScrollTo>, ); } diff --git a/__tests__/src/components/SearchPanelControls.test.js b/__tests__/src/components/SearchPanelControls.test.js index d09f641230cbb66e5cc7dcec22eaa22c2e3710db..64c57780ca19c57ccfa76ff0b03ff1aa340eca34 100644 --- a/__tests__/src/components/SearchPanelControls.test.js +++ b/__tests__/src/components/SearchPanelControls.test.js @@ -36,9 +36,7 @@ describe('SearchPanelControls', () => { const value = 'somestring'; wrapper.find(Autocomplete).prop('onChange')({}, { match: value }, {}); expect(wrapper.state().search).toEqual(value); - expect(fetchSearch).toHaveBeenCalledWith( - 'window', 'cw', 'http://example.com/search?q=somestring', 'somestring', - ); + expect(fetchSearch).toHaveBeenCalledWith('window', 'cw', 'http://example.com/search?q=somestring', 'somestring'); }); it('renders a text input through the renderInput prop', () => { const wrapper = createWrapper(); diff --git a/__tests__/src/components/SidebarIndexTableOfContents.test.js b/__tests__/src/components/SidebarIndexTableOfContents.test.js index 15c3729ea513c20f2fd7c09197d20262e90e17aa..c30044db7e6b9a0028151acafd6bdba79d3f524b 100644 --- a/__tests__/src/components/SidebarIndexTableOfContents.test.js +++ b/__tests__/src/components/SidebarIndexTableOfContents.test.js @@ -95,7 +95,7 @@ describe('SidebarIndexTableOfContents', () => { it('toggles branch nodes on click, but not leaf nodes', () => { const wrapper = createWrapper({ setCanvas, toggleNode }); - const treeView = wrapper.children(TreeView).at(0); + const treeView = wrapper.find(TreeView); const node0 = treeView.childAt(0).childAt(0); expect(node0.prop('nodeId')).toBe('0-0'); node0.simulate('click'); @@ -120,7 +120,7 @@ describe('SidebarIndexTableOfContents', () => { setCanvas, toggleNode, }); - const treeView = wrapper.children(TreeView).at(0); + const treeView = wrapper.find(TreeView); const node0 = treeView.childAt(0).childAt(0); expect(node0.prop('nodeId')).toBe('0-0'); @@ -143,7 +143,7 @@ describe('SidebarIndexTableOfContents', () => { setCanvas, toggleNode, }); - const treeView = wrapper.children(TreeView).at(0); + const treeView = wrapper.find(TreeView); const node0 = treeView.childAt(0).childAt(0); expect(node0.prop('nodeId')).toBe('0-0'); const node00 = node0.children().at(0).childAt(0); @@ -161,7 +161,7 @@ describe('SidebarIndexTableOfContents', () => { it('toggles branch nodes (but not leaf nodes) with Space or Enter key', () => { const wrapper = createWrapper({ setCanvas, toggleNode }); - const treeView = wrapper.children(TreeView).at(0); + const treeView = wrapper.find(TreeView); const node0 = treeView.childAt(0).childAt(0); node0.simulate(...createKeydownProps('Enter')); expect(toggleNode).toHaveBeenCalledTimes(1); @@ -180,7 +180,7 @@ describe('SidebarIndexTableOfContents', () => { it('calls setCanvas only on click for ranges with canvases that do not have children', () => { const wrapper = createWrapper({ setCanvas, toggleNode }); - const treeView = wrapper.children(TreeView).at(0); + const treeView = wrapper.find(TreeView); const node0 = treeView.childAt(0).childAt(0); expect(node0.prop('nodeId')).toBe('0-0'); node0.simulate('click'); @@ -204,7 +204,7 @@ describe('SidebarIndexTableOfContents', () => { toggleNode, windowId: 'w1', }); - const treeView = version2wrapper.children(TreeView).at(0); + const treeView = version2wrapper.find(TreeView); const node3 = treeView.childAt(3).childAt(0); expect(node3.prop('nodeId')).toBe('0-3'); node3.simulate('click'); @@ -216,7 +216,7 @@ describe('SidebarIndexTableOfContents', () => { toggleNode, windowId: 'w1', }); - const treeViewVersion3 = version3wrapper.children(TreeView).at(0); + const treeViewVersion3 = version3wrapper.find(TreeView); const rootNode = treeViewVersion3.childAt(0).childAt(0); const version3node1 = rootNode.childAt(1).childAt(0); expect(version3node1.prop('nodeId')).toBe('0-0-1'); @@ -236,7 +236,7 @@ describe('SidebarIndexTableOfContents', () => { it('does not select a canvas when opening a node with the right arrow key', () => { const wrapper = createWrapper({ setCanvas, toggleNode }); - const treeView = wrapper.children(TreeView).at(0); + const treeView = wrapper.find(TreeView); const node0 = treeView.childAt(0).childAt(0); expect(node0.prop('nodeId')).toBe('0-0'); node0.simulate(...createKeydownProps('ArrowRight')); @@ -246,7 +246,7 @@ describe('SidebarIndexTableOfContents', () => { it('does not select a canvas when closing a node with the left arrow key', () => { const wrapper = createWrapper({ expandedNodeIds: ['0-0'], setCanvas, toggleNode }); - const treeView = wrapper.children(TreeView).at(0); + const treeView = wrapper.find(TreeView); const node0 = treeView.childAt(0).childAt(0); expect(node0.prop('nodeId')).toBe('0-0'); node0.simulate(...createKeydownProps('ArrowLeft')); diff --git a/__tests__/src/components/Window.test.js b/__tests__/src/components/Window.test.js index c16b361fef230d10765295efaccef0786e4ec971..78bd9f3884e3690e29f85f8d67361bb369e4ffec 100644 --- a/__tests__/src/components/Window.test.js +++ b/__tests__/src/components/Window.test.js @@ -47,18 +47,14 @@ describe('Window', () => { describe('when workspaceType is mosaic', () => { xit('calls the context mosaicWindowActions connectDragSource method to make WindowTopBar draggable', () => { const connectDragSource = jest.fn(component => component); - wrapper = createWrapper( - { windowDraggable: true, workspaceType: 'mosaic' }, { mosaicWindowActions: { connectDragSource } }, - ); + wrapper = createWrapper({ windowDraggable: true, workspaceType: 'mosaic' }, { mosaicWindowActions: { connectDragSource } }); expect(wrapper.find(WindowTopBar)).toHaveLength(1); expect(connectDragSource).toHaveBeenCalled(); }); it('does not call the context mosaicWindowActions connectDragSource when the windowDraggable is set to false', () => { const connectDragSource = jest.fn(component => component); - wrapper = createWrapper( - { windowDraggable: false, workspaceType: 'mosaic' }, { mosaicWindowActions: { connectDragSource } }, - ); + wrapper = createWrapper({ windowDraggable: false, workspaceType: 'mosaic' }, { mosaicWindowActions: { connectDragSource } }); expect(wrapper.find(WindowTopBar)).toHaveLength(1); expect(connectDragSource).not.toHaveBeenCalled(); }); diff --git a/__tests__/src/components/WindowTopBarPluginMenu.test.js b/__tests__/src/components/WindowTopBarPluginMenu.test.js index 436019f42c32ebe0ab8cef43a0e3141445a412aa..c27ef214d9867e68ce60e042f83bc09e827cfe19 100644 --- a/__tests__/src/components/WindowTopBarPluginMenu.test.js +++ b/__tests__/src/components/WindowTopBarPluginMenu.test.js @@ -21,12 +21,9 @@ describe('WindowTopBarPluginMenu', () => { let wrapper; describe('when there are no plugins present', () => { - it('renders a Fragment (and no Button/Menu/PluginHook)', () => { + it('renders nothing (and no Button/Menu/PluginHook)', () => { wrapper = createWrapper(); - expect(wrapper.find('Fragment').length).toBe(1); - expect(wrapper.find(Menu).length).toBe(0); - expect(wrapper.find(MiradorMenuButton).length).toBe(0); - expect(wrapper.find(PluginHook).length).toBe(0); + expect(wrapper.isEmptyRender()).toBe(true); }); }); diff --git a/__tests__/src/lib/MiradorViewer.test.js b/__tests__/src/lib/MiradorViewer.test.js index 95e24df1a37ef6281bee1149ddec896302ff40fb..500ddff3b4979e7c7d272f05e994b132d220a7f2 100644 --- a/__tests__/src/lib/MiradorViewer.test.js +++ b/__tests__/src/lib/MiradorViewer.test.js @@ -30,32 +30,34 @@ describe('MiradorViewer', () => { }); describe('processConfig', () => { it('transforms config values to actions to dispatch to store', () => { - instance = new MiradorViewer({ - catalog: [ - { manifestId: 'http://media.nga.gov/public/manifests/nga_highlights.json', provider: 'National Gallery of Art' }, - ], - id: 'mirador', - windows: [ - { - canvasId: 'https://iiif.harvardartmuseums.org/manifests/object/299843/canvas/canvas-47174892', - loadedManifest: 'https://iiif.harvardartmuseums.org/manifests/object/299843', - thumbnailNavigationPosition: 'far-bottom', - }, - { - loadedManifest: 'https://iiif.harvardartmuseums.org/manifests/object/299843', - view: 'book', - }, - ], - }, - { - plugins: [{ - config: { - foo: 'bar', - }, - mode: 'add', - target: 'WindowTopBarPluginArea', - }], - }); + instance = new MiradorViewer( + { + catalog: [ + { manifestId: 'http://media.nga.gov/public/manifests/nga_highlights.json', provider: 'National Gallery of Art' }, + ], + id: 'mirador', + windows: [ + { + canvasId: 'https://iiif.harvardartmuseums.org/manifests/object/299843/canvas/canvas-47174892', + loadedManifest: 'https://iiif.harvardartmuseums.org/manifests/object/299843', + thumbnailNavigationPosition: 'far-bottom', + }, + { + loadedManifest: 'https://iiif.harvardartmuseums.org/manifests/object/299843', + view: 'book', + }, + ], + }, + { + plugins: [{ + config: { + foo: 'bar', + }, + mode: 'add', + target: 'WindowTopBarPluginArea', + }], + }, + ); const { windows, catalog, config } = instance.store.getState(); const windowIds = Object.keys(windows); @@ -74,35 +76,37 @@ describe('MiradorViewer', () => { expect(config.foo).toBe('bar'); }); it('merges translation configs from multiple plugins', () => { - instance = new MiradorViewer({ - id: 'mirador', - }, - { - plugins: [ - { - config: { - translations: { - en: { - foo: 'bar', + instance = new MiradorViewer( + { + id: 'mirador', + }, + { + plugins: [ + { + config: { + translations: { + en: { + foo: 'bar', + }, }, }, + mode: 'add', + target: 'WindowTopBarPluginArea', }, - mode: 'add', - target: 'WindowTopBarPluginArea', - }, - { - config: { - translations: { - en: { - bat: 'bar', + { + config: { + translations: { + en: { + bat: 'bar', + }, }, }, + mode: 'wrap', + target: 'Window', }, - mode: 'wrap', - target: 'Window', - }, - ], - }); + ], + }, + ); const { config } = instance.store.getState(); diff --git a/__tests__/src/sagas/windows.test.js b/__tests__/src/sagas/windows.test.js index 796c0adc0072fecef6b2290c6bdd649232d08d32..126a381625da55b0e2e9bef4e38053f7c8ceb908 100644 --- a/__tests__/src/sagas/windows.test.js +++ b/__tests__/src/sagas/windows.test.js @@ -218,8 +218,10 @@ describe('window-level sagas', () => { return expectSaga(setCurrentAnnotationsOnCurrentCanvas, action) .provide([ - [select(getSearchForWindow, - { windowId: 'abc123' }), {}], + [select( + getSearchForWindow, + { windowId: 'abc123' }, + ), {}], ]) .run() .then(({ allEffects }) => allEffects.length === 0); @@ -234,8 +236,10 @@ describe('window-level sagas', () => { return expectSaga(setCurrentAnnotationsOnCurrentCanvas, action) .provide([ - [select(getSearchForWindow, - { windowId: 'abc123' }), { cwid: { } }], + [select( + getSearchForWindow, + { windowId: 'abc123' }, + ), { cwid: { } }], [select(getAnnotationsBySearch, { canvasIds: ['a', 'b'], companionWindowIds: ['cwid'], windowId: 'abc123' }), { }], ]) @@ -256,8 +260,10 @@ describe('window-level sagas', () => { return expectSaga(setCurrentAnnotationsOnCurrentCanvas, action) .provide([ - [select(getSearchForWindow, - { windowId: 'abc123' }), { cwid: { } }], + [select( + getSearchForWindow, + { windowId: 'abc123' }, + ), { cwid: { } }], [select(getAnnotationsBySearch, { canvasIds: ['a', 'b'], companionWindowIds: ['cwid'], windowId: 'abc123' }), { cwid: ['annoId'] }], ]) diff --git a/__tests__/src/selectors/canvases.test.js b/__tests__/src/selectors/canvases.test.js index e8fc9adb8ce1f84de636ddfc70c2b75d7896de70..43d1a0f735df6545bf35ed622274b99498ea1483 100644 --- a/__tests__/src/selectors/canvases.test.js +++ b/__tests__/src/selectors/canvases.test.js @@ -355,9 +355,7 @@ describe('getVisibleCanvasNonTiledResources', () => { }, }, }; - expect(getVisibleCanvasNonTiledResources( - state, { windowId: 'a' }, - )[0].id).toBe('http://iiif.io/api/presentation/2.0/example/fixtures/resources/page1-full.png'); + expect(getVisibleCanvasNonTiledResources(state, { windowId: 'a' })[0].id).toBe('http://iiif.io/api/presentation/2.0/example/fixtures/resources/page1-full.png'); }); it('works for v3 Presentation API', () => { const state = { @@ -376,9 +374,7 @@ describe('getVisibleCanvasNonTiledResources', () => { }, }, }; - expect(getVisibleCanvasNonTiledResources( - state, { windowId: 'a' }, - )[0].id).toBe('http://iiif.io/api/presentation/2.1/example/fixtures/resources/page1-full.png'); + expect(getVisibleCanvasNonTiledResources(state, { windowId: 'a' })[0].id).toBe('http://iiif.io/api/presentation/2.1/example/fixtures/resources/page1-full.png'); }); describe('getVisibleCanvasVideoResources', () => { @@ -399,9 +395,7 @@ describe('getVisibleCanvasNonTiledResources', () => { }, }, }; - expect(getVisibleCanvasVideoResources( - state, { windowId: 'a' }, - )[0].id).toBe('https://fixtures.iiif.io/video/indiana/30-minute-clock/medium/30-minute-clock.mp4'); + expect(getVisibleCanvasVideoResources(state, { windowId: 'a' })[0].id).toBe('https://fixtures.iiif.io/video/indiana/30-minute-clock/medium/30-minute-clock.mp4'); }); }); @@ -423,9 +417,7 @@ describe('getVisibleCanvasNonTiledResources', () => { }, }, }; - expect(getVisibleCanvasCaptions( - state, { windowId: 'a' }, - )[0].id).toBe('https://example.com/file.vtt'); + expect(getVisibleCanvasCaptions(state, { windowId: 'a' })[0].id).toBe('https://example.com/file.vtt'); }); }); @@ -447,9 +439,7 @@ describe('getVisibleCanvasNonTiledResources', () => { }, }, }; - expect(getVisibleCanvasAudioResources( - state, { windowId: 'a' }, - )[0].id).toBe('https://fixtures.iiif.io/audio/indiana/mahler-symphony-3/CD1/medium/128Kbps.mp4'); + expect(getVisibleCanvasAudioResources(state, { windowId: 'a' })[0].id).toBe('https://fixtures.iiif.io/audio/indiana/mahler-symphony-3/CD1/medium/128Kbps.mp4'); }); }); }); diff --git a/__tests__/src/selectors/searches.test.js b/__tests__/src/selectors/searches.test.js index 3a9e3791a4e3e215d79cb17419d0785c21ea6a0e..378417caf92aed1c363bb59fe3bbc8365891669f 100644 --- a/__tests__/src/selectors/searches.test.js +++ b/__tests__/src/selectors/searches.test.js @@ -309,9 +309,7 @@ describe('getResourceAnnotationForSearchHit', () => { }; expect( - getResourceAnnotationForSearchHit( - state, { annotationUri: annoId, companionWindowId, windowId: 'a' }, - ).resource['@id'], + getResourceAnnotationForSearchHit(state, { annotationUri: annoId, companionWindowId, windowId: 'a' }).resource['@id'], ).toEqual(annoId); }); }); @@ -344,9 +342,7 @@ describe('getResourceAnnotationLabel', () => { }; expect( - getResourceAnnotationLabel( - state, { annotationUri: annoId, companionWindowId, windowId: 'a' }, - ), + getResourceAnnotationLabel(state, { annotationUri: annoId, companionWindowId, windowId: 'a' }), ).toEqual(['The Annotation Label']); }); @@ -369,9 +365,7 @@ describe('getResourceAnnotationLabel', () => { }; expect( - getResourceAnnotationLabel( - state, { annotationUri: annoId, companionWindowId, windowId: 'a' }, - ), + getResourceAnnotationLabel(state, { annotationUri: annoId, companionWindowId, windowId: 'a' }), ).toEqual([]); }); }); diff --git a/babel.config.js b/babel.config.js index 71e3e16ee133e18ccca49b8f41da25143ffc0c8f..fc73760539b35499cf110a984fc2bc76609bd3b3 100644 --- a/babel.config.js +++ b/babel.config.js @@ -53,6 +53,8 @@ module.exports = function (api) { loose: true, }, ], + ['@babel/plugin-proposal-private-property-in-object', { loose: true }], + ['@babel/plugin-proposal-private-methods', { loose: true }], [ '@babel/plugin-proposal-object-rest-spread', { diff --git a/jest-puppeteer.config.js b/jest-puppeteer.config.js index f2324320bc4f2d94696575f495b44753639c7e94..b7f3d29270c58ad2de562ff6eceb7c3ffa280179 100644 --- a/jest-puppeteer.config.js +++ b/jest-puppeteer.config.js @@ -4,6 +4,8 @@ module.exports = { }, server: [{ command: 'npm run server -- -p 4488', + host: '127.0.0.1', + launchTimeout: 5000, port: 4488, }], }; diff --git a/package.json b/package.json index cb2499fafc99afba02a93cd78169d89de5bccdee..01e0104c8f35f185197f00ec298b623dd7bcdfd2 100644 --- a/package.json +++ b/package.json @@ -75,60 +75,59 @@ "redux-saga": "^1.1.3", "redux-thunk": "^2.3.0", "reselect": "^4.0.0", + "url": "^0.11.0", "uuid": "^8.1.0" }, "devDependencies": { - "@babel/cli": "^7.10.3", - "@babel/core": "^7.10.3", - "@babel/plugin-proposal-class-properties": "^7.10.1", - "@babel/plugin-proposal-object-rest-spread": "^7.10.3", - "@babel/plugin-transform-regenerator": "^7.10.3", - "@babel/plugin-transform-runtime": "^7.10.3", - "@babel/preset-env": "^7.10.3", - "@babel/preset-react": "^7.10.1", - "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3", - "@typescript-eslint/eslint-plugin": "^4.21.0", - "@typescript-eslint/parser": "^4.21.0", - "babel-eslint": "^10.1.0", - "babel-jest": "^26.0.1", + "@babel/cli": "^7.17.6", + "@babel/core": "^7.17.7", + "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/plugin-proposal-object-rest-spread": "^7.17.3", + "@babel/plugin-transform-regenerator": "^7.16.7", + "@babel/plugin-transform-runtime": "^7.17.0", + "@babel/preset-env": "^7.16.11", + "@babel/preset-react": "^7.16.7", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4", + "@typescript-eslint/eslint-plugin": "^5.15.0", + "@typescript-eslint/parser": "^5.15.0", + "babel-jest": "^27.5.1", "babel-loader": "^8.0.6", "babel-plugin-lodash": "^3.3.4", "babel-plugin-macros": "^3.0.1", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", - "bundlewatch": "^0.3.2", + "bundlewatch": "^0.3.3", "chalk": "^4.1.0", "codecov": "^3.7.0", - "core-js": "^3.4.8", - "enzyme": "^3.10.0", + "core-js": "^3.21.1", + "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.0", - "eslint": "^7.23.0", - "eslint-config-airbnb": "^18.2.0", - "eslint-config-react-app": "^6.0.0", - "eslint-loader": "^4.0.2", - "eslint-plugin-flowtype": "^5.6.0", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-jest": "^24.0.0", + "eslint": "^8.11.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-react-app": "^7.0.0", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-jest": "^26.1.1", "eslint-plugin-jsx-a11y": "^6.4.1", - "eslint-plugin-react": "^7.23.2", + "eslint-plugin-react": "^7.29.4", "eslint-plugin-react-hooks": "^4.2.0", "glob": "^7.1.4", - "http-server": "^0.12.3", - "jest": "^26.0.1", + "http-server": "^14.1.0", + "jest": "^27.5.1", "jest-fetch-mock": "^3.0.0", - "jest-puppeteer": "^5.0.2", - "jsdom": "^16.5.3", - "puppeteer": "^9.0.0", + "jest-puppeteer": "^6.1.0", + "jsdom": "^19.0.0", + "puppeteer": "^13.5.1", "react": "^16.8.6", "react-dom": "^16.8.6", - "react-refresh": "^0.8.3", + "react-refresh": "^0.11.0", "redux-mock-store": "^1.5.1", "redux-saga-test-plan": "^4.0.0-rc.3", - "terser-webpack-plugin": "^4.0.0", + "terser-webpack-plugin": "^5.3.1", "unfetch": "^4.1.0", "url-polyfill": "^1.1.7", - "webpack": "^4.43.0", - "webpack-cli": "^4.6.0", - "webpack-dev-server": "^3.11.0" + "webpack": "^5.70.0", + "webpack-cli": "^4.9.2", + "webpack-dev-server": "^4.7.4" }, "peerDependencies": { "react": "^16.8.3", diff --git a/src/components/AccessTokenSender.js b/src/components/AccessTokenSender.js index 2760b012f1a06e992e9d802da45e47c6ce2c779e..90a731d6c068e94f1de6f68fc63b6895b52d353e 100644 --- a/src/components/AccessTokenSender.js +++ b/src/components/AccessTokenSender.js @@ -22,7 +22,7 @@ export class AccessTokenSender extends Component { /** */ render() { const { url } = this.props; - if (!url) return <></>; + if (!url) return null; /** login, clickthrough/kiosk open @id, wait for close diff --git a/src/components/AnnotationsOverlay.js b/src/components/AnnotationsOverlay.js index eb527f1d9f7a48679fcf88b544166120426bf968..454d407ec1562adb5d49c711cbcfbcb9e23d39f5 100644 --- a/src/components/AnnotationsOverlay.js +++ b/src/components/AnnotationsOverlay.js @@ -85,11 +85,10 @@ export class AnnotationsOverlay extends Component { this.initializeViewer(); - const annotationsUpdated = !AnnotationsOverlay.annotationsMatch( - annotations, prevProps.annotations, - ); + const annotationsUpdated = !AnnotationsOverlay.annotationsMatch(annotations, prevProps.annotations); const searchAnnotationsUpdated = !AnnotationsOverlay.annotationsMatch( - searchAnnotations, prevProps.searchAnnotations, + searchAnnotations, + prevProps.searchAnnotations, ); const hoveredAnnotationsUpdated = ( @@ -389,7 +388,7 @@ export class AnnotationsOverlay extends Component { render() { const { viewer } = this.props; - if (!viewer) return <></>; + if (!viewer) return null; return ReactDOM.createPortal( ( diff --git a/src/components/CanvasAnnotations.js b/src/components/CanvasAnnotations.js index 137a4ef0bcf3f1ed42a78e90ef5df08073752b2e..37263106c9ae5913124c29c44f1bf6b0318e98a0 100644 --- a/src/components/CanvasAnnotations.js +++ b/src/components/CanvasAnnotations.js @@ -62,7 +62,7 @@ export class CanvasAnnotations extends Component { listContainerComponent, htmlSanitizationRuleSet, hoveredAnnotationIds, containerRef, } = this.props; - if (annotations.length === 0) return <></>; + if (annotations.length === 0) return null; return ( <> diff --git a/src/components/ChangeThemeDialog.js b/src/components/ChangeThemeDialog.js index 37e04ca6629d504d44b870724cad9c1739b0c0ac..e5bb6102de498e007c2feb6130c8f645d3efbe8e 100644 --- a/src/components/ChangeThemeDialog.js +++ b/src/components/ChangeThemeDialog.js @@ -32,7 +32,6 @@ export class ChangeThemeDialog extends Component { */ constructor(props) { super(props); - this.selectedItemRef = React.createRef(); this.handleThemeChange = this.handleThemeChange.bind(this); } diff --git a/src/components/CollectionDialog.js b/src/components/CollectionDialog.js index 53e65d67117f2b899e2535677041313eb3fedd71..df4d9012427cfeb2757ff7b6bd375731d2b6986f 100644 --- a/src/components/CollectionDialog.js +++ b/src/components/CollectionDialog.js @@ -155,7 +155,7 @@ export class CollectionDialog extends Component { // to maybe pass a ref. if (!this.dialogContainer()) { this.forceUpdate(); - return <></>; + return null; } if (!ready) return this.placeholder(); diff --git a/src/components/CompanionWindowFactory.js b/src/components/CompanionWindowFactory.js index 909e73944ed823600330f2338fa7fc897f9a5a3b..7bc32d1e84afd2838eefe8575e48f90eae1cdb77 100644 --- a/src/components/CompanionWindowFactory.js +++ b/src/components/CompanionWindowFactory.js @@ -60,7 +60,7 @@ export class CompanionWindowFactory extends Component { const type = CompanionWindowRegistry[content]; - if (!type) return <></>; + if (!type) return null; return React.createElement(type, { id, windowId }); } diff --git a/src/components/LabelValueMetadata.js b/src/components/LabelValueMetadata.js index a003b9a4007fece59dd7c349cf6222c014485f80..91fd0b40d3305341d53c50f0bbc58ab67913db2d 100644 --- a/src/components/LabelValueMetadata.js +++ b/src/components/LabelValueMetadata.js @@ -17,7 +17,7 @@ export class LabelValueMetadata extends Component { const { defaultLabel, labelValuePairs } = this.props; if (labelValuePairs.length === 0) { - return (<></>); + return null; } /* eslint-disable react/no-array-index-key */ diff --git a/src/components/LocalePicker.js b/src/components/LocalePicker.js index e2177036c9f25700a5b8f7d7fc969b22814758aa..24cf3bd7b8d864036f46323de5d2ff34eda7ccc2 100644 --- a/src/components/LocalePicker.js +++ b/src/components/LocalePicker.js @@ -21,7 +21,7 @@ export class LocalePicker extends Component { setLocale, } = this.props; - if (!setLocale || availableLocales.length < 2) return <></>; + if (!setLocale || availableLocales.length < 2) return null; return ( <FormControl> <Select diff --git a/src/components/OpenSeadragonViewer.js b/src/components/OpenSeadragonViewer.js index d6353a624e52c31d715a76b1541b4f43e734c8ce..3f352eddd9139a964ae92f2b4d9f2e6b6d079696 100644 --- a/src/components/OpenSeadragonViewer.js +++ b/src/components/OpenSeadragonViewer.js @@ -167,10 +167,11 @@ export class OpenSeadragonViewer extends Component { /** */ addAllImageSources(zoomAfterAdd = true) { const { nonTiledImages, infoResponses } = this.props; - Promise.all( - infoResponses.map(infoResponse => this.addTileSource(infoResponse)), - nonTiledImages.map(image => this.addNonTiledImage(image)), - ).then(() => { + + return Promise.allSettled([ + ...infoResponses.map(infoResponse => this.addTileSource(infoResponse)), + ...nonTiledImages.map(image => this.addNonTiledImage(image)), + ]).then(() => { if (infoResponses[0] || nonTiledImages[0]) { if (zoomAfterAdd) this.zoomToWorld(); this.refreshTileProperties(); @@ -193,7 +194,7 @@ export class OpenSeadragonViewer extends Component { reject(); } - viewer.addSimpleImage({ + resolve(viewer.addSimpleImage({ error: event => reject(event), fitBounds: new OpenSeadragon.Rect( ...canvasWorld.contentResourceToWorldCoordinates(contentResource), @@ -202,7 +203,7 @@ export class OpenSeadragonViewer extends Component { opacity: canvasWorld.layerOpacityOfImageResource(contentResource), success: event => resolve(event), url: contentResource.id, - }); + })); }); } @@ -238,7 +239,11 @@ export class OpenSeadragonViewer extends Component { /** */ refreshTileProperties() { const { canvasWorld } = this.props; - const { viewer: { world } } = this.state; + const { viewer } = this.state; + + if (!viewer) return; + + const { world } = viewer; const items = []; for (let i = 0; i < world.getItemCount(); i += 1) { @@ -259,7 +264,7 @@ export class OpenSeadragonViewer extends Component { fitBounds(x, y, w, h, immediately = true) { const { viewer } = this.state; - viewer.viewport.fitBounds( + viewer && viewer.viewport && viewer.viewport.fitBounds( new OpenSeadragon.Rect(x, y, w, h), immediately, ); @@ -350,20 +355,18 @@ export class OpenSeadragonViewer extends Component { )); return ( - <> - <section - className={classNames(ns('osd-container'), classes.osdContainer)} - id={`${windowId}-osd`} - ref={this.ref} - aria-label={t('item', { label })} - aria-live="polite" - > - { drawAnnotations + <section + className={classNames(ns('osd-container'), classes.osdContainer)} + id={`${windowId}-osd`} + ref={this.ref} + aria-label={t('item', { label })} + aria-live="polite" + > + { drawAnnotations && <AnnotationsOverlay viewer={viewer} windowId={windowId} /> } - { enhancedChildren } - <PluginHook viewer={viewer} {...{ ...this.props, children: null }} /> - </section> - </> + { enhancedChildren } + <PluginHook viewer={viewer} {...{ ...this.props, children: null }} /> + </section> ); } } diff --git a/src/components/PrimaryWindow.js b/src/components/PrimaryWindow.js index 552681b8afaa228012d7332d67fb55a02422a2f0..05cecad9b6325d91cba30d8db6d7e54c5f22d0c0 100644 --- a/src/components/PrimaryWindow.js +++ b/src/components/PrimaryWindow.js @@ -33,11 +33,9 @@ export class PrimaryWindow extends Component { } = this.props; if (isCollection) { return ( - <> - <SelectCollection - windowId={windowId} - /> - </> + <SelectCollection + windowId={windowId} + /> ); } if (isFetching === false) { diff --git a/src/components/SearchPanelNavigation.js b/src/components/SearchPanelNavigation.js index 355a0787b4a29d3b171eec044dc8eab8eaf9d05e..21c76dda1e794a2db646fa8f58a6ef23a57b28ee 100644 --- a/src/components/SearchPanelNavigation.js +++ b/src/components/SearchPanelNavigation.js @@ -53,30 +53,29 @@ export class SearchPanelNavigation extends Component { if (searchHits.length < numTotal) { lengthText += '+'; } + + if (searchHits.length === 0) return null; + return ( - <> - {(searchHits.length > 0) && ( - <Typography variant="body2" align="center" classes={classes}> - <MiradorMenuButton - aria-label={t('searchPreviousResult')} - disabled={!this.hasPreviousResult(currentHitIndex)} - onClick={() => this.previousSearchResult(currentHitIndex)} - > - <ChevronLeftIcon style={iconStyle} /> - </MiradorMenuButton> - <span style={{ unicodeBidi: 'plaintext' }}> - {t('pagination', { current: currentHitIndex + 1, total: lengthText })} - </span> - <MiradorMenuButton - aria-label={t('searchNextResult')} - disabled={!this.hasNextResult(currentHitIndex)} - onClick={() => this.nextSearchResult(currentHitIndex)} - > - <ChevronRightIcon style={iconStyle} /> - </MiradorMenuButton> - </Typography> - )} - </> + <Typography variant="body2" align="center" classes={classes}> + <MiradorMenuButton + aria-label={t('searchPreviousResult')} + disabled={!this.hasPreviousResult(currentHitIndex)} + onClick={() => this.previousSearchResult(currentHitIndex)} + > + <ChevronLeftIcon style={iconStyle} /> + </MiradorMenuButton> + <span style={{ unicodeBidi: 'plaintext' }}> + {t('pagination', { current: currentHitIndex + 1, total: lengthText })} + </span> + <MiradorMenuButton + aria-label={t('searchNextResult')} + disabled={!this.hasNextResult(currentHitIndex)} + onClick={() => this.nextSearchResult(currentHitIndex)} + > + <ChevronRightIcon style={iconStyle} /> + </MiradorMenuButton> + </Typography> ); } } diff --git a/src/components/SidebarIndexItem.js b/src/components/SidebarIndexItem.js index 909b218a95b8fe3d3604ddb4d7105b89f4fbfe2b..b880e180a3b41a7f7ae9013c2f8731ce2a2264af 100644 --- a/src/components/SidebarIndexItem.js +++ b/src/components/SidebarIndexItem.js @@ -12,14 +12,12 @@ export class SidebarIndexItem extends Component { } = this.props; return ( - <> - <Typography - className={classNames(classes.label)} - variant="body1" - > - {label} - </Typography> - </> + <Typography + className={classNames(classes.label)} + variant="body1" + > + {label} + </Typography> ); } } diff --git a/src/components/SidebarIndexTableOfContents.js b/src/components/SidebarIndexTableOfContents.js index bea3b9c9d7841984402499ace1c848e58ae0cdf0..badc3e77fb57c5cdb6da04aed9726e9baf182ede 100644 --- a/src/components/SidebarIndexTableOfContents.js +++ b/src/components/SidebarIndexTableOfContents.js @@ -113,21 +113,19 @@ export class SidebarIndexTableOfContents extends Component { } = this.props; if (!treeStructure) { - return <></>; + return null; } return ( - <> - <TreeView - className={classes.root} - defaultCollapseIcon={<ExpandMoreIcon color="action" />} - defaultExpandIcon={<ChevronRightIcon color="action" />} - defaultEndIcon={<></>} - expanded={expandedNodeIds} - > - {this.buildTreeItems(treeStructure.nodes, visibleNodeIds, containerRef, nodeIdToScrollTo)} - </TreeView> - </> + <TreeView + className={classes.root} + defaultCollapseIcon={<ExpandMoreIcon color="action" />} + defaultExpandIcon={<ChevronRightIcon color="action" />} + defaultEndIcon={null} + expanded={expandedNodeIds} + > + {this.buildTreeItems(treeStructure.nodes, visibleNodeIds, containerRef, nodeIdToScrollTo)} + </TreeView> ); } } diff --git a/src/components/ThumbnailNavigation.js b/src/components/ThumbnailNavigation.js index 7b049704005b73cd073ea4ba67f153af4ff8f0e9..e7c6db2945535d7a293694054acb970da4307316 100644 --- a/src/components/ThumbnailNavigation.js +++ b/src/components/ThumbnailNavigation.js @@ -183,7 +183,7 @@ export class ThumbnailNavigation extends Component { windowId, } = this.props; if (position === 'off') { - return <></>; + return null; } const htmlDir = viewingDirection === 'right-to-left' ? 'rtl' : 'ltr'; const itemData = { diff --git a/src/components/Window.js b/src/components/Window.js index 56d3bd45e6c893b89638f30836c3aaccf1c771ed..30462d2e7ed84326a6f4687e16cb9043c0e9abf2 100644 --- a/src/components/Window.js +++ b/src/components/Window.js @@ -83,8 +83,11 @@ export class Window extends Component { elevation={1} id={windowId} className={ - cn(classes.window, ns('window'), - maximized ? classes.maximized : null) + cn( + classes.window, + ns('window'), + maximized ? classes.maximized : null, + ) } aria-label={t('window', { label })} > diff --git a/src/components/WindowSideBar.js b/src/components/WindowSideBar.js index 5381a60a305cb2c6cede7eb707c924bb55035477..eaeceaae43ea026917d68940037e207b4070d639 100644 --- a/src/components/WindowSideBar.js +++ b/src/components/WindowSideBar.js @@ -18,23 +18,21 @@ export class WindowSideBar extends Component { } = this.props; return ( - <> - <Drawer - variant="persistent" - className={classNames(classes.drawer)} - classes={{ paper: classNames(classes.paper) }} - anchor={direction === 'rtl' ? 'right' : 'left'} - PaperProps={{ - 'aria-label': t('sidebarPanelsNavigation'), - component: 'nav', - style: { height: '100%', position: 'relative' }, - }} - SlideProps={{ direction: direction === 'rtl' ? 'left' : 'right', mountOnEnter: true, unmountOnExit: true }} - open={sideBarOpen} - > - <WindowSideBarButtons windowId={windowId} /> - </Drawer> - </> + <Drawer + variant="persistent" + className={classNames(classes.drawer)} + classes={{ paper: classNames(classes.paper) }} + anchor={direction === 'rtl' ? 'right' : 'left'} + PaperProps={{ + 'aria-label': t('sidebarPanelsNavigation'), + component: 'nav', + style: { height: '100%', position: 'relative' }, + }} + SlideProps={{ direction: direction === 'rtl' ? 'left' : 'right', mountOnEnter: true, unmountOnExit: true }} + open={sideBarOpen} + > + <WindowSideBarButtons windowId={windowId} /> + </Drawer> ); } } diff --git a/src/components/WindowSideBarButtons.js b/src/components/WindowSideBarButtons.js index b5645522c2dd8ea863b44ebb1d907c29979bdf57..fd4c717f3f8c1762c2312d14137ef48969e6d07f 100644 --- a/src/components/WindowSideBarButtons.js +++ b/src/components/WindowSideBarButtons.js @@ -10,6 +10,28 @@ import AttributionIcon from '@material-ui/icons/CopyrightSharp'; import LayersIcon from '@material-ui/icons/LayersSharp'; import SearchIcon from '@material-ui/icons/SearchSharp'; import CanvasIndexIcon from './icons/CanvasIndexIcon'; + +/** */ +function TabButton({ t, value, ...tabProps }) { + return ( + <Tooltip title={t('openCompanionWindow', { context: value })}> + <Tab + {...tabProps} + value={value} + aria-label={ + t('openCompanionWindow', { context: value }) + } + disableRipple + /> + </Tooltip> + ); +} + +TabButton.propTypes = { + t: PropTypes.func.isRequired, + value: PropTypes.string.isRequired, +}; + /** * */ @@ -50,21 +72,6 @@ export class WindowSideBarButtons extends Component { t, } = this.props; - /** */ - const TabButton = props => ( - <Tooltip title={t('openCompanionWindow', { context: props.value })}> - <Tab - {...props} - classes={{ root: classes.tab, selected: classes.tabSelected }} - aria-label={ - t('openCompanionWindow', { context: props.value }) - } - disableRipple - onKeyUp={this.handleKeyUp} - /> - </Tooltip> - ); - return ( <Tabs classes={{ flexContainer: classes.tabsFlexContainer, indicator: classes.tabsIndicator }} @@ -80,24 +87,36 @@ export class WindowSideBarButtons extends Component { { panels.info && ( <TabButton value="info" + onKeyUp={this.handleKeyUp} + classes={{ root: classes.tab, selected: classes.tabSelected }} + t={t} icon={(<InfoIcon />)} /> )} { panels.attribution && ( <TabButton value="attribution" + onKeyUp={this.handleKeyUp} + classes={{ root: classes.tab, selected: classes.tabSelected }} + t={t} icon={(<AttributionIcon />)} /> )} { panels.canvas && ( <TabButton value="canvas" + onKeyUp={this.handleKeyUp} + classes={{ root: classes.tab, selected: classes.tabSelected }} + t={t} icon={(<CanvasIndexIcon />)} /> )} {panels.annotations && (hasAnnotations || hasAnyAnnotations) && ( <TabButton value="annotations" + onKeyUp={this.handleKeyUp} + classes={{ root: classes.tab, selected: classes.tabSelected }} + t={t} icon={( <Badge classes={{ badge: classes.badge }} invisible={!hasAnnotations} variant="dot"> <AnnotationIcon /> @@ -108,6 +127,9 @@ export class WindowSideBarButtons extends Component { {panels.search && hasSearchService && ( <TabButton value="search" + onKeyUp={this.handleKeyUp} + classes={{ root: classes.tab, selected: classes.tabSelected }} + t={t} icon={( <Badge classes={{ badge: classes.badge }} invisible={!hasSearchResults} variant="dot"> <SearchIcon /> @@ -118,6 +140,9 @@ export class WindowSideBarButtons extends Component { { panels.layers && hasAnyLayers && ( <TabButton value="layers" + onKeyUp={this.handleKeyUp} + classes={{ root: classes.tab, selected: classes.tabSelected }} + t={t} icon={( <Badge classes={{ badge: classes.badge }} invisible={!hasCurrentLayers} variant="dot"> <LayersIcon /> @@ -128,6 +153,9 @@ export class WindowSideBarButtons extends Component { { PluginComponents && PluginComponents.map(PluginComponent => ( <TabButton + onKeyUp={this.handleKeyUp} + classes={{ root: classes.tab, selected: classes.tabSelected }} + t={t} key={PluginComponent.value} value={PluginComponent.value} icon={<PluginComponent />} diff --git a/src/components/WindowSideBarCollectionPanel.js b/src/components/WindowSideBarCollectionPanel.js index f480ec42ca2bda9e07da7cf56283f0745e57a9e4..cf58cccfff8665ac17d55ffeb400e1e84e9d2c45 100644 --- a/src/components/WindowSideBarCollectionPanel.js +++ b/src/components/WindowSideBarCollectionPanel.js @@ -12,6 +12,40 @@ import ArrowUpwardIcon from '@material-ui/icons/ArrowUpwardSharp'; import CompanionWindow from '../containers/CompanionWindow'; import IIIFThumbnail from '../containers/IIIFThumbnail'; +/** */ +function Item({ + manifest, canvasNavigation, variant, ...otherProps +}) { + return ( + <MenuItem + alignItems="flex-start" + button + component="li" + {...otherProps} + > + { variant === 'thumbnail' && ( + <ListItemIcon> + <IIIFThumbnail + resource={manifest} + maxHeight={canvasNavigation.height} + maxWidth={canvasNavigation.width} + /> + </ListItemIcon> + )} + <ListItemText>{WindowSideBarCollectionPanel.getUseableLabel(manifest)}</ListItemText> + </MenuItem> + ); +} + +Item.propTypes = { + canvasNavigation: PropTypes.shape({ + height: PropTypes.number, + width: PropTypes.number, + }).isRequired, + manifest: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types + variant: PropTypes.string.isRequired, +}; + /** */ export class WindowSideBarCollectionPanel extends Component { /** */ @@ -54,29 +88,6 @@ export class WindowSideBarCollectionPanel extends Component { windowId, } = this.props; - /** */ - const Item = ({ manifest, ...otherProps }) => ( - <MenuItem - className={classes.menuItem} - alignItems="flex-start" - button - component="li" - selected={manifestId === manifest.id} - {...otherProps} - > - { variant === 'thumbnail' && ( - <ListItemIcon> - <IIIFThumbnail - resource={manifest} - maxHeight={canvasNavigation.height} - maxWidth={canvasNavigation.width} - /> - </ListItemIcon> - )} - <ListItemText>{WindowSideBarCollectionPanel.getUseableLabel(manifest)}</ListItemText> - </MenuItem> - ); - return ( <CompanionWindow title={t(this.isMultipart() ? 'multipartCollection' : 'collection')} @@ -127,7 +138,15 @@ export class WindowSideBarCollectionPanel extends Component { }; return ( - <Item key={manifest.id} onClick={onClick} manifest={manifest} /> + <Item + key={manifest.id} + onClick={onClick} + canvasNavigation={canvasNavigation} + manifest={manifest} + variant={variant} + className={classes.menuItem} + selected={manifestId === manifest.id} + /> ); }) } @@ -142,7 +161,15 @@ export class WindowSideBarCollectionPanel extends Component { }; return ( - <Item key={manifest.id} onClick={onClick} manifest={manifest} /> + <Item + key={manifest.id} + onClick={onClick} + canvasNavigation={canvasNavigation} + manifest={manifest} + variant={variant} + className={classes.menuItem} + selected={manifestId === manifest.id} + /> ); }) } @@ -159,14 +186,11 @@ WindowSideBarCollectionPanel.propTypes = { }).isRequired, classes: PropTypes.objectOf(PropTypes.string).isRequired, collection: PropTypes.object, // eslint-disable-line react/forbid-prop-types - collectionId: PropTypes.string.isRequired, collectionPath: PropTypes.arrayOf(PropTypes.string), - error: PropTypes.string, id: PropTypes.string.isRequired, isFetching: PropTypes.bool, manifestId: PropTypes.string.isRequired, parentCollection: PropTypes.object, // eslint-disable-line react/forbid-prop-types - ready: PropTypes.bool, t: PropTypes.func, updateCompanionWindow: PropTypes.func.isRequired, updateWindow: PropTypes.func.isRequired, @@ -177,10 +201,8 @@ WindowSideBarCollectionPanel.propTypes = { WindowSideBarCollectionPanel.defaultProps = { collection: null, collectionPath: [], - error: null, isFetching: false, parentCollection: null, - ready: false, t: k => k, variant: null, }; diff --git a/src/components/WindowTopBarPluginArea.js b/src/components/WindowTopBarPluginArea.js index ce5ec626f4317974bf6fd0dbf143fe6c41c219f0..63e32ac03e89f14dcb7895c4e7167c3ba5d4d8ad 100644 --- a/src/components/WindowTopBarPluginArea.js +++ b/src/components/WindowTopBarPluginArea.js @@ -8,9 +8,7 @@ export class WindowTopBarPluginArea extends Component { /** */ render() { return ( - <> - <PluginHook {...this.props} /> - </> + <PluginHook {...this.props} /> ); } } diff --git a/src/components/WindowTopBarPluginMenu.js b/src/components/WindowTopBarPluginMenu.js index b3e6e4b3c00aa04a6caf668076311e70619e6181..f19331c035f682de00e35ff64a8b22c83bd959f8 100644 --- a/src/components/WindowTopBarPluginMenu.js +++ b/src/components/WindowTopBarPluginMenu.js @@ -49,7 +49,7 @@ export class WindowTopBarPluginMenu extends Component { } = this.props; const { anchorEl } = this.state; - if (!PluginComponents || PluginComponents.length === 0) return (<></>); + if (!PluginComponents || PluginComponents.length === 0) return null; return ( <> diff --git a/src/components/WindowTopBarTitle.js b/src/components/WindowTopBarTitle.js index ed36e8aff8847d47fe33e70b65f489d6a7bb01e2..bc2c466825e0ac0fd1d2d46258f6285fe20eb815 100644 --- a/src/components/WindowTopBarTitle.js +++ b/src/components/WindowTopBarTitle.js @@ -4,6 +4,19 @@ import Typography from '@material-ui/core/Typography'; import Skeleton from '@material-ui/lab/Skeleton'; import ErrorIcon from '@material-ui/icons/ErrorOutlineSharp'; +/** */ +function TitleTypography({ children, ...props }) { + return ( + <Typography variant="h2" noWrap color="inherit" {...props}> + {children} + </Typography> + ); +} + +TitleTypography.propTypes = { + children: PropTypes.node.isRequired, +}; + /** * WindowTopBarTitle */ @@ -17,17 +30,10 @@ export class WindowTopBarTitle extends Component { classes, error, hideWindowTitle, isFetching, manifestTitle, } = this.props; - /** */ - const TitleTypography = props => ( - <Typography variant="h2" noWrap color="inherit" className={classes.title} {...props}> - {props.children} - </Typography> - ); - let title = null; if (isFetching) { title = ( - <TitleTypography> + <TitleTypography className={classes.title}> <Skeleton variant="text" /> </TitleTypography> ); @@ -35,7 +41,7 @@ export class WindowTopBarTitle extends Component { title = ( <> <ErrorIcon color="error" /> - <TitleTypography color="textSecondary"> + <TitleTypography color="textSecondary" className={classes.title}> {error} </TitleTypography> </> @@ -44,7 +50,7 @@ export class WindowTopBarTitle extends Component { title = (<div className={classes.title} />); } else { title = ( - <TitleTypography> + <TitleTypography className={classes.title}> {manifestTitle} </TitleTypography> ); diff --git a/src/components/WindowViewer.js b/src/components/WindowViewer.js index 667b99bff1fd32d65a8081972e3ab1a21283ece0..91257b42a43f02891218dcce4cb7b68c89c001ff 100644 --- a/src/components/WindowViewer.js +++ b/src/components/WindowViewer.js @@ -29,9 +29,7 @@ export class WindowViewer extends Component { const { hasError } = this.state; - if (hasError) { - return <></>; - } + if (hasError) return null; return ( <Suspense fallback={<div />}> diff --git a/src/containers/CanvasAnnotations.js b/src/containers/CanvasAnnotations.js index 7c9caa748aa43713f8f59b1928fbf8c94a959957..5bebeae7885399b8685ac82bab9462b38020add4 100644 --- a/src/containers/CanvasAnnotations.js +++ b/src/containers/CanvasAnnotations.js @@ -28,9 +28,7 @@ function getIdAndContentOfResources(resources) { /** For connect */ const mapStateToProps = (state, { canvasId, windowId }) => ({ annotations: getIdAndContentOfResources( - getAnnotationResourcesByMotivationForCanvas( - state, { canvasId, windowId }, - ), + getAnnotationResourcesByMotivationForCanvas(state, { canvasId, windowId }), ), htmlSanitizationRuleSet: getConfig(state).annotations.htmlSanitizationRuleSet, label: getCanvasLabel(state, { diff --git a/src/containers/SearchHit.js b/src/containers/SearchHit.js index 9c535f5d59163da10d05a58d5ddeceeb2ed47ca0..60abacf26dfe4560dcbdd509abc4d37ea33a4a1c 100644 --- a/src/containers/SearchHit.js +++ b/src/containers/SearchHit.js @@ -24,11 +24,14 @@ const mapStateToProps = (state, { }) => { const realAnnoId = annotationId || hit.annotations[0]; const hitAnnotation = getResourceAnnotationForSearchHit( - state, { annotationUri: realAnnoId, companionWindowId, windowId }, - ); - const annotationLabel = getResourceAnnotationLabel( - state, { annotationUri: realAnnoId, companionWindowId, windowId }, + state, + { + annotationUri: realAnnoId, + companionWindowId, + windowId, + }, ); + const annotationLabel = getResourceAnnotationLabel(state, { annotationUri: realAnnoId, companionWindowId, windowId }); const selectedCanvasIds = getVisibleCanvasIds(state, { windowId }); const selectedContentSearchAnnotationsIds = getSelectedContentSearchAnnotationIds(state, { diff --git a/src/containers/WindowAuthenticationBar.js b/src/containers/WindowAuthenticationBar.js index 3b42a93aa7f44067e0cdc09958a20a90c4059b66..f3b7f8733367a8b7f2adf4c1e35f74d49b3353f1 100644 --- a/src/containers/WindowAuthenticationBar.js +++ b/src/containers/WindowAuthenticationBar.js @@ -13,9 +13,7 @@ import { WindowAuthenticationBar } from '../components/WindowAuthenticationBar'; const styles = theme => ({ buttonInvert: { '&:hover': { - backgroundColor: alpha( - theme.palette.secondary.contrastText, 1 - theme.palette.action.hoverOpacity, - ), + backgroundColor: alpha(theme.palette.secondary.contrastText, 1 - theme.palette.action.hoverOpacity), }, backgroundColor: theme.palette.secondary.contrastText, marginLeft: theme.spacing(5), diff --git a/src/lib/CanvasWorld.js b/src/lib/CanvasWorld.js index acdd5c24539355d5df2166c0699c36d5d57302c1..7df30b0208bfd547f0a74a86cd79955246c2080e 100644 --- a/src/lib/CanvasWorld.js +++ b/src/lib/CanvasWorld.js @@ -136,7 +136,7 @@ export default class CanvasWorld { /** Get the IIIF content resource for an image */ contentResource(infoResponseId) { const miradorCanvas = this.canvases.find(c => c.imageServiceIds.some(id => ( - normalizeUrl(id, { stripAuthentication: false }) + id && infoResponseId && normalizeUrl(id, { stripAuthentication: false }) === normalizeUrl(infoResponseId, { stripAuthentication: false })))); if (!miradorCanvas) return undefined; return miradorCanvas.imageResources diff --git a/src/lib/ThumbnailFactory.js b/src/lib/ThumbnailFactory.js index 79c063f8852c82b9044640ed28e33d25b8e6de3d..6fb2ce0e9eda7be9d328d9fae3da109e429c276d 100644 --- a/src/lib/ThumbnailFactory.js +++ b/src/lib/ThumbnailFactory.js @@ -90,27 +90,23 @@ class ThumbnailFactory { const imageFitness = (test) => test.width * test.height - targetArea; /** Look for the size that's just bigger than we prefer... */ - closestSize = sizes.reduce( - (best, test) => { - const score = imageFitness(test); + closestSize = sizes.reduce((best, test) => { + const score = imageFitness(test); - if (score < 0) return best; + if (score < 0) return best; - return Math.abs(score) < Math.abs(imageFitness(best)) - ? test - : best; - }, closestSize, - ); + return Math.abs(score) < Math.abs(imageFitness(best)) + ? test + : best; + }, closestSize); /** .... but not "too" big; we'd rather scale up an image than download too much */ if (closestSize.width * closestSize.height > targetArea * 6) { - closestSize = sizes.reduce( - (best, test) => ( - Math.abs(imageFitness(test)) < Math.abs(imageFitness(best)) - ? test - : best - ), closestSize, - ); + closestSize = sizes.reduce((best, test) => ( + Math.abs(imageFitness(test)) < Math.abs(imageFitness(best)) + ? test + : best + ), closestSize); } if (closestSize.default) return undefined; @@ -313,4 +309,5 @@ function getBestThumbnail(resource, iiifOpts) { return new ThumbnailFactory(resource, iiifOpts).get(); } -export { getBestThumbnail as default, ThumbnailFactory }; +export { ThumbnailFactory }; +export default getBestThumbnail; diff --git a/src/lib/TruncatedHit.js b/src/lib/TruncatedHit.js index f6363cf3abe12678b45649e6c33db396fe1aa568..8317b098434cdb82146e2626a5bef2746bf458de 100644 --- a/src/lib/TruncatedHit.js +++ b/src/lib/TruncatedHit.js @@ -25,16 +25,12 @@ export default class TruncatedHit { /** */ get before() { if (!this.hit.before) return ''; - return this.hit.before.substring( - this.hit.before.length - this.charsOnSide, this.hit.before.length, - ); + return this.hit.before.substring(this.hit.before.length - this.charsOnSide, this.hit.before.length); } /** */ get after() { if (!this.hit.after) return ''; - return this.hit.after.substring( - 0, Math.min(this.hit.after.length, this.charsOnSide), - ); + return this.hit.after.substring(0, Math.min(this.hit.after.length, this.charsOnSide)); } } diff --git a/src/state/createStore.js b/src/state/createStore.js index 717b0b8915cc9dd00df4b0652a56a4d291b80c65..9467f322c5535aab6d73abcd48d0aaed5b3dc9a1 100644 --- a/src/state/createStore.js +++ b/src/state/createStore.js @@ -27,9 +27,7 @@ function configureStore(pluginReducers, pluginSagas = []) { const store = createStore( rootReducer, composeWithDevTools( - applyMiddleware( - thunkMiddleware, sagaMiddleware, - ), + applyMiddleware(thunkMiddleware, sagaMiddleware), ), ); diff --git a/src/state/sagas/iiif.js b/src/state/sagas/iiif.js index 41823156bc7b8ac6b423ec9dafe4e8efdb019e53..e661170c5eca3d5fb1a2106fb4c43125afaaec31 100644 --- a/src/state/sagas/iiif.js +++ b/src/state/sagas/iiif.js @@ -64,7 +64,10 @@ function* fetchIiifResourceWithAuth(url, iiifResource, options, { degraded, fail } const { error, json, response } = yield call( - fetchIiifResource, url, urlOptions, { failure: arg => arg, success: arg => arg }, + fetchIiifResource, + url, + urlOptions, + { failure: arg => arg, success: arg => arg }, ); // Hard error either requesting the resource or deserializing the JSON. diff --git a/src/state/sagas/windows.js b/src/state/sagas/windows.js index bc63cdcbb9a072fbdb5f4dd4dd23890d92974823..bfcd275aaadc06cc862791933b86f5f078ed4e90 100644 --- a/src/state/sagas/windows.js +++ b/src/state/sagas/windows.js @@ -94,9 +94,7 @@ export function* setWindowStartingCanvas(action) { const windowId = action.id || action.window.id; if (canvasId) { - const thunk = yield call( - setCanvas, windowId, canvasId, null, { preserveViewport: !!action.payload }, - ); + const thunk = yield call(setCanvas, windowId, canvasId, null, { preserveViewport: !!action.payload }); yield put(thunk); } else { const manifestoInstance = yield select(getManifestoInstance, { manifestId }); @@ -157,7 +155,12 @@ export function* setCurrentAnnotationsOnCurrentCanvas({ if (companionWindowIds.length === 0) return; const annotationBySearch = yield select( - getAnnotationsBySearch, { canvasIds: visibleCanvases, companionWindowIds, windowId }, + getAnnotationsBySearch, + { + canvasIds: visibleCanvases, + companionWindowIds, + windowId, + }, ); yield all( @@ -213,9 +216,7 @@ export function* setCanvasOfFirstSearchResult({ companionWindowId, windowId }) { if (selectedIds.length !== 0) return; - const annotations = yield select( - getSortedSearchAnnotationsForCompanionWindow, { companionWindowId, windowId }, - ); + const annotations = yield select(getSortedSearchAnnotationsForCompanionWindow, { companionWindowId, windowId }); if (!annotations || annotations.length === 0) return; yield put(selectAnnotation(windowId, annotations[0].id)); diff --git a/src/state/selectors/searches.js b/src/state/selectors/searches.js index 3e541084e325e3ca41cd962cf119dc91cf15af3a..ae63e2a090618dfbcfaf2a988405f403875e7cea 100644 --- a/src/state/selectors/searches.js +++ b/src/state/selectors/searches.js @@ -108,8 +108,8 @@ export const getSortedSearchHitsForCompanionWindow = createSelector( [ getSearchHitsForCompanionWindow, getCanvases, - (state, { companionWindowId, windowId }) => annotationUri => getResourceAnnotationForSearchHit( - state, { annotationUri, companionWindowId, windowId }, + (state, { companionWindowId, windowId }) => ( + annotationUri => getResourceAnnotationForSearchHit(state, { annotationUri, companionWindowId, windowId }) ), ], (searchHits, canvases, annotationForSearchHit) => { @@ -229,9 +229,7 @@ const getAnnotationById = createSelector( export const getCanvasForAnnotation = createSelector( [ getAnnotationById, - (state, { windowId }) => canvasId => getCanvas( - state, { canvasId, windowId }, - ), + (state, { windowId }) => canvasId => getCanvas(state, { canvasId, windowId }), ], (annotation, getCanvasById) => { const canvasId = annotation && annotation.targetId; diff --git a/webpack.config.js b/webpack.config.js index 3f78b6cad3e2a3339c9801046e1b21ef5bb89b0a..8c4e2eaf0637d3348b43251a47cd32f59d0fe097 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -27,12 +27,12 @@ const baseConfig = mode => ({ minimizer: [ new TerserPlugin({ extractComments: true, - sourceMap: true, }), ], }, output: { filename: 'mirador.min.js', + hashFunction: 'md5', library: 'Mirador', libraryExport: 'default', libraryTarget: 'umd', @@ -74,12 +74,12 @@ module.exports = (env, options) => { return { ...config, devServer: { - contentBase: [ + hot: true, + port: 4444, + static: [ './__tests__/integration/mirador', './__tests__/fixtures', ], - hot: true, - port: 4444, }, devtool: 'eval-source-map', mode: 'development',