Skip to content

Commit

Permalink
Allow the use of a custom input component (#739)
Browse files Browse the repository at this point in the history
  • Loading branch information
taifen authored Sep 23, 2024
1 parent a75fe1f commit 3a5036d
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/popular-toys-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"react-mentions": minor
---

Allow the use of a custom input component
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@

npm-debug.log*

*.log
*.log

.DS_Store
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ The `MentionsInput` supports the following props for configuring the widget:
| forceSuggestionsAboveCursor | boolean | false | Forces the SuggestionList to be rendered above the cursor |
| a11ySuggestionsListLabel | string | `''` | This label would be exposed to screen readers when suggestion popup appears |
| customSuggestionsContainer | function(children) | empty function | Allows customizing the container of the suggestions |
| inputComponent | React component | undefined | Allows the use of a custom input component component |

Each data source is configured using a `Mention` component, which has the following props:

Expand Down
37 changes: 37 additions & 0 deletions demo/src/examples/CustomInputComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react'

import { Mention, MentionsInput } from '../../../src'

import { provideExampleValue } from './higher-order'
import defaultStyle from './defaultStyle'
import defaultMentionStyle from './defaultMentionStyle'

// eslint-disable-next-line no-unused-vars
import classNames from './example.module.css' // uses global css selector

const CustomInput = React.forwardRef((props, ref) => {
return <textarea ref={ref} {...props} className="custom-textarea" />
})

function CustomInputComponent({ value, data, onChange, onAdd }) {
return (
<div className="custom-input">
<h3>Custom input component</h3>

<MentionsInput
value={value}
onChange={onChange}
style={defaultStyle}
placeholder={"Mention people using '@'"}
a11ySuggestionsListLabel={"Suggested mentions"}
inputComponent={CustomInput}
>
<Mention data={data} onAdd={onAdd} style={defaultMentionStyle} />
</MentionsInput>
</div>
)
}

const asExample = provideExampleValue('')

export default asExample(CustomInputComponent)
2 changes: 2 additions & 0 deletions demo/src/examples/Examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import SingleLineIgnoringAccents from './SingleLineIgnoringAccents'
import SuggestionPortal from './SuggestionPortal'
import BottomGuard from './BottomGuard'
import CustomSuggestionsContainer from './CustomSuggestionsContainer'
import CustomInputComponent from './CustomInputComponent'

const users = [
{
Expand Down Expand Up @@ -90,6 +91,7 @@ export default function Examples() {
<SuggestionPortal data={users} />
<BottomGuard data={users} />
<CustomSuggestionsContainer data={users} />
<CustomInputComponent data={users} />
</div>
</StylesViaJss>
)
Expand Down
22 changes: 22 additions & 0 deletions demo/src/examples/example.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,25 @@
text-decoration: underline;
pointer-events: none;
}


:global(.custom-textarea) {
top: 0px;
left: 0px;
width: 100%;
border: 4px solid blue;
bottom: 0px;
height: 100%;
min-height: 80px;
margin: 0px;
resize: none;
display: block;
padding: 12px;
overflow: hidden;
position: absolute;
font-size: inherit;
box-sizing: border-box;
font-family: inherit;
letter-spacing: inherit;
background-color: transparent;
}
12 changes: 8 additions & 4 deletions src/MentionsInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ const propTypes = {
: PropTypes.instanceOf(Element),
}),
]),
inputComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.elementType]),

children: PropTypes.oneOfType([
PropTypes.element,
Expand Down Expand Up @@ -229,15 +230,18 @@ class MentionsInput extends React.Component {
}

renderControl = () => {
let { singleLine, style } = this.props
let { singleLine, style, inputComponent: CustomInput } = this.props
let inputProps = this.getInputProps()

return (
<div {...style('control')}>
{this.renderHighlighter()}
{singleLine
? this.renderInput(inputProps)
: this.renderTextarea(inputProps)}
{CustomInput
? <CustomInput ref={this.setInputRef} {...inputProps} />
: singleLine
? this.renderInput(inputProps)
: this.renderTextarea(inputProps)
}
</div>
)
}
Expand Down
15 changes: 15 additions & 0 deletions src/MentionsInput.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,21 @@ describe('MentionsInput', () => {
expect(inputRef).toHaveBeenCalledWith(el)
})

it('should render a custom input when supplied.', () => {
const CustomInput = React.forwardRef((props, ref) => {
return <input id="testInput" ref={ref} {...props} />
})
const wrapper = mount(
<MentionsInput value="test" inputComponent={CustomInput}>
<Mention trigger="@" data={data} />
</MentionsInput>
)

expect(wrapper.find('textarea').length).toEqual(0)
expect(wrapper.find('input').length).toEqual(1)
expect(wrapper.find('input#testInput').length).toEqual(1)
})

describe('makeTriggerRegex', () => {
it('should return regular expressions', () => {
const trigger = /abc/
Expand Down

0 comments on commit 3a5036d

Please sign in to comment.