Commit 4b7c566d authored by Robert Knight's avatar Robert Knight

Update export style and documentation for sidebar store

Convert the `store/index.js` and `store/create-store.js` modules to use
named rather than default exports per our current conventions, and
revise the documentation.

The goal of the revised documentation is to more clearly describe what the
store is and how it is used within the sidebar/notebook app, assuming
that the reader is likely have at least some familiarity with Redux (or
can read the "Introduction" section of the linked website if not). In
particular I have tried to convey:

 - The separation between the "base" `createStore` function which is not
   application-specific and the `createSidebarStore` function and the
   modules it uses which are
 - How the store in the sidebar app differs from a standard/base Redux store
 - Best practices around using the store from other parts of the app
   (use `useStoreProxy` in UI components, use selector and action
   methods rather than `getState` and `dispatch`)
parent 40eaf5c9
......@@ -131,7 +131,7 @@ import { ThreadsService } from './services/threads';
import { ToastMessengerService } from './services/toast-messenger';
// Redux store.
import store from './store';
import { createSidebarStore } from './store';
// Utilities.
import { Injector } from '../shared/injector';
......@@ -167,7 +167,7 @@ function startApp(config, appEl) {
.register('tags', TagsService)
.register('threadsService', ThreadsService)
.register('toastMessenger', ToastMessengerService)
.register('store', store);
.register('store', { factory: createSidebarStore });
// Register utility values/classes.
//
......
......@@ -75,7 +75,8 @@ import { createReducer, bindSelectors } from './util';
*/
/**
* Redux store augmented with methods to dispatch actions and select state.
* Redux store augmented with selector methods to query specific state and
* action methods that dispatch specific actions.
*
* @template {object} Actions
* @template {object} Selectors
......@@ -97,16 +98,25 @@ import { createReducer, bindSelectors } from './util';
* - The _selectors_ for reading that state or computing things
* from that state.
*
* On top of the standard Redux store methods, the returned store also exposes
* each action and selector from the input modules as a method which operates on
* the store.
* In addition to the standard Redux store interface, the returned store also exposes
* each action and selector from the input modules as a method. For example, if
* a store is created from a module that has a `getWidget(<id>)` selector and
* an `addWidget(<object>)` action, a consumer would use `store.getWidget(<id>)`
* to fetch an item and `store.addWidget(<object>)` to dispatch an action that
* adds an item. External consumers of the store should in most cases use these
* selector and action methods rather than `getState` or `dispatch`. This
* makes it easier to refactor the internal state structure.
*
* Preact UI components access stores via the `useStoreProxy` hook defined in
* `use-store.js`. This returns a proxy which enables UI components to observe
* what store state a component depends upon and re-render when it changes.
*
* @param {Module<any,any,any,any>[]} modules
* @param {any[]} [initArgs] - Arguments to pass to each state module's `init` function
* @param {any[]} [middleware] - List of additional Redux middlewares to use
* @return Store<any,any,any>
*/
export default function createStore(modules, initArgs = [], middleware = []) {
export function createStore(modules, initArgs = [], middleware = []) {
// Create the initial state and state update function.
// Namespaced objects for initial states.
......
/**
* Central store of state for the sidebar application, managed using
* [Redux](http://redux.js.org/).
*
* State management in Redux apps work as follows:
*
* 1. All important application state is stored in a single, immutable object.
* 2. The user interface is a presentation of this state. Interaction with the
* UI triggers updates by creating `actions`.
* 3. Actions are plain JS objects which describe some event that happened in
* the application. Updates happen by passing actions to a `reducer`
* function which takes the current application state, the action and
* returns the new application state.
*
* The process of updating the app state using an action is known as
* 'dispatching' the action.
* 4. Other parts of the app can subscribe to changes in the app state.
* This is used to to update the UI etc.
*
* "middleware" functions can wrap the dispatch process in order to implement
* logging, trigger side effects etc.
*
* Tests for a given action consist of:
*
* 1. Checking that the UI (or other event source) dispatches the correct
* action when something happens.
* 2. Checking that given an initial state, and an action, a reducer returns
* the correct resulting state.
* 3. Checking that the UI correctly presents a given state.
*/
import createStore from './create-store';
import { createStore } from './create-store';
import debugMiddleware from './debug-middleware';
import activity from './modules/activity';
import annotations from './modules/annotations';
......@@ -74,18 +43,19 @@ import viewer from './modules/viewer';
*/
/**
* Factory which creates the sidebar app's state store.
* Create the central state store for the sidebar application.
*
* This is a Redux [1] store composed of several modules, augmented with
* _selector_ methods for querying it and _action_ methods for applying updates.
* See the `createStore` documentation for API and usage details.
*
* Returns a Redux store augmented with methods for each action and selector in
* the individual state modules. ie. `store.actionName(args)` dispatches an
* action through the store and `store.selectorName(args)` invokes a selector
* passing the current state of the store.
* [1] https://redux.js.org
*
* @param {import('../../types/config').SidebarConfig} settings
* @return {SidebarStore}
* @inject
*/
// @inject
export default function store(settings) {
export function createSidebarStore(settings) {
const middleware = [debugMiddleware];
const modules = [
......
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import activity from '../activity';
describe('sidebar/store/modules/activity', () => {
......
import * as fixtures from '../../../test/annotation-fixtures';
import * as metadata from '../../../helpers/annotation-metadata';
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import annotations from '../annotations';
import route from '../route';
......
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import defaults from '../defaults';
describe('store/modules/defaults', function () {
......
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import directLinked from '../direct-linked';
describe('sidebar/store/modules/direct-linked', () => {
......
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import annotations from '../annotations';
import drafts from '../drafts';
import { Draft } from '../drafts';
......
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import filters from '../filters';
import selection from '../selection';
......
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import frames from '../frames';
describe('sidebar/store/modules/frames', function () {
......
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import groups from '../groups';
import session from '../session';
import immutable from '../../../util/immutable';
......
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import links from '../links';
describe('sidebar/store/modules/links', () => {
......
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import annotations from '../annotations';
import groups from '../groups';
import realTimeUpdates from '../real-time-updates';
......
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import route from '../route';
describe('store/modules/route', () => {
......
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import annotations from '../annotations';
import filters from '../filters';
import selection from '../selection';
......
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import session from '../session';
describe('sidebar/store/modules/session', () => {
......
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import sidebarPanels from '../sidebar-panels';
describe('sidebar/store/modules/sidebar-panels', () => {
......
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import toastMessages from '../toast-messages';
describe('store/modules/toast-messages', function () {
......
import createStore from '../../create-store';
import { createStore } from '../../create-store';
import viewer from '../viewer';
describe('store/modules/viewer', function () {
......
/* global process */
import createStore from '../create-store';
import { createStore } from '../create-store';
const A = 0;
......@@ -73,7 +73,7 @@ function counterStore(initArgs = [], middleware = []) {
return createStore(modules, initArgs, middleware);
}
describe('sidebar/store/create-store', () => {
describe('createStore', () => {
it('returns a working Redux store', () => {
const store = counterStore();
assert.equal(store.getState().a.count, 0);
......
import * as annotationFixtures from '../../test/annotation-fixtures';
import storeFactory from '../index';
import { createSidebarStore } from '../index';
import immutable from '../../util/immutable';
const defaultAnnotation = annotationFixtures.defaultAnnotation;
......@@ -16,7 +16,7 @@ const fixtures = immutable({
],
});
describe('store', function () {
describe('createSidebarStore', function () {
let store;
function tagForID(id) {
......@@ -28,7 +28,7 @@ describe('store', function () {
}
beforeEach(function () {
store = storeFactory({});
store = createSidebarStore({});
});
describe('initialization', function () {
......@@ -38,12 +38,12 @@ describe('store', function () {
});
it('sets the selection when settings.annotations is set', function () {
store = storeFactory({ annotations: 'testid' });
store = createSidebarStore({ annotations: 'testid' });
assert.deepEqual(store.selectedAnnotations(), ['testid']);
});
it('expands the selected annotations when settings.annotations is set', function () {
store = storeFactory({ annotations: 'testid' });
store = createSidebarStore({ annotations: 'testid' });
assert.deepEqual(store.expandedMap(), {
testid: true,
});
......
import { mount } from 'enzyme';
import { act } from 'preact/test-utils';
import createStore from '../create-store';
import { createStore } from '../create-store';
import { useStoreProxy, $imports } from '../use-store';
// Store module for use with `createStore` in tests.
......
......@@ -3,7 +3,7 @@ import { useReducer } from 'preact/hooks';
import { act } from 'preact/test-utils';
import { Injector } from '../../../shared/injector';
import storeFactory from '../../store';
import { createSidebarStore } from '../../store';
import { ServiceContext } from '../../service-context';
import useRootThread from '../../components/hooks/use-root-thread';
......@@ -46,7 +46,7 @@ describe('integration: annotation threading', () => {
beforeEach(function () {
const container = new Injector()
.register('store', storeFactory)
.register('store', { factory: createSidebarStore })
.register('annotationsService', () => {})
.register('settings', { value: {} });
......
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