Commit 5011915f authored by Kyle Keating's avatar Kyle Keating Committed by Kyle Keating

Improve typechecking on remaining components

- hypothesis-app
- search-input
- top-bar
- index (root sidebar index)
parent 0fd7c6e2
......@@ -20,14 +20,21 @@ import StreamContent from './stream-content';
import ToastMessages from './toast-messages';
import TopBar from './top-bar';
/**
* @typedef {import('../../types/api').Profile} Profile
* @typedef {import('../services/service-url').ServiceUrlGetter} ServiceUrlGetter
* @typedef {import('../../types/config').MergedConfig} MergedConfig
* @typedef {import('../../shared/bridge').default} Bridge
*/
/**
* Return the user's authentication status from their profile.
*
* @param {Profile} profile - The profile object from the API.
*/
function authStateFromProfile(profile) {
if (profile.userid) {
const parsed = parseAccountID(profile.userid);
if (parsed) {
let displayName = parsed.username;
if (profile.user_info && profile.user_info.display_name) {
displayName = profile.user_info.display_name;
......@@ -44,11 +51,23 @@ function authStateFromProfile(profile) {
}
}
/**
* @typedef HypothesisAppProps
* @prop {Object} [auth]
* @prop {Bridge} [bridge]
* @prop {ServiceUrlGetter} [serviceUrl]
* @prop {MergedConfig} [settings]
* @prop {Object} [session]
* @prop {Object} [toastMessenger]
*/
/**
* The root component for the Hypothesis client.
*
* This handles login/logout actions and renders the top navigation bar
* and content appropriate for the current route.
*
* @param {HypothesisAppProps} props
*/
function HypothesisApp({
auth,
......@@ -198,7 +217,6 @@ function HypothesisApp({
}
HypothesisApp.propTypes = {
// Injected.
auth: propTypes.object,
bridge: propTypes.object,
serviceUrl: propTypes.func,
......
......@@ -13,7 +13,7 @@ import Spinner from './spinner';
* @prop {boolean} [alwaysExpanded] -
* If true, the input field is always shown. If false, the input field is only shown
* if the query is non-empty.
* @prop {string} [query] - The currently active filter query
* @prop {string|null} query - The currently active filter query
* @prop {(value: string) => any} [onSearch] -
* Callback to invoke when the current filter query changes
*/
......@@ -73,7 +73,7 @@ export default function SearchInput({ alwaysExpanded, query, onSearch }) {
placeholder={(isLoading && 'Loading…') || 'Search…'}
disabled={isLoading}
ref={input}
value={pendingQuery}
value={pendingQuery || ''}
onInput={e =>
setPendingQuery(/** @type {HTMLInputElement} */ (e.target).value)
}
......
......@@ -17,6 +17,24 @@ import SortMenu from './sort-menu';
import StreamSearchInput from './stream-search-input';
import UserMenu from './user-menu';
/**
* @typedef {import('../../types/config').MergedConfig} MergedConfig
* @typedef {import('../components/user-menu').AuthState} AuthState
* @typedef {import('../../shared/bridge').default} Bridge
*/
/**
* @typedef TopBarProps
* @prop {AuthState} [auth]
* @prop {Bridge} bridge
* @prop {boolean} [isSidebar] - Flag indicating whether the app is the sidebar or a top-level page.
* @prop {() => any} [onLogin] - Callback invoked when user clicks "Login" button.
* @prop {() => any} [onLogout] - Callback invoked when user clicks "Logout" action in account menu.
* @prop {() => any} [onSignUp] - Callback invoked when user clicks "Sign up" button.
* @prop {MergedConfig} [settings]
* @prop {Object} [streamer]
*/
/**
* The toolbar which appears at the top of the sidebar providing actions
* to switch groups, view account information, sort/filter annotations etc.
......@@ -57,8 +75,8 @@ function TopBar({
* help requests, fire a relevant event instead
*/
const requestHelp = () => {
const service = serviceConfig(settings) || {};
if (service.onHelpRequestProvided) {
const service = serviceConfig(settings);
if (service && service.onHelpRequestProvided) {
bridge.call(bridgeEvents.HELP_REQUESTED);
} else {
togglePanelFn(uiConstants.PANEL_HELP);
......@@ -127,7 +145,7 @@ function TopBar({
}`}
/>
)}
<SearchInput query={filterQuery} onSearch={setFilterQuery} />
<SearchInput query={filterQuery || null} onSearch={setFilterQuery} />
<SortMenu />
{showSharePageButton && (
<Button
......@@ -155,37 +173,18 @@ function TopBar({
}
TopBar.propTypes = {
/**
* Object containing current authentication status.
*/
auth: propTypes.shape({
status: propTypes.string.isRequired,
// Additional properties when user is logged in.
displayName: propTypes.string,
userid: propTypes.string,
username: propTypes.string,
}),
bridge: propTypes.object.isRequired,
/**
* Flag indicating whether the app is the sidebar or a top-level page.
*/
isSidebar: propTypes.bool,
/**
* Callback invoked when user clicks "Login" button.
*/
onLogin: propTypes.func,
/** Callback invoked when user clicks "Logout" action in account menu. */
onLogout: propTypes.func,
/** Callback invoked when user clicks "Sign up" button. */
onSignUp: propTypes.func,
// Services
settings: propTypes.object,
streamer: propTypes.object,
};
......
......@@ -13,7 +13,9 @@ import { fetchConfig } from './util/fetch-config';
import * as sentry from './util/sentry';
// Read settings rendered into sidebar app HTML by service/extension.
const appConfig = jsonConfigsFrom(document);
const appConfig = /** @type {import('../types/config').SidebarConfig} */ (jsonConfigsFrom(
document
));
if (appConfig.sentry) {
// Initialize Sentry. This is required at the top of this file
......@@ -182,7 +184,9 @@ function startApp(config) {
container.run(setupFrameSync);
// Render the UI.
const appEl = document.querySelector('hypothesis-app');
const appEl = /** @type {HTMLElement} */ (document.querySelector(
'hypothesis-app'
));
render(
<ServiceContext.Provider value={container}>
<HypothesisApp />
......
......@@ -88,10 +88,11 @@ function closeSidebarPanel(panelName) {
/**
* Toggle a sidebar panel from its current state, or set it to the
* designated `panelState`
* designated `panelState`.
*
* @param {string} panelName
* @param {boolean} panelState - Should the panel be active?
* @param {boolean} [panelState] -
* Should the panel be active? Omit this prop to simply toggle the value.
*/
function toggleSidebarPanel(panelName, panelState) {
return {
......
......@@ -15,6 +15,12 @@
import { createContext, createElement } from 'preact';
import { useContext } from 'preact/hooks';
/**
* @typedef ServiceProvider
* @prop {(serviceName: string) => any} get
*/
/** @type {ServiceProvider} */
const fallbackInjector = {
get(service) {
throw new Error(
......
......@@ -18,14 +18,5 @@
"**/test/**/*.js",
"test-util/**/*.js",
"karma.config.js",
// Enable this once the rest of `src/sidebar` is checked.
"sidebar/index.js",
// Files in `src/sidebar/components` that may still have errors.
// Remove them from this list as they are resolved.
"sidebar/components/hooks/use-root-thread.js",
"sidebar/components/hypothesis-app.js",
"sidebar/components/top-bar.js"
]
}
......@@ -84,13 +84,15 @@
*/
/**
* TODO - Fill out remaining properties
*
* @typedef Profile
* @prop {string|null} userid
* @prop {Object} preferences
* @prop {boolean} [preferences.show_sidebar_tutorial]
* @prop {Object.<string, boolean>} features
* @prop {Object} [user_info]
* @prop {string|null} user_info.display_name
*
* @prop {unknown} [groups] - Deprecated.
*/
/**
......
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