Skip to content

Commit 38b6757

Browse files
committed
chore(Card): use React.forwardRef() (#4243)
1 parent 92a0920 commit 38b6757

File tree

12 files changed

+112
-86
lines changed

12 files changed

+112
-86
lines changed

src/views/Card/Card.js

Lines changed: 70 additions & 67 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
childrenUtils,
@@ -9,6 +10,7 @@ import {
910
getUnhandledProps,
1011
SUI,
1112
useKeyOnly,
13+
useEventCallback,
1214
} from '../../lib'
1315
import Image from '../../elements/Image'
1416
import CardContent from './CardContent'
@@ -20,80 +22,79 @@ import CardMeta from './CardMeta'
2022
/**
2123
* A card displays site content in a manner similar to a playing card.
2224
*/
23-
export default class Card extends Component {
24-
handleClick = (e) => {
25-
const { onClick } = this.props
25+
const Card = React.forwardRef(function (props, ref) {
26+
const {
27+
centered,
28+
children,
29+
className,
30+
color,
31+
content,
32+
description,
33+
extra,
34+
fluid,
35+
header,
36+
href,
37+
image,
38+
link,
39+
meta,
40+
onClick,
41+
raised,
42+
} = props
43+
44+
const classes = cx(
45+
'ui',
46+
color,
47+
useKeyOnly(centered, 'centered'),
48+
useKeyOnly(fluid, 'fluid'),
49+
useKeyOnly(link, 'link'),
50+
useKeyOnly(raised, 'raised'),
51+
'card',
52+
className,
53+
)
54+
const rest = getUnhandledProps(Card, props)
55+
const ElementType = getElementType(Card, props, () => {
56+
if (onClick) {
57+
return 'a'
58+
}
59+
})
2660

27-
if (onClick) onClick(e, this.props)
28-
}
61+
const handleClick = useEventCallback((e) => {
62+
_.invoke(props, 'onClick', e, props)
63+
})
2964

30-
render() {
31-
const {
32-
centered,
33-
children,
34-
className,
35-
color,
36-
content,
37-
description,
38-
extra,
39-
fluid,
40-
header,
41-
href,
42-
image,
43-
link,
44-
meta,
45-
onClick,
46-
raised,
47-
} = this.props
48-
49-
const classes = cx(
50-
'ui',
51-
color,
52-
useKeyOnly(centered, 'centered'),
53-
useKeyOnly(fluid, 'fluid'),
54-
useKeyOnly(link, 'link'),
55-
useKeyOnly(raised, 'raised'),
56-
'card',
57-
className,
65+
if (!childrenUtils.isNil(children)) {
66+
return (
67+
<ElementType {...rest} className={classes} href={href} onClick={handleClick} ref={ref}>
68+
{children}
69+
</ElementType>
5870
)
59-
const rest = getUnhandledProps(Card, this.props)
60-
const ElementType = getElementType(Card, this.props, () => {
61-
if (onClick) return 'a'
62-
})
63-
64-
if (!childrenUtils.isNil(children)) {
65-
return (
66-
<ElementType {...rest} className={classes} href={href} onClick={this.handleClick}>
67-
{children}
68-
</ElementType>
69-
)
70-
}
71-
if (!childrenUtils.isNil(content)) {
72-
return (
73-
<ElementType {...rest} className={classes} href={href} onClick={this.handleClick}>
74-
{content}
75-
</ElementType>
76-
)
77-
}
78-
71+
}
72+
if (!childrenUtils.isNil(content)) {
7973
return (
80-
<ElementType {...rest} className={classes} href={href} onClick={this.handleClick}>
81-
{Image.create(image, {
82-
autoGenerateKey: false,
83-
defaultProps: {
84-
ui: false,
85-
wrapped: true,
86-
},
87-
})}
88-
{(description || header || meta) && (
89-
<CardContent description={description} header={header} meta={meta} />
90-
)}
91-
{extra && <CardContent extra>{extra}</CardContent>}
74+
<ElementType {...rest} className={classes} href={href} onClick={handleClick} ref={ref}>
75+
{content}
9276
</ElementType>
9377
)
9478
}
95-
}
9679

80+
return (
81+
<ElementType {...rest} className={classes} href={href} onClick={handleClick} ref={ref}>
82+
{Image.create(image, {
83+
autoGenerateKey: false,
84+
defaultProps: {
85+
ui: false,
86+
wrapped: true,
87+
},
88+
})}
89+
{(description || header || meta) && (
90+
<CardContent description={description} header={header} meta={meta} />
91+
)}
92+
{extra && <CardContent extra>{extra}</CardContent>}
93+
</ElementType>
94+
)
95+
})
96+
97+
Card.displayName = 'Card'
9798
Card.propTypes = {
9899
/** An element type to render as (string or function). */
99100
as: PropTypes.elementType,
@@ -155,3 +156,5 @@ Card.Description = CardDescription
155156
Card.Group = CardGroup
156157
Card.Header = CardHeader
157158
Card.Meta = CardMeta
159+
160+
export default Card

src/views/Card/CardContent.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import CardMeta from './CardMeta'
2020
/**
2121
* A card can contain blocks of content or extra content meant to be formatted separately from the main content.
2222
*/
23-
function CardContent(props) {
23+
const CardContent = React.forwardRef(function (props, ref) {
2424
const { children, className, content, description, extra, header, meta, textAlign } = props
2525

2626
const classes = cx(useKeyOnly(extra, 'extra'), useTextAlignProp(textAlign), 'content', className)
@@ -29,30 +29,31 @@ function CardContent(props) {
2929

3030
if (!childrenUtils.isNil(children)) {
3131
return (
32-
<ElementType {...rest} className={classes}>
32+
<ElementType {...rest} className={classes} ref={ref}>
3333
{children}
3434
</ElementType>
3535
)
3636
}
3737
if (!childrenUtils.isNil(content)) {
3838
return (
39-
<ElementType {...rest} className={classes}>
39+
<ElementType {...rest} className={classes} ref={ref}>
4040
{content}
4141
</ElementType>
4242
)
4343
}
4444

4545
return (
46-
<ElementType {...rest} className={classes}>
46+
<ElementType {...rest} className={classes} ref={ref}>
4747
{createShorthand(CardHeader, (val) => ({ content: val }), header, { autoGenerateKey: false })}
4848
{createShorthand(CardMeta, (val) => ({ content: val }), meta, { autoGenerateKey: false })}
4949
{createShorthand(CardDescription, (val) => ({ content: val }), description, {
5050
autoGenerateKey: false,
5151
})}
5252
</ElementType>
5353
)
54-
}
54+
})
5555

56+
CardContent.displayName = 'CardContent'
5657
CardContent.propTypes = {
5758
/** An element type to render as (string or function). */
5859
as: PropTypes.elementType,

src/views/Card/CardDescription.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,20 @@ import {
1515
/**
1616
* A card can contain a description with one or more paragraphs.
1717
*/
18-
function CardDescription(props) {
18+
const CardDescription = React.forwardRef(function (props, ref) {
1919
const { children, className, content, textAlign } = props
2020
const classes = cx(useTextAlignProp(textAlign), 'description', className)
2121
const rest = getUnhandledProps(CardDescription, props)
2222
const ElementType = getElementType(CardDescription, props)
2323

2424
return (
25-
<ElementType {...rest} className={classes}>
25+
<ElementType {...rest} className={classes} ref={ref}>
2626
{childrenUtils.isNil(children) ? content : children}
2727
</ElementType>
2828
)
29-
}
29+
})
3030

31+
CardDescription.displayName = 'CardDescription'
3132
CardDescription.propTypes = {
3233
/** An element type to render as (string or function). */
3334
as: PropTypes.elementType,

src/views/Card/CardGroup.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import Card from './Card'
1818
/**
1919
* A group of cards.
2020
*/
21-
function CardGroup(props) {
21+
const CardGroup = React.forwardRef(function (props, ref) {
2222
const {
2323
centered,
2424
children,
@@ -46,14 +46,14 @@ function CardGroup(props) {
4646

4747
if (!childrenUtils.isNil(children)) {
4848
return (
49-
<ElementType {...rest} className={classes}>
49+
<ElementType {...rest} className={classes} ref={ref}>
5050
{children}
5151
</ElementType>
5252
)
5353
}
5454
if (!childrenUtils.isNil(content)) {
5555
return (
56-
<ElementType {...rest} className={classes}>
56+
<ElementType {...rest} className={classes} ref={ref}>
5757
{content}
5858
</ElementType>
5959
)
@@ -65,12 +65,13 @@ function CardGroup(props) {
6565
})
6666

6767
return (
68-
<ElementType {...rest} className={classes}>
68+
<ElementType {...rest} className={classes} ref={ref}>
6969
{itemsJSX}
7070
</ElementType>
7171
)
72-
}
72+
})
7373

74+
CardGroup.displayName = 'CardGroup'
7475
CardGroup.propTypes = {
7576
/** An element type to render as (string or function). */
7677
as: PropTypes.elementType,

src/views/Card/CardHeader.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,20 @@ import {
1515
/**
1616
* A card can contain a header.
1717
*/
18-
function CardHeader(props) {
18+
const CardHeader = React.forwardRef(function (props, ref) {
1919
const { children, className, content, textAlign } = props
2020
const classes = cx(useTextAlignProp(textAlign), 'header', className)
2121
const rest = getUnhandledProps(CardHeader, props)
2222
const ElementType = getElementType(CardHeader, props)
2323

2424
return (
25-
<ElementType {...rest} className={classes}>
25+
<ElementType {...rest} className={classes} ref={ref}>
2626
{childrenUtils.isNil(children) ? content : children}
2727
</ElementType>
2828
)
29-
}
29+
})
3030

31+
CardHeader.displayName = 'CardHeader'
3132
CardHeader.propTypes = {
3233
/** An element type to render as (string or function). */
3334
as: PropTypes.elementType,

src/views/Card/CardMeta.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,20 @@ import {
1515
/**
1616
* A card can contain content metadata.
1717
*/
18-
function CardMeta(props) {
18+
const CardMeta = React.forwardRef(function (props, ref) {
1919
const { children, className, content, textAlign } = props
2020
const classes = cx(useTextAlignProp(textAlign), 'meta', className)
2121
const rest = getUnhandledProps(CardMeta, props)
2222
const ElementType = getElementType(CardMeta, props)
2323

2424
return (
25-
<ElementType {...rest} className={classes}>
25+
<ElementType {...rest} className={classes} ref={ref}>
2626
{childrenUtils.isNil(children) ? content : children}
2727
</ElementType>
2828
)
29-
}
29+
})
3030

31+
CardMeta.displayName = 'CardMeta'
3132
CardMeta.propTypes = {
3233
/** An element type to render as (string or function). */
3334
as: PropTypes.elementType,

test/specs/views/Card/Card-test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ import { sandbox } from 'test/utils'
1313

1414
describe('Card', () => {
1515
common.isConformant(Card)
16+
17+
common.forwardsRef(Card)
18+
common.forwardsRef(Card, { requiredProps: { children: <span /> } })
19+
common.forwardsRef(Card, { requiredProps: { content: faker.lorem.word() } })
20+
1621
common.hasSubcomponents(Card, [CardContent, CardDescription, CardGroup, CardHeader, CardMeta])
1722
common.hasUIClassName(Card)
1823
common.rendersChildren(Card)

test/specs/views/Card/CardContent-test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import faker from 'faker'
12
import _ from 'lodash'
3+
import React from 'react'
24

35
import { SUI } from 'src/lib'
46
import CardContent from 'src/views/Card/CardContent'
@@ -9,6 +11,9 @@ import * as common from 'test/specs/commonTests'
911

1012
describe('CardContent', () => {
1113
common.isConformant(CardContent)
14+
common.forwardsRef(CardContent)
15+
common.forwardsRef(CardContent, { requiredProps: { children: <span /> } })
16+
common.forwardsRef(CardContent, { requiredProps: { content: faker.lorem.word() } })
1217
common.rendersChildren(CardContent)
1318

1419
common.implementsShorthandProp(CardContent, {

test/specs/views/Card/CardDescription-test.js

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

77
describe('CardDescription', () => {
88
common.isConformant(CardDescription)
9+
common.forwardsRef(CardDescription)
910
common.rendersChildren(CardDescription)
1011
common.implementsTextAlignProp(CardDescription, _.without(SUI.TEXT_ALIGNMENTS, 'justified'))
1112
})

test/specs/views/Card/CardGroup-test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ import * as common from 'test/specs/commonTests'
88

99
describe('CardGroup', () => {
1010
common.isConformant(CardGroup)
11+
12+
common.forwardsRef(CardGroup)
13+
common.forwardsRef(CardGroup, { requiredProps: { children: <span /> } })
14+
common.forwardsRef(CardGroup, { requiredProps: { content: faker.lorem.word() } })
15+
1116
common.hasUIClassName(CardGroup)
1217
common.rendersChildren(CardGroup)
1318

0 commit comments

Comments
 (0)