Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search/filter command by pattern, function name, and effect type #21

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
26 changes: 13 additions & 13 deletions src/components/Effect/Effect.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ class Effect extends React.Component {

effectId = this.props.effect.effectId

highlightText = (text) => {
highlightFilter = (text) => {
const highlight = this.props.filter
// Split on higlight term and include term into parts, ignore case
let parts = text.split(new RegExp(`(${highlight})`, 'gi'));
return <span> { parts.map((part, i) =>
const parts = text.split(new RegExp(`(${highlight})`, 'gi'))

return <span>{parts.map((part, i) =>
<span key={i} style={part.toLowerCase() === highlight.toLowerCase() ? { fontWeight: 'bold', background: '#ffcc00' } : {} }>
{ part }
{part}
</span>)
} </span>;
} </span>
}

renderResult(status, result, error, winner) {
Expand All @@ -42,7 +42,7 @@ class Effect extends React.Component {

if(effect.root) {
nodes = nodes.concat(
renderFuncCall(effect.effect.saga, effect.effect.args, this.highlightText),
renderFuncCall(effect.effect.saga, effect.effect.args, this.highlightFilter),
this.renderResult(status, result, error)
)
}
Expand All @@ -53,7 +53,7 @@ class Effect extends React.Component {
<SagaValue
value={data.pattern || data.channel}
isIdentifier={true}
highlighter={this.highlightText}
highlighter={this.highlightFilter}
/>,
this.renderResult(status, result, error, winner)
)
Expand All @@ -73,7 +73,7 @@ class Effect extends React.Component {
else if((data = asEffect.call(effect.effect))) {
nodes = nodes.concat(
renderEffectType('call'),
renderFuncCall(data.fn, data.args, this.highlightText),
renderFuncCall(data.fn, data.args, this.highlightFilter),
this.renderResult(status, result, error, winner)
)
}
Expand All @@ -89,7 +89,7 @@ class Effect extends React.Component {
else if((data = asEffect.fork(effect.effect))) {
nodes = nodes.concat(
renderEffectType('fork'),
renderFuncCall(data.fn, data.args, this.highlightText),
renderFuncCall(data.fn, data.args, this.highlightFilter),
this.renderResult(status, result, error, winner)
)
}
Expand All @@ -101,7 +101,7 @@ class Effect extends React.Component {
value={data}
isIdentifier={true}
label={data.name}
highlighter={this.highlightText}
highlighter={this.highlightFilter}
/>,
this.renderResult(status, result, error, winner)
)
Expand All @@ -114,7 +114,7 @@ class Effect extends React.Component {
value={data}
isIdentifier={true}
label={data.name}
highlighter={this.highlightText}
highlighter={this.highlightFilter}
/>,
)
}
Expand All @@ -136,7 +136,7 @@ class Effect extends React.Component {
else if((data = asEffect.select(effect.effect))) {
nodes = nodes.concat(
renderEffectType('select'),
renderFuncCall(data.selector, data.args, this.highlightText),
renderFuncCall(data.selector, data.args, this.highlightFilter),
this.renderResult(status, result, error, winner)
)
}
Expand Down
27 changes: 17 additions & 10 deletions src/containers/EffectList/EffectList.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,26 @@ const cssMatchAction = css`
margin-top: -1px;
`

const effectTypes = ['take', 'put', 'fork', 'call', 'cps', 'join', 'cancel']
const EFFECT_TYPES = ['take', 'put', 'fork', 'call', 'cps', 'join', 'cancel']

class EffectList extends React.Component {

state = {
collapsedEffects: {}
}

componentDidMount() {
this.props.setFilterOptions(EFFECT_TYPES, { allCaps: true })
}

isCollapsed = effectId => {
return !!this.state.collapsedEffects[effectId]
}

isFiltered = effectId => {
passFilter = effectId => {
const { filter } = this.props

if (!filter.word) return true
if (!filter.word && !filter.type) return true

let data = {}, description
const effect = this.props.effectsById[effectId]
Expand All @@ -50,21 +54,23 @@ class EffectList extends React.Component {
data = { type: 'root', name: effect.effect.saga.name }
}

else {
effectTypes.forEach(type => {
else { // Currently only filters those with string data.name
EFFECT_TYPES.forEach(type => {
if (description = asEffect[type](effect.effect)) {
data = {
type,
name: description.pattern
|| description.channel
|| description.action
|| (description.fn && description.fn.name)
|| description.channel
|| description.action
|| (description.fn && description.fn.name)
}
}
})
}

return typeof data.name === 'string' && data.name.toLowerCase().includes(filter.word.toLowerCase())
return typeof data.name === 'string'
&& data.name.toLowerCase().includes(filter.word.toLowerCase())
&& (!filter.type || data.type === filter.type)
}

collapseEffect = (effectId, collapsed) => {
Expand Down Expand Up @@ -154,7 +160,7 @@ class EffectList extends React.Component {
pinned={this.props.pinnedEffectId === effectId}
collapsed={this.isCollapsed(effectId)}
filter={this.props.filter.word}
passFilter={this.isFiltered(effectId)}
passFilter={this.passFilter(effectId)}
onCollapse={this.collapseEffect}
onPin={this.props.onPin}
onUnpin={this.props.onUnpin}
Expand Down Expand Up @@ -189,6 +195,7 @@ EffectList.propTypes = {
onSelectionChange: PropTypes.func.isRequired,
rootEffectIds: PropTypes.array.isRequired,
filter: PropTypes.object.isRequired,
setFilterOptions: PropTypes.func.isRequired,
// Injected by redux
effectsById: PropTypes.object.isRequired,
effectsByParentId: PropTypes.object.isRequired,
Expand Down
6 changes: 4 additions & 2 deletions src/containers/EffectView/EffectView.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ class EffectView extends React.Component {


render() {
const rootEffectIds = this.props.rootEffectIds
const filter = this.props.filter
const {rootEffectIds, filter, setFilterOptions} = this.props

const selectedEffectId = this.state.selectedEffectId
const pinnedEffectId = this.state.pinnedEffectId

Expand All @@ -80,6 +80,7 @@ class EffectView extends React.Component {
onPin={this.handlePin}
onUnpin={this.handleUnpin}
filter={filter}
setFilterOptions={setFilterOptions}
/>
</EffectViewBody>
<EffectViewFooter>
Expand All @@ -97,6 +98,7 @@ class EffectView extends React.Component {
EffectView.propTypes = {
rootEffectIds: PropTypes.array.isRequired,
filter: PropTypes.object.isRequired,
// setFilterOptions: PropTypes.func.isRequired,
// Inject by Redux
effectsById: PropTypes.object.isRequired
}
Expand Down
50 changes: 45 additions & 5 deletions src/containers/SagaMonitorView/SagaMonitorView.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import {
SagaMonitorHeader,
SagaMonitorOption,
SagaMonitorBody,
FilterEffect
FilterEffect,
FilterDropdown,
FilterOption
} from './styles'

const EFFECT_VIEW = 'Effects'
Expand All @@ -24,7 +26,8 @@ class SagaMonitorView extends React.Component {
state = {
currentView: EFFECT_VIEW,
currentViewIndex: 0,
filter: { word: '', type: undefined }
filter: { word: '', type: undefined },
filterOptions: []
}

viewHandlers = {
Expand All @@ -33,13 +36,43 @@ class SagaMonitorView extends React.Component {
}

updateFilter = () => this.setState(({ filter }) => (
{ filter: { ...filter, word: this.filter.value } }
{ filter: { word: this.filterWord.value, type: this.filterType.value } }
))

setFilterOptions = (items, { allCaps }) => {
const options = items.map(item => {
let label, value

if(typeof item === 'object') {
label = item.option || item.value
value = item.value
}
else if(typeof item === 'string') {
label = item
value = item
}

if (allCaps) label = label.toUpperCase()

return { label, value }

})

this.setState({ filterOptions: options })
}

renderFilterOptions = () => this.state.filterOptions.map(option => {
return <FilterOption key={option.value} value={option.value}>{option.label}</FilterOption>
})

renderCurrentView() {
switch (this.state.currentView) {
case EFFECT_VIEW:
return <EffectView rootEffectIds={this.props.rootEffectIds} filter={this.state.filter} />
return <EffectView
rootEffectIds={this.props.rootEffectIds}
filter={this.state.filter}
setFilterOptions={this.setFilterOptions}
/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is also another EffectView deep inside ActionView/Reactions/EffectView which shows a similar effect tree but only for those sagas that reacted to the selected action. Right now when selecting an action the app crashes because the deeply nested EffectView receive a null filter. So we should forward filter options from ActionView down to it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review! I will fix it in the next PR.

case ACTION_VIEW:
return <ActionView />
default:
Expand Down Expand Up @@ -70,9 +103,16 @@ class SagaMonitorView extends React.Component {
{this.renderViewOption(ACTION_VIEW)}
<FilterEffect
onChange={this.updateFilter}
innerRef={filter => this.filter = filter}
innerRef={filter => this.filterWord = filter}
placeholder="filter..."
/>
<FilterDropdown
onChange={this.updateFilter}
innerRef={filter => this.filterType = filter}
>
<FilterOption value="">none</FilterOption>
{this.renderFilterOptions()}
</FilterDropdown>
<hr style={{ width: OPTION_WIDTH, left: OPTION_WIDTH * this.state.currentViewIndex }} />
</Row>
</SagaMonitorHeader>
Expand Down
8 changes: 8 additions & 0 deletions src/containers/SagaMonitorView/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,11 @@ export const FilterEffect = styled.input`
padding-left: 4px;
}
`

export const FilterDropdown = styled.select`
width: 60px;
`

export const FilterOption = styled.option`
width: 30px;
`