Skip to content

Commit 793e857

Browse files
authored
fix(Nav): add .card-header-* only if nested in card header (react-bootstrap#5720)
Rename CardContext to CardHeaderContext since it's only used for CardHeaders
1 parent 82fa40d commit 793e857

File tree

7 files changed

+116
-42
lines changed

7 files changed

+116
-42
lines changed

src/Card.tsx

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import classNames from 'classnames';
22
import * as React from 'react';
3-
import { useMemo } from 'react';
43
import PropTypes from 'prop-types';
54

65
import { useBootstrapPrefix } from './ThemeProvider';
76
import createWithBsPrefix from './createWithBsPrefix';
87
import divWithClassName from './divWithClassName';
9-
import CardContext from './CardContext';
108
import CardImg from './CardImg';
9+
import CardHeader from './CardHeader';
1110
import { BsPrefixProps, BsPrefixRefForwardingComponent } from './helpers';
1211
import { Color, Variant } from './types';
1312

@@ -22,7 +21,6 @@ const CardSubtitle = createWithBsPrefix('card-subtitle', {
2221
});
2322
const CardLink = createWithBsPrefix('card-link', { Component: 'a' });
2423
const CardText = createWithBsPrefix('card-text', { Component: 'p' });
25-
const CardHeader = createWithBsPrefix('card-header');
2624
const CardFooter = createWithBsPrefix('card-footer');
2725
const CardImgOverlay = createWithBsPrefix('card-img-overlay');
2826

@@ -95,29 +93,21 @@ const Card: BsPrefixRefForwardingComponent<'div', CardProps> = React.forwardRef<
9593
ref,
9694
) => {
9795
const prefix = useBootstrapPrefix(bsPrefix, 'card');
98-
const cardContext = useMemo(
99-
() => ({
100-
cardHeaderBsPrefix: `${prefix}-header`,
101-
}),
102-
[prefix],
103-
);
10496

10597
return (
106-
<CardContext.Provider value={cardContext}>
107-
<Component
108-
ref={ref}
109-
{...props}
110-
className={classNames(
111-
className,
112-
prefix,
113-
bg && `bg-${bg}`,
114-
text && `text-${text}`,
115-
border && `border-${border}`,
116-
)}
117-
>
118-
{body ? <CardBody>{children}</CardBody> : children}
119-
</Component>
120-
</CardContext.Provider>
98+
<Component
99+
ref={ref}
100+
{...props}
101+
className={classNames(
102+
className,
103+
prefix,
104+
bg && `bg-${bg}`,
105+
text && `text-${text}`,
106+
border && `border-${border}`,
107+
)}
108+
>
109+
{body ? <CardBody>{children}</CardBody> : children}
110+
</Component>
121111
);
122112
},
123113
);

src/CardContext.tsx

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/CardHeader.tsx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import classNames from 'classnames';
2+
import * as React from 'react';
3+
import { useMemo } from 'react';
4+
import PropTypes from 'prop-types';
5+
6+
import { useBootstrapPrefix } from './ThemeProvider';
7+
import CardHeaderContext from './CardHeaderContext';
8+
import { BsPrefixProps, BsPrefixRefForwardingComponent } from './helpers';
9+
10+
export interface CardHeaderProps
11+
extends BsPrefixProps,
12+
React.HTMLAttributes<HTMLElement> {}
13+
14+
const propTypes = {
15+
/**
16+
* @default 'card-header'
17+
*/
18+
bsPrefix: PropTypes.string,
19+
20+
as: PropTypes.elementType,
21+
};
22+
23+
const CardHeader: BsPrefixRefForwardingComponent<
24+
'div',
25+
CardHeaderProps
26+
> = React.forwardRef<HTMLElement, CardHeaderProps>(
27+
(
28+
{
29+
bsPrefix,
30+
className,
31+
// Need to define the default "as" during prop destructuring to be compatible with styled-components github.com/react-bootstrap/react-bootstrap/issues/3595
32+
as: Component = 'div',
33+
...props
34+
},
35+
ref,
36+
) => {
37+
const prefix = useBootstrapPrefix(bsPrefix, 'card-header');
38+
const contextValue = useMemo(
39+
() => ({
40+
cardHeaderBsPrefix: prefix,
41+
}),
42+
[prefix],
43+
);
44+
45+
return (
46+
<CardHeaderContext.Provider value={contextValue}>
47+
<Component
48+
ref={ref}
49+
{...props}
50+
className={classNames(className, prefix)}
51+
/>
52+
</CardHeaderContext.Provider>
53+
);
54+
},
55+
);
56+
57+
CardHeader.displayName = 'CardHeader';
58+
CardHeader.propTypes = propTypes;
59+
60+
export default CardHeader;

src/CardHeaderContext.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import * as React from 'react';
2+
3+
interface CardHeaderContextValue {
4+
cardHeaderBsPrefix: string;
5+
}
6+
7+
const context = React.createContext<CardHeaderContextValue | null>(null);
8+
context.displayName = 'CardHeaderContext';
9+
10+
export default context;

