@@ -3,8 +3,8 @@ import CardExpandable from 'material-ui/Card/CardExpandable'
3
3
import { connect } from 'react-redux'
4
4
import ContentAdd from 'material-ui/svg-icons/content/add'
5
5
import ContentRemove from 'material-ui/svg-icons/content/remove'
6
- import { excludeEmptyFilters } from '../modules/utilities'
7
6
import FloatingActionButton from 'material-ui/FloatingActionButton'
7
+ import isEqual from 'lodash.isequal'
8
8
import MetricsWrapper from './MetricsWrapper'
9
9
import { operators } from '../constants'
10
10
import RaisedButton from 'material-ui/RaisedButton'
@@ -13,6 +13,7 @@ import TextField from 'material-ui/TextField'
13
13
import Tooltip from './Tooltip'
14
14
import { verticalTop } from '../styles/common'
15
15
import { addFilter , editFilter , removeFilter } from '../modules/filters'
16
+ import { createFilter , excludeEmptyFilters } from '../modules/utilities'
16
17
import { cyan500 , grey400 , redA400 } from 'material-ui/styles/colors.js'
17
18
import React , { Component , PropTypes } from 'react'
18
19
@@ -55,18 +56,21 @@ class FilterCriteria extends Component {
55
56
headerText : PropTypes . string ,
56
57
label : PropTypes . string ,
57
58
style : PropTypes . object ,
59
+ valid : PropTypes . boolean ,
58
60
wrapperStyle : PropTypes . object ,
59
- onClickFilter : PropTypes . func ,
60
- onClickReset : PropTypes . func
61
+ onClickReset : PropTypes . func ,
62
+ onClickSubmit : PropTypes . func
61
63
}
62
64
63
65
static defaultProps = {
64
66
criteriaDataProperty : 'data' ,
65
67
headerStyle : { } ,
66
- headerText : 'Select filter criteria (optional)' ,
67
- onClickFilter : null ,
68
+ headerText : 'Select filter criteria' ,
68
69
style : { } ,
69
- wrapperStyle : { }
70
+ valid : true ,
71
+ wrapperStyle : { } ,
72
+ onClickReset : ( ) => { } ,
73
+ onClickSubmit : ( ) => { }
70
74
}
71
75
72
76
constructor ( props ) {
@@ -84,15 +88,78 @@ class FilterCriteria extends Component {
84
88
this . toggle = ::this . toggle
85
89
}
86
90
91
+ createFilterElement = ( filter , i , isFirstOptionalFilter ) => {
92
+ const {
93
+ criteriaDataProperty,
94
+ fields,
95
+ style : filterStyle
96
+ } = this . props
97
+ const field = fields . data [ filter . fieldIndex ] || { }
98
+ const { [ criteriaDataProperty ] : criteriaData } = field
99
+ const ValueField = criteriaData ? AutoComplete : TextField
100
+ const filterIndex = this . getFilterIndex ( filter )
101
+ const errorText = filter . required && ! filter . value
102
+ ? 'Required'
103
+ : undefined
104
+
105
+ return (
106
+ < div key = { filterIndex } >
107
+ < SelectField
108
+ disabled = { filter . required }
109
+ floatingLabelText = 'Select a field'
110
+ hintText = 'Select a field'
111
+ isFetching = { fields . isFetching }
112
+ items = { fields . data }
113
+ keyProp = { 'name' }
114
+ primaryTextProp = { 'name' }
115
+ style = { verticalTop }
116
+ value = { filter . field }
117
+ valueProp = { 'name' }
118
+ onChange = { ( ev , index , value ) => this . onChangeField ( ev , filterIndex , value , index ) }
119
+ />
120
+ < SelectField
121
+ floatingLabelText = 'Select an operator'
122
+ hintText = 'Select an operator'
123
+ items = { operators }
124
+ keyProp = { 'value' }
125
+ primaryTextProp = { 'primaryText' }
126
+ style = { verticalTop }
127
+ value = { filter . operator }
128
+ valueProp = { 'value' }
129
+ onChange = { ( ev , index , value ) => this . onChangeOperator ( ev , filterIndex , value , index ) }
130
+ />
131
+ < ValueField
132
+ dataSource = { criteriaData }
133
+ errorText = { errorText }
134
+ filter = { AutoComplete . caseInsensitiveFilter }
135
+ floatingLabelText = 'Filter Criteria'
136
+ hintText = 'Filter Criteria'
137
+ openOnFocus = { true }
138
+ style = { filterStyle }
139
+ value = { filter . value }
140
+ onChange = { ( ev ) => this . onChangeValue ( ev , filterIndex ) }
141
+ />
142
+ { ( ( ) => {
143
+ if ( filter . required ) {
144
+ return null
145
+ }
146
+
147
+ return [
148
+ isFirstOptionalFilter
149
+ ? null
150
+ : this . renderRemoveButton ( filterIndex ) ,
151
+ this . renderAddButton ( )
152
+ ]
153
+ } ) ( ) }
154
+ </ div >
155
+ )
156
+ }
157
+
87
158
onAddFilter ( ev ) {
88
159
const { dispatch} = this . props
89
160
90
161
ev . preventDefault ( )
91
- dispatch ( addFilter ( {
92
- field : '' ,
93
- operator : '' ,
94
- value : ''
95
- } ) )
162
+ dispatch ( addFilter ( createFilter ( ) ) )
96
163
}
97
164
98
165
onChangeField ( ev , index , field , fieldIndex ) {
@@ -155,14 +222,22 @@ class FilterCriteria extends Component {
155
222
} )
156
223
}
157
224
225
+ getFilterIndex ( filter ) {
226
+ const { filters} = this . props
227
+
228
+ return filters . findIndex ( ( currentFilter ) => {
229
+ return isEqual ( currentFilter , filter )
230
+ } )
231
+ }
232
+
158
233
handleClickFilter ( ...params ) {
159
- const { onClickFilter } = this . props
234
+ const { onClickSubmit } = this . props
160
235
161
236
if ( this . state . expanded ) {
162
237
this . toggle ( )
163
238
}
164
239
165
- onClickFilter ( ...params )
240
+ onClickSubmit ( ...params )
166
241
}
167
242
168
243
handleClickReset ( ...params ) {
@@ -171,10 +246,8 @@ class FilterCriteria extends Component {
171
246
onClickReset ( ...params )
172
247
}
173
248
174
- renderAddButton ( i ) {
175
- const { filters, style : filterStyle } = this . props
176
-
177
- if ( i !== filters . length - 1 ) return null
249
+ renderAddButton ( ) {
250
+ const { style : filterStyle } = this . props
178
251
179
252
return (
180
253
< Tooltip
@@ -197,52 +270,26 @@ class FilterCriteria extends Component {
197
270
</ Tooltip >
198
271
)
199
272
}
200
-
201
- renderRemoveButton ( i ) {
202
- const { filters, style : filterStyle } = this . props
203
-
204
- if ( filters . length === 1 ) return null
205
-
206
- return (
207
- < Tooltip
208
- label = 'Click to remove this filter'
209
- style = { {
210
- ...style . filterButton
211
- } }
212
- >
213
- < FloatingActionButton
214
- mini = { true }
215
- primary = { true }
216
- style = { {
217
- ...filterStyle ,
218
- ...style . filterButton
219
- } }
220
- onTouchTap = { ( ev ) => this . onRemoveFilter ( ev , i ) }
221
- >
222
- < ContentRemove />
223
- </ FloatingActionButton >
224
- </ Tooltip >
225
- )
226
- }
227
273
228
274
renderButtons ( ) {
229
275
const { expanded} = this . state
230
276
const {
231
277
filters,
232
278
label,
233
- onClickFilter ,
279
+ onClickSubmit ,
234
280
style : filterStyle
235
281
} = this . props
236
282
const searchFilters = excludeEmptyFilters ( filters )
237
283
238
- if ( ! expanded || ! onClickFilter ) return null
284
+ if ( ! expanded || ! onClickSubmit ) return null
239
285
240
286
return (
241
287
< span >
242
288
< MetricsWrapper
243
289
component = {
244
290
< RaisedButton
245
- label = 'Filter'
291
+ disabled = { ! this . props . valid }
292
+ label = 'Submit'
246
293
primary = { true }
247
294
style = { filterStyle . button }
248
295
/> }
@@ -264,69 +311,53 @@ class FilterCriteria extends Component {
264
311
265
312
renderFilters ( ) {
266
313
const { expanded} = this . state
314
+ const { filters} = this . props
315
+ const required = filters . filter ( ( filter ) => filter . required )
316
+ const optional = filters . filter ( ( filter ) => ! filter . required )
267
317
268
318
if ( ! expanded ) return null
269
319
270
- const { filters} = this . props
320
+ return required . concat ( optional ) . map ( ( filter , i , array ) => (
321
+ this . createFilterElement (
322
+ filter ,
323
+ i ,
324
+ i === Math . abs ( array . length - required . length - ( optional . length - 1 ) )
325
+ )
326
+ ) )
327
+ }
271
328
272
- return filters . map ( ( filter , i ) => {
273
- const {
274
- criteriaDataProperty,
275
- fields,
276
- style : filterStyle
277
- } = this . props
278
- const field = fields . data [ filter . fieldIndex ] || { }
279
- const { [ criteriaDataProperty ] : criteriaData } = field
280
- const ValueField = criteriaData ? AutoComplete : TextField
329
+ // (i) is the index of the filter in filters
330
+ renderRemoveButton ( i ) {
331
+ const { style : filterStyle } = this . props
281
332
282
- return (
283
- < div key = { i } >
284
- < SelectField
285
- floatingLabelText = 'Select a field'
286
- hintText = 'Select a field'
287
- isFetching = { fields . isFetching }
288
- items = { fields . data }
289
- keyProp = { 'name' }
290
- primaryTextProp = { 'name' }
291
- style = { verticalTop }
292
- value = { filter . field }
293
- valueProp = { 'name' }
294
- onChange = { ( ev , index , value ) => this . onChangeField ( ev , i , value , index ) }
295
- />
296
- < SelectField
297
- floatingLabelText = 'Select an operator'
298
- hintText = 'Select an operator'
299
- items = { operators }
300
- keyProp = { 'value' }
301
- primaryTextProp = { 'primaryText' }
302
- style = { verticalTop }
303
- value = { filter . operator }
304
- valueProp = { 'value' }
305
- onChange = { ( ev , index , value ) => this . onChangeOperator ( ev , i , value , index ) }
306
- />
307
- < ValueField
308
- dataSource = { criteriaData }
309
- filter = { AutoComplete . caseInsensitiveFilter }
310
- floatingLabelText = 'Filter Criteria'
311
- hintText = 'Filter Criteria'
312
- openOnFocus = { true }
313
- style = { filterStyle }
314
- value = { filter . value }
315
- onChange = { ( ev ) => this . onChangeValue ( ev , i ) }
316
- />
317
- { this . renderRemoveButton ( i ) }
318
- { this . renderAddButton ( i ) }
319
- </ div >
320
- )
321
- } )
333
+ return (
334
+ < Tooltip
335
+ label = 'Click to remove this filter'
336
+ style = { {
337
+ ...style . filterButton
338
+ } }
339
+ >
340
+ < FloatingActionButton
341
+ mini = { true }
342
+ primary = { true }
343
+ style = { {
344
+ ...filterStyle ,
345
+ ...style . filterButton
346
+ } }
347
+ onTouchTap = { ( ev ) => this . onRemoveFilter ( ev , i ) }
348
+ >
349
+ < ContentRemove />
350
+ </ FloatingActionButton >
351
+ </ Tooltip >
352
+ )
322
353
}
323
354
324
355
render ( ) {
325
356
const {
326
357
fields,
327
358
headerStyle,
328
359
headerText,
329
- onClickFilter ,
360
+ onClickSubmit ,
330
361
wrapperStyle
331
362
} = this . props
332
363
@@ -342,7 +373,7 @@ class FilterCriteria extends Component {
342
373
< div style = { wrapperStyle } >
343
374
< h3 style = { headerStyle } >
344
375
{ headerText }
345
- { onClickFilter
376
+ { onClickSubmit
346
377
? < CardExpandable
347
378
expanded = { this . state . expanded }
348
379
style = { style . expandButton }
0 commit comments