-
-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## 📜 Description Added an ability to dismiss keyboard while keeping focus. ## 💡 Motivation and Context <!-- Why is this change required? What problem does it solve? --> <!-- If it fixes an open issue, please link to the issue here. --> ## 📢 Changelog <!-- High level overview of important changes --> <!-- For example: fixed status bar manipulation; added new types declarations; --> <!-- If your changes don't affect one of platform/language below - then remove this platform/language --> ### Docs - added a note about using `keepFocus` flag; ### E2E - cover "Close" screen with e2e test; ### JS - added `DismissOptions` to `dismiss` method; - update spec and include `keepFocus` flag; - update examples app; ### iOS - added `KeyboardControllerModuleImpl` file; - attach resign listener + tap gesture if field was dismissed with `keepFocus`; - remove resign listener and tap gesture if keyboard opens again or if responder was resigned; - enhance `TextInput` protocol with new props; - remove `inputView` when we call `setFocusTo("current")`; ### Android - call `view.clearFocus()` conditionally depending on `keepFocus` flag; ## 🤔 How Has This Been Tested? Tested manually on: - Pixel 3a (API 33, emulator); - iPhone 15 Pro (iOS 17.5, simulator) ## 📸 Screenshots (if appropriate): |Keep focus|Android|iOS| |-----------|--------|----| |Yes|<video src="https://github.com/user-attachments/assets/a77bd617-22a3-4143-9adf-007de3c8e4e9">|<video src="https://github.com/user-attachments/assets/5b234a0f-fc65-4245-b1b9-21cb90c86a57">| |No|<video src="https://github.com/user-attachments/assets/3b06a0bf-534c-4ac3-aa93-03aa28d8dc34">|<video src="https://github.com/user-attachments/assets/9f809514-f23e-4f8e-a780-91a3f648bdf8">| ## 📝 Checklist - [x] CI successfully passed - [x] I added new mocks and corresponding unit-tests if library API was changed
- Loading branch information
1 parent
4886f74
commit 62b76d5
Showing
27 changed files
with
225 additions
and
21 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
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
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
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,64 @@ | ||
import { expect } from "detox"; | ||
|
||
import { expectBitmapsToBeEqual } from "./asserts"; | ||
import { | ||
scrollDownUntilElementIsVisible, | ||
waitAndTap, | ||
waitForExpect, | ||
} from "./helpers"; | ||
|
||
describe("`KeyboardController.dismiss()` specification", () => { | ||
it("should navigate to `CloseKeyboard` screen", async () => { | ||
await scrollDownUntilElementIsVisible("main_scroll_view", "close"); | ||
await waitAndTap("close"); | ||
}); | ||
|
||
it("should show keyboard", async () => { | ||
await waitAndTap("input"); | ||
await waitForExpect(async () => { | ||
await expectBitmapsToBeEqual("CloseKeyboardOpened"); | ||
}); | ||
}); | ||
|
||
it("should dismiss keyboard loosing focus", async () => { | ||
await waitAndTap("close_keyboard_button"); | ||
await expect(element(by.id("input"))).not.toBeFocused(); | ||
}); | ||
|
||
it("should show keyboard again when input tapped", async () => { | ||
await waitAndTap("input"); | ||
await waitForExpect(async () => { | ||
await expectBitmapsToBeEqual("CloseKeyboardOpened"); | ||
}); | ||
}); | ||
|
||
it("should dismiss keyboard keeping focus", async () => { | ||
await waitAndTap("keep_focus_button"); | ||
await waitAndTap("close_keyboard_button"); | ||
await expect(element(by.id("input"))).toBeFocused(); | ||
}); | ||
|
||
it("should show keyboard again when input with focus tapped", async () => { | ||
await waitAndTap("input"); | ||
await waitForExpect(async () => { | ||
await expectBitmapsToBeEqual("CloseKeyboardOpenedKeepingFocus"); | ||
}); | ||
}); | ||
|
||
it("should dismiss keyboard", async () => { | ||
await waitAndTap("close_keyboard_button"); | ||
await expect(element(by.id("input"))).toBeFocused(); | ||
}); | ||
|
||
it("should show keyboard when `KeyboardController.setFocusTo('current')` is called", async () => { | ||
await waitAndTap("set_focus_to_current"); | ||
await waitForExpect(async () => { | ||
await expectBitmapsToBeEqual("CloseKeyboardOpenedKeepingFocus"); | ||
}); | ||
}); | ||
|
||
it("should dismiss keyboard and blur input if `.blur()` is called", async () => { | ||
await waitAndTap("blur_from_ref"); | ||
await expect(element(by.id("input"))).not.toBeFocused(); | ||
}); | ||
}); |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+118 KB
e2e/kit/assets/android/e2e_emulator_28/CloseKeyboardOpenedKeepingFocus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+136 KB
e2e/kit/assets/android/e2e_emulator_31/CloseKeyboardOpenedKeepingFocus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
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,66 @@ | ||
// | ||
// KeyboardControllerModuleImpl.swift | ||
// Pods | ||
// | ||
// Created by Kiryl Ziusko on 19/11/2024. | ||
// | ||
|
||
import Foundation | ||
import UIKit | ||
|
||
@objc(KeyboardControllerModuleImpl) | ||
public class KeyboardControllerModuleImpl: NSObject { | ||
private static let keyboardRevealGestureName = "keyboardRevealGesture" | ||
|
||
@objc | ||
public static func dismiss(_ keepFocus: Bool) { | ||
guard let input = UIResponder.current as? TextInput else { return } | ||
|
||
if keepFocus { | ||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(onTextInputTapped(_:))) | ||
tapGesture.name = keyboardRevealGestureName | ||
input.addGestureRecognizer(tapGesture) | ||
|
||
input.inputView = UIView() | ||
input.reloadInputViews() | ||
|
||
NotificationCenter.default.addObserver( | ||
self, | ||
selector: #selector(onResponderResigned(_:)), | ||
name: UITextField.textDidEndEditingNotification, | ||
object: input | ||
) | ||
} else { | ||
input.resignFirstResponder() | ||
} | ||
} | ||
|
||
@objc static func onTextInputTapped(_ gesture: UITapGestureRecognizer) { | ||
if gesture.state == .ended { | ||
guard let input = UIResponder.current as? TextInput else { return } | ||
|
||
cleanup(input) | ||
|
||
input.becomeFirstResponder() | ||
} | ||
} | ||
|
||
@objc static func onResponderResigned(_ notification: Notification) { | ||
guard let input = notification.object as? TextInput else { return } | ||
|
||
cleanup(input) | ||
} | ||
|
||
static func cleanup(_ input: TextInput) { | ||
input.inputView = nil | ||
input.reloadInputViews() | ||
|
||
if let gestures = input.gestureRecognizers { | ||
for gesture in gestures where gesture.name == keyboardRevealGestureName { | ||
input.removeGestureRecognizer(gesture) | ||
} | ||
} | ||
|
||
NotificationCenter.default.removeObserver(self, name: UITextField.textDidEndEditingNotification, object: input) | ||
} | ||
} |
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
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
Oops, something went wrong.