feat: [UI - Swap] Create tag error component

- created a new component (`ErrorTag`) based on the existing
`InformationTag`, suitable for displaying a row of icon (optional), text
and (optional) button in red/danger color
- add ErrorTagPage.qml to StoryBook
- add some basic QML tests

- InformationTag: make more properties customizable, use the same
`bgRadius` consistently
- StatusSmartIdenticon: some fixes for the MouseArea hover/click
behavior

Fixes #14792
This commit is contained in:
Lukáš Tinkl 2024-05-21 20:48:07 +02:00 committed by Lukáš Tinkl
parent df3f9984a8
commit a2e85bb3eb
7 changed files with 252 additions and 27 deletions

View File

@ -0,0 +1,95 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import shared.controls 1.0
import Storybook 1.0
SplitView {
orientation: Qt.Horizontal
Logs { id: logs }
Pane {
SplitView.fillWidth: true
SplitView.fillHeight: true
background: Rectangle {
color: Theme.palette.baseColor4
}
ErrorTag {
width: ctrlWidth.value || implicitWidth
anchors.centerIn: parent
text: ctrlText.text
buttonText: ctrlButtonText.text
asset.name: ctrlAssetName.text
loading: ctrlLoading.checked
onButtonClicked: logs.logEvent("ErrorTag::onButtonClicked", [], arguments)
}
}
LogsAndControlsPanel {
SplitView.fillHeight: true
SplitView.preferredWidth: 300
logsView.logText: logs.logText
ColumnLayout {
anchors.fill: parent
RowLayout {
Layout.fillWidth: true
Label { text: "Text:" }
TextField {
Layout.fillWidth: true
id: ctrlText
text: "Insufficient funds to pay gas fees"
}
}
RowLayout {
Layout.fillWidth: true
Label { text: "Button text:" }
TextField {
Layout.fillWidth: true
id: ctrlButtonText
text: "Buy ETH"
}
}
RowLayout {
Layout.fillWidth: true
Label { text: "Asset name:" }
TextField {
Layout.fillWidth: true
id: ctrlAssetName
text: "warning"
}
}
RowLayout {
Layout.fillWidth: true
Label { text: "Width:" }
SpinBox {
id: ctrlWidth
from: 0
to: 400
value: 0 // 0 == implicitWidth
stepSize: 20
textFromValue: function(value, locale) { return value === 0 ? "Implicit" : value }
}
}
Switch {
id: ctrlLoading
text: "Loading"
}
Item { Layout.fillHeight: true }
}
}
}
// category: Controls
// https://www.figma.com/design/TS0eQX9dAZXqZtELiwKIoK/Swap---Milestone-1?node-id=3413-311788&t=D3qGKqNjDBuLEEaD-0

View File

@ -0,0 +1,68 @@
import QtQuick 2.15
import QtTest 1.15
import shared.controls 1.0
Item {
id: root
width: 600
height: 400
Component {
id: componentUnderTest
ErrorTag {
anchors.centerIn: parent
text: "Insufficient funds to pay gas fees"
}
}
SignalSpy {
id: signalSpy
target: controlUnderTest
signalName: "buttonClicked"
}
property ErrorTag controlUnderTest: null
TestCase {
name: "ErrorTag"
when: windowShown
function init() {
controlUnderTest = createTemporaryObject(componentUnderTest, root)
signalSpy.clear()
}
function test_basicGeometry() {
verify(!!controlUnderTest)
verify(controlUnderTest.width > 0)
verify(controlUnderTest.height > 0)
}
function test_noDefaultButton() {
verify(!!controlUnderTest)
const button = findChild(controlUnderTest, "rightComponentButton")
tryCompare(button, "visible", false)
}
function test_buttonClick() {
verify(!!controlUnderTest)
controlUnderTest.buttonText = "Buy crypto"
const button = findChild(controlUnderTest, "rightComponentButton")
verify(!!button)
tryCompare(button, "visible", true)
mouseClick(button)
tryCompare(signalSpy, "count", 1)
}
function test_loadingNotClickable() {
verify(!!controlUnderTest)
controlUnderTest.loading = true
const button = findChild(controlUnderTest, "rightComponentButton")
verify(!!button)
tryCompare(button, "visible", false)
mouseClick(button)
tryCompare(signalSpy, "count", 0)
}
}
}

View File

