Storybook: added ability to mark pages with their status
Closes: #16606
This commit is contained in:
parent
7854271c2e
commit
0c86fbf7b6
|
@ -8,7 +8,6 @@ import Qt.labs.settings 1.0
|
|||
import StatusQ.Core.Theme 0.1
|
||||
import Storybook 1.0
|
||||
|
||||
import utils 1.0
|
||||
|
||||
ApplicationWindow {
|
||||
id: root
|
||||
|
@ -181,6 +180,7 @@ ApplicationWindow {
|
|||
model: pagesModel
|
||||
|
||||
onPageSelected: root.currentPage = page
|
||||
onStatusClicked: statusStatsDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -287,17 +287,8 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
|
||||
Dialog {
|
||||
NoFigmaTokenDialog {
|
||||
id: noFigmaTokenDialog
|
||||
|
||||
anchors.centerIn: Overlay.overlay
|
||||
|
||||
title: "Figma token not set"
|
||||
standardButtons: Dialog.Ok
|
||||
|
||||
Label {
|
||||
text: "Please set Figma personal token in \"Settings\""
|
||||
}
|
||||
}
|
||||
|
||||
FigmaLinksCache {
|
||||
|
@ -310,24 +301,16 @@ ApplicationWindow {
|
|||
id: inspectionWindow
|
||||
}
|
||||
|
||||
Dialog {
|
||||
NothingToInspectDialog {
|
||||
id: nothingToInspectDialog
|
||||
|
||||
anchors.centerIn: Overlay.overlay
|
||||
width: contentItem.implicitWidth + leftPadding + rightPadding
|
||||
pageName: root.currentPage
|
||||
}
|
||||
|
||||
title: "No items to inspect found"
|
||||
standardButtons: Dialog.Ok
|
||||
modal: true
|
||||
StatusStatisticsDialog {
|
||||
id: statusStatsDialog
|
||||
|
||||
contentItem: Label {
|
||||
text: '
|
||||
Tips:\n\
|
||||
• For inline components use naming convention of adding\n\
|
||||
"Custom" at the begining (like Custom'+root.currentPage+')\n\
|
||||
• For popups set closePolicy to "Popup.NoAutoClose"\n\
|
||||
'
|
||||
}
|
||||
pagesModel: pagesModel
|
||||
}
|
||||
|
||||
Component {
|
||||
|
|
|
@ -3,6 +3,7 @@ import QtQuick.Controls 2.15
|
|||
import QtQuick.Layouts 1.15
|
||||
import QtQml 2.15
|
||||
|
||||
import StatusQ 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
|
@ -27,3 +28,4 @@ Item {
|
|||
}
|
||||
|
||||
// category: _
|
||||
// status: good
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include "directoryfileswatcher.h"
|
||||
|
||||
|
||||
namespace {
|
||||
const auto categoryUncategorized QStringLiteral("Uncategorized");
|
||||
}
|
||||
|
@ -35,29 +34,9 @@ PagesModelItem PagesModel::readMetadata(const QString& path)
|
|||
file.open(QIODevice::ReadOnly);
|
||||
QByteArray content = file.readAll();
|
||||
|
||||
static QRegularExpression categoryRegex(
|
||||
"^//(\\s)*category:(.+)$", QRegularExpression::MultilineOption);
|
||||
|
||||
QRegularExpressionMatch categoryMatch = categoryRegex.match(content);
|
||||
QString category = categoryMatch.hasMatch()
|
||||
? categoryMatch.captured(2).trimmed() : categoryUncategorized;
|
||||
|
||||
item.category = category.size() ? category : categoryUncategorized;
|
||||
|
||||
static QRegularExpression figmaRegex(
|
||||
"^(\\/\\/\\s*)((?:https:\\/\\/)?(?:www\\.)?figma\\.com\\/.*)$",
|
||||
QRegularExpression::MultilineOption);
|
||||
|
||||
QRegularExpressionMatchIterator i = figmaRegex.globalMatch(content);
|
||||
QStringList links;
|
||||
|
||||
while (i.hasNext()) {
|
||||
QRegularExpressionMatch match = i.next();
|
||||
QString link = match.captured(2);
|
||||
links << link;
|
||||
}
|
||||
|
||||
item.figmaLinks = links;
|
||||
item.category = extractCategory(content);
|
||||
item.status = extractStatus(content);
|
||||
item.figmaLinks = extractFigmaLinks(content);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
@ -75,6 +54,55 @@ QList<PagesModelItem> PagesModel::readMetadata(const QStringList &paths)
|
|||
return metadata;
|
||||
}
|
||||
|
||||
QString PagesModel::extractCategory(const QByteArray& content)
|
||||
{
|
||||
static QRegularExpression categoryRegex(
|
||||
"^//(\\s)*category:(.+)$", QRegularExpression::MultilineOption);
|
||||
|
||||
QRegularExpressionMatch categoryMatch = categoryRegex.match(content);
|
||||
QString category = categoryMatch.hasMatch()
|
||||
? categoryMatch.captured(2).trimmed() : categoryUncategorized;
|
||||
|
||||
return category.isEmpty() ? categoryUncategorized : category;
|
||||
}
|
||||
|
||||
PagesModel::Status PagesModel::extractStatus(const QByteArray& content)
|
||||
{
|
||||
static QRegularExpression statusRegex(
|
||||
"^//(\\s)*status:(.+)$", QRegularExpression::MultilineOption);
|
||||
|
||||
QRegularExpressionMatch statusMatch = statusRegex.match(content);
|
||||
QString status = statusMatch.hasMatch()
|
||||
? statusMatch.captured(2).trimmed() : "";
|
||||
|
||||
if (status == QStringLiteral("bad"))
|
||||
return Bad;
|
||||
if (status == QStringLiteral("decent"))
|
||||
return Decent;
|
||||
if (status == QStringLiteral("good"))
|
||||
return Good;
|
||||
|
||||
return Uncategorized;
|
||||
}
|
||||
|
||||
QStringList PagesModel::extractFigmaLinks(const QByteArray& content)
|
||||
{
|
||||
static QRegularExpression figmaRegex(
|
||||
"^(\\/\\/\\s*)((?:https:\\/\\/)?(?:www\\.)?figma\\.com\\/.*)$",
|
||||
QRegularExpression::MultilineOption);
|
||||
|
||||
QRegularExpressionMatchIterator i = figmaRegex.globalMatch(content);
|
||||
QStringList links;
|
||||
|
||||
while (i.hasNext()) {
|
||||
QRegularExpressionMatch match = i.next();
|
||||
QString link = match.captured(2);
|
||||
links << link;
|
||||
}
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
void PagesModel::onPagesChanged(const QStringList& added,
|
||||
const QStringList& removed,
|
||||
const QStringList& changed)
|
||||
|
@ -106,10 +134,11 @@ void PagesModel::onPagesChanged(const QStringList& added,
|
|||
PagesModelItem metadata = readMetadata(path);
|
||||
setFigmaLinks(metadata.title, metadata.figmaLinks);
|
||||
|
||||
if (previous.category != metadata.category) {
|
||||
// For simplicity category change is handled by removing and
|
||||
// adding item. In the future it can be changed to regular dataChanged
|
||||
// event and handled properly in upstream models like SectionSDecoratorModel.
|
||||
// For simplicity category and status change is handled by removing and
|
||||
// adding item. In the future it can be changed to regular dataChanged
|
||||
// event and handled properly in upstream models like SectionSDecoratorModel.
|
||||
if (previous.category != metadata.category
|
||||
|| previous.status != metadata.status) {
|
||||
beginRemoveRows({}, index, index);
|
||||
m_items.removeAt(index);
|
||||
endRemoveRows();
|
||||
|
@ -135,6 +164,7 @@ QHash<int, QByteArray> PagesModel::roleNames() const
|
|||
static const QHash<int,QByteArray> roles {
|
||||
{ TitleRole, QByteArrayLiteral("title") },
|
||||
{ CategoryRole, QByteArrayLiteral("category") },
|
||||
{ StatusRole, QByteArrayLiteral("status") },
|
||||
{ FigmaRole, QByteArrayLiteral("figma") }
|
||||
};
|
||||
|
||||
|
@ -157,6 +187,9 @@ QVariant PagesModel::data(const QModelIndex &index, int role) const
|
|||
if (role == CategoryRole)
|
||||
return m_items.at(index.row()).category;
|
||||
|
||||
if (role == StatusRole)
|
||||
return m_items.at(index.row()).status;
|
||||
|
||||
if (role == FigmaRole) {
|
||||
auto title = m_items.at(index.row()).title;
|
||||
auto it = m_figmaSubmodels.find(title);
|
||||
|
|
|
@ -12,6 +12,7 @@ struct PagesModelItem {
|
|||
QDateTime lastModified;
|
||||
QString title;
|
||||
QString category;
|
||||
int status = 0;
|
||||
QStringList figmaLinks;
|
||||
};
|
||||
|
||||
|
@ -24,14 +25,23 @@ public:
|
|||
enum Roles {
|
||||
TitleRole = Qt::UserRole + 1,
|
||||
CategoryRole,
|
||||
StatusRole,
|
||||
FigmaRole
|
||||
};
|
||||
|
||||
enum Status : int {
|
||||
Uncategorized = 0,
|
||||
Bad,
|
||||
Decent,
|
||||
Good
|
||||
};
|
||||
|
||||
Q_ENUM(Status)
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
|
||||
private:
|
||||
void onPagesChanged(const QStringList& added, const QStringList& removed,
|
||||
const QStringList& changed);
|
||||
|
@ -44,6 +54,10 @@ private:
|
|||
static void readMetadata(PagesModelItem &item);
|
||||
static void readMetadata(QList<PagesModelItem> &items);
|
||||
|
||||
static QString extractCategory(const QByteArray& content);
|
||||
static PagesModel::Status extractStatus(const QByteArray& content);
|
||||
static QStringList extractFigmaLinks(const QByteArray& content);
|
||||
|
||||
void setFigmaLinks(const QString& title, const QStringList& links);
|
||||
|
||||
QString m_path;
|
||||
|
|
|
@ -13,6 +13,7 @@ ColumnLayout {
|
|||
property alias currentPage: pagesList.currentPage
|
||||
|
||||
signal pageSelected(string page)
|
||||
signal statusClicked
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: filteredModel
|
||||
|
@ -96,5 +97,6 @@ ColumnLayout {
|
|||
|
||||
onPageSelected: root.pageSelected(page)
|
||||
onSectionClicked: sectionsModel.flipFolding(index)
|
||||
onStatusClicked: root.statusClicked()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
Dialog {
|
||||
anchors.centerIn: Overlay.overlay
|
||||
|
||||
title: "Figma token not set"
|
||||
standardButtons: Dialog.Ok
|
||||
|
||||
Label {
|
||||
text: "Please set Figma personal token in \"Settings\""
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
|
||||
property string pageName
|
||||
|
||||
anchors.centerIn: Overlay.overlay
|
||||
width: contentItem.implicitWidth + leftPadding + rightPadding
|
||||
|
||||
title: "No items to inspect found"
|
||||
standardButtons: Dialog.Ok
|
||||
modal: true
|
||||
|
||||
contentItem: Label {
|
||||
text: '
|
||||
Tips:\n\
|
||||
• For inline components use naming convention of adding\n\
|
||||
"Custom" at the begining (like Custom'+root.pageName+')\n\
|
||||
• For popups set closePolicy to "Popup.NoAutoClose"\n\
|
||||
'
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
import QtQuick 2.14
|
||||
import QtQuick.Controls 2.14
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
import Storybook 1.0
|
||||
|
||||
ListView {
|
||||
id: root
|
||||
|
@ -10,6 +12,7 @@ ListView {
|
|||
|
||||
signal pageSelected(string page)
|
||||
signal sectionClicked(int index)
|
||||
signal statusClicked
|
||||
|
||||
readonly property string foldedPrefix: "▶ "
|
||||
readonly property string unfoldedPrefix: "▼ "
|
||||
|
@ -27,6 +30,35 @@ ListView {
|
|||
"text/uri-list": `file:${pagesFolder}/${model.title}Page.qml`
|
||||
}
|
||||
|
||||
indicator: Rectangle {
|
||||
visible: !model.isSection
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: parent.leftPadding / 2
|
||||
|
||||
width: 6
|
||||
height: 6
|
||||
radius: 3
|
||||
|
||||
color: {
|
||||
if (model.status === PagesModel.Good)
|
||||
return "green"
|
||||
if (model.status === PagesModel.Decent)
|
||||
return "orange"
|
||||
if (model.status === PagesModel.Bad)
|
||||
return "red"
|
||||
|
||||
return "gray"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: root.statusClicked()
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: dragArea
|
||||
anchors.fill: parent
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import StatusQ 0.1
|
||||
|
||||
import Storybook 1.0
|
||||
import utils 1.0
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
|
||||
required property var pagesModel
|
||||
|
||||
readonly property string contributingMdLink:
|
||||
"https://github.com/status-im/status-desktop/blob/master/" +
|
||||
"CONTRIBUTING.md#page-classification"
|
||||
|
||||
anchors.centerIn: Overlay.overlay
|
||||
width: 420
|
||||
|
||||
title: "Page status statistics"
|
||||
standardButtons: Dialog.Ok
|
||||
modal: true
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
readonly property int total: root.pagesModel.ModelCount.count
|
||||
|
||||
function percent(val) {
|
||||
return (val / total * 100).toFixed(2)
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
Label {
|
||||
Layout.bottomMargin: 20
|
||||
|
||||
text: `Total number of pages: ${d.total}`
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: [
|
||||
{ status: "good", color: "green" },
|
||||
{ status: "decent", color: "orange" },
|
||||
{ status: "bad", color: "red" },
|
||||
{ status: "uncategorized", color: "gray" }
|
||||
]
|
||||
|
||||
Row {
|
||||
spacing: 10
|
||||
|
||||
Rectangle {
|
||||
readonly property int size: label.height - 2
|
||||
|
||||
width: size
|
||||
height: size
|
||||
radius: size / 2
|
||||
color: modelData.color
|
||||
}
|
||||
|
||||
Label {
|
||||
id: label
|
||||
|
||||
readonly property string status: modelData.status
|
||||
readonly property int count: statusAggregator[status]
|
||||
|
||||
text: `${status}: ${count} (${d.percent(count)}%)`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.topMargin: 20
|
||||
|
||||
text: `For details check <a href="${root.contributingMdLink}">CONTRIBUTING.md</a>`
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
|
||||
FunctionAggregator {
|
||||
id: statusAggregator
|
||||
|
||||
readonly property int bad: value.bad
|
||||
readonly property int decent: value.decent
|
||||
readonly property int good: value.good
|
||||
readonly property int uncategorized: value.uncategorized
|
||||
|
||||
model: root.pagesModel
|
||||
roleName: "status"
|
||||
|
||||
initialValue: ({
|
||||
bad: 0,
|
||||
decent: 0,
|
||||
good: 0,
|
||||
uncategorized: 0
|
||||
})
|
||||
|
||||
aggregateFunction: (aggr, value) => {
|
||||
let { bad, decent, good, uncategorized } = aggr
|
||||
|
||||
switch (value) {
|
||||
case PagesModel.Bad: bad++; break
|
||||
case PagesModel.Decent: decent++; break
|
||||
case PagesModel.Good: good++; break
|
||||
default: uncategorized++
|
||||
}
|
||||
|
||||
return { bad, decent, good, uncategorized }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,8 @@ LimitProxyModel 1.0 LimitProxyModel.qml
|
|||
Logs 1.0 Logs.qml
|
||||
LogsAndControlsPanel 1.0 LogsAndControlsPanel.qml
|
||||
LogsView 1.0 LogsView.qml
|
||||
NoFigmaTokenDialog 1.0 NoFigmaTokenDialog.qml
|
||||
NothingToInspectDialog 1.0 NothingToInspectDialog.qml
|
||||
PageToolBar 1.0 PageToolBar.qml
|
||||
PagesList 1.0 PagesList.qml
|
||||
PopupBackground 1.0 PopupBackground.qml
|
||||
|
@ -28,6 +30,7 @@ RadioButtonFlowSelector 1.0 RadioButtonFlowSelector.qml
|
|||
SettingsLayout 1.0 SettingsLayout.qml
|
||||
SingleItemProxyModel 1.0 SingleItemProxyModel.qml
|
||||
SourceCodeBox 1.0 SourceCodeBox.qml
|
||||
StatusStatisticsDialog 1.0 StatusStatisticsDialog.qml
|
||||
TestRunnerController 1.0 TestRunnerController.qml
|
||||
TestRunnerControls 1.0 TestRunnerControls.qml
|
||||
singleton FigmaUtils 1.0 FigmaUtils.qml
|
||||
|
|
Loading…
Reference in New Issue