diff --git a/src/views/Card/Card.js b/src/views/Card/Card.js index a414d633d3..13cefed144 100644 --- a/src/views/Card/Card.js +++ b/src/views/Card/Card.js @@ -1,6 +1,7 @@ import cx from 'clsx' +import _ from 'lodash' import PropTypes from 'prop-types' -import React, { Component } from 'react' +import React from 'react' import { childrenUtils, @@ -9,6 +10,7 @@ import { getUnhandledProps, SUI, useKeyOnly, + useEventCallback, } from '../../lib' import Image from '../../elements/Image' import CardContent from './CardContent' @@ -20,80 +22,79 @@ import CardMeta from './CardMeta' /** * A card displays site content in a manner similar to a playing card. */ -export default class Card extends Component { - handleClick = (e) => { - const { onClick } = this.props +const Card = React.forwardRef(function (props, ref) { + const { + centered, + children, + className, + color, + content, + description, + extra, + fluid, + header, + href, + image, + link, + meta, + onClick, + raised, + } = props + + const classes = cx( + 'ui', + color, + useKeyOnly(centered, 'centered'), + useKeyOnly(fluid, 'fluid'), + useKeyOnly(link, 'link'), + useKeyOnly(raised, 'raised'), + 'card', + className, + ) + const rest = getUnhandledProps(Card, props) + const ElementType = getElementType(Card, props, () => { + if (onClick) { + return 'a' + } + }) - if (onClick) onClick(e, this.props) - } + const handleClick = useEventCallback((e) => { + _.invoke(props, 'onClick', e, props) + }) - render() { - const { - centered, - children, - className, - color, - content, - description, - extra, - fluid, - header, - href, - image, - link, - meta, - onClick, - raised, - } = this.props - - const classes = cx( - 'ui', - color, - useKeyOnly(centered, 'centered'), - useKeyOnly(fluid, 'fluid'), - useKeyOnly(link, 'link'), - useKeyOnly(raised, 'raised'), - 'card', - className, + if (!childrenUtils.isNil(children)) { + return ( + + {children} + ) - const rest = getUnhandledProps(Card, this.props) - const ElementType = getElementType(Card, this.props, () => { - if (onClick) return 'a' - }) - - if (!childrenUtils.isNil(children)) { - return ( - - {children} - - ) - } - if (!childrenUtils.isNil(content)) { - return ( - - {content} - - ) - } - + } + if (!childrenUtils.isNil(content)) { return ( - - {Image.create(image, { - autoGenerateKey: false, - defaultProps: { - ui: false, - wrapped: true, - }, - })} - {(description || header || meta) && ( - - )} - {extra && {extra}} + + {content} ) } -} + return ( + + {Image.create(image, { + autoGenerateKey: false, + defaultProps: { + ui: false, + wrapped: true, + }, + })} + {(description || header || meta) && ( + + )} + {extra && {extra}} + + ) +}) + +Card.displayName = 'Card' Card.propTypes = { /** An element type to render as (string or function). */ as: PropTypes.elementType, @@ -155,3 +156,5 @@ Card.Description = CardDescription Card.Group = CardGroup Card.Header = CardHeader Card.Meta = CardMeta + +export default Card diff --git a/src/views/Card/CardContent.js b/src/views/Card/CardContent.js index d293948293..475ca2e719 100644 --- a/src/views/Card/CardContent.js +++ b/src/views/Card/CardContent.js @@ -20,7 +20,7 @@ import CardMeta from './CardMeta' /** * A card can contain blocks of content or extra content meant to be formatted separately from the main content. */ -function CardContent(props) { +const CardContent = React.forwardRef(function (props, ref) { const { children, className, content, description, extra, header, meta, textAlign } = props const classes = cx(useKeyOnly(extra, 'extra'), useTextAlignProp(textAlign), 'content', className) @@ -29,21 +29,21 @@ function CardContent(props) { if (!childrenUtils.isNil(children)) { return ( - + {children} ) } if (!childrenUtils.isNil(content)) { return ( - + {content} ) } return ( - + {createShorthand(CardHeader, (val) => ({ content: val }), header, { autoGenerateKey: false })} {createShorthand(CardMeta, (val) => ({ content: val }), meta, { autoGenerateKey: false })} {createShorthand(CardDescription, (val) => ({ content: val }), description, { @@ -51,8 +51,9 @@ function CardContent(props) { })} ) -} +}) +CardContent.displayName = 'CardContent' CardContent.propTypes = { /** An element type to render as (string or function). */ as: PropTypes.elementType, diff --git a/src/views/Card/CardDescription.js b/src/views/Card/CardDescription.js index 1edc971632..49352e62f7 100644 --- a/src/views/Card/CardDescription.js +++ b/src/views/Card/CardDescription.js @@ -15,19 +15,20 @@ import { /** * A card can contain a description with one or more paragraphs. */ -function CardDescription(props) { +const CardDescription = React.forwardRef(function (props, ref) { const { children, className, content, textAlign } = props const classes = cx(useTextAlignProp(textAlign), 'description', className) const rest = getUnhandledProps(CardDescription, props) const ElementType = getElementType(CardDescription, props) return ( - + {childrenUtils.isNil(children) ? content : children} ) -} +}) +CardDescription.displayName = 'CardDescription' CardDescription.propTypes = { /** An element type to render as (string or function). */ as: PropTypes.elementType, diff --git a/src/views/Card/CardGroup.js b/src/views/Card/CardGroup.js index 9402b13da0..276aa27056 100644 --- a/src/views/Card/CardGroup.js +++ b/src/views/Card/CardGroup.js @@ -18,7 +18,7 @@ import Card from './Card' /** * A group of cards. */ -function CardGroup(props) { +const CardGroup = React.forwardRef(function (props, ref) { const { centered, children, @@ -46,14 +46,14 @@ function CardGroup(props) { if (!childrenUtils.isNil(children)) { return ( - + {children} ) } if (!childrenUtils.isNil(content)) { return ( - + {content} ) @@ -65,12 +65,13 @@ function CardGroup(props) { }) return ( - + {itemsJSX} ) -} +}) +CardGroup.displayName = 'CardGroup' CardGroup.propTypes = { /** An element type to render as (string or function). */ as: PropTypes.elementType, diff --git a/src/views/Card/CardHeader.js b/src/views/Card/CardHeader.js index abf740da70..9dd0e16d31 100644 --- a/src/views/Card/CardHeader.js +++ b/src/views/Card/CardHeader.js @@ -15,19 +15,20 @@ import { /** * A card can contain a header. */ -function CardHeader(props) { +const CardHeader = React.forwardRef(function (props, ref) { const { children, className, content, textAlign } = props const classes = cx(useTextAlignProp(textAlign), 'header', className) const rest = getUnhandledProps(CardHeader, props) const ElementType = getElementType(CardHeader, props) return ( - + {childrenUtils.isNil(children) ? content : children} ) -} +}) +CardHeader.displayName = 'CardHeader' CardHeader.propTypes = { /** An element type to render as (string or function). */ as: PropTypes.elementType, diff --git a/src/views/Card/CardMeta.js b/src/views/Card/CardMeta.js index 20b0ebd9bd..ac1377d220 100644 --- a/src/views/Card/CardMeta.js +++ b/src/views/Card/CardMeta.js @@ -15,19 +15,20 @@ import { /** * A card can contain content metadata. */ -function CardMeta(props) { +const CardMeta = React.forwardRef(function (props, ref) { const { children, className, content, textAlign } = props const classes = cx(useTextAlignProp(textAlign), 'meta', className) const rest = getUnhandledProps(CardMeta, props) const ElementType = getElementType(CardMeta, props) return ( - + {childrenUtils.isNil(children) ? content : children} ) -} +}) +CardMeta.displayName = 'CardMeta' CardMeta.propTypes = { /** An element type to render as (string or function). */ as: PropTypes.elementType, diff --git a/test/specs/views/Card/Card-test.js b/test/specs/views/Card/Card-test.js index 2b4f14de10..fcc12c1aca 100644 --- a/test/specs/views/Card/Card-test.js +++ b/test/specs/views/Card/Card-test.js @@ -13,6 +13,11 @@ import { sandbox } from 'test/utils' describe('Card', () => { common.isConformant(Card) + + common.forwardsRef(Card) + common.forwardsRef(Card, { requiredProps: { children: } }) + common.forwardsRef(Card, { requiredProps: { content: faker.lorem.word() } }) + common.hasSubcomponents(Card, [CardContent, CardDescription, CardGroup, CardHeader, CardMeta]) common.hasUIClassName(Card) common.rendersChildren(Card) diff --git a/test/specs/views/Card/CardContent-test.js b/test/specs/views/Card/CardContent-test.js index 94ccbdc66b..c58e562ca6 100644 --- a/test/specs/views/Card/CardContent-test.js +++ b/test/specs/views/Card/CardContent-test.js @@ -1,4 +1,6 @@ +import faker from 'faker' import _ from 'lodash' +import React from 'react' import { SUI } from 'src/lib' import CardContent from 'src/views/Card/CardContent' @@ -9,6 +11,9 @@ import * as common from 'test/specs/commonTests' describe('CardContent', () => { common.isConformant(CardContent) + common.forwardsRef(CardContent) + common.forwardsRef(CardContent, { requiredProps: { children: } }) + common.forwardsRef(CardContent, { requiredProps: { content: faker.lorem.word() } }) common.rendersChildren(CardContent) common.implementsShorthandProp(CardContent, { diff --git a/test/specs/views/Card/CardDescription-test.js b/test/specs/views/Card/CardDescription-test.js index 4a2b1ca386..9929a6c7a8 100644 --- a/test/specs/views/Card/CardDescription-test.js +++ b/test/specs/views/Card/CardDescription-test.js @@ -6,6 +6,7 @@ import * as common from 'test/specs/commonTests' describe('CardDescription', () => { common.isConformant(CardDescription) + common.forwardsRef(CardDescription) common.rendersChildren(CardDescription) common.implementsTextAlignProp(CardDescription, _.without(SUI.TEXT_ALIGNMENTS, 'justified')) }) diff --git a/test/specs/views/Card/CardGroup-test.js b/test/specs/views/Card/CardGroup-test.js index fd114d491b..01cff4137b 100644 --- a/test/specs/views/Card/CardGroup-test.js +++ b/test/specs/views/Card/CardGroup-test.js @@ -8,6 +8,11 @@ import * as common from 'test/specs/commonTests' describe('CardGroup', () => { common.isConformant(CardGroup) + + common.forwardsRef(CardGroup) + common.forwardsRef(CardGroup, { requiredProps: { children: } }) + common.forwardsRef(CardGroup, { requiredProps: { content: faker.lorem.word() } }) + common.hasUIClassName(CardGroup) common.rendersChildren(CardGroup) diff --git a/test/specs/views/Card/CardHeader-test.js b/test/specs/views/Card/CardHeader-test.js index f39aa80566..45655f99a6 100644 --- a/test/specs/views/Card/CardHeader-test.js +++ b/test/specs/views/Card/CardHeader-test.js @@ -6,6 +6,7 @@ import * as common from 'test/specs/commonTests' describe('CardHeader', () => { common.isConformant(CardHeader) + common.forwardsRef(CardHeader) common.rendersChildren(CardHeader) common.implementsTextAlignProp(CardHeader, _.without(SUI.TEXT_ALIGNMENTS, 'justified')) diff --git a/test/specs/views/Card/CardMeta-test.js b/test/specs/views/Card/CardMeta-test.js index 221a82fa3d..834e6df494 100644 --- a/test/specs/views/Card/CardMeta-test.js +++ b/test/specs/views/Card/CardMeta-test.js @@ -6,6 +6,7 @@ import * as common from 'test/specs/commonTests' describe('CardMeta', () => { common.isConformant(CardMeta) + common.forwardsRef(CardMeta) common.rendersChildren(CardMeta) common.implementsTextAlignProp(CardMeta, _.without(SUI.TEXT_ALIGNMENTS, 'justified'))