fix(react): IonTabButton will call custom onClick handlers (#25313)

Resolves #22511
This commit is contained in:
Sean Perkins
2022-05-19 12:19:34 -04:00
committed by GitHub
parent 51f3179bcc
commit 6034418b33
7 changed files with 64 additions and 4 deletions

View File

@ -178,12 +178,22 @@ class IonTabBarUnwrapped extends React.PureComponent<InternalProps, IonTabBarSta
} }
private onTabButtonClick( private onTabButtonClick(
e: CustomEvent<{ href: string; selected: boolean; tab: string; routeOptions: unknown }> e: CustomEvent<{ href: string; selected: boolean; tab: string; routeOptions: unknown }>,
onClickFn?: (e: any) => void
) { ) {
const tappedTab = this.state.tabs[e.detail.tab]; const tappedTab = this.state.tabs[e.detail.tab];
const originalHref = tappedTab.originalHref; const originalHref = tappedTab.originalHref;
const currentHref = e.detail.href; const currentHref = e.detail.href;
const { activeTab: prevActiveTab } = this.state; const { activeTab: prevActiveTab } = this.state;
if (onClickFn) {
/**
* If the user provides an onClick function, we call it
* with the original event.
*/
onClickFn(e);
}
// this.props.onSetCurrentTab(e.detail.tab, this.props.routeInfo); // this.props.onSetCurrentTab(e.detail.tab, this.props.routeInfo);
// Clicking the current tab will bring you back to the original href // Clicking the current tab will bring you back to the original href
if (prevActiveTab === e.detail.tab) { if (prevActiveTab === e.detail.tab) {
@ -232,7 +242,7 @@ class IonTabBarUnwrapped extends React.PureComponent<InternalProps, IonTabBarSta
return React.cloneElement(child, { return React.cloneElement(child, {
href, href,
routeOptions, routeOptions,
onClick: this.onTabButtonClick, onClick: (ev: CustomEvent) => this.onTabButtonClick(ev, child.props.onClick),
}); });
} }
return null; return null;

View File

@ -34,6 +34,11 @@ export const IonTabButton = /*@__PURE__*/ (() =>
} }
render() { render() {
/**
* onClick is excluded from the props, since it has a custom
* implementation within IonTabBar.tsx. Calling onClick within this
* component would result in duplicate handler calls.
*/
const { onClick, ...rest } = this.props; const { onClick, ...rest } = this.props;
return ( return (
<IonTabButtonInner <IonTabButtonInner

View File

@ -0,0 +1,15 @@
describe('IonTabs', () => {
beforeEach(() => {
cy.visit('/tabs/tab1');
});
it('should handle onClick handlers on IonTabButton', () => {
const stub = cy.stub();
cy.on('window:alert', stub);
cy.get('ion-tab-button[tab="tab1"]').click().then(() => {
expect(stub.getCall(0)).to.be.calledWith('Tab was clicked')
});
});
});

View File

@ -23,6 +23,7 @@
"eject": "react-scripts eject", "eject": "react-scripts eject",
"sync": "sh ./scripts/sync.sh", "sync": "sh ./scripts/sync.sh",
"cypress": "cypress run --headless --browser chrome", "cypress": "cypress run --headless --browser chrome",
"cypress.open": "cypress open",
"e2e": "concurrently \"serve -s build -l 3000\" \"wait-on http-get://localhost:3000 && npm run cypress\" --kill-others --success first" "e2e": "concurrently \"serve -s build -l 3000\" \"wait-on http-get://localhost:3000 && npm run cypress\" --kill-others --success first"
}, },
"eslintConfig": { "eslintConfig": {

View File

@ -24,6 +24,7 @@ import './theme/variables.css';
import Main from './pages/Main'; import Main from './pages/Main';
import OverlayHooks from './pages/overlay-hooks/OverlayHooks'; import OverlayHooks from './pages/overlay-hooks/OverlayHooks';
import OverlayComponents from './pages/overlay-components/OverlayComponents'; import OverlayComponents from './pages/overlay-components/OverlayComponents';
import Tabs from './pages/Tabs';
setupIonicReact(); setupIonicReact();
@ -34,6 +35,7 @@ const App: React.FC = () => (
<Route path="/" component={Main} /> <Route path="/" component={Main} />
<Route path="/overlay-hooks" component={OverlayHooks} /> <Route path="/overlay-hooks" component={OverlayHooks} />
<Route path="/overlay-components" component={OverlayComponents} /> <Route path="/overlay-components" component={OverlayComponents} />
<Route path="/tabs" component={Tabs} />
</IonRouterOutlet> </IonRouterOutlet>
</IonReactRouter> </IonReactRouter>
</IonApp> </IonApp>

View File

@ -4,11 +4,10 @@ import {
IonHeader, IonHeader,
IonLabel, IonLabel,
IonPage, IonPage,
IonTitle, IonTitle,
IonToolbar, IonToolbar,
IonItem, IonItem,
IonList IonList,
} from '@ionic/react'; } from '@ionic/react';
interface MainProps {} interface MainProps {}
@ -32,6 +31,11 @@ const Main: React.FC<MainProps> = () => {
<IonLabel>Overlay Components</IonLabel> <IonLabel>Overlay Components</IonLabel>
</IonItem> </IonItem>
</IonList> </IonList>
<IonList>
<IonItem routerLink="/tabs">
<IonLabel>Tabs</IonLabel>
</IonItem>
</IonList>
</IonContent> </IonContent>
</IonPage> </IonPage>
); );

View File

@ -0,0 +1,23 @@
import React from 'react';
import { IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
import { Route, Redirect } from 'react-router';
interface TabsProps {}
const Tabs: React.FC<TabsProps> = () => {
return (
<IonTabs>
<IonRouterOutlet>
<Redirect from="/tabs" to="/tabs/tab1" exact />
<Route path="/tabs/tab1" render={() => <IonLabel>Tab 1</IonLabel>} />
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton tab="tab1" onClick={() => window.alert('Tab was clicked')}>
<IonLabel>Click Handler</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
);
};
export default Tabs;