-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Link picker dismisses iOS keyboard to ensure smooth animation
Focus of the URL text input caused animation stutter while the iOS keyboard visibility toggled from hide to show to hide. To address this, the Android-specific keyboard dismiss logic is now applied to iOS as well. When the keyboard visibility changes, the `KeyboardingAvoidingView` configures a `LayoutAnimation` to manage the change. The quick toggle of hide to show to hide could result in an unconsumed `LayoutAnimation` lingering as no changes occurred which would consume the `LayoutAnimation`. Unconsumed `LayoutAnimations` appear to cause issues for React Native's Modal component on iOS. Specifically, it can result in a transparent, non-dismissible modal sitting atop the rest of the app UI. Upgrading to React Native 0.66 appears to have increased the frequency of this occurring, for an unknown reason. - https://git.io/J1W3z - https://git.io/J1W32 - https://git.io/J1W3a - https://git.io/J1W3r - https://git.io/J1W36
- Loading branch information
Showing
5 changed files
with
285 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
packages/components/src/mobile/link-settings/test/link-settings-navigation.native.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { Keyboard, Platform } from 'react-native'; | ||
import { render, fireEvent, waitFor } from 'test/helpers'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import LinkSettingsNavigation from '../link-settings-navigation'; | ||
|
||
beforeAll( () => { | ||
jest.useFakeTimers(); | ||
jest.spyOn( Keyboard, 'dismiss' ); | ||
Keyboard.dismiss.mockImplementation( () => { | ||
'>>> WHY'; | ||
} ); | ||
} ); | ||
|
||
afterAll( () => { | ||
Keyboard.dismiss.mockRestore(); | ||
} ); | ||
|
||
const subject = ( | ||
<LinkSettingsNavigation | ||
setAttributes={ () => {} } | ||
hasPicker | ||
options={ { | ||
url: { | ||
label: 'Link URL', | ||
placeholder: 'Add URL', | ||
autoFocus: false, | ||
}, | ||
} } | ||
isVisible | ||
withBottomSheet | ||
/> | ||
); | ||
|
||
describe( 'Android', () => { | ||
it( 'ensures smooth back animation', async () => { | ||
const screen = render( subject ); | ||
fireEvent.press( screen.getByText( 'Link to' ) ); | ||
fireEvent.press( | ||
screen.getByA11yLabel( 'Link to, Search or type URL' ) | ||
); | ||
// Await back button to allow async state updates to complete | ||
const backButton = await waitFor( () => | ||
screen.getByA11yLabel( 'Go back' ) | ||
); | ||
Keyboard.dismiss.mockClear(); | ||
fireEvent.press( backButton ); | ||
|
||
expect( Keyboard.dismiss ).toHaveBeenCalledTimes( 1 ); | ||
} ); | ||
|
||
it( 'ensures smooth apply animation', async () => { | ||
const screen = render( subject ); | ||
fireEvent.press( screen.getByText( 'Link to' ) ); | ||
// Await back button to allow async state updates to complete | ||
const backButton = await waitFor( () => | ||
screen.getByA11yLabel( 'Apply' ) | ||
); | ||
Keyboard.dismiss.mockClear(); | ||
fireEvent.press( backButton ); | ||
|
||
expect( Keyboard.dismiss ).toHaveBeenCalledTimes( 1 ); | ||
} ); | ||
} ); | ||
|
||
describe( 'iOS', () => { | ||
const originalPlatform = Platform.OS; | ||
beforeAll( () => { | ||
Platform.OS = 'ios'; | ||
} ); | ||
|
||
afterAll( () => { | ||
Platform.OS = originalPlatform; | ||
} ); | ||
|
||
it( 'ensures smooth back animation', async () => { | ||
const screen = render( subject ); | ||
fireEvent.press( screen.getByText( 'Link to' ) ); | ||
// Await back button to allow async state updates to complete | ||
const backButton = await waitFor( () => | ||
screen.getByA11yLabel( 'Go back' ) | ||
); | ||
Keyboard.dismiss.mockClear(); | ||
fireEvent.press( backButton ); | ||
|
||
expect( Keyboard.dismiss ).toHaveBeenCalledTimes( 1 ); | ||
} ); | ||
|
||
it( 'ensures smooth apply animation', async () => { | ||
const screen = render( subject ); | ||
fireEvent.press( screen.getByText( 'Link to' ) ); | ||
// Await back button to allow async state updates to complete | ||
const backButton = await waitFor( () => | ||
screen.getByA11yLabel( 'Apply' ) | ||
); | ||
Keyboard.dismiss.mockClear(); | ||
fireEvent.press( backButton ); | ||
|
||
expect( Keyboard.dismiss ).toHaveBeenCalledTimes( 1 ); | ||
} ); | ||
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { Keyboard, Platform } from 'react-native'; | ||
import { render, fireEvent, waitFor } from 'test/helpers'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { Slot, SlotFillProvider } from '@wordpress/components'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { link } from '../index'; | ||
|
||
const { edit: LinkEdit } = link; | ||
|
||
// Simplified tree to render link format component | ||
const LinkEditSlot = ( props ) => ( | ||
<SlotFillProvider> | ||
<Slot name="RichText.ToolbarControls.link" /> | ||
<LinkEdit { ...props } /> | ||
</SlotFillProvider> | ||
); | ||
|
||
beforeAll( () => { | ||
jest.useFakeTimers(); | ||
jest.spyOn( Keyboard, 'dismiss' ); | ||
Keyboard.dismiss.mockImplementation(); | ||
} ); | ||
|
||
afterAll( () => { | ||
Keyboard.dismiss.mockRestore(); | ||
} ); | ||
|
||
describe( 'Android', () => { | ||
it( 'ensures smooth back animation', async () => { | ||
const screen = render( | ||
<LinkEditSlot | ||
activeAttributes={ {} } | ||
onChange={ () => {} } | ||
value={ { | ||
text: '', | ||
formats: [], | ||
replacements: [], | ||
} } | ||
/> | ||
); | ||
fireEvent.press( screen.getByA11yLabel( 'Link' ) ); | ||
fireEvent.press( | ||
screen.getByA11yLabel( 'Link to, Search or type URL' ) | ||
); | ||
// Await back button to allow async state updates to complete | ||
const backButton = await waitFor( () => | ||
screen.getByA11yLabel( 'Go back' ) | ||
); | ||
Keyboard.dismiss.mockClear(); | ||
fireEvent.press( backButton ); | ||
|
||
expect( Keyboard.dismiss ).toHaveBeenCalledTimes( 1 ); | ||
} ); | ||
|
||
it( 'ensures smooth apply animation', async () => { | ||
const { getByA11yLabel } = render( | ||
<LinkEditSlot | ||
activeAttributes={ {} } | ||
onChange={ () => {} } | ||
value={ { | ||
text: '', | ||
formats: [], | ||
replacements: [], | ||
} } | ||
/> | ||
); | ||
fireEvent.press( getByA11yLabel( 'Link' ) ); | ||
fireEvent.press( getByA11yLabel( 'Link to, Search or type URL' ) ); | ||
// Await back button to allow async state updates to complete | ||
const backButton = await waitFor( () => getByA11yLabel( 'Apply' ) ); | ||
Keyboard.dismiss.mockClear(); | ||
fireEvent.press( backButton ); | ||
|
||
expect( Keyboard.dismiss ).toHaveBeenCalledTimes( 1 ); | ||
} ); | ||
} ); | ||
|
||
describe( 'iOS', () => { | ||
const originalPlatform = Platform.OS; | ||
beforeAll( () => { | ||
Platform.OS = 'ios'; | ||
} ); | ||
|
||
afterAll( () => { | ||
Platform.OS = originalPlatform; | ||
} ); | ||
|
||
it( 'ensures smooth back animation', async () => { | ||
const screen = render( | ||
<LinkEditSlot | ||
activeAttributes={ {} } | ||
onChange={ () => {} } | ||
value={ { | ||
text: '', | ||
formats: [], | ||
replacements: [], | ||
} } | ||
/> | ||
); | ||
fireEvent.press( screen.getByA11yLabel( 'Link' ) ); | ||
fireEvent.press( | ||
screen.getByA11yLabel( 'Link to, Search or type URL' ) | ||
); | ||
// Await back button to allow async state updates to complete | ||
const backButton = await waitFor( () => | ||
screen.getByA11yLabel( 'Go back' ) | ||
); | ||
Keyboard.dismiss.mockClear(); | ||
fireEvent.press( backButton ); | ||
|
||
expect( Keyboard.dismiss ).toHaveBeenCalledTimes( 1 ); | ||
} ); | ||
|
||
it( 'ensures smooth apply animation', async () => { | ||
const { getByA11yLabel } = render( | ||
<LinkEditSlot | ||
activeAttributes={ {} } | ||
onChange={ () => {} } | ||
value={ { | ||
text: '', | ||
formats: [], | ||
replacements: [], | ||
} } | ||
/> | ||
); | ||
fireEvent.press( getByA11yLabel( 'Link' ) ); | ||
fireEvent.press( getByA11yLabel( 'Link to, Search or type URL' ) ); | ||
// Await back button to allow async state updates to complete | ||
const backButton = await waitFor( () => getByA11yLabel( 'Apply' ) ); | ||
Keyboard.dismiss.mockClear(); | ||
fireEvent.press( backButton ); | ||
|
||
expect( Keyboard.dismiss ).toHaveBeenCalledTimes( 1 ); | ||
} ); | ||
} ); |