@ -1,15 +1,15 @@
import QtQuick 2.13 import QtQuick 2.15
import QtQuick.Controls 2.12 import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import QtGraphicalEffects 1.12
/*! /*!
\qmltype LoadingComponent \qmltype LoadingComponent
\inherits Control \inherits Control
\inqmlmodule StatusQ.Components \inqmlmodule StatusQ.Components
\since StatusQ.Components 0.1 \since StatusQ.Components 0.1
\brief A componet that can be used to adding a loading state to a widget \brief A component that can be used to adding a loading state to a widget
Example: Example:
\qml \qml
@ -48,6 +48,7 @@ Control {
color: Theme.palette.statusLoadingHighlight color: Theme.palette.statusLoadingHighlight
radius: root.radius radius: root.radius
visible: false visible: false
LinearGradient { LinearGradient {
id: gradient id: gradient
width: 100 width: 100
@ -62,7 +63,7 @@ Control {
GradientStop { position: 0.8; color: "transparent"} GradientStop { position: 0.8; color: "transparent"}
} }
rotation: 20 rotation: 20
NumberAnimation on x { XAnimator on x {
id: animation id: animation
easing.type: Easing.Linear easing.type: Easing.Linear
loops: Animation.Infinite loops: Animation.Infinite

View File

@ -38,6 +38,7 @@ Loader {
property bool hoverEnabled: false property bool hoverEnabled: false
readonly property bool hovered: (sourceComponent === roundedIcon && item) ? readonly property bool hovered: (sourceComponent === roundedIcon && item) ?
item.hovered : false item.hovered : false
signal clicked(var mouse) signal clicked(var mouse)
Component { Component {
@ -91,18 +92,15 @@ Loader {
asset.bgBorderWidth: root.asset.bgBorderWidth asset.bgBorderWidth: root.asset.bgBorderWidth
asset.bgBorderColor: root.asset.bgBorderColor asset.bgBorderColor: root.asset.bgBorderColor
signal clicked(var mouse) readonly property alias hovered: mouseArea.containsMouse
property alias hovered: mouseArea.containsMouse
MouseArea { MouseArea {
id: mouseArea id: mouseArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: root.hoverEnabled hoverEnabled: root.hoverEnabled
cursorShape: loading ? Qt.ArrowCursor cursorShape: !loading && root.hoverEnabled ? Qt.PointingHandCursor : undefined
: Qt.PointingHandCursor onClicked: root.clicked(mouse)
onClicked: parent.clicked(mouse)
} }
} }
} }
@ -168,14 +166,4 @@ Loader {
width: root.asset.isImage ? root.asset.width : root.asset.bgWidth width: root.asset.isImage ? root.asset.width : root.asset.bgWidth
} }
} }
Connections {
target: item
enabled: status === Loader.Ready
ignoreUnknownSignals: true
function onClicked(mouse) {
root.clicked(mouse)
}
}
} }

View File

@ -0,0 +1,69 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import shared.controls 1.0
InformationTag {
id: root
property string text
property string buttonText
signal buttonClicked()
implicitHeight: 33
leftPadding: 10
rightPadding: 4
verticalPadding: 4
spacing: 6
backgroundColor: Theme.palette.dangerColor3
bgBorderColor: Theme.palette.dangerColor2
QtObject {
id: priv
readonly property int fontPixelSize: Theme.tertiaryTextFontSize
readonly property color foregroundColor: Theme.palette.dangerColor1
}
asset {
name: "warning"
color: priv.foregroundColor
}
tagPrimaryLabel.text: root.text
tagPrimaryLabel.color: priv.foregroundColor
tagPrimaryLabel.font.pixelSize: priv.fontPixelSize
tagPrimaryLabel.elide: Text.ElideRight
// NB: regular binding won't work as `tagPrimaryLabel` is an alias
Binding {
target: tagPrimaryLabel
property: "Layout.fillWidth"
value: true
}
rightComponent: StatusButton {
objectName: "rightComponentButton"
horizontalPadding: 8
width: visible || root.loading ? implicitWidth : 0
visible: !!text
size: StatusBaseButton.Size.Tiny
font.pixelSize: priv.fontPixelSize
type: StatusBaseButton.Type.Danger
normalColor: priv.foregroundColor
hoverColor: Theme.palette.hoverColor(normalColor)
textColor: Theme.palette.white
radius: root.bgRadius
text: root.buttonText
onClicked: root.buttonClicked()
}
}

View File

@ -1,6 +1,6 @@
import QtQuick 2.13 import QtQuick 2.15
import QtQuick.Layouts 1.13 import QtQuick.Layouts 1.15
import QtQuick.Controls 2.14 import QtQuick.Controls 2.15
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
@ -18,20 +18,23 @@ Control {
property alias rightComponent: rightComponent.sourceComponent property alias rightComponent: rightComponent.sourceComponent
property bool loading: false property bool loading: false
property int secondarylabelMaxWidth: 100 property int secondarylabelMaxWidth: 100
property color backgroundColor: "transparent" property color backgroundColor: "transparent"
property color bgBorderColor: Theme.palette.baseColor2
property real bgRadius: 36
property Component customBackground: Component { property Component customBackground: Component {
Rectangle { Rectangle {
color: root.backgroundColor color: root.backgroundColor
border.width: 1 border.width: 1
border.color: Theme.palette.baseColor2 border.color: root.bgBorderColor
radius: 36 radius: root.bgRadius
} }
} }
QtObject { QtObject {
id: d id: d
property var loadingComponent: Component { LoadingComponent {} } property var loadingComponent: Component { LoadingComponent { radius: root.bgRadius } }
} }
horizontalPadding: 12 horizontalPadding: 12

View File

@ -12,6 +12,7 @@ CurrencyAmountInput 1.0 CurrencyAmountInput.qml
EmojiHash 1.0 EmojiHash.qml EmojiHash 1.0 EmojiHash.qml
EmptyShapeRectangleFooterListView 1.0 EmptyShapeRectangleFooterListView.qml EmptyShapeRectangleFooterListView 1.0 EmptyShapeRectangleFooterListView.qml
ErrorDetails 1.0 ErrorDetails.qml ErrorDetails 1.0 ErrorDetails.qml
ErrorTag 1.0 ErrorTag.qml
FoldableHeader 1.0 FoldableHeader.qml FoldableHeader 1.0 FoldableHeader.qml
FormGroup 1.0 FormGroup.qml FormGroup 1.0 FormGroup.qml
GetSyncCodeDesktopInstructions 1.0 GetSyncCodeDesktopInstructions.qml GetSyncCodeDesktopInstructions 1.0 GetSyncCodeDesktopInstructions.qml