diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3c39eb3baaaa4ac33ad6e203edd5ff6488a2fd5b..85112414c6251a111f381f315398b3d12e6aa21e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,26 +1,47 @@
-This document serves as a reference for the Mirador community and others interested in the project. It aims to outline agreed upon practices that are used in the development and maintenance of the Mirador software.
+This document serves as a reference for the Mirador community and others interested in the project. It aims to outline agreed-upon practices that are used in the development and maintenance of the Mirador software.
+
 
 ## Contributing to Mirador
-Contributions to Mirador are always welcome, however, it will always be helpful to begin any large change by submitting an issue or engaging with the Mirador community. For more on this, see [plugin architecture](#plugin-architecture). Mirador 3.0 and beyond adheres to [semantic versioning](https://semver.org/) so that adopters and contributors can better understand what changes can be expected in released versions of the software.
 
-All contributions should be submitted as a [GitHub pull request](https://help.github.com/articles/about-pull-requests/) to the `master` branch. Pull requests must be reviewed and accepted by another Mirador maintainer and pass all continuous integration checks. Contributions should have tests for the feature or bug fix, documentation and should conform to the Mirador agreed upon coding style. Contributions should not include a “built” version of Mirador, this will help in reducing merge conflicts.
+Contributions to Mirador are always welcome!
+
+It is always helpful to begin any large change by submitting an issue or engaging with the Mirador community. Mirador 3.0 and beyond adheres to [semantic versioning](https://semver.org/) so that adopters and contributors can better understand what changes can be expected in released versions of the software.
+
+All contributions should be submitted as a [GitHub pull request](https://help.github.com/articles/about-pull-requests/) to the master branch. Pull requests must be reviewed and accepted by another Mirador maintainer and pass all continuous integration checks. Contributions should have tests for the feature or bug fix, documentation, should maintain high code coverage, and should conform to the Mirador agreed-upon coding style. Contributions should not include a “built” version of Mirador—this will help to reduce merge conflicts.
+
+### Code of Conduct
+Everyone interacting in this community is expected to follow the [Mirador Code of Conduct](https://github.com/ProjectMirador/mirador/blob/master/CODE_OF_CONDUCT.md).
+
+### Accessibility
+Mirador 3 aims to comply with the [Web Content Accessibility Guidelines (WCAG) 2.1 AA](https://www.w3.org/WAI/standards-guidelines/wcag/). These guidelines, authored by the W3C and legally adopted [internationally](https://www.w3.org/WAI/policies/?q=wcag-20), are comprehensive and provide the success criteria designers and developers need to build accessible web applications.
+
+Mirador’s documentation wiki offers [additional information, tools, and resources](https://github.com/ProjectMirador/mirador/wiki/M3-Accessibility-Guidelines-for-Contributors) for testing your contributions against accessibility criteria before submitting pull requests.
 
 ### Adding dependencies
-Careful consideration should be given when adding software dependencies to Mirador. During the code review process, new dependencies may be evaluated on the following considerations: 
-- added size of the dependency
-- whether or not that dependency is maintained and tested
-- how the dependency may interact with Mirador embedded in other environments
+Careful consideration should be given when adding software dependencies to Mirador. During the code review process, new dependencies may be evaluated on the following considerations:
 
-As a general rule, any dependencies added should not be committed directly, but should use a package manager to require the dependency.
+-   added size of the dependency
+-   whether or not that dependency is maintained and tested
+-   how the dependency may interact with Mirador embedded in other environments
+    
+As a general rule, dependencies added should not be committed directly, but should instead use a package manager to require the dependency.
 
 Dependencies should also be scoped (both for style and script) so that they do not interact with a containing application.
 
 ### Coding style
-Mirador has adopted the [AirBnb JavaScript Style Guide](https://github.com/airbnb/javascript) as its agreed upon coding style for all contributions. This helps us ensure a consistent codebase with higher readability. 
+Mirador has adopted the [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) as its agreed-upon coding style for all contributions. This helps us ensure a consistent codebase with higher readability.
+
 ### Testing
-Mirador should be well tested at several different levels. A component or class should be tested individually (unit tests) and the interaction of that component or class with other parts should also be tested (integration tests).
+Mirador should be well-tested at several different levels. A component or class should be tested individually (unit tests) and the interaction of that component or class with other parts should also be tested (integration tests).
+
 ### Documentation
 Added or modified code should be appropriately documented using JSDoc comments and established project conventions.
 
+### Plugin architecture
+Mirador 3 shifted to a plugin architecture to better support the wide array of uses of Mirador. This allows for the design to accommodate plugins in a more focused way. It also allows the community to maintain the core components while giving developers the freedom to develop new and innovative plugins. Your contribution might fit nicely as a Mirador plugin. For more on plugins, please see [Mirador 3 Plugins](https://github.com/ProjectMirador/mirador/wiki/Mirador-3-plugins) and [Creating a Mirador 3 Plugin](https://github.com/ProjectMirador/mirador/wiki/M3---Creating-a-Mirador-plugin) in the wiki.
+
+### Support
+If you have questions, please check the [wiki](https://github.com/ProjectMirador/mirador/wiki), add a [discussion](https://github.com/ProjectMirador/mirador/discussions) question, post on the #mirador channel of the [IIIF Slack workspace](http://iiif.slack.com/), or join a Mirador community call (see #mirador for call details). Slack and community calls are also both great places to meet users and maintainers.
+
 ### Plugin architecture
 Mirador 3 shifted to a plugin architecture to better support the wide array of uses of Mirador. This allows for the design to accommodate plugins in more focused way. It also allows the community to maintain the core components while giving developers the freedom to develop new and innovative plugins. Your contribution might fit nicely as a Mirador plugin.
diff --git a/__tests__/src/components/OpenSeadragonViewer.test.js b/__tests__/src/components/OpenSeadragonViewer.test.js
index 480f41c16d76d9259a62861be1841f0bae8e0c57..464c1d4aa9be4515da72727af4887b027cddcbac 100644
--- a/__tests__/src/components/OpenSeadragonViewer.test.js
+++ b/__tests__/src/components/OpenSeadragonViewer.test.js
@@ -2,11 +2,11 @@ import { cloneElement } from 'react';
 import { render, screen, waitFor } from 'test-utils';
 import PropTypes from 'prop-types';
 import userEvent from '@testing-library/user-event';
-import OpenSeadragon from 'openseadragon';
 import { Utils } from 'manifesto.js';
 import { OpenSeadragonViewer } from '../../../src/components/OpenSeadragonViewer';
 import CanvasWorld from '../../../src/lib/CanvasWorld';
 import fixture from '../../fixtures/version-2/019.json';
+import { OSDReferences } from '../../../src/plugins/OSDReferences';
 
 const canvases = Utils.parseManifest(fixture).getSequences()[0].getCanvases();
 
@@ -56,7 +56,7 @@ function createWrapper(props) {
 
   const rendered = render(component);
 
-  const viewer = OpenSeadragon.getViewer(screen.getByLabelText('item'));
+  const viewer = OSDReferences.get('base').current;
 
   return { ...rendered, component, viewer };
 }
diff --git a/src/locales/de/translation.json b/src/locales/de/translation.json
index 7bbadf2c18288a5ba372b49e659595b3f90616a5..ad23350c7cc9eb51b61e4312375d41d101852129 100644
--- a/src/locales/de/translation.json
+++ b/src/locales/de/translation.json
@@ -21,12 +21,14 @@
     "canvasIndex": "Index",
     "changeTheme": "Farbschema ändern",
     "clearSearch": "zurücksetzen",
+    "close": "Schließen",
     "closeAddResourceForm": "Formular schließen",
     "closeAddResourceMenu": "Ressourcenliste schließen",
     "closeCompanionWindow": "Hilfsfenster schließen",
     "closeWindow": "Fenster schließen",
     "collapseSection": "Bereich \"{{section}}\" zuklappen",
     "collapseSidePanel": "Seitenleiste zuklappen",
+    "collection": "Sammlung",
     "continue": "Fortfahren",
     "copy": "Kopieren",
     "currentItem": "Aktuelles Objekt",
@@ -90,8 +92,11 @@
     "mosaicDescription": "Fenster können innerhalb der Arbeitsfläche in Relation zueinander verschoben und ihrer Größe verändert werden.",
     "moveCompanionWindowToBottom": "Unten anordnen",
     "moveCompanionWindowToRight": "Rechts anordnen",
+    "multipartCollection": "Mehrteilige Sammlung",
     "nextCanvas": "Nächstes Objekt",
+    "noItemSelected": "Kein Objekt ausgewählt",
     "numItems_one": "{{number}} Elemente",
+    "numItems_other": "{{number}} Elemente",
     "off": "Keine",
     "openCompanionWindow_annotations": "Annotationen",
     "openCompanionWindow_attribution": "Rechte",
@@ -119,7 +124,9 @@
     "searchSubmitAria": "Suchen",
     "searchTitle": "Suche",
     "selectWorkspaceMenu": "Wählen Sie einen Arbeitsflächentyp",
-    "showingNumAnnotations_one": "{{number}} Annotationen werden angezeigt",
+    "showCollection": "Zeige Sammlung",
+    "showingNumAnnotations_one": "{{number}} Annotation wird angezeigt",
+    "showingNumAnnotations_other": "{{number}} Annotationen werden angezeigt",
     "showZoomControls": "Zoomsteuerung anzeigen",
     "sidebarPanelsNavigation": "Hilfsfensternavigation",
     "single": "Einzeln",
@@ -131,9 +138,14 @@
     "thumbnailNavigation": "Miniaturansicht",
     "thumbnails": "Miniaturansicht",
     "toggleWindowSideBar": "Seitenleiste umschalten",
+    "totalCollections_one": "{{count}} Sammlung",
+    "totalCollections_other": "{{count}} Sammlungen",
+    "totalManifests_one": "{{count}} Manifest",
+    "totalManifests_other": "{{count}} Manifeste",
     "tryAgain": "Wiederholen",
     "untitled": "[Unbenannt]",
     "view": "Ansicht",
+    "viewWorkspaceConfiguration": "Zeige Konfiguration der Arbeitsfläche",
     "welcome": "Willkommen bei Mirador",
     "window": "Fenster: {{label}}",
     "windowMenu": "Fenstermenü und Miniaturansicht",
diff --git a/src/state/selectors/annotations.js b/src/state/selectors/annotations.js
index e48c1db4892672605228c9569955236b874da90d..c2554f0fb4177885b640d6a804f584aec5053a93 100644
--- a/src/state/selectors/annotations.js
+++ b/src/state/selectors/annotations.js
@@ -7,7 +7,11 @@ import { getCanvas, getVisibleCanvasIds } from './canvases';
 import { getConfig } from './config';
 import { getWindow } from './getters';
 
-/** */
+/**
+ * Returns the annotation object from the mirador slice.
+ * @param {object} state redux state
+ * @returns {object} Annotations from the state
+ */
 export const getAnnotations = state => miradorSlice(state).annotations;
 
 const getMotivation = createSelector(
@@ -60,6 +64,12 @@ const getAnnotationsOnSelectedCanvases = createSelector(
   },
 );
 
+/**
+ * Returns an array of present annotations given a canvasId.
+ * @param {object} state redux state
+ * @param {string} canvasId canvasId
+ * @returns {Array} An array of present annotations
+ */
 export const getPresentAnnotationsOnSelectedCanvases = createSelector(
   [
     getAnnotationsOnSelectedCanvases,
@@ -72,11 +82,11 @@ export const getPresentAnnotationsOnSelectedCanvases = createSelector(
 );
 
 /**
-* Return an array of annotation resources filtered by the given motivation for a particular canvas
-* @param {Array} annotations
-* @param {Array} motivations
-* @return {Array}
-*/
+ * Returns an array of annotation resources filtered by the given motivation for a particular canvas.
+ * @param {Array} annotations
+ * @param {Array} motivations
+ * @returns {Array}
+ */
 export const getAnnotationResourcesByMotivationForCanvas = createSelector(
   [
     getPresentAnnotationsCanvas,
@@ -91,11 +101,11 @@ export const getAnnotationResourcesByMotivationForCanvas = createSelector(
 );
 
 /**
-* Return an array of annotation resources filtered by the given motivation
-* @param {Array} annotations
-* @param {Array} motivations
-* @return {Array}
-*/
+ * Returns an array of annotation resources filtered by the given motivation.
+ * @param {Array} annotations
+ * @param {Array} motivations
+ * @returns {Array}
+ */
 export const getAnnotationResourcesByMotivation = createSelector(
   [
     getPresentAnnotationsOnSelectedCanvases,
@@ -110,11 +120,11 @@ export const getAnnotationResourcesByMotivation = createSelector(
 );
 
 /**
- * Return the selected annotations IDs of a given CanvasId
- * @param {Object} state
- * @param {String} windowId
+ * Returns the selected annotations IDs.
+ * @param {object} state
+ * @param {string} windowId
  * @param {Array} targetIds
- * @return {Array}
+ * @returns {Array}
  */
 export const getSelectedAnnotationId = createSelector(
   [
@@ -123,6 +133,11 @@ export const getSelectedAnnotationId = createSelector(
   ({ selectedAnnotationId }) => selectedAnnotationId,
 );
 
+/**
+ * Returns annotations on selected canvases.
+ * @param {object} state
+ * @returns {Array}
+ */
 export const getSelectedAnnotationsOnCanvases = createSelector(
   [
     getPresentAnnotationsOnSelectedCanvases,
diff --git a/src/state/selectors/auth.js b/src/state/selectors/auth.js
index 7c290bfca47b82095b693d23e3d518f55db2803b..bd93f718bf03a8bc3750da79e90f89618cc726d5 100644
--- a/src/state/selectors/auth.js
+++ b/src/state/selectors/auth.js
@@ -6,6 +6,11 @@ import { miradorSlice } from './utils';
 import { getConfig } from './config';
 import { getVisibleCanvases, selectInfoResponses } from './canvases';
 
+/**
+ * Returns the authentification profile from the configuration
+ * @param {object} state
+ * @returns {Array}
+ */
 export const getAuthProfiles = createSelector(
   [
     getConfig,
@@ -13,12 +18,26 @@ export const getAuthProfiles = createSelector(
   ({ auth: { serviceProfiles = [] } = {} }) => serviceProfiles,
 );
 
-/** */
+/**
+ * Returns access tokens from the state
+ * @param {object} state
+ * @returns {object}
+ */
 export const getAccessTokens = state => miradorSlice(state).accessTokens || {};
 
-/** */
+/**
+ * Return the authentification data from the state
+ * @param {object} state
+ * @returns {object}
+ */
 export const getAuth = state => miradorSlice(state).auth || {};
 
+/**
+ * Returns current authentification services based on state and windowId
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {Array}
+ */
 export const selectCurrentAuthServices = createSelector(
   [
     getVisibleCanvases,
diff --git a/src/state/selectors/canvases.js b/src/state/selectors/canvases.js
index d836cc58478ba4ee78037bc313622ab45c05d1b7..88b65f74bf667d7d49376a1c7699f6667027fcba 100644
--- a/src/state/selectors/canvases.js
+++ b/src/state/selectors/canvases.js
@@ -7,7 +7,11 @@ import { getWindow } from './getters';
 import { getSequence } from './sequences';
 import { getWindowViewType } from './windows';
 
-/** */
+/**
+ * Returns the info response.
+ * @param {object} state
+ * @returns {object}
+ */
 export const selectInfoResponses = state => miradorSlice(state).infoResponses;
 
 export const getCanvases = createSelector(
@@ -16,13 +20,12 @@ export const getCanvases = createSelector(
 );
 
 /**
-* Return the canvas selected by an id
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {Object}
-*/
+ * Return the canvas selected by an id
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.canvasId
+ * @returns {object}
+ */
 export const getCanvas = createSelector(
   [
     getSequence,
@@ -35,6 +38,13 @@ export const getCanvas = createSelector(
   },
 );
 
+/**
+ * Returns the current canvas.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.windowId
+ * @returns {object|undefined}
+ */
 export const getCurrentCanvas = createSelector(
   [
     getSequence,
@@ -49,27 +59,39 @@ export const getCurrentCanvas = createSelector(
   },
 );
 
-/** */
+/**
+ * Returns the visible canvas ids.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.windowId
+ * @returns {Array}
+ */
 export const getVisibleCanvasIds = createSelector(
   [getWindow],
   window => (window && (window.visibleCanvases || (window.canvasId && [window.canvasId]))) || [],
 );
 
-/** */
+/**
+ * Returns the visible canvses.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.windowId
+ * @returns {Array}
+ */
 export const getVisibleCanvases = createSelector(
   [getVisibleCanvasIds, getCanvases],
   (canvasIds, canvases) => (canvases || []).filter(c => canvasIds.includes(c.id)),
 );
 
 /**
-* Return the current canvases grouped by how they'll appear in the viewer
-* For book view returns groups of 2, for single returns 1
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {Array}
-*/
+ * Return the current canvases grouped by how they'll appear in the viewer.
+ * For book view returns groups of 2, for single returns 1.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {Array}
+ */
 export const getCanvasGroupings = createSelector(
   [
     getCanvases,
@@ -83,14 +105,15 @@ export const getCanvasGroupings = createSelector(
 );
 
 /**
-* Return the current canvases selected in a window
-* For book view returns 2, for single returns 1
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {Array}
-*/
+ * Return the current canvases selected in a window.
+ * For book view returns 2, for single returns 1.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @param {string} props.canvasId
+ * @returns {Array}
+ */
 export const getCanvasGrouping = createSelector(
   [
     getCanvasGroupings,
@@ -101,14 +124,14 @@ export const getCanvasGrouping = createSelector(
 );
 
 /**
-* Return the next canvas(es) for a window
-* For book view returns 2, for single returns 1
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {Array}
-*/
+ * Return the next canvas(es) for a window.
+ * For book view returns 2, for single returns 1.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {Array}
+ */
 export const getNextCanvasGrouping = createSelector(
   [
     getCanvasGroupings,
@@ -126,14 +149,14 @@ export const getNextCanvasGrouping = createSelector(
 );
 
 /**
-* Return the previous canvas(es) for a window
-* For book view returns 2, for single returns 1
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {Array}
-*/
+ * Return the previous canvas(es) for a window.
+ * For book view returns 2, for single returns 1.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {Array}
+ */
 export const getPreviousCanvasGrouping = createSelector(
   [
     getCanvasGroupings,
@@ -152,10 +175,13 @@ export const getPreviousCanvasGrouping = createSelector(
 );
 
 /**
-* Return canvas label, or alternatively return the given index + 1 to be displayed
-* @param {object} canvas
-* @return {String|Integer}
-*/
+ * Return canvas label, or alternatively return the given index + 1 to be displayed.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.canvasId
+ * @param {string} props.manifestId
+ * @returns {string|number}
+ */
 export const getCanvasLabel = createSelector(
   [getCanvas],
   canvas => (canvas && (
@@ -166,15 +192,21 @@ export const getCanvasLabel = createSelector(
 );
 
 /**
-* Return canvas description
-* @param {object} canvas
-* @param {String}
-*/
+ * Return canvas description.
+ * @param {object} canvas
+ * @param {string} companionWindowId
+ */
 export const getCanvasDescription = createSelector(
   [getCanvas],
   canvas => canvas && canvas.getProperty('description'),
 );
 
+/**
+ * Return visible non tiled canvas resources.
+ * @param {object}
+ * @param {string} windowId
+ * @returns {Array}
+ */
 export const getVisibleCanvasNonTiledResources = createSelector(
   [
     getVisibleCanvases,
@@ -184,6 +216,12 @@ export const getVisibleCanvasNonTiledResources = createSelector(
     .filter(resource => resource.getServices().length < 1),
 );
 
+/**
+ * Returns visible canvas video resources.
+ * @param {object} state
+ * @param {string} windowId
+ * @return {Array}
+ */
 export const getVisibleCanvasVideoResources = createSelector(
   [
     getVisibleCanvases,
@@ -192,6 +230,12 @@ export const getVisibleCanvasVideoResources = createSelector(
     .map(canvas => new MiradorCanvas(canvas).videoResources)),
 );
 
+/**
+ * Returns visible canvas captions.
+ * @param {object} state
+ * @param {string} windowId
+ * @return {Array}
+ */
 export const getVisibleCanvasCaptions = createSelector(
   [
     getVisibleCanvases,
@@ -204,6 +248,12 @@ export const getVisibleCanvasCaptions = createSelector(
   })),
 );
 
+/**
+ * Returns visible canvas audio resources.
+ * @param {object} state
+ * @param {string} windowId
+ * @return {Array}
+ */
 export const getVisibleCanvasAudioResources = createSelector(
   [
     getVisibleCanvases,
@@ -212,6 +262,15 @@ export const getVisibleCanvasAudioResources = createSelector(
     .map(canvas => new MiradorCanvas(canvas).audioResources)),
 );
 
+/**
+ * Returns info response.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.windowId
+ * @param {string} props.canvasId
+ * @param {string} props.infoId
+ * @returns {object|undefined}
+ */
 export const selectInfoResponse = createSelector(
   [
     (state, { infoId }) => infoId,
diff --git a/src/state/selectors/companionWindows.js b/src/state/selectors/companionWindows.js
index 914c73a2f0148bd3d4e285e558d9397cd234332b..ea91987ef004fd3b4d6b2088cf8d1f702709b797 100644
--- a/src/state/selectors/companionWindows.js
+++ b/src/state/selectors/companionWindows.js
@@ -3,11 +3,22 @@ import groupBy from 'lodash/groupBy';
 import { miradorSlice } from './utils';
 import { getWindow, getWindows } from './getters';
 
-/** */
+/**
+ * Returns companion windows.
+ * @param {object} state
+ * @returns {object}
+ */
 export function getCompanionWindows(state) {
   return miradorSlice(state).companionWindows || {};
 }
 
+/**
+ * Returns the companion window.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.companionWindowId
+ * @returns {object|undefined}
+ */
 export const getCompanionWindow = createSelector(
   [
     getCompanionWindows,
@@ -16,11 +27,13 @@ export const getCompanionWindow = createSelector(
   (companionWindows, companionWindowId) => companionWindowId && companionWindows[companionWindowId],
 );
 
-/** Return position of thumbnail navigation in a certain window.
-* @param {object} state
-* @param {String} windowId
-* @param {String}
-*/
+/**
+ * Return position of thumbnail navigation in a certain window.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.windowId
+ * @returns {string|undefined}
+ */
 export const getThumbnailNavigationPosition = createSelector(
   [
     getWindow,
@@ -32,10 +45,10 @@ export const getThumbnailNavigationPosition = createSelector(
 );
 
 /**
-* Return compantion window ids from a window
-* @param {String} windowId
-* @return {Array}
-*/
+ * Return companion window ids from a window.
+ * @param {string} windowId
+ * @returns {Array}
+ */
 const getCompanionWindowIndexByWindowAndPosition = createSelector(
   [getWindows, getCompanionWindows],
   (windows, companionWindows) => (
@@ -51,10 +64,10 @@ const getCompanionWindowIndexByWindowAndPosition = createSelector(
 );
 
 /**
-* Return compantion window ids from a window
-* @param {String} windowId
-* @return {Array}
-*/
+ * Return companion window ids from a window.
+ * @param {string} windowId
+ * @returns {Array}
+ */
 const getCompanionWindowsByWindowAndPosition = createSelector(
   [getWindows, getCompanionWindows],
   (windows, companionWindows) => (
@@ -69,9 +82,10 @@ const getCompanionWindowsByWindowAndPosition = createSelector(
 );
 
 /**
- * Return companion windows of a window
- * @param {String} windowId
- * @return {Array}
+ * Return companion windows of a window.
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {Array}
  */
 const getCompanionWindowsOfWindow = createSelector(
   [(state, { windowId }) => windowId, getCompanionWindowsByWindowAndPosition],
@@ -79,9 +93,10 @@ const getCompanionWindowsOfWindow = createSelector(
 );
 
 /**
- * Return companion windows of a window
- * @param {String} windowId
- * @return {Array}
+ * Return companion windows ids of a window.
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {Array}
  */
 const getCompanionWindowIdsOfWindow = createSelector(
   [(state, { windowId }) => windowId, getCompanionWindowIndexByWindowAndPosition],
@@ -89,53 +104,60 @@ const getCompanionWindowIdsOfWindow = createSelector(
 );
 
 /**
-* Return the companion window string from state in a given windowId and position
-* @param {object} state
-* @param {String} windowId
-* @param {String} position
-* @return {String}
-*/
+ * Return the companion window string from state in a given windowId and position.
+ * @param {object} state
+ * @param {string} windowId
+ * @param {string} position
+ * @returns {string}
+ */
 export const getCompanionWindowsForPosition = createSelector(
   [
     getCompanionWindowsOfWindow,
-    (state, { position }) => ({ position }),
+    (state, { position }) => (position),
   ],
-  (companionWindows, { position }) => companionWindows[position] || EMPTY_ARRAY,
+  (companionWindows, position) => companionWindows[position] || EMPTY_ARRAY,
 );
 
 /**
-* Return the companion window string from state in a given windowId and content type
-* @param {object} state
-* @param {String} windowId
-* @param {String} position
-* @return {String}
-*/
+ * Return the companion window string from state in a given windowId and content type.
+ * @param {object} state
+ * @param {string} windowId
+ * @param {string} position
+ * @returns {string}
+ */
 export const getCompanionWindowsForContent = createSelector(
   [
     getCompanionWindowsOfWindow,
-    (state, { content }) => ({ content }),
+    (state, { content }) => (content),
   ],
-  (companionWindows, { content }) => (
+  (companionWindows, content) => (
     [].concat(...Object.values(companionWindows)).filter(w => w.content === content)
   ),
 );
 
 const EMPTY_ARRAY = [];
 
-/** */
+/**
+ * Returns companion window ids for position.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.windowId
+ * @param {string} props.position
+ * @returns {Array}
+ */
 export const getCompanionWindowIdsForPosition = createSelector(
   [
     getCompanionWindowIdsOfWindow,
-    (state, { position }) => ({ position }),
+    (state, { position }) => (position),
   ],
-  (companionWindowIds, { position }) => companionWindowIds[position] || EMPTY_ARRAY,
+  (companionWindowIds, position) => companionWindowIds[position] || EMPTY_ARRAY,
 );
 
 /**
- * Returns the visibility of the companion area
+ * Returns the visibility of the companion area.
  * @param {object} state
- * @param {object} props
- * @return {Boolean}
+ * @param {string} position
+ * @returns {boolean}
  */
 export const getCompanionAreaVisibility = createSelector(
   [
@@ -150,6 +172,12 @@ export const getCompanionAreaVisibility = createSelector(
   },
 );
 
+/**
+ * Returns the dimensions.
+ * @param {object} state
+ * @param {string} companionWindowId
+ * @returns {object} containing height and width
+ */
 export const selectCompanionWindowDimensions = createSelector(
   [getCompanionWindowsOfWindow],
   (companionWindows) => {
diff --git a/src/state/selectors/config.js b/src/state/selectors/config.js
index d5b99995d2b5458b05dd54e52ee76ed4001b7c09..7a025b347468f22e7e41e2e52851a5646b529a4d 100644
--- a/src/state/selectors/config.js
+++ b/src/state/selectors/config.js
@@ -3,7 +3,11 @@ import deepmerge from 'deepmerge';
 import { miradorSlice } from './utils';
 import { getWorkspace } from './getters';
 
-/** */
+/**
+ * Returns the config from the redux state.
+ * @param {object} state
+ * @returns {object} containing config
+ */
 export function getConfig(state) {
   const slice = miradorSlice(state || {});
   return slice.config || {};
@@ -11,6 +15,8 @@ export function getConfig(state) {
 
 /**
  * Extract an exportable version of state using the configuration from the config.
+ * @param {object} state
+ * @returns {object} containing exportable state
  */
 export function getExportableState(state) {
   const exportConfig = getConfig(state).export;
@@ -37,9 +43,9 @@ export function getExportableState(state) {
 }
 
 /**
-* Return languages from config (in state) and indicate which is currently set
+* Return languages from config (in state) and indicate which is currently set.
 * @param {object} state
-* @return {Array} [ {locale: 'de', label: 'Deutsch', current: true}, ... ]
+* @returns {Array} [ {locale: 'de', label: 'Deutsch', current: true}, ... ]
 */
 export const getLanguagesFromConfigWithCurrent = createSelector(
   [getConfig],
@@ -50,6 +56,11 @@ export const getLanguagesFromConfigWithCurrent = createSelector(
   })),
 );
 
+/**
+ * Returns if showZoomControls is set in the config.
+ * @param {object} state
+ * @returns {boolean}
+ */
 export const getShowZoomControlsConfig = createSelector(
   [
     getWorkspace,
@@ -62,11 +73,21 @@ export const getShowZoomControlsConfig = createSelector(
   ),
 );
 
+/**
+ * Returns the theme from the config.
+ * @param {object} state
+ * @returns {object} {palette: {...}, typography: {...}, overrides: {...}, ...}
+ */
 export const getTheme = createSelector(
   [getConfig],
   ({ theme, themes, selectedTheme }) => deepmerge(theme, themes[selectedTheme] || {}),
 );
 
+/**
+ * Returns the theme ids from the config.
+ * @param {object} state
+ * @returns {Array} ['dark', 'light']
+ */
 export const getThemeIds = createSelector(
   [getConfig],
   ({ themes }) => Object.keys(themes),
@@ -78,11 +99,20 @@ export const getContainerId = createSelector(
   ({ id }) => id,
 );
 
+/**
+ * Returns the theme direction from the config.
+ * @param {object} state
+ * @returns {string}
+ */
 export const getThemeDirection = createSelector(
   [getConfig],
   ({ theme }) => theme.direction || 'ltr',
 );
-
+/**
+ * Returns the theme direction from the config.
+ * @param {object} state
+ * @returns {object} {preprocessor: [...], postprocessor: [...]}
+ */
 export const getRequestsConfig = createSelector(
   [getConfig],
   ({ requests }) => requests || {},
diff --git a/src/state/selectors/getters.js b/src/state/selectors/getters.js
index 066dce47da749c50eaa42e10ae5e619a9f3b8d19..747554577898e7d3340a893385e35a8307b70c97 100644
--- a/src/state/selectors/getters.js
+++ b/src/state/selectors/getters.js
@@ -2,24 +2,41 @@ import { createSelector } from 'reselect';
 import { miradorSlice } from './utils';
 
 /**
- * Return the manifest titles for all open windows
+ * Returns the manifest titles for all open windows.
  * @param {object} state
- * @return {object}
+ * @returns {Array} containing manifests ids.
  */
 export function getWindowManifests(state) {
   return Object.values(miradorSlice(state).windows).map(window => window.manifestId);
 }
 
-/** */
+/**
+ * Returns the opened windows.
+ * @param {object} state
+ * @returns {object} {id: {canvasId: {...}, ...}, ...}
+ */
 export function getWindows(state) {
   return miradorSlice(state).windows || {};
 }
 
-/** */
+/**
+ * Returns a window based on a given windowId.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.windowId
+ * @returns {object|undefined}
+ */
 export function getWindow(state, { windowId }) {
   return getWindows(state)[windowId];
 }
 
+/**
+ * Returns the viewer for a given window.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.windowId
+ * @returns {object|undefined} {flip: false, rotation: 0, x: 1, y: 2, zoom: 0.5}
+ */
 export const getViewer = createSelector(
   [
     state => miradorSlice(state).viewers,
@@ -28,23 +45,42 @@ export const getViewer = createSelector(
   (viewers, windowId) => viewers[windowId],
 );
 
-/** */
+/**
+ * Returns the workspace.
+ * @param {object} state
+ * @returns {object}
+ */
 export function getWorkspace(state) {
   return miradorSlice(state).workspace;
 }
 
-/** */
+/**
+ * Returns the windowIds.
+ * @param {object} state
+ * @returns {Array}
+ */
 export const getWindowIds = createSelector(
   [getWorkspace],
   ({ windowIds }) => windowIds || [],
 );
 
-/** */
+/**
+ * Returns all manifests including manifest information.
+ * @param {object} state
+ * @returns {object}
+ */
 export function getManifests(state) {
   return miradorSlice(state).manifests || {};
 }
 
-/** Get the relevant manifest information */
+/**
+ * Get the relevant manifest information for a given manifest.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {object}
+ */
 export function getManifest(state, { manifestId, windowId }) {
   const manifests = getManifests(state);
   return manifests && manifests[
@@ -53,7 +89,11 @@ export function getManifest(state, { manifestId, windowId }) {
   ];
 }
 
-/** */
+/**
+ * Get the opened catalog.
+ * @param {object} state
+ * @returns {object} containing manifestIds for the manifests in the catalog
+ */
 export function getCatalog(state) {
   return miradorSlice(state).catalog || {};
 }
diff --git a/src/state/selectors/layers.js b/src/state/selectors/layers.js
index e75c165b7e2c195c9153d2c87f030b38b72ed127..f2850e83c57ab237409b56382d1f07bc85c0bf21 100644
--- a/src/state/selectors/layers.js
+++ b/src/state/selectors/layers.js
@@ -4,7 +4,13 @@ import { getCanvas, getVisibleCanvasIds } from './canvases';
 import { miradorSlice } from './utils';
 
 /**
- * Get the image layers from a canvas
+ * Get the image layers from a canvas.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.canvasId
+ * @param {string} props.windowId
+ * @param {string} props.companionWindowId
+ * @returns {Array}
  */
 export const getCanvasLayers = createSelector(
   [
@@ -17,20 +23,36 @@ export const getCanvasLayers = createSelector(
   },
 );
 
+const EMPTY_ARRAY = Object.freeze([]);
+
+export const getLayersForWindow = createSelector(
+  [
+    state => miradorSlice(state).layers,
+    (state, { windowId }) => windowId,
+  ],
+  (layers, windowId) => (layers ? (layers[windowId] || EMPTY_ARRAY) : EMPTY_ARRAY),
+);
+
 /**
- * Get the layer state for a particular canvas
+ * Get the layer state for a particular canvas.
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {object}
  */
 export const getLayers = createSelector(
   [
-    state => miradorSlice(state).layers || {},
-    (state, { windowId }) => windowId,
+    getLayersForWindow,
     (state, { canvasId }) => canvasId,
   ],
-  (layers, windowId, canvasId) => (layers[windowId] || {})[canvasId],
+  (layers, canvasId) => layers[canvasId],
 );
 
 /**
- * Returns a list of canvas layers, sorted by the layer state configuration
+ * Returns a list of canvas layers, sorted by the layer state configuration.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.companionWindowId
+ * @returns {Array}
  */
 export const getSortedLayers = createSelector(
   [
@@ -58,16 +80,20 @@ export const getSortedLayers = createSelector(
 );
 
 /**
- * Get all the layer configuration for visible canvases
+ * Get all the layer configuration for visible canvases.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.windowId
+ * @returns {object}
  */
 export const getLayersForVisibleCanvases = createSelector(
   [
     getVisibleCanvasIds,
-    (state, { windowId }) => (canvasId => getLayers(state, { canvasId, windowId })),
+    getLayersForWindow,
   ],
-  (canvasIds, getLayersForCanvas) => (
+  (canvasIds, layers) => (
     canvasIds.reduce((acc, canvasId) => {
-      acc[canvasId] = getLayersForCanvas(canvasId);
+      acc[canvasId] = layers[canvasId];
       return acc;
     }, {})
   ),
diff --git a/src/state/selectors/manifests.js b/src/state/selectors/manifests.js
index 496d8456216a94e999cce9b7940b7e2fe9d6cf59..070b82ae45d174ed572ddb1b3648a3da9fb23fff 100644
--- a/src/state/selectors/manifests.js
+++ b/src/state/selectors/manifests.js
@@ -29,13 +29,25 @@ const getLocale = createSelector(
   ),
 );
 
-/** Convenience selector to get a manifest (or placeholder) */
+/**
+ * Convenience selector to get a manifest (or placeholder).
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.windowId
+ * @returns {object} {error: null: id: string, isFetching: boolean, json: {...}}
+ */
 export const getManifestStatus = createSelector(
   [getManifest],
   manifest => manifest || { missing: true },
 );
 
-/** Convenience selector to get a manifest loading error */
+/**
+ * Convenience selector to get a manifest loading error
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @returns {string|null}
+ */
 export const getManifestError = createSelector(
   [getManifest],
   manifest => manifest && manifest.error,
@@ -55,7 +67,13 @@ const getContextualManifestoInstance = createCachedSelector(
   ].join(' - '), // Cache key consisting of manifestId, windowId, and locale
 );
 
-/** Instantiate a manifesto instance */
+/**
+ * Instantiate a manifesto instance
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @returns {object}
+ */
 export const getManifestoInstance = createSelector(
   getContextualManifestoInstance,
   (state, { json }) => json,
@@ -78,7 +96,13 @@ function getProperty(property) {
   );
 }
 
-/** */
+/**
+ * Returns the manifest provider.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.companionWindowId
+ * @returns {string}
+ */
 export const getManifestProvider = createSelector(
   [
     getProperty('provider'),
@@ -87,13 +111,13 @@ export const getManifestProvider = createSelector(
 );
 
 /**
-* Return the IIIF v3 provider of a manifest or null
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {String|null}
-*/
+ * Return the IIIF v3 provider of a manifest or null.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {string|null}
+ */
 export const getManifestProviderName = createSelector(
   [
     getProperty('provider'),
@@ -105,10 +129,10 @@ export const getManifestProviderName = createSelector(
 );
 
 /**
- * Return the IIIF v3 provider logo
+ * Return the IIIF v3 provider logo.
  * @param {object} state
  * @param {object} props
- * @return {String|null}
+ * @returns {string|null}
  */
 export const getProviderLogo = createSelector(
   [getManifestProvider],
@@ -120,10 +144,10 @@ export const getProviderLogo = createSelector(
 );
 
 /**
- * Get the logo for a manifest
+ * Get the logo for a manifest.
  * @param {object} state
  * @param {object} props
- * @return {String|null}
+ * @returns {string|null}
  */
 export const getManifestLogo = createSelector(
   [getManifestoInstance, getProviderLogo],
@@ -131,13 +155,13 @@ export const getManifestLogo = createSelector(
 );
 
 /**
-* Return the IIIF v3 homepage of a manifest or null
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {String|null}
-*/
+ * Return the IIIF v3 homepage of a manifest or null.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {string|null}
+ */
 export const getManifestHomepage = createSelector(
   [
     getProperty('homepage'),
@@ -154,13 +178,13 @@ export const getManifestHomepage = createSelector(
 );
 
 /**
-* Return the IIIF v3 renderings of a manifest or null
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {String|null}
-*/
+ * Return the IIIF v3 renderings of a manifest or null.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {string|null}
+ */
 export const getManifestRenderings = createSelector(
   [getManifestoInstance],
   manifest => manifest
@@ -173,14 +197,13 @@ export const getManifestRenderings = createSelector(
 );
 
 /**
-* Return the IIIF v2/v3 seeAlso data from a manifest or null
-*
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {String|null}
-*/
+ * Return the IIIF v2/v3 seeAlso data from a manifest or null.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {string|null}
+ */
 export const getManifestSeeAlso = createSelector(
   [
     getProperty('seeAlso'),
@@ -198,27 +221,26 @@ export const getManifestSeeAlso = createSelector(
 );
 
 /**
-* Return the IIIF v2/v3 seeAlso data from a manifest or null
-*
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {String|null}
-* @deprecated This does not actually return the content of "related" and
-* might be removed in a future version.
-* @see getManifestSeeAlso
-*/
+ * Return the IIIF v2/v3 seeAlso data from a manifest or null.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {string|null}
+ * @deprecated This does not actually return the content of "related" and
+ * might be removed in a future version.
+ * @see getManifestSeeAlso
+ */
 export const getManifestRelatedContent = getManifestSeeAlso;
 
 /**
-* Return the IIIF v2 realated links manifest or null
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {String|null}
-*/
+ * Return the IIIF v2 realated links manifest or null
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {string|null}
+ */
 export const getManifestRelated = createSelector(
   [
     getProperty('related'),
@@ -240,13 +262,13 @@ export const getManifestRelated = createSelector(
 );
 
 /**
-* Return the IIIF requiredStatement (v3) or attribution (v2) data from a manifest or null
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {String|null}
-*/
+ * Return the IIIF requiredStatement (v3) or attribution (v2) data from a manifest or null
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {string|null}
+ */
 export const getRequiredStatement = createSelector(
   [getManifestoInstance],
   manifest => manifest
@@ -259,12 +281,12 @@ export const getRequiredStatement = createSelector(
 );
 
 /**
-* Return the IIIF v2 rights (v3) or license (v2) data from a manifest or null
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {String|null}
+ * Return the IIIF v2 rights (v3) or license (v2) data from a manifest or null
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {string|null}
 */
 export const getRights = createSelector(
   [
@@ -279,13 +301,13 @@ export const getRights = createSelector(
 );
 
 /**
-* Return the supplied thumbnail for a manifest or null
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {String|null}
-*/
+ * Return the supplied thumbnail for a manifest or null.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {string|null}
+ */
 export function getManifestThumbnail(state, props) {
   const manifest = getManifestoInstance(state, props);
   const { thumbnails = {} } = getConfig(state);
@@ -300,13 +322,13 @@ export function getManifestThumbnail(state, props) {
 }
 
 /**
-* Return manifest title
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {String}
-*/
+ * Return manifest title.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {string}
+ */
 export const getManifestTitle = createSelector(
   [getManifestoInstance],
   manifest => manifest
@@ -314,26 +336,26 @@ export const getManifestTitle = createSelector(
 );
 
 /**
-* Return manifest description (IIIF v2) -- distinct from any description field nested under metadata
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {String|null}
-*/
+ * Return manifest description (IIIF v2) -- distinct from any description field nested under metadata.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {string|null}
+ */
 export const getManifestDescription = createSelector(
-  [getManifestoInstance],
-  manifest => manifest
-    && manifest.getDescription().getValue(),
+  [getLocale, getManifestoInstance],
+  (locale, manifest) => manifest
+    && manifest.getDescription().getValue(locale),
 );
 
 /**
-* Return manifest summary (IIIF v3)
+* Return manifest summary (IIIF v3).
 * @param {object} state
 * @param {object} props
 * @param {string} props.manifestId
 * @param {string} props.windowId
-* @return {String|null}
+* @return {string|null}
 */
 export const getManifestSummary = createSelector(
   [
@@ -341,30 +363,31 @@ export const getManifestSummary = createSelector(
     getManifestLocale,
   ],
   (summary, locale) => summary
-    && PropertyValue.parse(summary, locale).getValue(),
+    && PropertyValue.parse(summary, locale).getValue(locale),
 );
 
 /**
-* Return manifest title
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @return {String}
-*/
+ * Return manifest title.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @returns {string}
+ */
 export const getManifestUrl = createSelector(
   [getManifestoInstance],
   manifest => manifest && manifest.id,
 );
 
 /**
-* Return metadata in a label / value structure
-* This is a potential seam for pulling the i18n locale from
-* state and plucking out the appropriate language.
-* For now we're just getting the first.
-* @param {object} Manifesto IIIF Resource (e.g. canvas, manifest)
-* @return {Array[Object]}
-*/
+ * Return metadata in a label / value structure
+ * This is a potential seam for pulling the i18n locale from
+ * state and plucking out the appropriate language.
+ * For now we're just getting the first.
+ * @param {object} Manifesto IIIF Resource (e.g. canvas, manifest)
+ * @param iiifResource
+ * @returns {Array[Object]}
+ */
 export function getDestructuredMetadata(iiifResource) {
   return (iiifResource
     && iiifResource.getMetadata().map(labelValuePair => ({
@@ -380,7 +403,7 @@ export function getDestructuredMetadata(iiifResource) {
  * @param {object} props
  * @param {string} props.manifestId
  * @param {string} props.windowId
- * @return {Array[Object]}
+ * @returns {Array[Object]}
  */
 export const getManifestMetadata = createSelector(
   [getManifestoInstance],
@@ -419,7 +442,13 @@ export const getMetadataLocales = createSelector(
   manifest => getLocales(manifest),
 );
 
-/** */
+/**
+ * Returns manifest search service.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @returns {string|null}
+ */
 export const getManifestSearchService = createSelector(
   [getManifestoInstance],
   (manifest) => {
@@ -431,7 +460,13 @@ export const getManifestSearchService = createSelector(
   },
 );
 
-/** */
+/**
+ * Returns manifest autocomplete service.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @returns {string|null}
+ */
 export const getManifestAutocompleteService = createSelector(
   [getManifestSearchService],
   (searchService) => {
diff --git a/src/state/selectors/ranges.js b/src/state/selectors/ranges.js
index a2e312955850fd488421b479a8b6d4f4cecda7ec..c025b5a3a6dded7b9b02e593ecf33c89b2605b05 100644
--- a/src/state/selectors/ranges.js
+++ b/src/state/selectors/ranges.js
@@ -66,7 +66,13 @@ const getVisibleLeafAndBranchNodeIds = createSelector(
   },
 );
 
-/** */
+/**
+ * Returns visible node ids.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.windowId
+ * @returns {Array}
+ */
 export const getVisibleNodeIds = createSelector(
   [
     getVisibleLeafAndBranchNodeIds,
@@ -94,7 +100,14 @@ const getCanvasContainingNodeIds = createSelector(
   ),
 );
 
-/** */
+/**
+ * Returns ids of manually exanded nodes.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.companionWindowId
+ * @param expanded
+ * @returns {Array}
+ */
 export function getManuallyExpandedNodeIds(state, { companionWindowId }, expanded) {
   const companionWindow = getCompanionWindow(state, { companionWindowId });
   return companionWindow.tocNodes ? Object.keys(companionWindow.tocNodes).reduce(
@@ -105,7 +118,14 @@ export function getManuallyExpandedNodeIds(state, { companionWindowId }, expande
   ) : [];
 }
 
-/** */
+/**
+ * Returns ids of exanded nodes.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.companionWindowId
+ * @param {string} props.windowId
+ * @returns {Array}
+ */
 export function getExpandedNodeIds(state, { companionWindowId, windowId }) {
   const visibleBranchNodeIds = getVisibleBranchNodeIds(state, { companionWindowId, windowId });
   const manuallyExpandedNodeIds = getManuallyExpandedNodeIds(state, { companionWindowId }, true);
@@ -113,7 +133,15 @@ export function getExpandedNodeIds(state, { companionWindowId, windowId }) {
   return without(union(manuallyExpandedNodeIds, visibleBranchNodeIds), ...manuallyClosedNodeIds);
 }
 
-/** */
+/**
+ * Returns id of node to scroll to.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.companionWindowId
+ * @param {string} props.windowId
+ * @param {string} props.manifestid
+ * @returns {string}
+ */
 export function getNodeIdToScrollTo(state, { ...args }) {
   const canvasContainingNodeIds = getCanvasContainingNodeIds(state, { ...args });
   const collapsedNodeIds = getManuallyExpandedNodeIds(state, args, false);
@@ -129,7 +157,10 @@ export function getNodeIdToScrollTo(state, { ...args }) {
 }
 
 /**
- * Returns the default sidebar variant depending on whether or not ranges exist
+ * Returns the default sidebar variant depending on whether or not ranges exist.
+ * @param {object} state
+ * @param {string}
+ * @returns {string}
  */
 export const getDefaultSidebarVariant = createSelector(
   [
diff --git a/src/state/selectors/searches.js b/src/state/selectors/searches.js
index 11f9feaf73c68708a11856a17f2f34421b0d7c15..9b4ee7168ac9a0c7cab2f87bef0eff5ddb639bde 100644
--- a/src/state/selectors/searches.js
+++ b/src/state/selectors/searches.js
@@ -7,9 +7,17 @@ import { getWindow } from './getters';
 import { getManifestLocale } from './manifests';
 import { miradorSlice } from './utils';
 
-/** Get searches from state */
+/**
+ *  Get searches from state.
+ */
 const getSearches = (state) => miradorSlice(state).searches;
 
+/**
+ * Returns the search result for a specific window.
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {object}
+ */
 export const getSearchForWindow = createSelector(
   [
     (state, { windowId }) => windowId,
@@ -22,6 +30,12 @@ export const getSearchForWindow = createSelector(
   },
 );
 
+/**
+ * Returns the search result for a specific companion window.
+ * @param {object} state
+ * @param {string} companionWindowId
+ * @returns {object|undefined}
+ */
 const getSearchForCompanionWindow = createSelector(
   [
     getSearchForWindow,
@@ -33,6 +47,11 @@ const getSearchForCompanionWindow = createSelector(
   },
 );
 
+/**
+ * Returns an array of search responses for a specific companion window.
+ * @param {object} state
+ * @returns {Array}
+ */
 const getSearchResponsesForCompanionWindow = createSelector(
   [
     getSearchForCompanionWindow,
@@ -43,6 +62,12 @@ const getSearchResponsesForCompanionWindow = createSelector(
   },
 );
 
+/**
+ * Returns the search query for a specific companion window.
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {string|undefined}
+ */
 export const getSearchQuery = createSelector(
   [
     getSearchForCompanionWindow,
@@ -50,6 +75,11 @@ export const getSearchQuery = createSelector(
   results => results && results.query,
 );
 
+/**
+ * Returns if search response for a companion window is fetching.
+ * @param {object} state
+ * @returns {boolean}
+ */
 export const getSearchIsFetching = createSelector(
   [
     getSearchResponsesForCompanionWindow,
@@ -57,6 +87,12 @@ export const getSearchIsFetching = createSelector(
   results => results.some(result => result.isFetching),
 );
 
+/**
+ * Returns the total number of search results for a companion window.
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {number|undefined}
+ */
 export const getSearchNumTotal = createSelector(
   [
     getSearchForCompanionWindow,
@@ -76,6 +112,12 @@ export const getSearchNumTotal = createSelector(
   },
 );
 
+/**
+ * Returns the Id of the next search.
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {number|undefined}
+ */
 export const getNextSearchId = createSelector(
   [
     getSearchForCompanionWindow,
@@ -107,6 +149,12 @@ const getSearchHitsForCompanionWindow = createSelector(
   })),
 );
 
+/**
+ * Returns sorted search hits based on canvas order.
+ * @param {object} state
+ * @param {string} manifestId
+ * @returns {Array}
+ */
 export const getSortedSearchHitsForCompanionWindow = createSelector(
   [
     getSearchHitsForCompanionWindow,
@@ -152,7 +200,10 @@ export const getSearchAnnotationsForCompanionWindow = createSelector(
   results => results && searchResultsToAnnotation(results),
 );
 
-/** */
+/**
+ * Sorts search annotations based on canvas order.
+ * @returns {Array}
+ */
 export function sortSearchAnnotationsByCanvasOrder(searchAnnotations, canvases) {
   if (!searchAnnotations
       || !searchAnnotations.resources
@@ -165,6 +216,12 @@ export function sortSearchAnnotationsByCanvasOrder(searchAnnotations, canvases)
   );
 }
 
+/**
+ * Returns sorted search annotations for companion window.
+ * @param {object} state
+ * @param {string} companionWindowId
+ * @returns {Array}
+ */
 export const getSortedSearchAnnotationsForCompanionWindow = createSelector(
   [
     getSearchAnnotationsForCompanionWindow,
@@ -173,6 +230,12 @@ export const getSortedSearchAnnotationsForCompanionWindow = createSelector(
   (searchAnnotations, canvases) => sortSearchAnnotationsByCanvasOrder(searchAnnotations, canvases),
 );
 
+/**
+ * Returns sorted search annotations for window.
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {Array}
+ */
 export const getSearchAnnotationsForWindow = createSelector(
   [
     getSearchForWindow,
@@ -185,6 +248,12 @@ export const getSearchAnnotationsForWindow = createSelector(
   },
 );
 
+/**
+ * Returns ids of selected content search annotations.
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {Array}
+ */
 export const getSelectedContentSearchAnnotationIds = createSelector(
   [
     getWindow,
@@ -194,6 +263,12 @@ export const getSelectedContentSearchAnnotationIds = createSelector(
     || [],
 );
 
+/**
+ * Returns resource annotations for search hit.
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {Array}
+ */
 export const getResourceAnnotationForSearchHit = createSelector(
   [
     getSearchAnnotationsForCompanionWindow,
@@ -204,6 +279,12 @@ export const getResourceAnnotationForSearchHit = createSelector(
   ),
 );
 
+/**
+ * Returns annotation label.
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {Array}
+ */
 export const getResourceAnnotationLabel = createSelector(
   [
     getResourceAnnotationForSearchHit,
@@ -229,6 +310,12 @@ const getAnnotationById = createSelector(
   },
 );
 
+/**
+ * Returns annotation label.
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {Array}
+ */
 export const getCanvasForAnnotation = createSelector(
   [
     getAnnotationById,
diff --git a/src/state/selectors/sequences.js b/src/state/selectors/sequences.js
index c8014a70c0d3860dba8aaa703c9c13e880937bc8..097d341d5e82da26a9ba56d863e0acd872252aa2 100644
--- a/src/state/selectors/sequences.js
+++ b/src/state/selectors/sequences.js
@@ -5,6 +5,13 @@ import {
 } from './manifests';
 import { getWindow } from './getters';
 
+/**
+ * Returns the sequences for a given windowId
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.windowId
+ * @returns {Array}
+ */
 export const getSequences = createSelector(
   [getManifestoInstance],
   (manifest) => {
@@ -29,6 +36,13 @@ export const getSequences = createSelector(
   },
 );
 
+/**
+ * Returns the sequence for a given windowId
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.windowId
+ * @returns {Array}
+ */
 export const getSequence = createSelector(
   [
     getSequences,
@@ -48,11 +62,12 @@ export const getSequence = createSelector(
   },
 );
 
-/** Return the canvas index for a certain window.
-* @param {object} state
-* @param {String} windowId
-* @param {Number}
-*/
+/**
+ * Return the canvas index for a certain window.
+ * @param {Object} state
+ * @param {string} windowId
+ * @returns {number}
+ */
 export const getCanvasIndex = createSelector(
   [
     getWindow,
@@ -65,12 +80,12 @@ export const getCanvasIndex = createSelector(
 );
 
 /**
- * Returns the viewing hint for the first sequence in the manifest or the manifest
+ * Returns the viewing hint for the first sequence in the manifest or the manifest.
  * @param {object} state
  * @param {object} props
  * @param {string} props.manifestId
  * @param {string} props.windowId
- * @return {Number}
+ * @returns {number}
  */
 export const getSequenceViewingHint = createSelector(
   [getSequence, getManifestoInstance],
@@ -83,7 +98,11 @@ export const getSequenceViewingHint = createSelector(
   },
 );
 
-/** */
+/**
+ * @param {object} state
+ * @param {string} windowId
+ * @return {string|null}
+ */
 export const getSequenceViewingDirection = createSelector(
   [getWindow, getSequence, getManifestoInstance],
   (window, sequence, manifest) => {
@@ -97,11 +116,11 @@ export const getSequenceViewingDirection = createSelector(
 
 /**
  * Returns the behaviors viewing hint for the manifest
- * @param {object} state
- * @param {object} props
+ * @param {Object} state
+ * @param {Object} props
  * @param {string} props.manifestId
  * @param {string} props.windowId
- * @return {Number}
+ * @return {number}
  */
 export const getSequenceBehaviors = createSelector(
   [getSequence, getManifestoInstance],
@@ -122,7 +141,13 @@ export const getSequenceBehaviors = createSelector(
   },
 );
 
-/** */
+/**
+ * Retruns a sequence tree structure.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.windowId
+ * @returns {object}
+ */
 export const getSequenceTreeStructure = createSelector(
   [getSequence, getManifestoInstance],
   (sequence, manifest) => {
diff --git a/src/state/selectors/utils.js b/src/state/selectors/utils.js
index 22de1e7913643e2dd12f2cf1637ae9cc9c79aa11..8f494c03aaf5e201ff3c675e795d07e90c631ed7 100644
--- a/src/state/selectors/utils.js
+++ b/src/state/selectors/utils.js
@@ -1,6 +1,11 @@
 import settings from '../../config/settings';
 
-/** */
+/**
+ * Returns a slice of the mirador redux state based on settings.
+ * Otherwise the entire Redux state is returned.
+ * @param {object} state
+ * @returns {object}
+ */
 export function miradorSlice(state) {
   if (settings.state.slice) return state[settings.state.slice];
 
diff --git a/src/state/selectors/viewer.js b/src/state/selectors/viewer.js
index 27b5992553d5d5f2d739f35d2f863c90d46e99d7..2c573572065966ed8f0ecf5be29646f6f549b93d 100644
--- a/src/state/selectors/viewer.js
+++ b/src/state/selectors/viewer.js
@@ -5,10 +5,13 @@ import { getVisibleCanvases } from './canvases';
 import { getLayersForVisibleCanvases } from './layers';
 import { getSequenceViewingDirection } from './sequences';
 
-/** Instantiate a manifesto instance */
+/**
+ *  Instantiate a manifesto instance.
+ * @param {object} state
+ * @param {string} windowId
+ * @return {object}
+ */
 export const getCurrentCanvasWorld = createSelector(
-  getVisibleCanvases,
-  getLayersForVisibleCanvases,
-  getSequenceViewingDirection,
+  [getVisibleCanvases, getLayersForVisibleCanvases, getSequenceViewingDirection],
   (canvases, layers, viewingDirection) => new CanvasWorld(canvases, layers, viewingDirection),
 );
diff --git a/src/state/selectors/windows.js b/src/state/selectors/windows.js
index d2a5434f1bedc640db336374df4eb76dd1457efa..3e3ade3fba0b7ce92b7e4485ce2e93da5c735906 100644
--- a/src/state/selectors/windows.js
+++ b/src/state/selectors/windows.js
@@ -7,16 +7,21 @@ import { getWindows, getWindow, getWindowIds } from './getters';
 import { getWorkspaceType } from './workspace';
 import { getSequenceViewingHint, getSequenceBehaviors } from './sequences';
 
-/** */
+/**
+ * Returns the window configuration based.
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {object}
+ */
 export const getWindowConfig = createSelector(
   [getConfig, getWindow],
   ({ window: defaultConfig }, windowConfig = {}) => ({ ...defaultConfig, ...windowConfig }),
 );
 
 /**
- * Return the manifest titles for all open windows
+ * Returns the manifest titles for all open windows.
  * @param {object} state
- * @return {object}
+ * @returns {object}
  */
 export function getWindowTitles(state) {
   const result = {};
@@ -28,7 +33,11 @@ export function getWindowTitles(state) {
   return result;
 }
 
-/** */
+/**
+ * Returns an array containing the maximized windowIds.
+ * @param {object} state
+ * @return {Array}
+ */
 export const getMaximizedWindowsIds = createSelector(
   [getWindows],
   windows => Object.values(windows)
@@ -36,13 +45,14 @@ export const getMaximizedWindowsIds = createSelector(
     .map(window => window.id),
 );
 
-/** Return type of view in a certain window.
-* @param {object} state
-* @param {object} props
-* @param {string} props.manifestId
-* @param {string} props.windowId
-* @param {String}
-*/
+/**
+ * Returns type of view in a certain window.
+ * @param {object} state
+ * @param {object} props
+ * @param {string} props.manifestId
+ * @param {string} props.windowId
+ * @param {string}
+ */
 export const getWindowViewType = createSelector(
   [
     getWindow,
@@ -62,7 +72,12 @@ export const getWindowViewType = createSelector(
   },
 );
 
-/** */
+/**
+ * Returns the window view type for a given window.
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {string} 'single' | 'book' | 'scroll' | 'gallery'
+ */
 export const getAllowedWindowViewTypes = createSelector(
   [
     getSequenceViewingHint,
@@ -83,10 +98,10 @@ export const getAllowedWindowViewTypes = createSelector(
 );
 
 /**
- * Returns the draggability of a window
+ * Return the draggability of a window.
  * @param {object} state
  * @param {object} props
- * @return {Boolean}
+ * @returns {boolean}
  */
 export const getWindowDraggability = createSelector(
   [
diff --git a/src/state/selectors/workspace.js b/src/state/selectors/workspace.js
index 8a7a05dbec8cf113777639638dcad6c8687694ed..6ac19e2154e694f5187d8fb47a4d8e311d46f6f4 100644
--- a/src/state/selectors/workspace.js
+++ b/src/state/selectors/workspace.js
@@ -2,18 +2,29 @@ import { createSelector } from 'reselect';
 import { getWorkspace } from './getters';
 import { miradorSlice } from './utils';
 
-/** */
+/**
+ * Returns the elastic layout from the state.
+ * @param {object} state
+ * @returns {Object}
+ */
 export function getElasticLayout(state) {
   return miradorSlice(state).elasticLayout;
 }
 
+/**
+ * Returns if fullscreen is enabled.
+ * @param {object} state
+ * @returns {boolean}
+ */
 export const getFullScreenEnabled = createSelector(
   [getWorkspace],
   workspace => workspace.isFullscreenEnabled,
 );
 
-/** Returns the latest error from the state
+/**
+ * Returns the latest error from the state.
  * @param {object} state
+ * @returns {object|undefined}
  */
 export function getLatestError(state) {
   const [errorId] = miradorSlice(state).errors.items;
@@ -21,17 +32,32 @@ export function getLatestError(state) {
   return miradorSlice(state).errors[errorId];
 }
 
+/**
+ * Returns the type of the workspace.
+ * @param {Object} state
+ * @returns {string} 'mosaic' | 'elastic'
+ */
 export const getWorkspaceType = createSelector(
   [getWorkspace],
   ({ type }) => type,
 );
 
+/**
+ * Returns the ID of the focused window.
+ * @param {object} state
+ * @returns {string|undefined}
+ */
 export const getFocusedWindowId = createSelector(
   [getWorkspace],
   ({ focusedWindowId }) => focusedWindowId,
 );
 
-/** Check if the current window is focused */
+/**
+ * Returns if a given window is focused.
+ * @param {object} state
+ * @param {string} windowId
+ * @returns {boolean}
+ */
 export const isFocused = (state, { windowId }) => (
   getFocusedWindowId(state) === windowId
 );