src/Nav.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { useUncontrolled } from 'uncontrollable';
88

99
import { useBootstrapPrefix } from './ThemeProvider';
1010
import NavbarContext from './NavbarContext';
11-
import CardContext from './CardContext';
11+
import CardHeaderContext from './CardHeaderContext';
1212
import AbstractNav from './AbstractNav';
1313
import NavItem from './NavItem';
1414
import NavLink from './NavLink';
@@ -133,13 +133,13 @@ const Nav: BsPrefixRefForwardingComponent<'div', NavProps> = React.forwardRef<
133133
let isNavbar = false;
134134

135135
const navbarContext = useContext(NavbarContext);
136-
const cardContext = useContext(CardContext);
136+
const cardHeaderContext = useContext(CardHeaderContext);
137137

138138
if (navbarContext) {
139139
navbarBsPrefix = navbarContext.bsPrefix;
140140
isNavbar = navbar == null ? true : navbar;
141-
} else if (cardContext) {
142-
({ cardHeaderBsPrefix } = cardContext);
141+
} else if (cardHeaderContext) {
142+
({ cardHeaderBsPrefix } = cardHeaderContext);
143143
}
144144

145145
return (

test/NavSpec.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { mount } from 'enzyme';
2-
import Card from '../src/Card';
2+
import CardHeader from '../src/CardHeader';
33
import Nav from '../src/Nav';
44
import Navbar from '../src/Navbar';
55
import NavDropdown from '../src/NavDropdown';
@@ -73,14 +73,14 @@ describe('<Nav>', () => {
7373
).assertSingle('div.navbar-nav');
7474
});
7575

76-
it('should be card aware', () => {
76+
it('should be card header aware', () => {
7777
mount(
78-
<Card>
78+
<CardHeader>
7979
<Nav variant="pills">
8080
<Nav.Link eventKey={1}>Pill 1 content</Nav.Link>
8181
<Nav.Link eventKey={2}>Pill 2 content</Nav.Link>
8282
</Nav>
83-
</Card>,
83+
</CardHeader>,
8484
).assertSingle('div.card-header-pills');
8585
});
8686

www/src/pages/components/cards.mdx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,14 @@ Use `Row`'s [grid column](/layout/grid/#row-layout-col-sizing) props to control
144144

145145
<ComponentApi metadata={props.data.Card} />
146146
<ComponentApi metadata={props.data.CardBody} exportedBy={props.data.Card} />
147+
<ComponentApi metadata={props.data.CardFooter} exportedBy={props.data.Card} />
148+
<ComponentApi metadata={props.data.CardHeader} exportedBy={props.data.Card} />
147149
<ComponentApi metadata={props.data.CardImg} exportedBy={props.data.Card} />
148150
<ComponentApi metadata={props.data.CardImgOverlay} exportedBy={props.data.Card} />
151+
<ComponentApi metadata={props.data.CardLink} exportedBy={props.data.Card} />
152+
<ComponentApi metadata={props.data.CardSubtitle} exportedBy={props.data.Card} />
153+
<ComponentApi metadata={props.data.CardText} exportedBy={props.data.Card} />
154+
<ComponentApi metadata={props.data.CardTitle} exportedBy={props.data.Card} />
149155

150156
<ComponentApi metadata={props.data.CardGroup} />
151157
<ComponentApi metadata={props.data.CardColumns} />
@@ -159,12 +165,30 @@ export const query = graphql`
159165
CardBody: componentMetadata(displayName: { eq: "CardBody" }) {
160166
...ComponentApi_metadata
161167
}
168+
CardFooter: componentMetadata(displayName: { eq: "CardFooter" }) {
169+
...ComponentApi_metadata
170+
}
171+
CardHeader: componentMetadata(displayName: { eq: "CardHeader" }) {
172+
...ComponentApi_metadata
173+
}
162174
CardImg: componentMetadata(displayName: { eq: "CardImg" }) {
163175
...ComponentApi_metadata
164176
}
165177
CardImgOverlay: componentMetadata(displayName: { eq: "CardImgOverlay" }) {
166178
...ComponentApi_metadata
167179
}
180+
CardLink: componentMetadata(displayName: { eq: "CardLink" }) {
181+
...ComponentApi_metadata
182+
}
183+
CardSubtitle: componentMetadata(displayName: { eq: "CardSubtitle" }) {
184+
...ComponentApi_metadata
185+
}
186+
CardText: componentMetadata(displayName: { eq: "CardText" }) {
187+
...ComponentApi_metadata
188+
}
189+
CardTitle: componentMetadata(displayName: { eq: "CardTitle" }) {
190+
...ComponentApi_metadata
191+
}
168192
CardGroup: componentMetadata(displayName: { eq: "CardGroup" }) {
169193
...ComponentApi_metadata
170194
}

0 commit comments

Comments
 (0)