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:
Noelia
2026-07-27 15:06:22 +02:00
committed by Noelia
parent a4ad0bf80f
commit bbdb601445
3 changed files with 147 additions and 36 deletions
+6 -7
View File
@@ -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