mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-15 17:42:15 +08:00
fix(react): ensure tabs are resilient to optional tabs. (#17862)
This commit is contained in:
102
react/src/components/__tests__/IonTabs.spec.tsx
Normal file
102
react/src/components/__tests__/IonTabs.spec.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { createMemoryHistory } from 'history'
|
||||
import { IonTabs, IonTab, IonTabBar, IonTabButton, IonLabel, IonIcon} from '../index';
|
||||
import { render, cleanup } from 'react-testing-library';
|
||||
|
||||
afterEach(cleanup)
|
||||
|
||||
function renderWithRouter(
|
||||
ui: ReactElement<any>,
|
||||
{
|
||||
route = '/',
|
||||
history = createMemoryHistory({ initialEntries: [route] }),
|
||||
} = {}
|
||||
) {
|
||||
return {
|
||||
...render(<Router history={history}>{ui}</Router>),
|
||||
history
|
||||
}
|
||||
}
|
||||
|
||||
describe('IonTabs', () => {
|
||||
test('should render happy path', () => {
|
||||
const { container } = renderWithRouter(
|
||||
<IonTabs>
|
||||
<IonTab tab="schedule">Schedule Content</IonTab>
|
||||
<IonTab tab="speakers">Speakers Content</IonTab>
|
||||
<IonTab tab="map">Map Content</IonTab>
|
||||
<IonTab tab="about">About Content</IonTab>
|
||||
|
||||
<IonTabBar slot="bottom">
|
||||
<IonTabButton tab="schedule">
|
||||
<IonLabel>Schedule</IonLabel>
|
||||
<IonIcon name="schedule"></IonIcon>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="speakers">
|
||||
<IonLabel>Speakers</IonLabel>
|
||||
<IonIcon name="speakers"></IonIcon>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="map">
|
||||
<IonLabel>Map</IonLabel>
|
||||
<IonIcon name="map"></IonIcon>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="about">
|
||||
<IonLabel>About</IonLabel>
|
||||
<IonIcon name="about"></IonIcon>
|
||||
</IonTabButton>
|
||||
</IonTabBar>
|
||||
</IonTabs>
|
||||
);
|
||||
|
||||
expect(container.children[0].children.length).toEqual(2);
|
||||
expect(container.children[0].children[0].tagName).toEqual('DIV');
|
||||
expect(container.children[0].children[0].className).toEqual('tabs-inner');
|
||||
|
||||
expect(container.children[0].children[1].tagName).toEqual('ION-TAB-BAR');
|
||||
expect(container.children[0].children[1].children.length).toEqual(4);
|
||||
expect(Array.from(container.children[0].children[1].children).map(c => c.tagName)).toEqual(['ION-TAB-BUTTON', 'ION-TAB-BUTTON', 'ION-TAB-BUTTON', 'ION-TAB-BUTTON']);
|
||||
});
|
||||
|
||||
test('should allow for conditional children', () => {
|
||||
const { container } = renderWithRouter(
|
||||
<IonTabs>
|
||||
{false &&
|
||||
<IonTab tab="schedule">Schedule Content</IonTab>
|
||||
}
|
||||
<IonTab tab="speakers">Speakers Content</IonTab>
|
||||
<IonTab tab="map">Map Content</IonTab>
|
||||
<IonTab tab="about">About Content</IonTab>
|
||||
|
||||
<IonTabBar slot="bottom">
|
||||
{false &&
|
||||
<IonTabButton tab="schedule">
|
||||
<IonLabel>Schedule</IonLabel>
|
||||
<IonIcon name="schedule"></IonIcon>
|
||||
</IonTabButton>
|
||||
}
|
||||
<IonTabButton tab="speakers">
|
||||
<IonLabel>Speakers</IonLabel>
|
||||
<IonIcon name="speakers"></IonIcon>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="map">
|
||||
<IonLabel>Map</IonLabel>
|
||||
<IonIcon name="map"></IonIcon>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="about">
|
||||
<IonLabel>About</IonLabel>
|
||||
<IonIcon name="about"></IonIcon>
|
||||
</IonTabButton>
|
||||
</IonTabBar>
|
||||
</IonTabs>
|
||||
);
|
||||
|
||||
expect(container.children[0].children.length).toEqual(2);
|
||||
expect(container.children[0].children[0].tagName).toEqual('DIV');
|
||||
expect(container.children[0].children[0].className).toEqual('tabs-inner');
|
||||
|
||||
expect(container.children[0].children[1].tagName).toEqual('ION-TAB-BAR');
|
||||
expect(container.children[0].children[1].children.length).toEqual(3);
|
||||
expect(Array.from(container.children[0].children[1].children).map(c => c.tagName)).toEqual(['ION-TAB-BUTTON', 'ION-TAB-BUTTON', 'ION-TAB-BUTTON']);
|
||||
});
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Components } from '@ionic/core'
|
||||
import { Components } from '@ionic/core';
|
||||
import { createReactComponent } from '../createComponent';
|
||||
import { render, fireEvent, cleanup } from 'react-testing-library';
|
||||
|
@ -25,7 +25,7 @@ class IonTabBar extends Component<Props, State> {
|
||||
const tabActiveUrls: { [key: string]: Tab } = {};
|
||||
|
||||
React.Children.forEach(this.props.children, (child) => {
|
||||
if (typeof child === 'object' && child.type === IonTabButton) {
|
||||
if (child != null && typeof child === 'object' && child.props && child.type === IonTabButton) {
|
||||
tabActiveUrls[child.props.tab] = {
|
||||
originalHref: child.props.href,
|
||||
currentHref: child.props.href
|
||||
@ -72,12 +72,15 @@ class IonTabBar extends Component<Props, State> {
|
||||
}
|
||||
|
||||
renderChild = (activeTab: string) => (child: React.ReactElement<Components.IonTabButtonAttributes & { onIonTabButtonClick: (e: CustomEvent) => void }>) => {
|
||||
if (child != null && typeof child === 'object' && child.props && child.type === IonTabButton) {
|
||||
const href = (child.props.tab === activeTab) ? this.props.location.pathname : (this.state.tabs[child.props.tab].currentHref);
|
||||
|
||||
return React.cloneElement(child, {
|
||||
href,
|
||||
onIonTabButtonClick: this.onTabButtonClick
|
||||
})
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -32,10 +32,13 @@ export default class IonTabs extends Component<Props> {
|
||||
let tabBar: React.ReactElement<{ slot: 'bottom' | 'top' }>;
|
||||
|
||||
React.Children.forEach(this.props.children, child => {
|
||||
if (typeof child === 'object' && child.type === IonRouterOutlet) {
|
||||
if (child == null || typeof child !== 'object' || !child.hasOwnProperty('type')) {
|
||||
return;
|
||||
}
|
||||
if (child.type === IonRouterOutlet) {
|
||||
outlet = child;
|
||||
}
|
||||
if (typeof child === 'object' && child.type === IonTabBar) {
|
||||
if (child.type === IonTabBar) {
|
||||
tabBar = child;
|
||||
}
|
||||
});
|
||||
|
Reference in New Issue
Block a user