Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
coopwire-hypothesis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
孙灵跃 Leon Sun
coopwire-hypothesis
Commits
e160c23f
Unverified
Commit
e160c23f
authored
Apr 06, 2018
by
Hannah Stepanek
Committed by
GitHub
Apr 06, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #700 from hypothesis/extract-create-store
Extract `createStore` helper out of `store` service
parents
fbfc20e0
1edd8b07
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
174 additions
and
144 deletions
+174
-144
create-store.js
src/sidebar/store/create-store.js
+53
-0
index.js
src/sidebar/store/index.js
+26
-58
index.js
src/sidebar/store/modules/index.js
+0
-52
create-store-test.js
src/sidebar/store/test/create-store-test.js
+95
-0
index-test.js
src/sidebar/store/test/index-test.js
+0
-34
No files found.
src/sidebar/store/create-store.js
0 → 100644
View file @
e160c23f
'use strict'
;
const
redux
=
require
(
'redux'
);
// `.default` is needed because 'redux-thunk' is built as an ES2015 module
const
thunk
=
require
(
'redux-thunk'
).
default
;
const
{
createReducer
,
bindSelectors
}
=
require
(
'./util'
);
/**
* Create a Redux store from a set of _modules_.
*
* Each module defines the logic related to a particular piece of the application
* state, including:
*
* - The initial value of that state
* - The _actions_ that can change that state
* - 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.
*
* @param {Object[]} modules
* @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
=
[])
{
// 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
));
// Create the store.
const
defaultMiddleware
=
[
// The `thunk` middleware handles actions which are functions.
// This is used to implement actions which have side effects or are
// asynchronous (see https://github.com/gaearon/redux-thunk#motivation)
thunk
,
];
const
enhancer
=
redux
.
applyMiddleware
(...
defaultMiddleware
,
...
middleware
);
const
store
=
redux
.
createStore
(
reducer
,
initialState
,
enhancer
);
// Add actions and selectors as methods to the store.
const
actions
=
Object
.
assign
({},
...
modules
.
map
(
m
=>
m
.
actions
));
const
boundActions
=
redux
.
bindActionCreators
(
actions
,
store
.
dispatch
);
const
selectors
=
Object
.
assign
({},
...
modules
.
map
(
m
=>
m
.
selectors
));
const
boundSelectors
=
bindSelectors
(
selectors
,
store
.
getState
);
Object
.
assign
(
store
,
boundActions
,
boundSelectors
);
return
store
;
}
module
.
exports
=
createStore
;
src/sidebar/store/index.js
View file @
e160c23f
...
...
@@ -31,21 +31,15 @@
* 3. Checking that the UI correctly presents a given state.
*/
var
redux
=
require
(
'redux'
);
// `.default` is needed because 'redux-thunk' is built as an ES2015 module
var
thunk
=
require
(
'redux-thunk'
).
default
;
var
modules
=
require
(
'./modules'
);
var
annotationsModule
=
require
(
'./modules/annotations'
);
var
framesModule
=
require
(
'./modules/frames'
);
var
linksModule
=
require
(
'./modules/links'
);
var
selectionModule
=
require
(
'./modules/selection'
);
var
sessionModule
=
require
(
'./modules/session'
);
var
viewerModule
=
require
(
'./modules/viewer'
);
var
createStore
=
require
(
'./create-store'
);
var
debugMiddleware
=
require
(
'./debug-middleware'
);
var
util
=
require
(
'./util'
);
var
annotations
=
require
(
'./modules/annotations'
);
var
frames
=
require
(
'./modules/frames'
);
var
links
=
require
(
'./modules/links'
);
var
selection
=
require
(
'./modules/selection'
);
var
session
=
require
(
'./modules/session'
);
var
viewer
=
require
(
'./modules/viewer'
);
/**
* Redux middleware which triggers an Angular change-detection cycle
...
...
@@ -73,55 +67,29 @@ function angularDigestMiddleware($rootScope) {
}
/**
* Create the Redux store for the application.
* Factory which creates the sidebar app's state store.
*
* 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.
*/
// @ngInject
function
store
(
$rootScope
,
settings
)
{
var
enhancer
=
redux
.
applyMiddleware
(
// The `thunk` middleware handles actions which are functions.
// This is used to implement actions which have side effects or are
// asynchronous (see https://github.com/gaearon/redux-thunk#motivation)
thunk
,
var
middleware
=
[
debugMiddleware
,
angularDigestMiddleware
.
bind
(
null
,
$rootScope
)
);
var
store
=
redux
.
createStore
(
modules
.
update
,
modules
.
init
(
settings
),
enhancer
);
// Expose helper functions that create actions as methods of the
// `store` service to make using them easier from app code. eg.
//
// Instead of:
// store.dispatch(annotations.actions.addAnnotations(annotations))
// You can use:
// store.addAnnotations(annotations)
//
var
actionCreators
=
redux
.
bindActionCreators
(
Object
.
assign
({},
annotationsModule
.
actions
,
framesModule
.
actions
,
linksModule
.
actions
,
selectionModule
.
actions
,
sessionModule
.
actions
,
viewerModule
.
actions
),
store
.
dispatch
);
// Expose selectors as methods of the `store` to make using them easier
// from app code.
//
// eg. Instead of:
// selection.isAnnotationSelected(store.getState(), id)
// You can use:
// store.isAnnotationSelected(id)
var
selectors
=
util
.
bindSelectors
(
Object
.
assign
({},
annotationsModule
.
selectors
,
framesModule
.
selectors
,
linksModule
.
selectors
,
selectionModule
.
selectors
,
sessionModule
.
selectors
,
viewerModule
.
selectors
),
store
.
getState
);
angularDigestMiddleware
.
bind
(
null
,
$rootScope
),
];
return
Object
.
assign
(
store
,
actionCreators
,
selectors
);
var
modules
=
[
annotations
,
frames
,
links
,
selection
,
session
,
viewer
,
];
return
createStore
(
modules
,
[
settings
],
middleware
);
}
module
.
exports
=
store
;
src/sidebar/store/modules/index.js
deleted
100644 → 0
View file @
fbfc20e0
'use strict'
;
/**
* This module defines the main update function (or 'reducer' in Redux's
* terminology) that handles app state updates. For an overview of how state
* management in Redux works, see the comments at the top of `store/index.js`
*
* Each sub-module in this folder defines:
*
* - An `init` function that returns the initial state relating to some aspect
* of the application
* - An `update` object mapping action types to a state update function for
* that action
* - A set of action creators - functions that return actions that can then
* be passed to `store.dispatch()`
* - A set of selectors - Utility functions that calculate some derived data
* from the state
*/
var
annotations
=
require
(
'./annotations'
);
var
frames
=
require
(
'./frames'
);
var
links
=
require
(
'./links'
);
var
selection
=
require
(
'./selection'
);
var
session
=
require
(
'./session'
);
var
viewer
=
require
(
'./viewer'
);
var
util
=
require
(
'../util'
);
function
init
(
settings
)
{
return
Object
.
assign
(
{},
annotations
.
init
(),
frames
.
init
(),
links
.
init
(),
selection
.
init
(
settings
),
session
.
init
(),
viewer
.
init
()
);
}
var
update
=
util
.
createReducer
(...[
annotations
.
update
,
frames
.
update
,
links
.
update
,
selection
.
update
,
session
.
update
,
viewer
.
update
,
]);
module
.
exports
=
{
init
:
init
,
update
:
update
,
};
src/sidebar/store/test/create-store-test.js
0 → 100644
View file @
e160c23f
'use strict'
;
const
createStore
=
require
(
'../create-store'
);
const
counterModule
=
{
init
(
value
=
0
)
{
return
{
count
:
value
};
},
update
:
{
[
'INCREMENT_COUNTER'
](
state
,
action
)
{
return
{
count
:
state
.
count
+
action
.
amount
};
},
},
actions
:
{
increment
(
amount
)
{
return
{
type
:
'INCREMENT_COUNTER'
,
amount
};
},
},
selectors
:
{
getCount
(
state
)
{
return
state
.
count
;
},
},
};
function
counterStore
(
initArgs
=
[],
middleware
=
[])
{
return
createStore
([
counterModule
],
initArgs
,
middleware
);
}
describe
(
'sidebar.store.create-store'
,
()
=>
{
it
(
'returns a working Redux store'
,
()
=>
{
const
store
=
counterStore
();
store
.
dispatch
(
counterModule
.
actions
.
increment
(
5
));
assert
.
equal
(
store
.
getState
().
count
,
5
);
});
it
(
'notifies subscribers when state changes'
,
()
=>
{
const
store
=
counterStore
();
const
subscriber
=
sinon
.
spy
(()
=>
assert
.
equal
(
store
.
getCount
(),
1
));
store
.
subscribe
(
subscriber
);
store
.
increment
(
1
);
assert
.
calledWith
(
subscriber
);
});
it
(
'passes initial state args to `init` function'
,
()
=>
{
const
store
=
counterStore
([
21
]);
assert
.
equal
(
store
.
getState
().
count
,
21
);
});
it
(
'adds actions as methods to the store'
,
()
=>
{
const
store
=
counterStore
();
store
.
increment
(
5
);
assert
.
equal
(
store
.
getState
().
count
,
5
);
});
it
(
'adds selectors as methods to the store'
,
()
=>
{
const
store
=
counterStore
();
store
.
dispatch
(
counterModule
.
actions
.
increment
(
5
));
assert
.
equal
(
store
.
getCount
(),
5
);
});
it
(
'applies `thunk` middleware by default'
,
()
=>
{
const
store
=
counterStore
();
const
doubleAction
=
(
dispatch
,
getState
)
=>
{
dispatch
(
counterModule
.
actions
.
increment
(
getState
().
count
));
};
store
.
increment
(
5
);
store
.
dispatch
(
doubleAction
);
assert
.
equal
(
store
.
getCount
(),
10
);
});
it
(
'applies additional middleware'
,
()
=>
{
const
actions
=
[];
const
middleware
=
()
=>
{
return
next
=>
{
return
action
=>
{
actions
.
push
(
action
);
return
next
(
action
);
};
};
};
const
store
=
counterStore
([],
[
middleware
]);
store
.
increment
(
5
);
assert
.
deepEqual
(
actions
,
[{
type
:
'INCREMENT_COUNTER'
,
amount
:
5
}]);
});
});
src/sidebar/store/test/index-test.js
View file @
e160c23f
...
...
@@ -289,15 +289,6 @@ describe('store', function () {
]);
});
describe
(
'#subscribe()'
,
function
()
{
it
(
'notifies subscribers when the UI state changes'
,
function
()
{
var
listener
=
sinon
.
stub
();
store
.
subscribe
(
listener
);
store
.
addAnnotations
([
annotationFixtures
.
defaultAnnotation
()]);
assert
.
called
(
listener
);
});
});
describe
(
'#setForceVisible()'
,
function
()
{
it
(
'sets the visibility of the annotation'
,
function
()
{
store
.
setForceVisible
(
'id1'
,
true
);
...
...
@@ -520,29 +511,4 @@ describe('store', function () {
assert
.
equal
(
store
.
getState
().
annotations
[
0
].
$orphan
,
true
);
});
});
describe
(
'selector functions'
,
function
()
{
// The individual state management modules in reducers/*.js define various
// 'selector' functions for extracting data from the app state. These are
// then re-exported on the store module.
it
(
're-exports selectors from reducers'
,
function
()
{
var
selectors
=
[
// Selection
'hasSelectedAnnotations'
,
'isAnnotationSelected'
,
// Annotations
'annotationExists'
,
'findIDsForTags'
,
'savedAnnotations'
,
// App Status
'isSidebar'
,
];
selectors
.
forEach
(
function
(
fnName
)
{
assert
.
equal
(
typeof
store
[
fnName
],
'function'
,
fnName
+
' was exported'
);
});
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment