Skip to content

Commit f71a654

Browse files
authored
test: Move Tabs and ThemeProvider specs to rtl (react-bootstrap#6178)
* migrate ThemeProviderSpec to rtl * further changes * more tabs tests * further changes * reinstate commented out test * cr suggestions
1 parent 85028b7 commit f71a654

File tree

5 files changed

+276
-214
lines changed

5 files changed

+276
-214
lines changed

src/ThemeProvider.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export interface ThemeContextValue {
88
}
99

1010
export interface ThemeProviderProps extends Partial<ThemeContextValue> {
11-
children: React.ElementType;
11+
children: React.ReactNode;
1212
}
1313

1414
const ThemeContext = React.createContext<ThemeContextValue>({ prefixes: {} });
@@ -50,11 +50,13 @@ function createBootstrapComponent(Component, opts) {
5050
// If it's a functional component make sure we don't break it with a ref
5151
const { prefix, forwardRefAs = isClassy ? 'ref' : 'innerRef' } = opts;
5252

53-
const Wrapped = React.forwardRef(({ ...props }, ref) => {
54-
props[forwardRefAs] = ref;
55-
const bsPrefix = useBootstrapPrefix((props as any).bsPrefix, prefix);
56-
return <Component {...props} bsPrefix={bsPrefix} />;
57-
});
53+
const Wrapped = React.forwardRef<any, { bsPrefix?: string }>(
54+
({ ...props }, ref) => {
55+
props[forwardRefAs] = ref;
56+
const bsPrefix = useBootstrapPrefix((props as any).bsPrefix, prefix);
57+
return <Component {...props} bsPrefix={bsPrefix} />;
58+
},
59+
);
5860

5961
Wrapped.displayName = `Bootstrap(${Component.displayName || Component.name})`;
6062
return Wrapped;

test/TabsSpec.js

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

test/TabsSpec.tsx

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import { fireEvent, render } from '@testing-library/react';
2+
import sinon from 'sinon';
3+
import React from 'react';
4+
5+
import Tab from '../src/Tab';
6+
import Tabs from '../src/Tabs';
7+
8+
import { shouldWarn } from './helpers';
9+
10+
const checkEventKey = (elem: Element, eventKey: string | number) =>
11+
elem.getAttribute('data-rr-ui-event-key') === `${eventKey}` &&
12+
elem.getAttribute('id') === `test-tab-${eventKey}` &&
13+
elem.getAttribute('aria-controls') === `test-tabpane-${eventKey}`;
14+
15+
describe('<Tabs>', () => {
16+
it('Should show the correct tab and assign correct eventKeys', () => {
17+
const { getByText } = render(
18+
<Tabs id="test" defaultActiveKey={1}>
19+
<Tab title="Tab 1 title" eventKey={1}>
20+
Tab 1 content
21+
</Tab>
22+
<Tab title="Tab 2 title" eventKey={2}>
23+
Tab 2 content
24+
</Tab>
25+
</Tabs>,
26+
);
27+
const firstTabButton = getByText('Tab 1 title');
28+
const firstTabContent = getByText('Tab 1 content');
29+
const secondTabButton = getByText('Tab 2 title');
30+
31+
firstTabButton.tagName.toLowerCase().should.equal('button');
32+
firstTabButton.classList.contains('active').should.be.true;
33+
firstTabContent.classList.contains('active').should.be.true;
34+
35+
secondTabButton.classList.contains('active').should.be.false;
36+
secondTabButton.tagName.toLowerCase().should.equal('button');
37+
38+
checkEventKey(firstTabButton, 1).should.be.true;
39+
checkEventKey(secondTabButton, 2).should.be.true;
40+
});
41+
42+
it('should get defaultActiveKey (if null) from first child tab with eventKey', () => {
43+
const { getByText } = render(
44+
<Tabs id="test" data-testid="test-id">
45+
<Tab title="Tab 1 title" eventKey={1}>
46+
Tab 1 content
47+
</Tab>
48+
<Tab title="Tab 2 title" eventKey={2}>
49+
Tab 2 content
50+
</Tab>
51+
</Tabs>,
52+
);
53+
54+
const firstTabButton = getByText('Tab 1 title');
55+
const firstTabContent = getByText('Tab 1 content');
56+
const secondTabButton = getByText('Tab 2 title');
57+
58+
firstTabButton.tagName.toLowerCase().should.equal('button');
59+
firstTabButton.classList.contains('active').should.be.true;
60+
firstTabContent.classList.contains('active').should.be.true;
61+
62+
secondTabButton.classList.contains('active').should.be.false;
63+
secondTabButton.tagName.toLowerCase().should.equal('button');
64+
});
65+
66+
it('Should allow tab title to have React components', () => {
67+
const tabTitle = <strong className="special-tab">React Tab 2</strong>;
68+
69+
const { getByText } = render(
70+
<Tabs id="test" defaultActiveKey={2}>
71+
<Tab title="Tab 1" eventKey={1}>
72+
Tab 1 content
73+
</Tab>
74+
<Tab title={tabTitle} eventKey={2}>
75+
Tab 2 content
76+
</Tab>
77+
</Tabs>,
78+
);
79+
getByText('React Tab 2').classList.contains('special-tab').should.be.true;
80+
});
81+
82+
it('Should call onSelect when tab is selected', () => {
83+
const onSelect = (key) => {
84+
key.should.equal('2');
85+
};
86+
const onSelectSpy = sinon.spy(onSelect);
87+
88+
const { getByText } = render(
89+
<Tabs id="test" onSelect={onSelectSpy} activeKey={1}>
90+
<Tab title="Tab 1" eventKey="1">
91+
Tab 1 content
92+
</Tab>
93+
<Tab title="Tab 2" eventKey="2">
94+
Tab 2 content
95+
</Tab>
96+
</Tabs>,
97+
);
98+
99+
fireEvent.click(getByText('Tab 2'));
100+
onSelectSpy.should.have.been.called;
101+
});
102+
103+
it('Should have children with the correct DOM properties', () => {
104+
const { getByText } = render(
105+
<Tabs id="test" defaultActiveKey={1}>
106+
<Tab title="Tab 1" className="custom" eventKey={1}>
107+
Tab 1 content
108+
</Tab>
109+
<Tab title="Tab 2" tabClassName="tcustom" eventKey={2}>
110+
Tab 2 content
111+
</Tab>
112+
</Tabs>,
113+
);
114+
const firstTabContent = getByText('Tab 1 content');
115+
const secondTabContent = getByText('Tab 2 content');
116+
117+
const firstTabTitle = getByText('Tab 1');
118+
const secondTabTitle = getByText('Tab 2');
119+
120+
firstTabContent.classList.contains('custom').should.be.true;
121+
secondTabContent.classList.contains('tcustom').should.be.false;
122+
123+
firstTabTitle.classList.contains('custom').should.be.false;
124+
secondTabTitle.classList.contains('tcustom').should.be.true;
125+
});
126+
127+
it('Should pass variant to Nav', () => {
128+
const { getByTestId } = render(
129+
<Tabs
130+
data-testid="test"
131+
variant="pills"
132+
defaultActiveKey={1}
133+
transition={false}
134+
>
135+
<Tab title="Tab 1" eventKey={1}>
136+
Tab 1 content
137+
</Tab>
138+
<Tab title="Tab 2" eventKey={2}>
139+
Tab 2 content
140+
</Tab>
141+
</Tabs>,
142+
);
143+
144+
getByTestId('test').classList.contains('nav-pills').should.be.true;
145+
});
146+
147+
it('Should pass disabled to Nav', () => {
148+
const onSelect = (e) => e;
149+
const onSelectSpy = sinon.spy(onSelect);
150+
151+
const { getByText } = render(
152+
<Tabs id="test" defaultActiveKey={1} onSelect={onSelectSpy}>
153+
<Tab title="Tab 1" eventKey={1}>
154+
Tab 1 content
155+
</Tab>
156+
<Tab title="Tab 2" eventKey={2} disabled>
157+
Tab 2 content
158+
</Tab>
159+
</Tabs>,
160+
);
161+
const secondTabTitle = getByText('Tab 2');
162+
secondTabTitle.classList.contains('disabled').should.be.true;
163+
164+
onSelectSpy.should.not.have.been.called;
165+
});
166+
167+
it('Should not render a Tab without a title', () => {
168+
shouldWarn('Failed prop');
169+
const { getByTestId } = render(
170+
<Tabs data-testid="testid" id="test" defaultActiveKey={1}>
171+
<Tab eventKey={1}>Tab 1 content</Tab>
172+
<Tab title="Tab 2" eventKey={2} disabled>
173+
Tab 2 content
174+
</Tab>
175+
</Tabs>,
176+
);
177+
const tabs = getByTestId('testid');
178+
tabs.children.should.have.length(1);
179+
});
180+
});
181+
182+
// describe('<Tab>', () => {
183+
// it('should throw error message on attempt to render', () => {
184+
// expect(() =>
185+
// render(
186+
// <Tab title="Tab 1" eventKey={1}>
187+
// Tab 1 content
188+
// </Tab>,
189+
// ),
190+
// ).to.throw();
191+
// });
192+
// });

0 commit comments

Comments
 (0)