Unverified Commit 1b2cd85e authored by Robert Knight's avatar Robert Knight Committed by GitHub

Merge pull request #2268 from hypothesis/add-config-types

Begin to add JSDoc types for client configuration objects
parents 487a1b43 2ae8d153
...@@ -6,44 +6,13 @@ import { ...@@ -6,44 +6,13 @@ import {
toString, toString,
} from '../shared/type-coercions'; } from '../shared/type-coercions';
/** /** @typedef {import('../types/config').HostConfig} HostConfig */
* @typedef RequestConfigFromFrameOptions
* @prop {number} ancestorLevel
* @prop {string} origin
*/
/**
* Configuration for the client provided by the frame embedding it.
*
* User-facing documentation exists at
* https://h.readthedocs.io/projects/client/en/latest/publishers/config/
*
* @typedef Config
* @prop {string} [annotations] - Direct-linked annotation ID
* @prop {string} [group] - Direct-linked group ID
* @prop {string} [query] - Initial filter query
* @prop {string} [appType] - Method used to load the client
* @prop {boolean} [openSidebar] - Whether to open the sidebar on the initial load
* @prop {boolean} [showHighlights] - Whether to show highlights
* @prop {Object[]} [services] -
* Configuration for the annotation services that the client connects to
* @prop {Object} [branding] -
* Theme properties (fonts, colors etc.)
* @prop {boolean} [enableExperimentalNewNoteButton] -
* Whether to show the "New note" button on the "Page Notes" tab
* @prop {RequestConfigFromFrameOptions|string} [requestConfigFromFrame]
* Origin of the ancestor frame to request configuration from
* @prop {string} [theme]
* Name of the base theme to use.
* @prop {string} [usernameUrl]
* URL template for username links
*/
/** /**
* Return the app configuration specified by the frame embedding the Hypothesis * Return the app configuration specified by the frame embedding the Hypothesis
* client. * client.
* *
* @return {Config} * @return {HostConfig}
*/ */
export default function hostPageConfig(window) { export default function hostPageConfig(window) {
const configStr = window.location.hash.slice(1); const configStr = window.location.hash.slice(1);
......
/**
* @typedef {import('../types/config').HostConfig} HostConfig
* @typedef {import('../types/config').Service} Service
*/
/** /**
* Return the configuration for the annotation service which the client would retrieve * Return the configuration for the annotation service which the client would retrieve
* annotations from which may contain the authority, grantToken and icon. * annotations from which may contain the authority, grantToken and icon.
* *
* @param {Object} settings - The settings object which would contain the services array. * @param {HostConfig} settings
* @return {Service|null}
*/ */
export default function serviceConfig(settings) { export default function serviceConfig(settings) {
......
/**
* @typedef {import('../../types/config').HostConfig} HostConfig
*/
import serviceConfig from '../service-config'; import serviceConfig from '../service-config';
/** /**
* Is the sharing of annotations enabled? Check for any defined `serviceConfig`, * Is the sharing of annotations enabled? Check for any defined `serviceConfig`,
* but default to `true` if none found. * but default to `true` if none found.
* *
* @param {object} settings * @param {HostConfig} settings
* @return {boolean} * @return {boolean}
*/ */
export function sharingEnabled(settings) { export function sharingEnabled(settings) {
...@@ -35,7 +39,7 @@ export function shareURI(annotation) { ...@@ -35,7 +39,7 @@ export function shareURI(annotation) {
* and the annotation itself needs to have a sharing URI. * and the annotation itself needs to have a sharing URI.
* *
* @param {object} annotation * @param {object} annotation
* @param {object} settings * @param {HostConfig} settings
* @return {boolean} * @return {boolean}
*/ */
export function isShareable(annotation, settings) { export function isShareable(annotation, settings) {
......
...@@ -2,6 +2,11 @@ import getApiUrl from '../get-api-url'; ...@@ -2,6 +2,11 @@ import getApiUrl from '../get-api-url';
import hostConfig from '../host-config'; import hostConfig from '../host-config';
import * as postMessageJsonRpc from './postmessage-json-rpc'; import * as postMessageJsonRpc from './postmessage-json-rpc';
/**
* @typedef {import('../../types/config').SidebarConfig} SidebarConfig
* @typedef {import('../../types/config').MergedConfig} MergedConfig
*/
/** /**
* @deprecated * @deprecated
*/ */
...@@ -180,9 +185,9 @@ async function fetchGroupsAsync(config, rpcCall) { ...@@ -180,9 +185,9 @@ async function fetchGroupsAsync(config, rpcCall) {
* Legacy RPC with unknown parent - From a ancestor parent frame that passes it down via RPC. (deprecated) * Legacy RPC with unknown parent - From a ancestor parent frame that passes it down via RPC. (deprecated)
* RPC with known parent - From a ancestor parent frame that passes it down via RPC. * RPC with known parent - From a ancestor parent frame that passes it down via RPC.
* *
* @param {Object} appConfig - Settings rendered into `app.html` by the h service. * @param {SidebarConfig} appConfig
* @param {Window} window_ - Test seam. * @param {Window} window_ - Test seam.
* @return {Promise<Object>} - The merged settings. * @return {Promise<MergedConfig>} - The merged settings.
*/ */
export async function fetchConfig(appConfig, window_ = window) { export async function fetchConfig(appConfig, window_ = window) {
const hostPageConfig = hostConfig(window); const hostPageConfig = hostConfig(window);
......
/** @typedef {import('../../types/api').Group} Group */ /**
* @typedef {import('../../types/config').HostConfig} HostConfig
* @typedef {import('../../types/api').Group} Group
*/
import escapeStringRegexp from 'escape-string-regexp'; import escapeStringRegexp from 'escape-string-regexp';
import serviceConfig from '../service-config'; import serviceConfig from '../service-config';
...@@ -9,14 +12,15 @@ import serviceConfig from '../service-config'; ...@@ -9,14 +12,15 @@ import serviceConfig from '../service-config';
* explicitly disallowed in the service configuration of the * explicitly disallowed in the service configuration of the
* `settings` object. * `settings` object.
* *
* @param {object} settings * @param {HostConfig} settings
* @return {boolean} * @return {boolean}
*/ */
function allowLeavingGroups(settings) { function allowLeavingGroups(settings) {
const config = serviceConfig(settings) || {}; const config = serviceConfig(settings);
return typeof config.allowLeavingGroups === 'boolean' if (!config) {
? config.allowLeavingGroups return true;
: true; }
return !!config.allowLeavingGroups;
} }
/** /**
...@@ -28,7 +32,7 @@ function allowLeavingGroups(settings) { ...@@ -28,7 +32,7 @@ function allowLeavingGroups(settings) {
* @param {Group[]} userGroups - groups the user is a member of * @param {Group[]} userGroups - groups the user is a member of
* @param {Group[]} featuredGroups - all other groups, may include some duplicates from the userGroups * @param {Group[]} featuredGroups - all other groups, may include some duplicates from the userGroups
* @param {string} uri - uri of the current page * @param {string} uri - uri of the current page
* @param {object} settings - The settings object. * @param {HostConfig} settings
*/ */
export function combineGroups(userGroups, featuredGroups, uri, settings) { export function combineGroups(userGroups, featuredGroups, uri, settings) {
const worldGroup = featuredGroups.find(g => g.id === '__world__'); const worldGroup = featuredGroups.find(g => g.id === '__world__');
......
/**
* @typedef {import('../../types/config').MergedConfig} MergedConfig
*/
import serviceConfig from '../service-config'; import serviceConfig from '../service-config';
/** /**
...@@ -8,7 +12,7 @@ import serviceConfig from '../service-config'; ...@@ -8,7 +12,7 @@ import serviceConfig from '../service-config';
* *
* If no custom annotation services are configured then return `false`. * If no custom annotation services are configured then return `false`.
* *
* @param {Object} settings - the sidebar settings object * @param {MergedConfig} settings
* @return {boolean} * @return {boolean}
*/ */
export default function isThirdPartyService(settings) { export default function isThirdPartyService(settings) {
......
import serviceConfig from '../service-config'; import serviceConfig from '../service-config';
/** @typedef {import('../../types/api').Profile} Profile */ /**
* @typedef {import('../../types/config').HostConfig} HostConfig
* @typedef {import('../../types/api').Profile} Profile
*/
/** /**
* Returns true if the sidebar tutorial has to be shown to a user for a given session. * Returns true if the sidebar tutorial has to be shown to a user for a given session.
...@@ -28,14 +31,15 @@ export function shouldShowSidebarTutorial(sessionState) { ...@@ -28,14 +31,15 @@ export function shouldShowSidebarTutorial(sessionState) {
* *
* @param {boolean} isSidebar - is the app currently displayed in a sidebar? * @param {boolean} isSidebar - is the app currently displayed in a sidebar?
* @param {Profile} profile - User profile returned from the API * @param {Profile} profile - User profile returned from the API
* @param {Object} settings - app configuration/settings * @param {HostConfig} settings
* @return {boolean} - Tutorial panel should be displayed automatically * @return {boolean} - Tutorial panel should be displayed automatically
*/ */
export function shouldAutoDisplayTutorial(isSidebar, profile, settings) { export function shouldAutoDisplayTutorial(isSidebar, profile, settings) {
const shouldShowBasedOnProfile = const shouldShowBasedOnProfile =
typeof profile.preferences === 'object' && typeof profile.preferences === 'object' &&
!!profile.preferences.show_sidebar_tutorial; !!profile.preferences.show_sidebar_tutorial;
const service = serviceConfig(settings) || {};
const service = serviceConfig(settings) || { onHelpRequestProvided: false };
return ( return (
isSidebar && !service.onHelpRequestProvided && shouldShowBasedOnProfile isSidebar && !service.onHelpRequestProvided && shouldShowBasedOnProfile
); );
......
/**
* Configuration for an annotation service.
*
* See https://h.readthedocs.io/projects/client/en/latest/publishers/config/#cmdoption-arg-services
*
* The `onXXX` functions may be set by the embedder of the client. The
* `onXXXProvided` booleans are correspondingly set in the annotator if a
* particular function is provided.
*
* @typedef Service
* @prop {string} apiUrl
* @prop {string} authority
* @prop {string} grantToken
* @prop {boolean} [allowLeavingGroups]
* @prop {boolean} [enableShareLinks]
* @prop {Function} [onLoginRequest]
* @prop {boolean} [onLoginRequestProvided]
* @prop {Function} [onLogoutRequest]
* @prop {boolean} [onLogoutRequestProvided]
* @prop {Function} [onSignupRequest]
* @prop {boolean} [onSignupRequestProvided]
* @prop {Function} [onProfileRequest]
* @prop {boolean} [onProfileRequestProvided]
* @prop {Function} [onHelpRequest]
* @prop {boolean} [onHelpRequestProvided]
*/
/**
* Configuration for the Sentry crash-reporting service.
*
* @typedef SentryConfig
* @prop {string} dsn
* @prop {string} environment
*/
/**
* Configuration for the sidebar app set by the Hypothesis backend ("h")
* or baked into the sidebar app at build time (in the browser extension).
*
* See `h.views.client` in the "h" application.
*
* @typedef SidebarConfig
* @prop {string} apiUrl
* @prop {string} authDomain
* @prop {string} [googleAnalytics]
* @prop {string} oauthClientId
* @prop {string[]} rpcAllowedOrigins
* @prop {SentryConfig} [sentry]
* @prop {string} [websocketUrl]
*/
/**
* @typedef RequestConfigFromFrameOptions
* @prop {number} ancestorLevel
* @prop {string} origin
*/
/**
* Configuration set by the embedder of the client and used by the sidebar.
*
* This is the subset of keys from
* https://h.readthedocs.io/projects/client/en/latest/publishers/config/ which
* excludes any keys used only by the "annotator" part of the application.
*
* @typedef HostConfig
* @prop {string} [annotations] - Direct-linked annotation ID
* @prop {string} [group] - Direct-linked group ID
* @prop {string} [query] - Initial filter query
* @prop {string} [appType] - Method used to load the client
* @prop {boolean} [openSidebar] - Whether to open the sidebar on the initial load
* @prop {boolean} [showHighlights] - Whether to show highlights
* @prop {Object} [branding] -
* Theme properties (fonts, colors etc.)
* @prop {boolean} [enableExperimentalNewNoteButton] -
* Whether to show the "New note" button on the "Page Notes" tab
* @prop {RequestConfigFromFrameOptions|string} [requestConfigFromFrame]
* Origin of the ancestor frame to request configuration from
* @prop {Service[]} [services] -
* Configuration for the annotation services that the client connects to
* @prop {string} [theme]
* Name of the base theme to use.
* @prop {string} [usernameUrl]
* URL template for username links
*/
/**
* The `settings` object used in the sidebar app that is a result of merging
* (filtered and validated) configuration from the host page with configuration
* from h / the browser extension.
*
* @typedef {HostConfig & SidebarConfig} MergedConfig
*/
// Make TypeScript treat this file as a module.
export const unused = {};
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