Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
beat
beat.editor
Commits
e6a0519f
Commit
e6a0519f
authored
May 02, 2018
by
Jaden DIEFENBAUGH
Browse files
add a bunch of comments
parent
8aee5a11
Changes
4
Hide whitespace changes
Inline
Side-by-side
conda/js/src/components/CacheInput.jsx
View file @
e6a0519f
...
...
@@ -12,22 +12,36 @@ import {
import
{
rxField
}
from
'
@helpers/beat
'
;
type
Props
=
{
// consumer-supplied func for validating input
// if the func finds the input isnt valid, it should return a react.node of text for the error message
validateFunc
?:
(
string
)
=>
(
true
|
React
.
Node
),
// if the current input in the CacheInput instance is valid, this func will be called with that value
onChange
:
(
any
)
=>
any
,
// the current value for this cacheinput
value
:
string
,
// for edge cases
children
?:
React
.
Node
,
// many fields in the beat editor are restricted to a certain "slugified" format
// if this cacheinput needs that, use this flag
fieldTest
?:
boolean
,
// delay in ms, defaults to 500
// update delay in ms, defaults to 500
// basically just simple throttling to keep from overwhelming complex updates in the parent
delay
?:
number
,
};
type
State
=
{
// the current string in the CacheInput input field,
// which may or may not be valid
cache
:
string
,
// whether the cache is valid
valid
:
boolean
,
// a text-like react node that represents an error message
invalidityText
?:
React
.
Node
,
// the delay to use (default 500)
msDelay
:
number
,
};
// test for the "fieldTest" flag, with a nice error message
const
fieldTestResults
=
(
input
:
string
):
true
|
React
.
Node
=>
{
return
(
rxField
.
test
(
input
)
||
<
span
>
...
...
@@ -36,6 +50,8 @@ const fieldTestResults = (input: string): true | React.Node => {
);
};
// top-level validity func with some edgecase checking
// validateFunc arg lets you also check using a user-defined validate func
const
isValid
=
(
input
:
string
,
currVal
:
string
,
validateFunc
:
(
string
)
=>
true
|
React
.
Node
,
fieldTest
:
boolean
):
true
|
React
.
Node
=>
{
if
(
input
===
currVal
)
return
true
;
...
...
@@ -51,6 +67,8 @@ const isValid = (input: string, currVal: string, validateFunc: (string) => true
return
funcRes
;
};
// Provides a bootstrap-styled wrapper around <input /> that caches the raw user input until a valid input is provided.
// Also throttles the onChange event to prevent many parent state updates in a short time
class
CacheInput
extends
React
.
Component
<
Props
,
State
>
{
constructor
(
props
:
Props
){
super
(
props
);
...
...
@@ -64,10 +82,12 @@ class CacheInput extends React.Component<Props, State>{
msDelay
:
this
.
props
.
delay
!==
undefined
?
this
.
props
.
delay
:
500
,
};
// check validation as soon as it mounts
componentDidMount
()
{
this
.
updateValidity
(
this
.
state
.
cache
);
}
// if the parent's model changes, reset input
componentWillReceiveProps
(
nextProps
:
any
)
{
this
.
setState
({
cache
:
(
nextProps
.
value
),
...
...
@@ -75,6 +95,7 @@ class CacheInput extends React.Component<Props, State>{
this
.
clearTimer
();
}
// gets the validity info of an input string given a curr val
updateValidity
=
(
input
:
string
,
currVal
?:
string
=
this
.
props
.
value
)
=>
{
const
func
=
this
.
props
.
validateFunc
||
(()
=>
true
);
const
validRes
=
isValid
(
input
,
currVal
,
func
,
this
.
props
.
fieldTest
||
false
);
...
...
@@ -86,6 +107,8 @@ class CacheInput extends React.Component<Props, State>{
return
validRes
===
true
;
}
// the user can pass any props they want onto the inner <Input /> element
// some props shouldn't be passed though
getInputProps
=
()
=>
{
const
inputProps
=
{
...
this
.
props
};
delete
inputProps
[
'
validateFunc
'
];
...
...
@@ -95,12 +118,16 @@ class CacheInput extends React.Component<Props, State>{
return
inputProps
;
}
// throttle timer ref
onChangeTimer
=
null
;
// clears the throttle timer
clearTimer
=
()
=>
{
if
(
this
.
onChangeTimer
)
clearTimeout
(
this
.
onChangeTimer
);
}
// called whenever the user changes the raw input with the raw input event
// for perf reasons it really focuses on returning when possible
change
=
(
e
:
any
)
=>
{
this
.
clearTimer
();
const
newInput
=
e
.
target
.
value
;
...
...
@@ -118,6 +145,9 @@ class CacheInput extends React.Component<Props, State>{
if
(
this
.
state
.
msDelay
===
0
){
this
.
props
.
onChange
(
e
);
}
else
{
// by default in react events have a "persist" method
// that lets you keep it around for a bit longer than normal
// in testing environments the event persists by default
if
(
e
.
persist
)
e
.
persist
();
this
.
onChangeTimer
=
setTimeout
(()
=>
{
...
...
@@ -127,6 +157,7 @@ class CacheInput extends React.Component<Props, State>{
}
}
// the Tooltips are annoying in test environments and need a react ref
target
=
null
render
()
{
...
...
conda/js/src/components/DeleteInputBtn.jsx
View file @
e6a0519f
...
...
@@ -9,6 +9,8 @@ type Props = {
deleteFunc
:
(
any
)
=>
any
,
}
// super simple wrapper for a button that attaches to an input
// styled like a Delete button
const
DeleteInputBtn
=
({
deleteFunc
}:
Props
)
=>
(
<
InputGroupAddon
addonType
=
'prepend'
>
<
Button
...
...
conda/js/src/components/EntityDetail.jsx
View file @
e6a0519f
...
...
@@ -37,18 +37,28 @@ import PlotterEditorContainer from './plotter';
import
PlotterparameterEditorContainer
from
'
./plotterparameter
'
;
type
Props
=
{
// the match object from react-router
// used to find which object to render
match
:
any
,
// the history object from react-router
// used to push new history to
history
:
any
,
// gets the object based on the current URL
getEntityObject
:
()
=>
BeatObject
,
// updates the current object
updateFunc
:
(
BeatObject
)
=>
any
,
};
// 3 tabs so far: editor, docs, raw json
type
Tab
=
'
0
'
|
'
1
'
|
'
2
'
;
type
State
=
{
// which tab is active
activeTab
:
Tab
,
};
// wrapper for editors
// doesnt really do much now besides the raw json tab
export
class
EntityDetail
extends
React
.
Component
<
Props
,
State
>
{
constructor
(
props
:
Props
){
super
(
props
);
...
...
@@ -72,6 +82,7 @@ export class EntityDetail extends React.Component<Props, State> {
return
(
<
Container
>
<
Row
>
{
/* title line (name & validation info) */
}
<
Col
>
<
h4
style
=
{
{
'
textAlign
'
:
'
center
'
}
}
>
<
span
style
=
{
{
'
textTransform
'
:
'
capitalize
'
}
}
>
...
...
@@ -114,6 +125,7 @@ export class EntityDetail extends React.Component<Props, State> {
</
NavItem
>
</
Nav
>
<
TabContent
activeTab
=
{
this
.
state
.
activeTab
}
className
=
'mt-2'
>
{
/* Uses the appropriate editor, could probably be done nicer */
}
<
TabPane
tabId
=
'0'
>
{
this
.
props
.
match
.
params
.
entity
===
'
algorithm
'
&&
...
...
@@ -191,9 +203,13 @@ export class EntityDetail extends React.Component<Props, State> {
}
const
mapStateToProps
=
(
state
,
ownProps
)
=>
{
// which beat entity is currently active: 'algorithm', 'plotter', 'database', etc.
const
entity
=
ownProps
.
match
.
params
.
entity
;
// the name of the object ("atnt/1", "plot/isoroc/1", "user/tc/test/1/exp", etc.)
const
name
=
ownProps
.
match
.
params
.
name
;
const
obj
=
{
// uses a selector based off the entity and finds the obj given the name
// if the obj doesnt exist (huge edge case) just return a default obj to not break everything
getEntityObject
:
():
BeatObject
=>
Selectors
[
`
${
entity
}
Get`
](
state
).
find
(
o
=>
o
.
name
===
name
)
||
getDefaultEntityObject
(),
};
...
...
@@ -201,6 +217,7 @@ const mapStateToProps = (state, ownProps) => {
};
const
mapDispatchToProps
=
(
dispatch
,
ownProps
)
=>
({
// replace the obj in the Redux store with the new object
updateFunc
:
(
obj
)
=>
{
dispatch
(
Actions
[
`
${
ownProps
.
match
.
params
.
entity
}
Update`
](
ownProps
.
match
.
params
.
name
,
obj
));
ownProps
.
history
.
push
(
`/
${
ownProps
.
match
.
params
.
entity
}
/
${
obj
.
name
}
`
);
...
...
conda/js/src/components/algorithm/AlgorithmEditor.jsx
View file @
e6a0519f
...
...
@@ -40,20 +40,29 @@ type Props = {
};
type
State
=
{
// which tab the user is viewing (endpoints, params, etc.)
activeTab
:
string
,
// data that the user edits, needs use to click "save" to save changes
cache
:
any
,
// algorithm-specific validity checking (alerts for common errors & warnings)
validity
:
AlgorithmValidatorObject
,
// parameters can restrict their values to individual choices
// this string represents a choice that is being edited
choiceCache
:
string
,
};
// represents an input or output value
// inputs/outputs are just an object with a "type" field
type
IOEntry
=
{
|
type
:
string
,
|
};
// represents a collection of inputs/outputs
type
IOObject
=
{
[
string
]:
IOEntry
,
};
// represents a group/endpoint, optionally with outputs
type
Group
=
{
name
:
string
,
inputs
:
IOObject
,
...
...
@@ -73,6 +82,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
choiceCache
:
''
,
}
// if the data changes behind the scenes, update the editor with these changes
componentWillReceiveProps
(
nextProps
:
Props
)
{
this
.
setState
({
cache
:
getValidObj
(
nextProps
.
data
),
...
...
@@ -80,12 +90,16 @@ export class AlgorithmEditor extends React.Component<Props, State> {
});
}
// get the result types (analyzer result types are restricted to a subset of all available types)
getResultDfs
=
():
string
[]
=>
ANALYZER_RESULT_TYPES
.
concat
(
this
.
props
.
dataformats
.
filter
(
d
=>
d
.
name
.
startsWith
(
'
plot
'
)).
map
(
d
=>
d
.
name
))
// returns a validity object based on the given algorithm cache and list of all algorithms
getValidity
=
(
cache
:
BeatObject
=
this
.
state
.
cache
,
algs
:
BeatObject
[]
=
this
.
props
.
algorithms
):
AlgorithmValidatorObject
=>
{
return
algorithmValidator
(
cache
,
algs
.
filter
(
o
=>
o
!==
this
.
props
.
data
));
}
// much like getValidity but just returns a bool
// doesnt worry about warnings, just errors
isValid
=
(
cache
:
BeatObject
):
boolean
=>
{
const
isValid
=
Object
.
entries
(
this
.
getValidity
(
cache
))
.
filter
(([
name
,
bool
])
=>
name
!==
(
this
.
isAnalyzer
()
?
'
endpoint0OutputExists
'
:
'
result0Exists
'
))
...
...
@@ -95,10 +109,15 @@ export class AlgorithmEditor extends React.Component<Props, State> {
return
isValid
;
}
// change the tab
tabTo
=
(
tab
:
string
)
=>
{
if
(
this
.
state
.
activeTab
!==
tab
)
this
.
setState
({
activeTab
:
tab
});
}
// is the algorithm an analyzer?
// theres no flag set, so instead check for the existence of a 'splittable' field which only normal algorithms have
isAnalyzer
=
():
boolean
=>
{
return
!
Object
.
keys
(
this
.
state
.
cache
.
contents
).
includes
(
'
splittable
'
);
}
// helper to change a value in the "contents" subobject of an algorithm
// (this is where the vast majority of change happens)
changeContentsVal
=
(
field
:
string
,
val
:
any
)
=>
{
const
newCache
=
{
...
this
.
state
.
cache
,
...
...
@@ -113,6 +132,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
});
}
// updates an old group object to a new group object
updateGroup
=
(
oldGroup
:
Group
,
newGroup
:
Group
)
=>
{
const
gIdx
=
this
.
state
.
cache
.
contents
.
groups
.
findIndex
(
g
=>
JSON
.
stringify
(
g
)
===
JSON
.
stringify
(
oldGroup
));
const
newGroups
=
jsonClone
(
this
.
state
.
cache
.
contents
.
groups
);
...
...
@@ -134,12 +154,14 @@ export class AlgorithmEditor extends React.Component<Props, State> {
this
.
changeContentsVal
(
'
parameters
'
,
params
);
}
// updates a result given the result name and the value obj
updateResult
=
(
name
:
string
,
res
:
any
)
=>
{
const
rs
=
this
.
state
.
cache
.
contents
.
results
;
rs
[
name
]
=
res
;
this
.
changeContentsVal
(
'
results
'
,
rs
);
}
// toggles the algorithm to an analyzer algorithm
changeToAnalyzer
=
()
=>
{
const
contents
=
jsonClone
(
this
.
state
.
cache
.
contents
);
contents
.
groups
=
contents
.
groups
.
map
(
g
=>
{
...
...
@@ -159,6 +181,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
});
}
// toggles the algorithm to a normal algorithm
changeToNormal
=
()
=>
{
const
contents
=
jsonClone
(
this
.
state
.
cache
.
contents
);
delete
contents
.
results
;
...
...
@@ -176,6 +199,8 @@ export class AlgorithmEditor extends React.Component<Props, State> {
});
}
// generates a func from a given group & type that takes an input/output name
// and deletes it from the group
deleteIO
=
(
group
:
Group
,
type
:
'
inputs
'
|
'
outputs
'
)
=>
(
name
:
string
)
=>
{
if
(
!
group
[
type
])
return
;
...
...
@@ -183,10 +208,14 @@ export class AlgorithmEditor extends React.Component<Props, State> {
this
.
updateGroup
(
group
,
{
...
group
});
}
// rendergs the endpoints tab
renderEndpoints
=
()
=>
(
<
TabPane
tabId
=
'0'
>
{
// loop through all groups in the alg
this
.
state
.
cache
.
contents
.
groups
.
map
((
group
:
Group
,
i
,
groups
:
Group
[])
=>
{
// func to update an input/output
// lets you change the name and/or the type
const
ioUpdate
=
(
subObj
:
'
inputs
'
|
'
outputs
'
)
=>
(
oldName
:
string
,
newName
:
string
,
newObj
:
IOEntry
)
=>
{
const
changedObj
=
changeObjFieldName
(
group
[
subObj
],
oldName
,
newName
);
changedObj
[
newName
]
=
newObj
;
...
...
@@ -325,8 +354,9 @@ export class AlgorithmEditor extends React.Component<Props, State> {
<
Button
outline
block
id
=
'newGroupBtn'
onClick
=
{
(
e
)
=>
{
const
newGroupName
=
generateNewKey
(
'
group
'
,
this
.
state
.
cache
.
contents
.
groups
.
map
(
g
=>
g
.
name
));
const
newGroup
=
{
name
:
`group
${
this
.
state
.
cache
.
contents
.
groups
.
length
}
`
,
name
:
newGroupName
,
inputs
:
{},
};
if
(
!
this
.
isAnalyzer
()
&&
this
.
state
.
cache
.
contents
.
groups
.
length
===
0
)
...
...
@@ -343,10 +373,12 @@ export class AlgorithmEditor extends React.Component<Props, State> {
</
TabPane
>
);
// renders the parameters tab
renderParameters
=
()
=>
{
return
(
<
TabPane
tabId
=
'1'
>
{
// loop through all the parameters
(
Object
.
entries
(
this
.
state
.
cache
.
contents
.
parameters
):
[
string
,
any
][]).
map
(([
name
,
param
],
i
,
params
)
=>
(
<
Row
key
=
{
i
}
className
=
'mb-2'
>
<
Col
sm
=
'12'
>
...
...
@@ -382,11 +414,13 @@ export class AlgorithmEditor extends React.Component<Props, State> {
);
}
// renders the libraries tab
renderLibraries
=
()
=>
(
<
TabPane
tabId
=
'2'
>
<
Row
>
<
Col
sm
=
'12'
>
{
// loop through all the used libs
(
Object
.
entries
(
this
.
state
.
cache
.
contents
.
uses
):
[
string
,
any
][])
.
map
(([
name
,
lib
],
i
,
lEntries
)
=>
(
<
TypedField
...
...
@@ -396,6 +430,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
types
=
{
this
.
props
.
libraries
.
map
(
l
=>
l
.
name
)
}
existingFields
=
{
lEntries
.
map
(([
n
,
v
])
=>
n
)
}
nameUpdateFunc
=
{
str
=>
{
// update the alias
this
.
changeContentsVal
(
'
uses
'
,
changeObjFieldName
(
this
.
state
.
cache
.
contents
.
uses
,
...
...
@@ -405,6 +440,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
);
}
}
typeUpdateFunc
=
{
str
=>
{
// update the chosen library for the alias
const
libs
=
{
...
this
.
state
.
cache
.
contents
.
uses
,
[
name
]:
str
...
...
@@ -438,9 +474,11 @@ export class AlgorithmEditor extends React.Component<Props, State> {
</
TabPane
>
);
// renders the results tab (only if the alg is an analyzer)
renderResults
=
()
=>
(
<
TabPane
tabId
=
'3'
>
{
// loop through results
(
Object
.
entries
(
this
.
state
.
cache
.
contents
.
results
):
[
string
,
any
][])
.
map
(([
name
,
result
],
i
,
rEntries
)
=>
(
<
TypedField
...
...
@@ -475,6 +513,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
}
}
>
<
Col
>
{
/* display by default checkbox */
}
<
Label
check
>
<
Input
type
=
'checkbox'
...
...
@@ -518,6 +557,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
render
=
()
=>
(
<
div
>
{
/* header content (save button & template gen button) */
}
<
div
className
=
'd-flex'
>
<
Button
className
=
'mx-auto'
...
...
@@ -532,6 +572,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
entity
=
{
'
algorithm
'
}
/>
</
div
>
{
/* simple universal stuff - description, what type of alg, if its splittable */
}
<
Form
>
<
FormGroup
tag
=
'fieldset'
>
<
FormGroup
>
...
...
@@ -580,6 +621,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
</
FormGroup
>
}
</
FormGroup
>
{
/* most errors/warnings from the current AlgorithmValidator object are displayed here */
}
{
(
!
this
.
isAnalyzer
()
||
this
.
getValidity
().
result0Exists
)
||
<
Alert
color
=
'danger'
>
...
...
@@ -605,6 +647,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
You need at least 1 input in the first endpoint group.
</
Alert
>
}
{
/* all the tabs: endpoints, params, libs, results */
}
<
Nav
tabs
className
=
'mt-3 mb-3'
>
<
NavItem
>
<
NavLink
...
...
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