chore: factor out and create new `SendRecipientInput` component
- some minor visual fixes (padding & clear button color) - make it always paste plain text, eventhough the base component has to stay RichText - use it in SendModal->RecipientView - add a storybook page - added QML tests Fixes #15252
This commit is contained in:
parent
9fcb54cb74
commit
48d8846e29
|
@ -0,0 +1,76 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
|
||||
import Storybook 1.0
|
||||
|
||||
import utils 1.0
|
||||
|
||||
import shared.popups.send.controls 1.0
|
||||
|
||||
SplitView {
|
||||
id: root
|
||||
|
||||
Logs { id: logs }
|
||||
|
||||
SplitView {
|
||||
orientation: Qt.Vertical
|
||||
SplitView.fillWidth: true
|
||||
|
||||
Rectangle {
|
||||
SplitView.fillHeight: true
|
||||
SplitView.fillWidth: true
|
||||
color: Theme.palette.baseColor2
|
||||
|
||||
SendRecipientInput {
|
||||
anchors.centerIn: parent
|
||||
interactive: ctrlInteractive.checked
|
||||
checkMarkVisible: ctrlCheckmark.checked
|
||||
Component.onCompleted: forceActiveFocus()
|
||||
|
||||
onClearClicked: logs.logEvent("SendRecipientInput::clearClicked", [], arguments)
|
||||
onValidateInputRequested: logs.logEvent("SendRecipientInput::validateInputRequested", [], arguments)
|
||||
}
|
||||
}
|
||||
|
||||
LogsAndControlsPanel {
|
||||
id: logsAndControlsPanel
|
||||
|
||||
SplitView.minimumHeight: 100
|
||||
SplitView.preferredHeight: 200
|
||||
|
||||
logsView.logText: logs.logText
|
||||
|
||||
ColumnLayout {
|
||||
TextEdit {
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
text: "valid address: 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc4"
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: ctrlInteractive
|
||||
text: "Interactive"
|
||||
checked: true
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: ctrlCheckmark
|
||||
text: "Checkmark visible"
|
||||
checked: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// category: Controls
|
||||
|
||||
// https://www.figma.com/design/FkFClTCYKf83RJWoifWgoX/Wallet-v2?node-id=9707-106469&t=MeyLezc91kfFYcm9-0
|
||||
// https://www.figma.com/design/FkFClTCYKf83RJWoifWgoX/Wallet-v2?node-id=10259-120493&t=MeyLezc91kfFYcm9-0
|
||||
// https://www.figma.com/design/FkFClTCYKf83RJWoifWgoX/Wallet-v2?node-id=9019-88679&t=MeyLezc91kfFYcm9-0
|
||||
// https://www.figma.com/design/FkFClTCYKf83RJWoifWgoX/Wallet-v2?node-id=9707-105782&t=MeyLezc91kfFYcm9-0
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
import QtQuick 2.15
|
||||
import QtTest 1.15
|
||||
|
||||
import StatusQ 0.1
|
||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
||||
|
||||
import shared.popups.send.controls 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
width: 600
|
||||
height: 400
|
||||
|
||||
Component {
|
||||
id: componentUnderTest
|
||||
SendRecipientInput {
|
||||
anchors.centerIn: parent
|
||||
focus: true
|
||||
}
|
||||
}
|
||||
|
||||
SignalSpy {
|
||||
id: signalSpyClearClicked
|
||||
target: controlUnderTest
|
||||
signalName: "clearClicked"
|
||||
}
|
||||
|
||||
SignalSpy {
|
||||
id: signalSpyValidateInputRequested
|
||||
target: controlUnderTest
|
||||
signalName: "validateInputRequested"
|
||||
}
|
||||
|
||||
property SendRecipientInput controlUnderTest: null
|
||||
|
||||
TestCase {
|
||||
name: "SendRecipientInput"
|
||||
when: windowShown
|
||||
|
||||
function init() {
|
||||
controlUnderTest = createTemporaryObject(componentUnderTest, root)
|
||||
signalSpyClearClicked.clear()
|
||||
signalSpyValidateInputRequested.clear()
|
||||
QClipboardProxy.clear()
|
||||
}
|
||||
|
||||
function test_basicGeometry() {
|
||||
verify(!!controlUnderTest)
|
||||
verify(controlUnderTest.width > 0)
|
||||
verify(controlUnderTest.height > 0)
|
||||
}
|
||||
|
||||
function test_textInput() {
|
||||
verify(!!controlUnderTest)
|
||||
|
||||
verify(controlUnderTest.input.edit.focus)
|
||||
verify(controlUnderTest.interactive)
|
||||
compare(controlUnderTest.placeholderText, qsTr("Enter an ENS name or address"))
|
||||
|
||||
keyClick(Qt.Key_0)
|
||||
keyClick(Qt.Key_X)
|
||||
keyClick(Qt.Key_D)
|
||||
keyClick(Qt.Key_E)
|
||||
keyClick(Qt.Key_A)
|
||||
keyClick(Qt.Key_D)
|
||||
keyClick(Qt.Key_B)
|
||||
keyClick(Qt.Key_E)
|
||||
keyClick(Qt.Key_E)
|
||||
keyClick(Qt.Key_F)
|
||||
|
||||
const plainText = StatusQUtils.StringUtils.plainText(controlUnderTest.text)
|
||||
|
||||
// input's text should be what we typed,
|
||||
compare(plainText, "0xdeadbeef")
|
||||
// ... and for each letter pressed the signal `validateInputRequested` should be emitted
|
||||
compare(signalSpyValidateInputRequested.count, plainText.length)
|
||||
}
|
||||
|
||||
function test_interactive() {
|
||||
verify(!!controlUnderTest)
|
||||
verify(controlUnderTest.input.edit.focus)
|
||||
verify(controlUnderTest.interactive)
|
||||
|
||||
controlUnderTest.interactive = false
|
||||
|
||||
keyClick(Qt.Key_A)
|
||||
keyClick(Qt.Key_B)
|
||||
keyClick(Qt.Key_C)
|
||||
|
||||
const plainText = StatusQUtils.StringUtils.plainText(controlUnderTest.text)
|
||||
|
||||
// when non-interactive, any text input should be ignored
|
||||
compare(plainText, "")
|
||||
}
|
||||
|
||||
function test_pasteButton() {
|
||||
verify(!!controlUnderTest)
|
||||
const pasteButton = findChild(controlUnderTest, "pasteButton")
|
||||
verify(!!pasteButton)
|
||||
compare(pasteButton.visible, false)
|
||||
|
||||
// copy sth to clipboard
|
||||
controlUnderTest.text = "0xdeadbeef"
|
||||
controlUnderTest.input.edit.selectAll()
|
||||
controlUnderTest.input.edit.copy()
|
||||
|
||||
// paste button should be visible if input is empty and clipboard is not
|
||||
compare(pasteButton.visible, false)
|
||||
controlUnderTest.input.edit.clear()
|
||||
compare(pasteButton.visible, true)
|
||||
}
|
||||
|
||||
function test_pasteUsingButton() {
|
||||
verify(!!controlUnderTest)
|
||||
const pasteButton = findChild(controlUnderTest, "pasteButton")
|
||||
verify(!!pasteButton)
|
||||
tryCompare(pasteButton, "visible", false)
|
||||
|
||||
const richTextToTest = "<b>this is bold text</b>"
|
||||
|
||||
// copy rich text to clipboard
|
||||
controlUnderTest.text = richTextToTest
|
||||
controlUnderTest.input.edit.selectAll()
|
||||
controlUnderTest.input.edit.copy()
|
||||
controlUnderTest.input.edit.clear()
|
||||
compare(controlUnderTest.input.edit.length , 0)
|
||||
|
||||
compare(pasteButton.visible, true)
|
||||
mouseClick(pasteButton)
|
||||
verify(!controlUnderTest.text.includes("<b>this is bold text</b>"))
|
||||
}
|
||||
|
||||
function test_pasteUsingKbdShortcut() {
|
||||
verify(!!controlUnderTest)
|
||||
const pasteButton = findChild(controlUnderTest, "pasteButton")
|
||||
verify(!!pasteButton)
|
||||
tryCompare(pasteButton, "visible", false)
|
||||
|
||||
const richTextToTest = "<b>this is bold text</b>"
|
||||
|
||||
// copy rich text to clipboard
|
||||
controlUnderTest.text = richTextToTest
|
||||
controlUnderTest.input.edit.selectAll()
|
||||
controlUnderTest.input.edit.copy()
|
||||
controlUnderTest.input.edit.clear()
|
||||
compare(controlUnderTest.input.edit.length , 0)
|
||||
|
||||
compare(pasteButton.visible, true)
|
||||
keySequence(StandardKey.Paste)
|
||||
verify(!controlUnderTest.text.includes("<b>this is bold text</b>"))
|
||||
}
|
||||
|
||||
function test_clearButton() {
|
||||
verify(!!controlUnderTest)
|
||||
verify(controlUnderTest.input.edit.focus)
|
||||
verify(controlUnderTest.interactive)
|
||||
|
||||
const clearButton = findChild(controlUnderTest, "clearButton")
|
||||
compare(clearButton.visible, false)
|
||||
|
||||
controlUnderTest.text = "0xdeadbeef"
|
||||
|
||||
// clear button should be visible with some text
|
||||
verify(clearButton.visible)
|
||||
mouseClick(clearButton)
|
||||
compare(StatusQUtils.StringUtils.plainText(controlUnderTest.text), "")
|
||||
compare(signalSpyClearClicked.count, 1)
|
||||
|
||||
// and when interactive
|
||||
controlUnderTest.interactive = false
|
||||
compare(clearButton.visible, false)
|
||||
}
|
||||
|
||||
function test_checkmark() {
|
||||
verify(!!controlUnderTest)
|
||||
compare(controlUnderTest.checkMarkVisible, false)
|
||||
controlUnderTest.checkMarkVisible = true
|
||||
const checkmarkIcon = findChild(controlUnderTest, "checkmarkIcon")
|
||||
verify(!!checkmarkIcon)
|
||||
verify(checkmarkIcon.visible)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -71,8 +71,9 @@ QtObject {
|
|||
return textAddrss
|
||||
}
|
||||
|
||||
function resolveENS(value) {
|
||||
return root.mainModuleInst.resolvedENS("", "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc4", "") // return some valid address
|
||||
function resolveENS(value: string) {
|
||||
if (!!value && value.endsWith(".eth"))
|
||||
root.mainModuleInst.resolvedENS("", "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc4", "") // return some valid address
|
||||
}
|
||||
|
||||
function getAsset(assetsList, symbol) {
|
||||
|
|
|
@ -55,6 +55,7 @@ public:
|
|||
Q_INVOKABLE bool isValidImageUrl(const QUrl &url, const QStringList &acceptedExtensions) const;
|
||||
Q_INVOKABLE qint64 getFileSize(const QUrl &url) const;
|
||||
Q_INVOKABLE void copyTextToClipboard(const QString& text);
|
||||
Q_INVOKABLE void clear();
|
||||
|
||||
signals:
|
||||
void contentChanged();
|
||||
|
|
|
@ -92,3 +92,8 @@ void QClipboardProxy::copyTextToClipboard(const QString &text)
|
|||
{
|
||||
m_clipboard->setText(text);
|
||||
}
|
||||
|
||||
void QClipboardProxy::clear()
|
||||
{
|
||||
m_clipboard->clear();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,6 @@ StatusFlatRoundButton {
|
|||
icon.height: 16
|
||||
implicitWidth: 24
|
||||
implicitHeight: 24
|
||||
icon.color: Theme.palette.baseColor1
|
||||
icon.color: Theme.palette.directColor9
|
||||
backgroundHoverColor: "transparent"
|
||||
}
|
||||
|
|
|
@ -455,7 +455,7 @@ Item {
|
|||
onKeyPressed: {
|
||||
root.keyPressed(event);
|
||||
}
|
||||
onEditChanged: {
|
||||
onEditClicked: {
|
||||
root.editClicked();
|
||||
}
|
||||
onEditingFinished: {
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import StatusQ 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
StatusInput {
|
||||
id: root
|
||||
|
||||
property bool interactive: true
|
||||
property bool checkMarkVisible
|
||||
|
||||
signal clearClicked()
|
||||
signal validateInputRequested()
|
||||
|
||||
placeholderText: qsTr("Enter an ENS name or address")
|
||||
input.background.color: Theme.palette.indirectColor1
|
||||
input.background.border.width: 0
|
||||
input.implicitHeight: 56
|
||||
rightPadding: 12
|
||||
input.clearable: false // custom button below
|
||||
input.edit.readOnly: !root.interactive
|
||||
multiline: false
|
||||
input.edit.textFormat: TextEdit.RichText
|
||||
|
||||
input.rightComponent: RowLayout {
|
||||
StatusButton {
|
||||
objectName: "pasteButton"
|
||||
font.weight: Font.Normal
|
||||
borderColor: Theme.palette.primaryColor1
|
||||
borderWidth: 1
|
||||
size: StatusBaseButton.Size.Tiny
|
||||
text: qsTr("Paste")
|
||||
visible: root.input.edit.length === 0 && root.input.edit.canPaste
|
||||
focusPolicy: Qt.NoFocus
|
||||
onClicked: {
|
||||
root.input.edit.forceActiveFocus()
|
||||
root.text = QClipboardProxy.text // paste plain text
|
||||
root.input.edit.cursorPosition = root.input.edit.length
|
||||
root.validateInputRequested()
|
||||
}
|
||||
}
|
||||
StatusIcon {
|
||||
objectName: "checkmarkIcon"
|
||||
Layout.preferredWidth: 16
|
||||
Layout.preferredHeight: 16
|
||||
icon: "tiny/checkmark"
|
||||
color: Theme.palette.primaryColor1
|
||||
visible: root.checkMarkVisible
|
||||
}
|
||||
StatusClearButton {
|
||||
objectName: "clearButton"
|
||||
visible: root.input.edit.length !== 0 && root.interactive
|
||||
onClicked: {
|
||||
root.input.edit.clear()
|
||||
root.clearClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.input
|
||||
function onKeyPressed(event) {
|
||||
if (event.matches(StandardKey.Paste)) {
|
||||
event.accepted = true
|
||||
root.text = QClipboardProxy.text // paste plain text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onTabPressed: event.accepted = true
|
||||
Keys.onReleased: root.validateInputRequested()
|
||||
}
|
|
@ -10,3 +10,4 @@ BalanceExceeded 1.0 BalanceExceeded.qml
|
|||
CollectibleBackButtonWithInfo 1.0 CollectibleBackButtonWithInfo.qml
|
||||
CollectibleNestedDelegate 1.0 CollectibleNestedDelegate.qml
|
||||
HeaderTitleText 1.0 HeaderTitleText.qml
|
||||
SendRecipientInput 1.0 SendRecipientInput.qml
|
||||
|
|
|
@ -175,24 +175,14 @@ Loader {
|
|||
|
||||
Component {
|
||||
id: addressRecipient
|
||||
StatusInput {
|
||||
id: recipientInput
|
||||
SendRecipientInput {
|
||||
width: parent.width
|
||||
height: visible ? implicitHeight: 0
|
||||
visible: !root.isBridgeTx && !!root.selectedAsset
|
||||
|
||||
placeholderText: qsTr("Enter an ENS name or address")
|
||||
input.background.color: Theme.palette.indirectColor1
|
||||
input.background.border.width: 0
|
||||
input.implicitHeight: 56
|
||||
input.clearable: false // custom button below
|
||||
input.edit.readOnly: !root.interactive
|
||||
multiline: false
|
||||
input.edit.textFormat: TextEdit.RichText
|
||||
text: root.addressText
|
||||
|
||||
function validateInput() {
|
||||
const plainText = store.plainText(recipientInput.text)
|
||||
const plainText = store.plainText(text)
|
||||
root.isLoading()
|
||||
if (Utils.isValidEns(plainText)) {
|
||||
d.isPending = true
|
||||
|
@ -202,38 +192,10 @@ Loader {
|
|||
}
|
||||
}
|
||||
|
||||
input.rightComponent: RowLayout {
|
||||
StatusButton {
|
||||
font.weight: Font.Normal
|
||||
borderColor: Theme.palette.primaryColor1
|
||||
size: StatusBaseButton.Size.Tiny
|
||||
text: qsTr("Paste")
|
||||
visible: recipientInput.input.edit.length === 0 && recipientInput.input.edit.canPaste
|
||||
focusPolicy: Qt.NoFocus
|
||||
onClicked: {
|
||||
recipientInput.input.edit.forceActiveFocus()
|
||||
recipientInput.input.edit.paste()
|
||||
recipientInput.input.edit.cursorPosition = recipientInput.input.edit.length
|
||||
recipientInput.validateInput()
|
||||
}
|
||||
}
|
||||
StatusIcon {
|
||||
Layout.preferredWidth: 16
|
||||
Layout.preferredHeight: 16
|
||||
icon: "tiny/checkmark"
|
||||
color: Theme.palette.primaryColor1
|
||||
visible: root.ready
|
||||
}
|
||||
StatusClearButton {
|
||||
visible: recipientInput.input.edit.length !== 0 && root.interactive
|
||||
onClicked: {
|
||||
recipientInput.input.edit.clear()
|
||||
d.clearValues()
|
||||
}
|
||||
}
|
||||
}
|
||||
Keys.onTabPressed: event.accepted = true
|
||||
Keys.onReleased: recipientInput.validateInput()
|
||||
interactive: root.interactive
|
||||
checkMarkVisible: root.ready
|
||||
onClearClicked: d.clearValues()
|
||||
onValidateInputRequested: validateInput()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue