From bb374460320b0ba2ee03a5a0ecebb3e7a9f0728e Mon Sep 17 00:00:00 2001 From: Amanda Johnston <90629384+amandaejohnston@users.noreply.github.com> Date: Tue, 16 Aug 2022 13:22:43 -0500 Subject: [PATCH] fix(footer): remove toolbar bottom padding if near bottom slot tabs or keyboard is open (#25746) Co-authored-by: EinfachHans --- core/src/components/footer/footer.scss | 2 +- core/src/components/footer/footer.tsx | 23 +++++- .../footer/test/with-tabs/footer.e2e.ts | 13 ++++ ...-with-tabs-ios-ltr-Mobile-Chrome-linux.png | Bin 0 -> 3572 bytes ...with-tabs-ios-ltr-Mobile-Firefox-linux.png | Bin 0 -> 1856 bytes ...-with-tabs-ios-ltr-Mobile-Safari-linux.png | Bin 0 -> 3501 bytes ...r-with-tabs-md-ltr-Mobile-Chrome-linux.png | Bin 0 -> 3944 bytes ...-with-tabs-md-ltr-Mobile-Firefox-linux.png | Bin 0 -> 2008 bytes ...r-with-tabs-md-ltr-Mobile-Safari-linux.png | Bin 0 -> 3831 bytes .../footer/test/with-tabs/index.html | 71 ++++++++++++++++++ core/src/components/tab-bar/tab-bar.tsx | 34 +++------ .../src/utils/keyboard/keyboard-controller.ts | 48 ++++++++++++ .../keyboard/test/keyboard-controller.spec.ts | 24 ++++++ 13 files changed, 190 insertions(+), 25 deletions(-) create mode 100644 core/src/components/footer/test/with-tabs/footer.e2e.ts create mode 100644 core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-ios-ltr-Mobile-Chrome-linux.png create mode 100644 core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-ios-ltr-Mobile-Firefox-linux.png create mode 100644 core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-ios-ltr-Mobile-Safari-linux.png create mode 100644 core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-md-ltr-Mobile-Chrome-linux.png create mode 100644 core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-md-ltr-Mobile-Firefox-linux.png create mode 100644 core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-md-ltr-Mobile-Safari-linux.png create mode 100644 core/src/components/footer/test/with-tabs/index.html create mode 100644 core/src/utils/keyboard/keyboard-controller.ts create mode 100644 core/src/utils/keyboard/test/keyboard-controller.spec.ts diff --git a/core/src/components/footer/footer.scss b/core/src/components/footer/footer.scss index 9682747b41..c6b9c24d15 100644 --- a/core/src/components/footer/footer.scss +++ b/core/src/components/footer/footer.scss @@ -14,6 +14,6 @@ ion-footer { z-index: $z-index-toolbar; } -ion-footer ion-toolbar:last-of-type { +ion-footer.footer-toolbar-padding ion-toolbar:last-of-type { padding-bottom: var(--ion-safe-area-bottom, 0); } \ No newline at end of file diff --git a/core/src/components/footer/footer.tsx b/core/src/components/footer/footer.tsx index 7e0ae4b9ab..a52eccccff 100644 --- a/core/src/components/footer/footer.tsx +++ b/core/src/components/footer/footer.tsx @@ -1,8 +1,10 @@ import type { ComponentInterface } from '@stencil/core'; -import { Component, Element, Host, Prop, h } from '@stencil/core'; +import { Component, Element, Host, Prop, State, h } from '@stencil/core'; import { getIonMode } from '../../global/ionic-global'; import { findIonContent, getScrollElement, printIonContentErrorMsg } from '../../utils/content'; +import type { KeyboardController } from '../../utils/keyboard/keyboard-controller'; +import { createKeyboardController } from '../../utils/keyboard/keyboard-controller'; import { handleFooterFade } from './footer.utils'; @@ -19,6 +21,9 @@ import { handleFooterFade } from './footer.utils'; export class Footer implements ComponentInterface { private scrollEl?: HTMLElement; private contentScrollCallback: any; + private keyboardCtrl: KeyboardController | null = null; + + @State() private keyboardVisible = false; @Element() el!: HTMLIonFooterElement; @@ -46,6 +51,18 @@ export class Footer implements ComponentInterface { this.checkCollapsibleFooter(); } + connectedCallback() { + this.keyboardCtrl = createKeyboardController((keyboardOpen) => { + this.keyboardVisible = keyboardOpen; // trigger re-render by updating state + }); + } + + disconnectedCallback() { + if (this.keyboardCtrl) { + this.keyboardCtrl.destroy(); + } + } + private checkCollapsibleFooter = () => { const mode = getIonMode(this); if (mode !== 'ios') { @@ -94,6 +111,9 @@ export class Footer implements ComponentInterface { render() { const { translucent, collapse } = this; const mode = getIonMode(this); + const tabs = this.el.closest('ion-tabs'); + const tabBar = tabs?.querySelector('ion-tab-bar'); + return ( { + test('should not have extra padding when near a tab bar', async ({ page }, testInfo) => { + test.skip(testInfo.project.metadata.rtl === true, 'This does not test LTR vs. RTL layout.'); + + await page.goto('/src/components/footer/test/with-tabs'); + + const footer = page.locator('[tab="tab-one"] ion-footer'); + expect(await footer.screenshot()).toMatchSnapshot(`footer-with-tabs-${page.getSnapshotSettings()}.png`); + }); +}); diff --git a/core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..e6e81adaa0ed6e1e91fb2493ded07f51a00a762a GIT binary patch literal 3572 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 D&K!2$ literal 0 HcmV?d00001 diff --git a/core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..15dc2a449245c949d94260c82344c73a7eecc511 GIT binary patch literal 1856 zcmW-icT^MU8pekr1QtY^6ongP1dT}11r~!+jNrJmphy+zp$K>c0t-tEHBt zIv6iPVgaI3WR;d(2ttTK)-@ERC0?{m>Ki?_?sj%Rn zQC#kZxnvsXpo~>JXEz++wa{y)tgJy%SIC!>3fdos4}57hmLkfGOy)T47~RZVX<3aq zSU|s$K)*O)IbY*jk+{Ns<{oSdNbbu3q)|wcr@K1=d}@vaBoMZL*h`)#9FY34bV9{e zg?&&gLS}$ZWq+(AttC*zwc&CJSc^21CSiiyo&OL^dR1*z=I)t^ciILaki`DF3R^wc zG21W$>^&JQBaH+y`}f(Z5FztSd$=^h6@apFuN4q*4x(NLsB$|}G6$}-uLdYZj1_8$ zJyp&E5NN=ZmKPVZmH?G&v`&gsfVn{Fd8d{*ONP>a&UjT?M4ymhJaAM(|Mo4Ue$N(f z@nTo}Vrjal3HzWenZI-gZ%o&eoOS@95MPIkbMI@On;;}=NI z)u)&*M9fytGBWvU#QZ-6dZcLhc(l;$&)W|`?nou>wxO%>`n?MCC?7jJyL-W_voD@h zTE_ZBL^LNX{ym^=8RMrnO}0sA33oP^=lb&eAI2{*UbT3%mI=PhkCB(x=K3sBHm=#= zH&?E-dOq^E!gifJg^JD{8(AvVXHl`FLyfUCylpN6qo8#21mnx7x3Pc8F>foY_7VdIt*cdy=zPj z9V+u?ws;7)KD^3W{`P6eB5}np<@;8vCHV0^VYC2e+&qI5X`QNNs3@%gr7HwJy zUOmZFk&$xzGK*>8QWfraOP3$obj*|D1t)R(_0m#ocbbspa7(wk)VZOizZ#v`h8wH# zZ$D_A#CTn(^>N;9PkX}W%TcV(W=}`Vl%42VG zQ=`o=GdP^n{VXeGom)hWdCy9pJTZ|ir|PGsq-(u)Az-jr=jcnHx2z7_txZEvr^u<) zZNN12t=iDg(0XWHz`NW1*Z=)fncP}$BzJ^_2@I^&LF0|4h`PsKxp z2P`S<*g1IEEcq&(-5o&#@_BU}3`2<#D_GM1zAK!WZ1WB-&@iFpDjx8GAfjmUMI^ID zZ3^!6G~Drr4n^AZVZI?$M>e?_)@f89sZYu-dj#sqNZ-f-O8>m$ba?BOh-O&(N&mQh z1e>0U_Bb7m!|rS>3%4?*WUqahYTx}ZiQ{eeq--`Uj8<8V+dLS%`Xa5T3Y@4!;EkJo z7%r_o)X440DccK+&4SpE+7P(qF!~px1XFBa4BZtzDP_?9hX2$(8nwYhe}rr^i(lm| zD}9Z6NAaGaMIR?+q7=*?R#BxKMRJ7OD|NR&m@U^4Y;W z1+9O2-QvE_Jc-?vtf7rZB8gs{3L-)t0uPNm(+Sap!apyT9rwL9-r(F^L!dPpd;Ra_ zeq%o`?l~*-27W1fdwpIwN0CjPrKd*Ek5=zW`U^;YwCEWwn%tS{;Tvk+nS+N(U~C}g zc-A+h(UrV{ZXsx_lQ1!}Sr9!LEcy4#pf#Rm$7K zpTwBu!kS@KrzA0d@58*tE|_VEnLaFsB$gYNuZj7Kfq8wO5o|2>EQh&o=4v`;2Qxk2 z9)K{lgMMO9Gno59H37X@Vy43bm2ZFRnb`a&+Uw_e6gctoe-~ frz9?>CMg1bFW9r$_KR#2d@};jITsAYK7jCl^}1Yk literal 0 HcmV?d00001 diff --git a/core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-ios-ltr-Mobile-Safari-linux.png b/core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..a9ea0358ce9f445014dde940f99343da4a2eaca3 GIT binary patch literal 3501 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 GjQS@UREymJ literal 0 HcmV?d00001 diff --git a/core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-md-ltr-Mobile-Chrome-linux.png b/core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..bdab430c251f44411b679267e53e5fdbd8f63550 GIT binary patch literal 3944 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 literal 0 HcmV?d00001 diff --git a/core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-md-ltr-Mobile-Safari-linux.png b/core/src/components/footer/test/with-tabs/footer.e2e.ts-snapshots/footer-with-tabs-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..6d0b17bdf3cc0cba2f3a8c5c42c17844f623f86c GIT binary patch literal 3831 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 literal 0 HcmV?d00001 diff --git a/core/src/components/footer/test/with-tabs/index.html b/core/src/components/footer/test/with-tabs/index.html new file mode 100644 index 0000000000..a1bc4b9ff5 --- /dev/null +++ b/core/src/components/footer/test/with-tabs/index.html @@ -0,0 +1,71 @@ + + + + + Footer - With Tabs + + + + + + + + + + + + + + + + Tab One + + + +

Tab One

+
+ + + Footer + + +
+ + + + + Tab Two + + + +

Tab Two

+
+ + + Footer + + +
+ + + + Tab One + + + + + Tab Two + + + +
+
+ + diff --git a/core/src/components/tab-bar/tab-bar.tsx b/core/src/components/tab-bar/tab-bar.tsx index 2eb7251927..2ca61f5eb5 100644 --- a/core/src/components/tab-bar/tab-bar.tsx +++ b/core/src/components/tab-bar/tab-bar.tsx @@ -1,5 +1,7 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core'; import { Component, Element, Event, Host, Prop, State, Watch, h } from '@stencil/core'; +import type { KeyboardController } from '@utils/keyboard/keyboard-controller'; +import { createKeyboardController } from '@utils/keyboard/keyboard-controller'; import { getIonMode } from '../../global/ionic-global'; import type { Color, TabBarChangedEventDetail } from '../../interface'; @@ -17,8 +19,7 @@ import { createColorClasses } from '../../utils/theme'; shadow: true, }) export class TabBar implements ComponentInterface { - private keyboardWillShowHandler?: () => void; - private keyboardWillHideHandler?: () => void; + private keyboardCtrl: KeyboardController | null = null; @Element() el!: HTMLElement; @@ -59,43 +60,30 @@ export class TabBar implements ComponentInterface { } connectedCallback() { - if (typeof (window as any) !== 'undefined') { - this.keyboardWillShowHandler = () => { - if (this.el.getAttribute('slot') !== 'top') { - this.keyboardVisible = true; - } - }; - - this.keyboardWillHideHandler = () => { - setTimeout(() => (this.keyboardVisible = false), 50); - }; - - window.addEventListener('keyboardWillShow', this.keyboardWillShowHandler!); - window.addEventListener('keyboardWillHide', this.keyboardWillHideHandler!); - } + this.keyboardCtrl = createKeyboardController((keyboardOpen) => { + this.keyboardVisible = keyboardOpen; // trigger re-render by updating state + }); } disconnectedCallback() { - if (typeof (window as any) !== 'undefined') { - window.removeEventListener('keyboardWillShow', this.keyboardWillShowHandler!); - window.removeEventListener('keyboardWillHide', this.keyboardWillHideHandler!); - - this.keyboardWillShowHandler = this.keyboardWillHideHandler = undefined; + if (this.keyboardCtrl) { + this.keyboardCtrl.destroy(); } } render() { const { color, translucent, keyboardVisible } = this; const mode = getIonMode(this); + const shouldHide = keyboardVisible && this.el.getAttribute('slot') !== 'top'; return ( diff --git a/core/src/utils/keyboard/keyboard-controller.ts b/core/src/utils/keyboard/keyboard-controller.ts new file mode 100644 index 0000000000..845bb6f1d0 --- /dev/null +++ b/core/src/utils/keyboard/keyboard-controller.ts @@ -0,0 +1,48 @@ +import { win } from '../window'; + +/** + * Creates a controller that tracks and reacts to opening or closing the keyboard. + * + * @internal + * @param keyboardChangeCallback A function to call when the keyboard opens or closes. + */ +export const createKeyboardController = ( + keyboardChangeCallback?: (keyboardOpen: boolean) => void +): KeyboardController => { + let keyboardWillShowHandler: (() => void) | undefined; + let keyboardWillHideHandler: (() => void) | undefined; + let keyboardVisible: boolean; + + const init = () => { + keyboardWillShowHandler = () => { + keyboardVisible = true; + if (keyboardChangeCallback) keyboardChangeCallback(true); + }; + + keyboardWillHideHandler = () => { + keyboardVisible = false; + if (keyboardChangeCallback) keyboardChangeCallback(false); + }; + + win?.addEventListener('keyboardWillShow', keyboardWillShowHandler); + win?.addEventListener('keyboardWillHide', keyboardWillHideHandler); + }; + + const destroy = () => { + win?.removeEventListener('keyboardWillShow', keyboardWillShowHandler!); + win?.removeEventListener('keyboardWillHide', keyboardWillHideHandler!); + + keyboardWillShowHandler = keyboardWillHideHandler = undefined; + }; + + const isKeyboardVisible = () => keyboardVisible; + + init(); + return { init, destroy, isKeyboardVisible }; +}; + +export type KeyboardController = { + init: () => void; + destroy: () => void; + isKeyboardVisible: () => boolean; +}; diff --git a/core/src/utils/keyboard/test/keyboard-controller.spec.ts b/core/src/utils/keyboard/test/keyboard-controller.spec.ts new file mode 100644 index 0000000000..e623ac9d10 --- /dev/null +++ b/core/src/utils/keyboard/test/keyboard-controller.spec.ts @@ -0,0 +1,24 @@ +import { createKeyboardController } from '../keyboard-controller'; + +describe('Keyboard Controller', () => { + it('should update isKeyboardVisible', () => { + const keyboardCtrl = createKeyboardController(); + + window.dispatchEvent(new Event('keyboardWillShow')); + expect(keyboardCtrl.isKeyboardVisible()).toBe(true); + + window.dispatchEvent(new Event('keyboardWillHide')); + expect(keyboardCtrl.isKeyboardVisible()).toBe(false); + }); + + it('should run the callback', () => { + const callbackMock = jest.fn(); + createKeyboardController(callbackMock); + + window.dispatchEvent(new Event('keyboardWillShow')); + expect(callbackMock).toHaveBeenCalledWith(true); + + window.dispatchEvent(new Event('keyboardWillHide')); + expect(callbackMock).toHaveBeenCalledWith(false); + }); +});