Skip to content

Commit ee09958

Browse files
committed
chore(Search*): use React.forwardRef() (#4270)
1 parent 4565f8c commit ee09958

11 files changed

+66
-33
lines changed

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ export RatingIcon from './modules/Rating/RatingIcon'
154154

155155
export Search from './modules/Search'
156156
export SearchCategory from './modules/Search/SearchCategory'
157+
export SearchCategoryLayout from './modules/Search/SearchCategoryLayout'
157158
export SearchResult from './modules/Search/SearchResult'
158159
export SearchResults from './modules/Search/SearchResults'
159160

src/modules/Search/Search.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
} from '../../lib'
2323
import Input from '../../elements/Input'
2424
import SearchCategory from './SearchCategory'
25+
import SearchCategoryLayout from './SearchCategoryLayout'
2526
import SearchResult from './SearchResult'
2627
import SearchResults from './SearchResults'
2728

@@ -683,5 +684,6 @@ Search.defaultProps = {
683684
Search.autoControlledProps = ['open', 'value']
684685

685686
Search.Category = SearchCategory
687+
Search.CategoryLayout = SearchCategoryLayout
686688
Search.Result = SearchResult
687689
Search.Results = SearchResults

src/modules/Search/SearchCategory.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ import {
1111
} from '../../lib'
1212
import SearchCategoryLayout from './SearchCategoryLayout'
1313

14-
function SearchCategory(props) {
14+
const SearchCategory = React.forwardRef(function (props, ref) {
1515
const { active, children, className, content, layoutRenderer, renderer } = props
16+
1617
const classes = cx(useKeyOnly(active, 'active'), 'category', className)
1718
const rest = getUnhandledProps(SearchCategory, props)
1819
const ElementType = getElementType(SearchCategory, props)
@@ -21,17 +22,18 @@ function SearchCategory(props) {
2122
const resultsContent = childrenUtils.isNil(children) ? content : children
2223

2324
return (
24-
<ElementType {...rest} className={classes}>
25+
<ElementType {...rest} className={classes} ref={ref}>
2526
{layoutRenderer({ categoryContent, resultsContent })}
2627
</ElementType>
2728
)
28-
}
29+
})
2930

3031
SearchCategory.defaultProps = {
3132
layoutRenderer: SearchCategoryLayout,
3233
renderer: ({ name }) => name,
3334
}
3435

36+
SearchCategory.displayName = 'SearchCategory'
3537
SearchCategory.propTypes = {
3638
/** An element type to render as (string or function). */
3739
as: PropTypes.elementType,

src/modules/Search/SearchCategoryLayout.d.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import * as React from 'react'
22

3-
import { SemanticShorthandContent } from '../../generic'
4-
import SearchResult from './SearchResult'
5-
63
export interface SearchCategoryLayoutProps extends StrictSearchCategoryLayoutProps {
74
[key: string]: any
85
}

src/modules/Search/SearchCategoryLayout.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React from 'react'
33

44
function SearchCategoryLayout(props) {
55
const { categoryContent, resultsContent } = props
6+
67
return (
78
<>
89
<div className='name'>{categoryContent}</div>

src/modules/Search/SearchResult.js

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import cx from 'clsx'
2+
import _ from 'lodash'
23
import PropTypes from 'prop-types'
3-
import React, { Component } from 'react'
4+
import React from 'react'
45

56
import {
67
createHTMLImage,
@@ -30,32 +31,29 @@ const defaultRenderer = ({ image, price, title, description }) => [
3031
</div>,
3132
]
3233

33-
export default class SearchResult extends Component {
34-
handleClick = (e) => {
35-
const { onClick } = this.props
34+
const SearchResult = React.forwardRef(function (props, ref) {
35+
const { active, className, renderer } = props
3636

37-
if (onClick) onClick(e, this.props)
37+
const handleClick = (e) => {
38+
_.invoke(props, 'onClick', e, props)
3839
}
3940

40-
render() {
41-
const { active, className, renderer } = this.props
42-
43-
const classes = cx(useKeyOnly(active, 'active'), 'result', className)
44-
const rest = getUnhandledProps(SearchResult, this.props)
45-
const ElementType = getElementType(SearchResult, this.props)
46-
47-
// Note: You technically only need the 'content' wrapper when there's an
48-
// image. However, optionally wrapping it makes this function a lot more
49-
// complicated and harder to read. Since always wrapping it doesn't affect
50-
// the style in any way let's just do that.
51-
return (
52-
<ElementType {...rest} className={classes} onClick={this.handleClick}>
53-
{renderer(this.props)}
54-
</ElementType>
55-
)
56-
}
57-
}
58-
41+
const classes = cx(useKeyOnly(active, 'active'), 'result', className)
42+
const rest = getUnhandledProps(SearchResult, props)
43+
const ElementType = getElementType(SearchResult, props)
44+
45+
// Note: You technically only need the 'content' wrapper when there's an
46+
// image. However, optionally wrapping it makes this function a lot more
47+
// complicated and harder to read. Since always wrapping it doesn't affect
48+
// the style in any way let's just do that.
49+
return (
50+
<ElementType {...rest} className={classes} onClick={handleClick} ref={ref}>
51+
{renderer(props)}
52+
</ElementType>
53+
)
54+
})
55+
56+
SearchResult.displayName = 'SearchResult'
5957
SearchResult.propTypes = {
6058
/** An element type to render as (string or function). */
6159
as: PropTypes.elementType,
@@ -104,3 +102,5 @@ SearchResult.propTypes = {
104102
SearchResult.defaultProps = {
105103
renderer: defaultRenderer,
106104
}
105+
106+
export default SearchResult

src/modules/Search/SearchResults.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@ import React from 'react'
44

55
import { childrenUtils, customPropTypes, getElementType, getUnhandledProps } from '../../lib'
66

7-
function SearchResults(props) {
7+
const SearchResults = React.forwardRef(function (props, ref) {
88
const { children, className, content } = props
99
const classes = cx('results transition', className)
1010
const rest = getUnhandledProps(SearchResults, props)
1111
const ElementType = getElementType(SearchResults, props)
1212

1313
return (
14-
<ElementType {...rest} className={classes}>
14+
<ElementType {...rest} className={classes} ref={ref}>
1515
{childrenUtils.isNil(children) ? content : children}
1616
</ElementType>
1717
)
18-
}
18+
})
1919

20+
SearchResults.displayName = 'SearchResults'
2021
SearchResults.propTypes = {
2122
/** An element type to render as (string or function). */
2223
as: PropTypes.elementType,

test/specs/modules/Search/SearchCategory-test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as common from 'test/specs/commonTests'
55

66
describe('SearchCategory', () => {
77
common.isConformant(SearchCategory)
8+
common.forwardsRef(SearchCategory)
89
common.rendersChildren(SearchCategory)
910

1011
describe('children', () => {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as React from 'react'
2+
3+
import SearchCategoryLayout from 'src/modules/Search/SearchCategoryLayout'
4+
import * as common from 'test/specs/commonTests'
5+
6+
const requiredProps = {
7+
categoryContent: <div />,
8+
resultsContent: <div />,
9+
}
10+
11+
describe('SearchCategoryLayout', () => {
12+
common.isConformant(SearchCategoryLayout, { requiredProps, rendersChildren: false })
13+
})
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
1+
import React from 'react'
2+
13
import SearchResult from 'src/modules/Search/SearchResult'
24
import * as common from 'test/specs/commonTests'
5+
import { sandbox } from 'test/utils'
36

47
const requiredProps = { title: '' }
58

69
describe('SearchResult', () => {
710
common.isConformant(SearchResult, { requiredProps })
11+
common.forwardsRef(SearchResult, { requiredProps })
812
common.propKeyOnlyToClassName(SearchResult, 'active', { requiredProps })
13+
14+
describe('onClick', () => {
15+
it('is called with (e, data) when clicked', () => {
16+
const onClick = sandbox.spy()
17+
mount(<SearchResult onClick={onClick} {...requiredProps} />).simulate('click')
18+
19+
onClick.should.have.been.calledOnce()
20+
onClick.should.have.been.calledWithMatch({ type: 'click' }, requiredProps)
21+
})
22+
})
923
})

test/specs/modules/Search/SearchResults-test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ import * as common from 'test/specs/commonTests'
33

44
describe('SearchResults', () => {
55
common.isConformant(SearchResults)
6+
common.forwardsRef(SearchResults)
67
common.rendersChildren(SearchResults)
78
})

0 commit comments

Comments
 (0)