-
Notifications
You must be signed in to change notification settings - Fork 42
Custom Dropdown component with search support #148
Changes from all commits
af94db3
e146ff8
ca1e944
f7b8208
2c3188c
6ad4b53
3d32a22
38e819f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,8 +3,9 @@ import PropTypes from 'prop-types'; | |
import { noop, bindMethods } from 'patternfly-react'; | ||
import { Field, reduxForm } from 'redux-form'; | ||
import { length } from 'redux-form-validators'; | ||
import SourceClusterSelect from '../SourceClusterSelect/SourceClusterSelect'; | ||
import NetworksStepForm from './components/NetworksStepForm/NetworksStepForm'; | ||
import { BootstrapSelect } from '../../../../../common/forms/BootstrapSelect'; | ||
import { getClusterOptions } from '../helpers'; | ||
|
||
class MappingWizardNetworksStep extends React.Component { | ||
constructor(props) { | ||
|
@@ -107,14 +108,25 @@ class MappingWizardNetworksStep extends React.Component { | |
|
||
const { selectedCluster, selectedClusterMapping } = this.state; | ||
|
||
const clusterOptions = getClusterOptions(clusterMappings); | ||
|
||
return ( | ||
<div> | ||
<SourceClusterSelect | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we delete the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fo sho! This is a huge upgrade, nice job @AparnaKarve! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 alright sounds good. If we remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the feedback guys -- will be deleting |
||
clusterMappings={clusterMappings} | ||
selectSourceCluster={this.selectSourceCluster} | ||
selectedCluster={selectedCluster} | ||
selectedClusterMapping={selectedClusterMapping} | ||
form={form} | ||
<Field | ||
name="cluster_select" | ||
label={__('Map source networks to target networks for cluster')} | ||
data_live_search="true" | ||
component={BootstrapSelect} | ||
options={clusterOptions} | ||
option_key="id" | ||
option_value="name" | ||
onSelect={this.selectSourceCluster} | ||
pre_selected_value={ | ||
clusterOptions.length === 1 ? clusterOptions[0].id : '' | ||
} | ||
choose_text={`<${__('Select a source cluster')}>`} | ||
render_within_form="true" | ||
form_name={form} | ||
/> | ||
<Field | ||
name="networksMappings" | ||
|
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
export const getClusterOptions = clusterMappings => { | ||
const sourceClustersWithAssociatedTargetClusters = clusterMappings.reduce( | ||
(mappings, targetClusterWithSourceClusters) => { | ||
const { | ||
nodes: sourceClusters, | ||
...targetCluster | ||
} = targetClusterWithSourceClusters; | ||
const sourceToTargetMappings = sourceClusters.map(sourceCluster => ({ | ||
sourceCluster, | ||
targetCluster, | ||
sourceClusterMappedToTargetCluster: { | ||
name: `${sourceCluster.name} (${targetCluster.name})`, | ||
id: sourceCluster.id | ||
} | ||
})); | ||
return mappings.concat(sourceToTargetMappings); | ||
}, | ||
[] | ||
); | ||
|
||
return sourceClustersWithAssociatedTargetClusters.map( | ||
({ sourceClusterMappedToTargetCluster }) => | ||
sourceClusterMappedToTargetCluster | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { | ||
Form, | ||
FormGroup, | ||
Grid, | ||
ControlLabel, | ||
bindMethods | ||
} from 'patternfly-react'; | ||
import { focus } from 'redux-form'; | ||
|
||
const $ = require('jquery'); | ||
require('bootstrap-select'); | ||
|
||
export class BootstrapSelect extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
bindMethods(this, ['renderFormGroup']); | ||
} | ||
componentDidMount() { | ||
const { | ||
input, | ||
form_name, | ||
meta: { visited, dispatch }, | ||
onSelect, | ||
pre_selected_value | ||
} = this.props; | ||
|
||
$(`#${input.name}`).selectpicker('val', input.value || pre_selected_value); | ||
|
||
$(`.${input.name}_select`).on('click', '.dropdown-toggle', e => { | ||
if (!visited) dispatch(focus(form_name, input.name)); | ||
}); | ||
if (onSelect) | ||
$(`#${input.name}`).on('changed.bs.select', e => { | ||
onSelect(e.target.value); | ||
}); | ||
} | ||
|
||
renderFormGroup = (labelWidth, controlWidth) => { | ||
const { | ||
input, | ||
label, | ||
required, | ||
data_live_search, | ||
options, | ||
option_key, | ||
option_value, | ||
choose_text, | ||
meta: { visited, error, active } | ||
} = this.props; | ||
const formGroupProps = { key: { label }, ...this.props }; | ||
|
||
if (visited && !active && error) formGroupProps.validationState = 'error'; | ||
|
||
return ( | ||
<FormGroup {...formGroupProps}> | ||
<Grid.Col componentClass={ControlLabel} sm={labelWidth}> | ||
{label} | ||
{required && ' *'} | ||
</Grid.Col> | ||
<Grid.Col sm={controlWidth}> | ||
<select | ||
id={input.name} | ||
data-live-search={data_live_search} | ||
className={`form-control ${input.name}_select`} | ||
{...input} | ||
> | ||
<option disabled value=""> | ||
{choose_text || `<${__('Choose')}>`} | ||
</option> | ||
{options.map(val => ( | ||
<option value={val[option_key]} key={val[option_value]}> | ||
{val[option_value]} | ||
</option> | ||
))} | ||
</select> | ||
{visited && | ||
!active && | ||
error && <Form.HelpBlock>{error}</Form.HelpBlock>} | ||
</Grid.Col> | ||
</FormGroup> | ||
); | ||
}; | ||
|
||
render = () => { | ||
const { render_within_form } = this.props; | ||
|
||
if (render_within_form) { | ||
return ( | ||
<div> | ||
<Form horizontal>{this.renderFormGroup(6, 4)}</Form> | ||
</div> | ||
); | ||
} | ||
return this.renderFormGroup(2, 9); | ||
}; | ||
} | ||
|
||
BootstrapSelect.propTypes = { | ||
label: PropTypes.string, | ||
input: PropTypes.object, | ||
required: PropTypes.bool, | ||
data_live_search: PropTypes.string, | ||
type: PropTypes.string, | ||
options: PropTypes.array, | ||
option_key: PropTypes.string, | ||
option_value: PropTypes.string, | ||
meta: PropTypes.object, | ||
form_name: PropTypes.string, | ||
onSelect: PropTypes.func, | ||
pre_selected_value: PropTypes.string, | ||
choose_text: PropTypes.string, | ||
render_within_form: PropTypes.string | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, since we're now hooking the selects into
redux-form
, this enables us to make a couple improvementsselectedCluster
component state to the redux storeredux-form
validation to handle thenext
button disabling. Before I had to use a workaround where I was renderingDualPaneMapper
, but hiding it (find hack here). We can instead uselength
fromredux-form-validators
and go back to conditionally rendering this guyIf we want to make these changes, I imagine it would be best to do it as a separate PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes - i think lifting
selectedCluster
to redux store is fine (so good w/ # 1).For # 2 (handle next button disabling)... i think that's probably OK but we have to vet this change w/ @mturley 's wizard abstraction. I believe
shouldDisableNextStep(activeStepIndex)
callback is called for a step now, so we'd just have to verify we can still map this toredux-form-validators
. This should all jive in a future PR... but I'm fine w/ what's been added here...