Commit e61711b9 authored by Robert Knight's avatar Robert Knight

Add store module for groups

Add a Redux module that stores state related to groups, including the
currently focused group and the list of all known groups.

This is an initial step towards moving this state from the groups
service into the store, which will make reacting to changes in the set
of loaded groups, or the focused group, easier.
parent e160c23f
......@@ -25,7 +25,7 @@ const { createReducer, bindSelectors } = require('./util');
* @param {any[]} initArgs - Arguments to pass to each state module's `init` function
* @param [any[]] middleware - List of additional Redux middlewares to use.
*/
function createStore(modules, initArgs, middleware = []) {
function createStore(modules, initArgs = [], middleware = []) {
// Create the initial state and state update function.
const initialState = Object.assign({}, ...modules.map(m => m.init(...initArgs)));
const reducer = createReducer(...modules.map(m => m.update));
......
......@@ -37,6 +37,7 @@ var debugMiddleware = require('./debug-middleware');
var annotations = require('./modules/annotations');
var frames = require('./modules/frames');
var links = require('./modules/links');
var groups = require('./modules/groups');
var selection= require('./modules/selection');
var session = require('./modules/session');
var viewer = require('./modules/viewer');
......@@ -85,6 +86,7 @@ function store($rootScope, settings) {
annotations,
frames,
links,
groups,
selection,
session,
viewer,
......
'use strict';
const util = require('../util');
function init() {
return {
/**
* List of groups.
* @type {Group[]}
*/
groups: [],
/**
* ID of currently selected group.
* @type {string|null}
*/
focusedGroup: null,
};
}
const update = {
FOCUS_GROUP(state, action) {
const g = state.groups.find(g => g.id === action.id);
return { focusedGroup: g ? action.id : null };
},
LOAD_GROUPS(state, action) {
const groups = action.groups;
let focusedGroup = state.focusedGroup;
// Reset focused group if not in the new set of groups.
if (state.focusedGroup === null || !groups.find(g => g.id === state.focusedGroup)) {
if (groups.length > 0) {
focusedGroup = groups[0].id;
} else {
focusedGroup = null;
}
}
return {
focusedGroup,
groups: action.groups,
};
},
};
const actions = util.actionTypes(update);
/**
* Set the current focused group.
*
* @param {string} id
*/
function focusGroup(id) {
return {
type: actions.FOCUS_GROUP,
id,
};
}
/**
* Update the set of loaded groups.
*
* @param {Group[]} groups
*/
function loadGroups(groups) {
return {
type: actions.LOAD_GROUPS,
groups,
};
}
/**
* Return the currently focused group.
*
* @return {Group|null}
*/
function focusedGroup(state) {
if (!state.focusedGroup) {
return null;
}
return getGroup(state, state.focusedGroup);
}
/**
* Return the list of all groups.
*
* @return {Group[]}
*/
function allGroups(state) {
return state.groups;
}
/**
* Return the group with the given ID.
*
* @return {Group|undefined}
*/
function getGroup(state, id) {
return state.groups.find(g => g.id === id);
}
module.exports = {
init,
update,
actions: {
focusGroup,
loadGroups,
},
selectors: {
allGroups,
getGroup,
focusedGroup,
},
};
'use strict';
const createStore = require('../../create-store');
const groups = require('../groups');
describe('sidebar.store.modules.groups', () => {
const publicGroup = {
id: '__world__',
name: 'Public',
};
const privateGroup = {
id: 'foo',
name: 'Private',
};
let store;
beforeEach(() => {
store = createStore([groups]);
});
describe('focusGroup', () => {
it('updates the focused group if valid', () => {
store.loadGroups([publicGroup]);
store.focusGroup(publicGroup.id);
assert.equal(store.getState().focusedGroup, publicGroup.id);
});
it('does not set the focused group if invalid', () => {
store.loadGroups([publicGroup]);
store.focusGroup(privateGroup.id);
assert.equal(store.getState().focusedGroup, null);
});
});
describe('loadGroups', () => {
it('updates the set of groups', () => {
store.loadGroups([publicGroup]);
assert.deepEqual(store.getState().groups, [publicGroup]);
});
it('resets the focused group if not in new set of groups', () => {
store.loadGroups([publicGroup]);
store.focusGroup(publicGroup.id);
store.loadGroups([]);
assert.equal(store.getState().focusedGroup, null);
});
it('leaves focused group unchanged if in new set of groups', () => {
store.loadGroups([publicGroup]);
store.focusGroup(publicGroup.id);
store.loadGroups([publicGroup, privateGroup]);
assert.equal(store.getState().focusedGroup, publicGroup.id);
});
});
describe('allGroups', () => {
it('returns all groups', () => {
store.loadGroups([publicGroup, privateGroup]);
assert.deepEqual(store.allGroups(), [publicGroup, privateGroup]);
});
});
describe('getGroup', () => {
it('returns the group with the given ID', () => {
store.loadGroups([publicGroup, privateGroup]);
assert.deepEqual(store.getGroup(privateGroup.id), privateGroup);
});
});
describe('focusedGroup', () => {
it('returns `null` if no group is focused', () => {
assert.equal(store.focusedGroup(), null);
});
it('returns the focused group if a group has been focused', () => {
store.loadGroups([privateGroup]);
store.focusGroup(privateGroup.id);
assert.deepEqual(store.focusedGroup(), privateGroup);
});
});
});
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