Skip to content

Commit 0fd05bd

Browse files
committed
Added searchable variant for PF3 select
1 parent fdd430f commit 0fd05bd

File tree

6 files changed

+285
-19
lines changed

6 files changed

+285
-19
lines changed

packages/common/src/select/index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ import clsx from 'clsx';
55

66
class Select extends Component {
77
render() {
8-
console.log(this.props.invalid)
98
return (
109
<ReactSelect className={ clsx(this.props.classNamePrefix, {
1110
'has-error': this.props.invalid,
12-
}) } { ...this.props } />
11+
}) } { ...this.props } {...this.props.input} />
1312
);
1413
}
1514
}

packages/pf3-component-mapper/demo/index.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,38 @@ import Switch from "../src/form-fields/switch-field";
1212

1313
const selectSchema = {
1414
fields: [{
15+
component: 'select-field',
16+
name: 'select-search',
17+
label: 'Select search',
18+
isRequired: true,
19+
validateOnMount: true,
20+
isClearable: true,
21+
multi: true,
22+
isSearchable: true,
23+
placeholder: 'Placeholder',
24+
validate: [{
25+
type: 'required-validator'
26+
}],
27+
options: [{
28+
label: 'foo',
29+
value: 123
30+
}, {
31+
label: 'bar',
32+
value: 231
33+
}, {
34+
label: 'Super long option label, Super long option label, Super long option label, Super long option label, Super long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option label',
35+
value: 'x'
36+
}]
37+
}, {
1538
component: 'select-field',
1639
name: 'select',
1740
label: 'Select',
1841
isRequired: true,
1942
validateOnMount: true,
20-
menuIsOpen: true,
2143
isClearable: true,
2244
multi: false,
23-
placeholder: 'Super long option label, Super long option label, Super long option label, Super long option label, Super long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option labelSuper long option label, Super long option label',
45+
isSearchable: true,
46+
placeholder: 'Placeholder',
2447
validate: [{
2548
type: 'required-validator'
2649
}],

packages/pf3-component-mapper/src/form-fields/form-fields.js

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3-
import { FormControl, HelpBlock, Checkbox, FormGroup, ControlLabel, FieldLevelHelp } from 'patternfly-react';
3+
import { FormControl, HelpBlock, Checkbox, FormGroup, ControlLabel, FieldLevelHelp, DropdownButton } from 'patternfly-react';
44
import { componentTypes } from '@data-driven-forms/react-form-renderer';
55
import { validationError } from './helpers';
66
import MultipleChoiceList from './multiple-choice-list';
@@ -10,12 +10,7 @@ import { DateTimePicker } from './date-time-picker/date-time-picker';
1010

1111
import RagioGroup from './radio';
1212
import PlainText from './plain-text';
13-
14-
import Select from '@data-driven-forms/common/src/select';
15-
import './select/react-select.scss';
16-
import Option from './select/option';
17-
import DropdownIndicator from './select/dropdown-indicator';
18-
import ClearIndicator from './select/clear-indicator';
13+
import { P3Select } from './select';
1914

2015
const selectComponent = ({
2116
componentType,
@@ -56,12 +51,7 @@ const selectComponent = ({
5651
/>
5752
),
5853
[componentTypes.SELECT_COMPONENT]: () => <div>
59-
<Select
60-
components={{
61-
ClearIndicator,
62-
Option,
63-
DropdownIndicator,
64-
}}
54+
<P3Select
6555
classNamePrefix="ddorg__pf3-component-mapper__select"
6656
loadOptions={ loadOptions }
6757
options={ options }

packages/pf3-component-mapper/src/form-fields/select/index.js

Lines changed: 171 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
1-
import React, { Component } from 'react';
1+
import React, { Component, createRef, Fragment } from 'react';
22
import PropTypes from 'prop-types';
33
import ReactSelect, { components } from 'react-select';
44
import customStyles from './select-styles';
55
import isEqual from 'lodash/isEqual';
66
import './react-select.scss';
77

8+
/**
9+
* New implementation of select of PF4
10+
*/
11+
12+
import NewSelect from '@data-driven-forms/common/src/select';
13+
import './react-select.scss';
14+
import Option from './option';
15+
import DropdownIndicator from './dropdown-indicator';
16+
import ClearIndicator from './clear-indicator';
17+
import { DropdownButton, FormControl } from 'patternfly-react';
18+
import clsx from 'clsx';
19+
820
const fnToString = (fn = '') => fn.toString().replace(/\s+/g, ' ');
921

1022
const ValueContainer = ({ children, ...props }) => {
@@ -174,3 +186,161 @@ Select.defaultProps = {
174186
};
175187

176188
export default Select;
189+
190+
const getDropdownText = (value, placeholder) => {
191+
if (Array.isArray(value)) {
192+
if (value.length === 0) {
193+
return [ placeholder, true ];
194+
}
195+
196+
if (typeof value[0] === 'object') {
197+
return [ value.map(({ label }) => label).join(', '), false ];
198+
}
199+
200+
return [ value.join(', '), false ];
201+
}
202+
203+
if (typeof value === 'object') {
204+
return [ value.label, false ];
205+
}
206+
207+
if (!value) {
208+
return [ placeholder, true ];
209+
}
210+
211+
return [ value, false ];
212+
};
213+
214+
class SearchInput extends Component {
215+
inputRef = createRef(null);
216+
componentDidMount() {
217+
this.inputRef.current.focus();
218+
}
219+
220+
componentDidUpdate(prevProps, prevState) {
221+
console.log(prevProps, this.props);
222+
}
223+
224+
shouldComponentUpdate(nextProps) {
225+
226+
return nextProps.value !== this.props.value;
227+
}
228+
229+
componentWillUnmount() {
230+
this.inputRef.current.blur();
231+
}
232+
233+
render() {
234+
return <input ref={ this.inputRef } type="text" { ...this.props } className="form-control" />;
235+
}
236+
237+
}
238+
239+
const SelectTitle = ({ title, classNamePrefix, isClearable, value, onClear }) => (
240+
<Fragment>
241+
<span key="searchable-select-value-label" className={ `${classNamePrefix}-value` }>{ title }</span>
242+
{ isClearable && value && (
243+
<div
244+
className={ `${classNamePrefix}-searchebale-clear` }
245+
onClick={ (event) => {
246+
event.stopPropagation();
247+
return onClear(undefined);
248+
} } >
249+
<i className="fa fa-times"/>
250+
</div>
251+
) }
252+
</Fragment>
253+
);
254+
255+
export class P3Select extends Component {
256+
state = {
257+
isOpen: false,
258+
}
259+
handleToggleOpen = () => this.setState(({ isOpen }) => ({ isOpen: !isOpen }))
260+
261+
componentDidUpdate(prevProps, prevState) {
262+
//console.log('root update', prevState, this.state);
263+
}
264+
265+
shouldComponentUpdate(nextProps, nextState) {
266+
if (nextState.isOpen !== this.state.isOpen) {
267+
return true;
268+
}
269+
270+
if (JSON.stringify(nextProps) !== JSON.stringify(this.props)) {
271+
return true;
272+
}
273+
274+
return false;
275+
}
276+
277+
render () {
278+
const { input, ...props } = this.props;
279+
const { isOpen } = this.state;
280+
const [ title, isPlaceholder ] = getDropdownText(input.value, props.placeholder);
281+
if (props.isSearchable) {
282+
const searchableInput = {
283+
...input,
284+
onChange: props.isMulti || props.multi
285+
? input.onChange
286+
: (...args) => {
287+
this.handleToggleOpen();
288+
return input.onChange(...args);
289+
},
290+
};
291+
return (
292+
<div className={ `${props.classNamePrefix}-button` }>
293+
<DropdownButton
294+
onToggle={ () => this.handleToggleOpen() }
295+
open={ isOpen }
296+
id={ props.id || props.input.name }
297+
title={ <SelectTitle
298+
classNamePrefix={ this.props.classNamePrefix }
299+
value={ input.value }
300+
isClearable={ props.isClearable }
301+
title={ title }
302+
onClear={ input.onChange }
303+
/> }
304+
className={ clsx(`${props.classNamePrefix}-dropdown`, {
305+
'is-empty': isPlaceholder,
306+
}) }>
307+
{ isOpen &&
308+
<NewSelect
309+
input={ searchableInput }
310+
{ ...props }
311+
className={ clsx(props.classNamePrefix, {
312+
sercheable: props.isSearchable,
313+
}) }
314+
controlShouldRenderValue={ false }
315+
hideSelectedOptions={ false }
316+
isClearable={ false }
317+
tabSelectsValue={ false }
318+
menuIsOpen
319+
backspaceRemovesValue={ false }
320+
isMulti={ props.isMulti || props.multi }
321+
placeholder="Search..."
322+
components={{
323+
ClearIndicator,
324+
Option,
325+
DropdownIndicator: null,
326+
IndicatorSeparator: null,
327+
Placeholder: () => null,
328+
Input: ({ selectProps, cx, isHidden, isDisabled, innerRef, getStyles, ...props }) => <SearchInput id={ this.props.input.name } { ...props } />,
329+
}} /> }
330+
</DropdownButton>
331+
</div>
332+
);
333+
}
334+
335+
return (
336+
<NewSelect { ...props }
337+
className={ props.classNamePrefix }
338+
components={{
339+
ClearIndicator,
340+
Option,
341+
DropdownIndicator,
342+
}} />
343+
);
344+
345+
}
346+
}

packages/pf3-component-mapper/src/form-fields/select/option.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { components } from 'react-select';
33
import PropTypes from 'prop-types';
44
import clsx from 'clsx';
55

6-
const Option = (props) => {
6+
const Option = props => {
77
return (
88
<div className={ clsx('ddorg__pf3-component-mapper__select__option', {
99
'ddorg__pf3-component-mapper__select__option--is-focused': props.isFocused,

packages/pf3-component-mapper/src/form-fields/select/react-select.scss

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,90 @@ div.field-group {
152152
position: relative;
153153
}
154154
.ddorg__pf3-component-mapper__select__placeholder {
155+
padding: 0;
155156
position: relative;
156157
}
158+
&.sercheable {
159+
border: none;
160+
.ddorg__pf3-component-mapper__select__control {
161+
box-shadow: none;
162+
background: unset;
163+
background-color: transparent;
164+
}
165+
.ddorg__pf3-component-mapper__select__input {
166+
border: 1px solid red;
167+
width: 100%;
168+
}
169+
.ddorg__pf3-component-mapper__select__value-container {
170+
display: flex;
171+
margin-right: 0;
172+
width: calc(100% - 2px);
173+
background-color: transparent;
174+
height: unset;
175+
>* {
176+
flex: 1
177+
}
178+
}
179+
.ddorg__pf3-component-mapper__select__indicators {
180+
display: none;
181+
}
182+
.ddorg__pf3-component-mapper__select__menu {
183+
margin: 0;
184+
border: 1px solid #ddd;
185+
border-top: none;
186+
box-shadow: none;
187+
}
188+
.ddorg__pf3-component-mapper__select__menu {
189+
left: -1px;
190+
box-sizing: content-box;
191+
}
192+
input {
193+
border: 1px solid #bbb;
194+
&:focus {
195+
border-color: #0088ce;
196+
outline: 0;
197+
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(0, 136, 206, 0.6);
198+
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(0, 136, 206, 0.6);
199+
}
200+
}
201+
}
202+
}
203+
.ddorg__pf3-component-mapper__select-dropdown {
204+
&.is-empty {
205+
color: rgb(153, 153, 153)
206+
};
207+
min-width: 250px;
208+
max-width: calc(100% - 2px);
209+
display: flex;
210+
justify-content: space-between;
211+
align-items: center;
212+
}
213+
.ddorg__pf3-component-mapper__select-button {
214+
.ddorg__pf3-component-mapper__select-searchebale-clear {
215+
margin-right: 4px;
216+
}
217+
.ddorg__pf3-component-mapper__select {
218+
max-width: unset;
219+
width: 100%;
220+
min-width: 248px;
221+
}
222+
.dropdown {
223+
max-width: 100%;
224+
>button {
225+
max-width: 100%;
226+
display: flex;
227+
.ddorg__pf3-component-mapper__select-value {
228+
flex: 1;
229+
overflow: hidden;
230+
text-overflow: ellipsis;
231+
margin-right: 4px;
232+
text-align: initial;
233+
}
234+
}
235+
}
236+
.dropdown-menu {
237+
width: 100%;
238+
border-bottom: none;
239+
border-color: #ddd;
240+
}
157241
}

0 commit comments

Comments
 (0)