Skip to content

Commit

Permalink
implement min/max Tags and validation (also README)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandernst committed Nov 15, 2020
1 parent 592d43c commit 42e37da
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 3 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ ReactDOM.render(<App />, document.getElementById('app'))
- [`tagComponent`](#tagcomponent-optional)
- [`suggestionComponent`](#suggestioncomponent-optional)
- [`inputAttributes`](#inputAttributes-optional)
- [minTags](#minTags-optional)
- [maxTags](#maxTags-optional)

#### id (optional)

Expand Down Expand Up @@ -315,6 +317,20 @@ function SuggestionComponent({ item, query }) {
An object containing additional attributes that will be applied to the text input. _Please note_ that this prop cannot overwrite existing attributes, it can only add new ones. Defaults to `{}`.


#### minTags (optional)

An integer representing the minimum number of tags that should be selected
by the user. If the amount of selected tags is lower, the input will be
considered to be in an invalid state.


#### maxTags (optional)

An integer representing the maximum number of tags that should be selected
by the user. If the amount of selected tags is higher, the input will be
considered to be in an invalid state.


### API

By adding a `ref` to any instances of this component you can access its API methods.
Expand All @@ -332,6 +348,12 @@ Removes a tag from the list of selected tags. This will trigger the delete callb
Clears the input and current query.


#### `isValid()`

Return the validation state of the input. Note that this function will return
`true` even in the case you haven't specified `minTags` and `maxTags`.


### Styling

It is possible to customize the appearance of the component, the included styles found in `/example/styles.css` are only an example.
Expand Down
3 changes: 3 additions & 0 deletions example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ <h3>Country Selector</h3>
<hr>
<h3>Custom tags</h3>
<div id="demo-2"></div>
<hr>
<h3>Validation</h3>
<div id="demo-3"></div>
</div>

<script src="bundle.js"></script>
Expand Down
56 changes: 56 additions & 0 deletions example/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,59 @@ class CustomTags extends React.Component {
}

ReactDOM.render(<CustomTags />, document.getElementById('demo-2'))

/**
* Demo 3 - Validation
*/
class Validation extends React.Component {
constructor (props) {
super(props)

this.state = {
tags: [],
suggestions: []
}

this.reactTags = React.createRef()
}

onDelete (i) {
const tags = this.state.tags.slice(0)
tags.splice(i, 1)
this.setState({ tags })
}

onAddition (tag) {
const tags = [].concat(this.state.tags, tag)
this.setState({ tags })
}

onValidate (tag) {
return /^[a-z]{3,12}$/i.test(tag.name);
}

render () {
return (
<>
<p>Enter new tags meeting the requirements below:</p>
<ReactTags
allowNew={true}
newTagPrefix="Create new tag: "
ref={this.reactTags}
tags={this.state.tags}
suggestions={this.state.suggestions}
onDelete={this.onDelete.bind(this)}
onAddition={this.onAddition.bind(this)}
onValidate={this.onValidate.bind(this)}
minTags={1}
maxTags={2}
/>
<p style={{margin: '0.25rem 0', color: 'gray' }}><small><em>Tags must be 3–12 characters in length and only contain the letters A-Z</em></small></p>
<p><b>Output:</b></p>
<pre><code>{JSON.stringify(this.state.tags, null, 2)}</code></pre>
</>
)
}
}

ReactDOM.render(<Validation />, document.getElementById('demo-3'))
8 changes: 8 additions & 0 deletions example/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@
cursor: text;
}

.react-tags.react-tags__valid {
border: 1px solid green;
}

.react-tags.react-tags__invalid {
border: 1px solid red;
}

.react-tags.is-focused {
border-color: #B1B1B1;
}
Expand Down
43 changes: 40 additions & 3 deletions lib/ReactTags.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const KEYS = {

const CLASS_NAMES = {
root: 'react-tags',
rootValid: 'react-tags__valid',
rootInvalid: 'react-tags__invalid',
rootFocused: 'is-focused',
selected: 'react-tags__selected',
selectedTag: 'react-tags__selected-tag',
Expand Down Expand Up @@ -108,7 +110,8 @@ class ReactTags extends React.Component {
this.state = {
query: '',
focused: false,
index: -1
index: -1,
validationState: this._getValidationState()
}

this.inputEventHandlers = {
Expand Down Expand Up @@ -243,6 +246,35 @@ class ReactTags extends React.Component {
})
}

isValid () {
return this.state.validationState
}

_getValidationState () {
const tl = this.props.tags.length

let validationState = true
if (this.props.minTags && tl < this.props.minTags) {
validationState = false
}

if (this.props.maxTags && tl > this.props.maxTags) {
validationState = false
}

return validationState
}

componentDidUpdate (prevProps) {
const validationState = this._getValidationState()

if (this.state.validationState !== validationState) {
this.setState({
validationState
})
}
}

render () {
const TagComponent = this.props.tagComponent || Tag

Expand All @@ -251,6 +283,7 @@ class ReactTags extends React.Component {
const rootClasses = [classes.root]

this.state.focused && rootClasses.push(classes.rootFocused)
rootClasses.push(this.state.validationState ? classes.rootValid : classes.rootInvalid)

return (
<div ref={this.container} className={rootClasses.join(' ')} onClick={this.onClick.bind(this)}>
Expand Down Expand Up @@ -329,7 +362,9 @@ ReactTags.defaultProps = {
addOnBlur: false,
tagComponent: null,
suggestionComponent: null,
inputAttributes: {}
inputAttributes: {},
minTags: null,
maxTags: null
}

ReactTags.propTypes = {
Expand Down Expand Up @@ -365,7 +400,9 @@ ReactTags.propTypes = {
PropTypes.func,
PropTypes.element
]),
inputAttributes: PropTypes.object
inputAttributes: PropTypes.object,
minTags: PropTypes.number,
maxTags: PropTypes.number
}

export default ReactTags

0 comments on commit 42e37da

Please sign in to comment.