mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 19:21:34 +08:00
fix(react): IonTabButton will call custom onClick handlers (#25313)
Resolves #22511
This commit is contained in:
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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')
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
@ -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": {
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
23
packages/react/test-app/src/pages/Tabs.tsx
Normal file
23
packages/react/test-app/src/pages/Tabs.tsx
Normal 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;
|
Reference in New Issue
Block a user