mirror of
https://github.com/status-im/status-app.git
synced 2026-08-27 15:11:20 +00:00
feat(TokenListPopup): Migrate to StatusAdaptiveDialog
Replaces StatusDialog with StatusAdaptiveDialog as the base type. The StatusListView content is detected as a Flickable (Strategy A) and placed directly in the content viewport without double-wrapping. Column widths for symbol and address are now relative fractions of the dialog width instead of fixed pixel values. Also fixes stale role names in the storybook page (key→id, image→logoUri, updatedAt→timestamp).
This commit is contained in:
@@ -54,7 +54,7 @@ SplitView {
|
||||
filters: ValueFilter {
|
||||
id: keyFilter
|
||||
|
||||
roleName: "key"
|
||||
roleName: "id"
|
||||
value : uniswapBtn.checked ? "uniswap" : "status"
|
||||
}
|
||||
}
|
||||
@@ -63,23 +63,22 @@ SplitView {
|
||||
id: delegate
|
||||
|
||||
required property string name
|
||||
required property string image
|
||||
required property string logoUri
|
||||
required property string source
|
||||
required property string version
|
||||
required property double updatedAt
|
||||
required property double timestamp
|
||||
|
||||
readonly property TokenListPopup popup: TokenListPopup {
|
||||
parent: root
|
||||
|
||||
parent: root.Overlay.overlay
|
||||
visible: true
|
||||
modal: false
|
||||
closePolicy: Popup.NoAutoClose
|
||||
|
||||
title: qsTr("%1 Token List").arg(delegate.name)
|
||||
sourceImage: delegate.image
|
||||
sourceImage: delegate.logoUri
|
||||
sourceUrl: delegate.source
|
||||
sourceVersion: delegate.version
|
||||
updatedAt: delegate.updatedAt
|
||||
updatedAt: delegate.timestamp
|
||||
|
||||
tokensListModel: SortFilterProxyModel {
|
||||
sourceModel: root.tokensProxyModel
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
import QtQuick
|
||||
import QtTest
|
||||
|
||||
import AppLayouts.Profile.popups
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
width: 1024
|
||||
height: 768
|
||||
|
||||
ListModel {
|
||||
id: tokensModel
|
||||
|
||||
ListElement {
|
||||
name: "Ether"
|
||||
symbol: "ETH"
|
||||
image: ""
|
||||
chainId: 1
|
||||
chainName: "Ethereum"
|
||||
address: "0x0000000000000000000000000000000000000000"
|
||||
blockExplorerURL: "https://etherscan.io"
|
||||
isTest: false
|
||||
}
|
||||
ListElement {
|
||||
name: "USDC"
|
||||
symbol: "USDC"
|
||||
image: ""
|
||||
chainId: 1
|
||||
chainName: "Ethereum"
|
||||
address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
||||
blockExplorerURL: "https://etherscan.io"
|
||||
isTest: false
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: componentUnderTest
|
||||
|
||||
TokenListPopup {
|
||||
destroyOnClose: false
|
||||
title: "Uniswap Token List"
|
||||
sourceImage: ""
|
||||
sourceUrl: "https://tokens.uniswap.org"
|
||||
sourceVersion: "11.6.0"
|
||||
updatedAt: 1710538948
|
||||
tokensListModel: tokensModel
|
||||
}
|
||||
}
|
||||
|
||||
property TokenListPopup controlUnderTest: null
|
||||
|
||||
SignalSpy {
|
||||
id: linkClickedSpy
|
||||
target: controlUnderTest
|
||||
signalName: "linkClicked"
|
||||
}
|
||||
|
||||
TestCase {
|
||||
name: "TokenListPopup"
|
||||
when: windowShown
|
||||
|
||||
function init() {
|
||||
controlUnderTest = createTemporaryObject(componentUnderTest, root);
|
||||
linkClickedSpy.clear();
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
if (controlUnderTest) {
|
||||
controlUnderTest.close();
|
||||
controlUnderTest.destroy();
|
||||
controlUnderTest = null;
|
||||
}
|
||||
linkClickedSpy.clear();
|
||||
}
|
||||
|
||||
function openDialog() {
|
||||
verify(!!controlUnderTest);
|
||||
controlUnderTest.open();
|
||||
tryCompare(controlUnderTest, "opened", true);
|
||||
}
|
||||
|
||||
function test_subtitle_shows_token_count() {
|
||||
openDialog();
|
||||
verify(controlUnderTest.subtitle.includes("2"));
|
||||
}
|
||||
|
||||
function test_done_button_closes_dialog() {
|
||||
openDialog();
|
||||
|
||||
const doneButton = findChild(controlUnderTest, "tokenListPopupDoneButton");
|
||||
verify(!!doneButton);
|
||||
|
||||
mouseClick(doneButton);
|
||||
|
||||
tryCompare(controlUnderTest, "opened", false);
|
||||
}
|
||||
|
||||
function test_content_host_is_present() {
|
||||
openDialog();
|
||||
|
||||
const contentHost = findChild(controlUnderTest, "statusAdaptiveDialogContentHost");
|
||||
verify(!!contentHost);
|
||||
verify(contentHost.visible);
|
||||
}
|
||||
|
||||
function test_link_clicked_signal_emitted() {
|
||||
openDialog();
|
||||
|
||||
controlUnderTest.linkClicked("https://etherscan.io/token/0x0000000000000000000000000000000000000000");
|
||||
|
||||
compare(linkClickedSpy.count, 1);
|
||||
compare(linkClickedSpy.signalArguments[0][0],
|
||||
"https://etherscan.io/token/0x0000000000000000000000000000000000000000");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQml.Models
|
||||
|
||||
import StatusQ.Core
|
||||
import StatusQ.Controls
|
||||
@@ -15,7 +16,7 @@ import shared.panels
|
||||
|
||||
import QtModelsToolkit
|
||||
|
||||
StatusDialog {
|
||||
StatusAdaptiveDialog {
|
||||
id: root
|
||||
|
||||
required property string sourceImage
|
||||
@@ -29,24 +30,21 @@ StatusDialog {
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
readonly property int symbolColumnWidth: 90
|
||||
readonly property int addressColumnWidth: 106
|
||||
readonly property real symbolColumnFraction: 1 / 6
|
||||
readonly property real addressColumnFraction: 1 / 5
|
||||
readonly property int externalLinkBtnWidth: 32
|
||||
readonly property int maxTokenListBodyHeight: 420
|
||||
}
|
||||
|
||||
width: 521 // by design
|
||||
padding: 0
|
||||
horizontalPadding: Theme.padding
|
||||
fillHeightOnBottomSheet: true
|
||||
subtitle: qsTr("%n token(s)", "", root.tokensListModel.ModelCount.count)
|
||||
leftHeaderComponent: StatusSmartIdenticon {
|
||||
asset.name: root.sourceImage
|
||||
asset.isImage: !!asset.name
|
||||
}
|
||||
|
||||
contentItem: StatusListView {
|
||||
contentComponent: StatusListView {
|
||||
id: list
|
||||
|
||||
topMargin: Theme.padding
|
||||
bottomMargin: Theme.padding
|
||||
// Cap ListView implicit height so delegates recycle instead of laying out the full list
|
||||
implicitHeight: Math.min(d.maxTokenListBodyHeight, contentHeight + topMargin + bottomMargin)
|
||||
ScrollBar.vertical: null
|
||||
|
||||
model: root.tokensListModel
|
||||
|
||||
@@ -64,7 +62,7 @@ StatusDialog {
|
||||
CustomHeaderDelegate {}
|
||||
}
|
||||
delegate: CustomDelegate {
|
||||
width: contentItem.width
|
||||
width: ListView.view.width
|
||||
height: 64
|
||||
|
||||
name: model.name
|
||||
@@ -77,19 +75,14 @@ StatusDialog {
|
||||
}
|
||||
}
|
||||
|
||||
header: StatusDialogHeader {
|
||||
headline.title: root.title
|
||||
headline.subtitle: qsTr("%n token(s)", "", root.tokensListModel.ModelCount.count)
|
||||
actions.closeButton.onClicked: root.close()
|
||||
leftComponent: StatusSmartIdenticon {
|
||||
asset.name: root.sourceImage
|
||||
asset.isImage: !!asset.name
|
||||
footerRightButtons: ObjectModel {
|
||||
StatusButton {
|
||||
objectName: "tokenListPopupDoneButton"
|
||||
text: qsTr("Done")
|
||||
onClicked: root.close()
|
||||
}
|
||||
}
|
||||
|
||||
standardButtons: Dialog.Ok
|
||||
okButtonText: qsTr("Done")
|
||||
|
||||
component CustomTextBlock: ColumnLayout {
|
||||
id: textBlock
|
||||
|
||||
@@ -153,8 +146,10 @@ StatusDialog {
|
||||
}
|
||||
|
||||
component CustomHeaderDelegate: RowLayout {
|
||||
id: headerDelegate
|
||||
|
||||
height: 34
|
||||
width: contentItem.width
|
||||
width: ListView.view ? ListView.view.width : 0
|
||||
spacing: 0
|
||||
|
||||
StatusBaseText {
|
||||
@@ -167,7 +162,7 @@ StatusDialog {
|
||||
|
||||
StatusBaseText {
|
||||
Layout.leftMargin: Theme.padding
|
||||
Layout.preferredWidth: d.symbolColumnWidth - Layout.leftMargin
|
||||
Layout.preferredWidth: headerDelegate.width * d.symbolColumnFraction - Layout.leftMargin
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
|
||||
text: qsTr("Symbol")
|
||||
@@ -176,7 +171,7 @@ StatusDialog {
|
||||
|
||||
StatusBaseText {
|
||||
Layout.leftMargin: Theme.padding
|
||||
Layout.preferredWidth: d.addressColumnWidth - Layout.leftMargin
|
||||
Layout.preferredWidth: headerDelegate.width * d.addressColumnFraction - Layout.leftMargin
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
|
||||
text: qsTr("Address")
|
||||
@@ -249,7 +244,7 @@ StatusDialog {
|
||||
|
||||
StatusBaseText {
|
||||
Layout.leftMargin: Theme.padding
|
||||
Layout.preferredWidth: d.symbolColumnWidth - Layout.leftMargin
|
||||
Layout.preferredWidth: customDelegate.width * d.symbolColumnFraction - Layout.leftMargin
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
|
||||
text: customDelegate.symbol
|
||||
@@ -258,7 +253,7 @@ StatusDialog {
|
||||
|
||||
StatusBaseText {
|
||||
Layout.leftMargin: Theme.padding
|
||||
Layout.preferredWidth: d.addressColumnWidth - Layout.leftMargin
|
||||
Layout.preferredWidth: customDelegate.width * d.addressColumnFraction - Layout.leftMargin
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
|
||||
text: customDelegate.address
|
||||
|
||||
Reference in New Issue
Block a user