Unverified Commit 86382c83 authored by Kyle Keating's avatar Kyle Keating Committed by GitHub

Improve typechecking for util/ and services/ (#2315)

parent 360d308a
......@@ -34,7 +34,6 @@ export const events = {
* behavior across different environments in which the client is used.
*
* @param {Window} win
* @param {Object} settings - Settings rendered into sidebar HTML
* @return {string}
*/
function clientType(win, settings = {}) {
......@@ -65,9 +64,18 @@ function clientType(win, settings = {}) {
return type;
}
/**
* @typedef Analytics
* @prop {() => any} sendPageView
* @prop {(action: string, label?: string, value?: number) => void} track
* @prop {Object.<string, string>} events
*/
/**
* Analytics service for tracking page views and user interactions with the
* application.
* @param {Window} $window - Test seam
* @return {Analytics}
*/
// @ngInject
export default function analytics($window, settings) {
......@@ -78,6 +86,7 @@ export default function analytics($window, settings) {
// is replaced when analytics.js fully loads.
//
// See https://developers.google.com/analytics/devguides/collection/analyticsjs/command-queue-reference
// @ts-ignore The window interface needs to be expanded to include this property
const commandQueue = () => $window.ga || noop;
return {
......
/** @typedef {import('../../types/api').Annotation} Annotation */
/** @typedef {import('../../types/annotator').AnnotationData} AnnotationData */
/**
* A service for creating, manipulating and persisting annotations and their
* application-store representations. Interacts with API services as needed.
......@@ -17,6 +20,8 @@ export default function annotationsService(api, store) {
/**
* Apply changes for the given `annotation` from its draft in the store (if
* any) and return a new object with those changes integrated.
*
* @param {Annotation} annotation
*/
function applyDraftChanges(annotation) {
const changes = {};
......@@ -36,6 +41,10 @@ export default function annotationsService(api, store) {
/**
* Extend new annotation objects with defaults and permissions.
*
* @param {AnnotationData} annotationData
* @param {Date} now
* @return {Annotation}
*/
function initialize(annotationData, now = new Date()) {
const defaultPrivacy = store.getDefault('annotationPrivacy');
......@@ -50,27 +59,29 @@ export default function annotationsService(api, store) {
// as it has not been persisted to the service.
const $tag = generateHexString(8);
let permissions = defaultPermissions(userid, groupid, defaultPrivacy);
// Highlights are peculiar in that they always have private permissions
if (metadata.isHighlight(annotationData)) {
permissions = privatePermissions(userid);
}
return Object.assign(
/** @type {Annotation} */
const annotation = Object.assign(
{
created: now.toISOString(),
group: groupid,
permissions,
permissions: defaultPermissions(userid, groupid, defaultPrivacy),
tags: [],
text: '',
updated: now.toISOString(),
user: userid,
user_info: userInfo,
$tag: $tag,
hidden: false,
links: {},
},
annotationData
);
// Highlights are peculiar in that they always have private permissions
if (metadata.isHighlight(annotation)) {
annotation.permissions = privatePermissions(userid);
}
return annotation;
}
/**
......
......@@ -19,6 +19,8 @@ function translateResponseToError(response, data) {
/**
* Return a shallow clone of `obj` with all client-only properties removed.
* Client-only properties are marked by a '$' prefix.
*
* @param {Object} obj
*/
function stripInternalProperties(obj) {
const result = {};
......
......@@ -7,6 +7,7 @@
* @throws {Error}
* This function may throw an exception if the browser rejects the attempt
* to copy text.
* @param {string} text
*/
export function copyText(text) {
const temp = document.createElement('pre');
......
......@@ -31,7 +31,7 @@ export function getElementHeightWithMargins(element) {
* @param {string[]} events
* @param {EventListener} listener
* @param {Object} options
* @param {boolean} [options.useCapture]
* @param {boolean} [options.useCapture]
* @return {function} Function which removes the event listeners.
*/
export function listen(element, events, listener, { useCapture = false } = {}) {
......
......@@ -9,7 +9,3 @@
export function orgName(group) {
return group.organization && group.organization.name;
}
export function trackViewGroupActivity(analytics) {
analytics.track(analytics.events.GROUP_VIEW_ACTIVITY);
}
import { events } from '../../services/analytics';
import * as groupListItemCommon from '../group-list-item-common';
describe('sidebar/util/groupListItemCommon', () => {
describe('trackViewGroupActivity', () => {
it('triggers the GROUP_VIEW_ACTIVITY event when called', () => {
const fakeAnalytics = {
track: sinon.stub(),
events,
};
groupListItemCommon.trackViewGroupActivity(fakeAnalytics);
assert.calledWith(
fakeAnalytics.track,
fakeAnalytics.events.GROUP_VIEW_ACTIVITY
);
});
});
describe('orgName', () => {
it('returns the organization name if it exists', () => {
const fakeGroup = { id: 'groupid', organization: { name: 'org' } };
......
......@@ -27,6 +27,7 @@
"sidebar/store/create-store.js",
"sidebar/store/debug-middleware.js",
"sidebar/store/use-store.js",
"sidebar/store/utils.js",
],
"exclude": [
......
/**
* Type definitions for objects passed between the annotator and sidebar.
*/
/** @typedef {import("./api").Target} Target */
/**
* @typedef AnnotationData
* @prop {string} uri
* @prop {Target[]} target
* @prop {string} $tag
* @prop {boolean} [$highlight]
* @prop {DocumentMetadata} document
*/
/**
* @typedef DocumentMetadata
* @prop {string} title
* @prop {Object[]} link
* @prop {string} link.rel
* @prop {string} link.type
* // html pages
* @prop {Object.<string, string[]>} [dc]
* @prop {Object.<string, string[]>} [eprints]
* @prop {Object.<string, string[]>} [facebook]
* @prop {Object.<string, string[]>} [highwire]
* @prop {Object.<string, string[]>} [prism]
* @prop {Object.<string, string[]>} [twitter]
* // pdf files
* @prop {string} [documentFingerprint]
*/
// Make TypeScript treat this file as a module.
export const unused = {};
......@@ -33,6 +33,12 @@
* @typedef {TextQuoteSelector | TextPositionSelector | RangeSelector} Selector
*/
/**
* @typedef Target
* @prop {string} source
* @prop {Selector[]} [selector]
*
/**
* TODO - Fill out remaining properties
*
......@@ -58,9 +64,7 @@
* @prop {string[]} permissions.update
* @prop {string[]} permissions.delete
*
* @prop {Object[]} target
* @prop {string} target.source
* @prop {Selector[]} [target.selector]
* @prop {Target[]} target
*
* @prop {Object} [moderation]
* @prop {number} moderation.flagCount
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment