Skip to content
This repository has been archived by the owner on Jul 19, 2019. It is now read-only.

Make wrapper-element configurable #314

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ We try to follow [http://keepachangelog.com/](http://keepachangelog.com/) recomm

## [Unreleased]
_(add items here for easier creation of next log entry)_
### Added
- `props.renderWrapper` which allows a custom rendering function for the wrapper element of input and menu

## [1.8.1] - 2018-02-11
### Fixed
Expand Down
7 changes: 7 additions & 0 deletions examples/custom-menu/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ class App extends React.Component {
) : items}
</div>
)}
renderWrapper={(wrapperStyles, wrapperProps, renderedInput, renderedMenu, renderedDebugInfo) => (
<span style = {{ ...wrapperStyles }} {...wrapperProps}>
Enter value here: {renderedInput}
{renderedMenu}
{renderedDebugInfo}
</span>
)}
isItemSelectable={(item) => !item.header}
/>
</div>
Expand Down
62 changes: 42 additions & 20 deletions lib/Autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,21 @@ class Autocomplete extends React.Component {
* will win if it contains a `style` entry.
*/
wrapperStyle: PropTypes.object,
/**
* Arguments: `wrapperStyles: Object, wrapperProps: Object,
* renderedInput: Any, renderedMenu: Any`
*
* Invoked to generate the wrapper element. The `wrapperStyles` and
* `wrapperProps` objects are the corresponding properties of the
* `<Autocomplete />` component itself.
* `renderedInput` and `renderedMenu` are the generated input and
* menu elements.
* The default implementation uses a div-element as the wrapper with input
* and menu as direct children. You can use this function to change the
* wrapper, e.g. by using an inline-element like span or omit it completely
* by using a fragment (supported from react version 16).
*/
renderWrapper: PropTypes.func,
/**
* Whether or not to automatically highlight the top match in the dropdown
* menu.
Expand Down Expand Up @@ -175,6 +190,13 @@ class Autocomplete extends React.Component {
renderMenu(items, value, style) {
return <div style={{ ...style, ...this.menuStyle }} children={items}/>
},
renderWrapper(wrapperStyle, wrapperProps, renderedInput, renderedMenu, renderedDebugInfo) {
return (<div style={{ ...wrapperStyle }} {...wrapperProps}>
{renderedInput}
{renderedMenu}
{renderedDebugInfo}
</div>)
},
menuStyle: {
borderRadius: '3px',
boxShadow: '0 2px 12px rgba(0, 0, 0, 0.1)',
Expand Down Expand Up @@ -570,29 +592,29 @@ class Autocomplete extends React.Component {

const { inputProps } = this.props
const open = this.isOpen()
return (
<div style={{ ...this.props.wrapperStyle }} {...this.props.wrapperProps}>
{this.props.renderInput({
...inputProps,
role: 'combobox',
'aria-autocomplete': 'list',
'aria-expanded': open,
autoComplete: 'off',
ref: this.exposeAPI,
onFocus: this.handleInputFocus,
onBlur: this.handleInputBlur,
onChange: this.handleChange,
onKeyDown: this.composeEventHandlers(this.handleKeyDown, inputProps.onKeyDown),
onClick: this.composeEventHandlers(this.handleInputClick, inputProps.onClick),
value: this.props.value,
})}
{open && this.renderMenu()}
{this.props.debug && (
return this.props.renderWrapper(
this.props.wrapperStyle,
this.props.wrapperProps,
this.props.renderInput({
...inputProps,
role: 'combobox',
'aria-autocomplete': 'list',
'aria-expanded': open,
autoComplete: 'off',
ref: this.exposeAPI,
onFocus: this.handleInputFocus,
onBlur: this.handleInputBlur,
onChange: this.handleChange,
onKeyDown: this.composeEventHandlers(this.handleKeyDown, inputProps.onKeyDown),
onClick: this.composeEventHandlers(this.handleInputClick, inputProps.onClick),
value: this.props.value,
}),
open && this.renderMenu(),
this.props.debug && (
<pre style={{ marginLeft: 300 }}>
{JSON.stringify(this._debugStates.slice(Math.max(0, this._debugStates.length - 5), this._debugStates.length), null, 2)}
</pre>
)}
</div>
)
)
}
}
Expand Down
22 changes: 22 additions & 0 deletions lib/__tests__/Autocomplete-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,28 @@ describe('Autocomplete mouse event handlers', () => {
})
})

describe('Autocomplete.props.renderWrapper', () => {
it('should be invoked in `render` to render the root element', () => {
const renderWrapper = jest.fn((wrapperStyles, wrapperProps, renderedInput, renderedMenu, renderedDebugInfo) => {
expect(wrapperStyles).toMatchSnapshot()
expect(wrapperProps).toMatchSnapshot()
return (
<span>
{renderedInput}
{renderedMenu}
{renderedDebugInfo}
</span>
)
})
const tree = shallow(AutocompleteComponentJSX({
value: 'pants',
renderWrapper,
}))
expect(renderWrapper).toHaveBeenCalledTimes(1)
expect(tree).toMatchSnapshot()
})
})

describe('Autocomplete.props.renderInput', () => {
it('should be invoked in `render` to create the <input> element', () => {
const renderInput = jest.fn(props => {
Expand Down
25 changes: 25 additions & 0 deletions lib/__tests__/__snapshots__/Autocomplete-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,28 @@ exports[`Autocomplete.props.renderInput should be invoked in \`render\` to creat
</div>
</div>
`;

exports[`Autocomplete.props.renderWrapper should be invoked in \`render\` to render the root element 1`] = `
Object {
"display": "inline-block",
}
`;

exports[`Autocomplete.props.renderWrapper should be invoked in \`render\` to render the root element 2`] = `Object {}`;

exports[`Autocomplete.props.renderWrapper should be invoked in \`render\` to render the root element 3`] = `
<span>
<input
aria-autocomplete="list"
aria-expanded={false}
autoComplete="off"
onBlur={[Function]}
onChange={[Function]}
onClick={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
role="combobox"
value="pants"
/>
</span>
`;