on xenial help sign in
Add CardTool to determine category-wide card properties based on the category template.
d7e5175Michał Sawicz
qml/Dash/Card.qml+3−12
@@ -23,17 +23,8 @@ Item {
23 property var components
23 property var components
24 property var cardData
24 property var cardData
25
25
26− width: {
26+ implicitWidth: childrenRect.width
27− if (template !== undefined) {
28− if (template["card-layout"] === "horizontal") return units.gu(38);
29− switch (template['card-size']) {
30− case "small": return units.gu(12);
31− case "large": return units.gu(38);
32− }
33− }
34− return units.gu(18.5);
35− }
36− height: childrenRect.height
27+ implicitHeight: childrenRect.height
37
28
38 UbuntuShape {
29 UbuntuShape {
39 id: artShape
30 id: artShape
@@ -61,7 +52,7 @@ Item {
61 // FIXME uncomment when having investigated / fixed the crash
52 // FIXME uncomment when having investigated / fixed the crash
62 //sourceSize.width: width > height ? width : 0
53 //sourceSize.width: width > height ? width : 0
63 //sourceSize.height: height > width ? height : 0
54 //sourceSize.height: height > width ? height : 0
64− fillMode: components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop
55+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop
65
56
66 property real aspect: implicitWidth / implicitHeight
57 property real aspect: implicitWidth / implicitHeight
67 }
58 }
qml/Dash/CardTool.qmlnew file+136
@@ -0,0 +1,136 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+
19+/*!
20+ \brief Tool for introspecting Card properties.
21+
22+ Some properties of Cards we need to determine category-wide (like card sizes in grid),
23+ so we should not do them per-Card but in the category renderer.
24+
25+ This component creates an invisible card filled with maximum mapped data and calculates
26+ or measures card properties for this configuration.
27+ */
28+
29+Item {
30+ id: cardTool
31+ /*!
32+ \brief Template supplied for the category.
33+ */
34+ property var template
35+
36+ /*!
37+ \brief Component mapping supplied for the category.
38+ */
39+ property var components
40+
41+ /*!
42+ \brief Width of the view, based on which carousel width is calculated.
43+ */
44+ property var viewWidth
45+
46+ // FIXME: Saviq
47+ // Only way for the card below to actually be laid out completely.
48+ // If invisible or in "data" array, some components are not taken into account.
49+ width: 0
50+ height: 0
51+ clip: true
52+
53+ /*!
54+ type:real \brief Width to be enforced on the card in this configuration.
55+
56+ If undefined, should use implicit width of the actual card.
57+ */
58+ readonly property var cardWidth: {
59+ switch (template !== undefined && template["category-layout"]) {
60+ case "grid":
61+ case "vertical-journal":
62+ if (template["card-layout"] === "horizontal") return units.gu(38);
63+ switch (template["card-size"]) {
64+ case "small": return units.gu(12);
65+ case "large": return units.gu(38);
66+ }
67+ return units.gu(18.5);
68+ case "carousel":
69+ if (viewWidth === undefined) return undefined
70+ if (viewWidth <= units.gu(40)) return units.gu(18)
71+ if (viewWidth >= units.gu(128)) return units.gu(26)
72+ return units.gu(18 + Math.round((viewWidth - units.gu(40))/ units.gu(11)))
73+ case undefined:
74+ case "organic-grid":
75+ case "journal":
76+ default:
77+ return undefined;
78+ }
79+ }
80+
81+ /*!
82+ type:real \brief Height to be enforced on the card in this configuration.
83+
84+ If undefined, should use implicit height of the actual card.
85+ */
86+ readonly property var cardHeight: {
87+ switch (template !== undefined && template["category-layout"]) {
88+ case "journal":
89+ if (template["card-size"] >= 12 && template["card-size"] <= 38) return units.gu(template["card-size"]);
90+ return units.gu(18.5);
91+ case "grid":
92+ return card.implicitHeight
93+ case "carousel":
94+ return cardWidth / (components ? components["art"]["aspect-ratio"] : 1)
95+ case undefined:
96+ case "organic-grid":
97+ case "vertical-journal":
98+ default:
99+ return undefined;
100+ }
101+ }
102+
103+ readonly property QtObject priv: card
104+
105+ Card {
106+ id: card
107+ template: cardTool.template
108+ components: cardTool.components
109+
110+ width: cardTool.cardWidth || implicitWidth
111+ height: cardTool.cardHeight || implicitHeight
112+
113+ property var fields: ["art", "mascot", "title", "subtitle", "summary"]
114+ property var maxData: {
115+ "art": Qt.resolvedUrl("graphics/checkers.png"),
116+ "mascot": Qt.resolvedUrl("graphics/checkers.png"),
117+ "title": "—\n—",
118+ "subtitle": "—",
119+ "summary": "—\n—\n—\n—\n—"
120+ }
121+
122+ onComponentsChanged: {
123+ var data = {};
124+ for (var k in fields) {
125+ var component = components[fields[k]];
126+ var key = fields[k];
127+ if ((typeof component === "string" && component.length > 0) ||
128+ (typeof component === "object" && component !== null
129+ && typeof component["field"] === "string" && component["field"].length > 0)) {
130+ data[key] = maxData[key];
131+ }
132+ }
133+ cardData = data;
134+ }
135+ }
136+}
qml/Dash/graphics/checkers.pngnew file

Binary file not shown.

tests/qmltests/CMakeLists.txt+1
@@ -44,6 +44,7 @@ add_qml_test(Dash DashPreview)
44add_qml_test(Dash GenericPreview)
44add_qml_test(Dash GenericPreview)
45add_qml_test(Dash Card)
45add_qml_test(Dash Card)
46add_qml_test(Dash CardHeader)
46add_qml_test(Dash CardHeader)
47+add_qml_test(Dash CardTool)
47add_qml_test(Dash GenericScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
48add_qml_test(Dash GenericScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
48add_qml_test(Dash FilterGrids IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_SOURCE_DIR}/plugins
49add_qml_test(Dash FilterGrids IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_SOURCE_DIR}/plugins
49 ${CMAKE_BINARY_DIR}/tests/mocks)
50 ${CMAKE_BINARY_DIR}/tests/mocks)
tests/qmltests/Dash/CardHelpers.js+45
@@ -18,6 +18,51 @@
18
18
19var components = ["title", "art", "subtitle", "mascot", "emblem", "old-price", "price", "alt-price", "rating", "alt-rating", "summary"]
19var components = ["title", "art", "subtitle", "mascot", "emblem", "old-price", "price", "alt-price", "rating", "alt-rating", "summary"]
20
20
21+var defaultLayout = ' \
22+{ \
23+ "schema-version": 1, \
24+ "template": { \
25+ "category-layout": "grid", \
26+ "card-layout": "vertical", \
27+ "card-size": "medium", \
28+ "overlay-mode": null, \
29+ "collapsed-rows": 2 \
30+ }, \
31+ "components": { \
32+ "title": null, \
33+ "art": { \
34+ "aspect-ratio": 1.0, \
35+ "fill-mode": "crop" \
36+ }, \
37+ "subtitle": null, \
38+ "mascot": null, \
39+ "emblem": null, \
40+ "old-price": null, \
41+ "price": null, \
42+ "alt-price": null, \
43+ "rating": { \
44+ "type": "stars", \
45+ "range": [0, 5], \
46+ "full": "image://theme/rating-star-full", \
47+ "half": "image://theme/rating-star-half", \
48+ "empty": "image://theme/rating-star-empty" \
49+ }, \
50+ "alt-rating": null, \
51+ "summary": null \
52+ }, \
53+ "resources": {} \
54+}'
55+
56+var fullMapping = ' \
57+{ \
58+ "title": "title", \
59+ "art": "art", \
60+ "subtitle": "subtitle", \
61+ "mascot": "mascot", \
62+ "summary": "summary" \
63+}'
64+
65+
21function tryParse(json, errorLabel) {
66function tryParse(json, errorLabel) {
22 var o = undefined;
67 var o = undefined;
23 if (errorLabel !== undefined) {
68 if (errorLabel !== undefined) {
tests/qmltests/Dash/tst_Card.qml+29−65
@@ -27,41 +27,6 @@ Rectangle {
27 height: units.gu(72)
27 height: units.gu(72)
28 color: "#88FFFFFF"
28 color: "#88FFFFFF"
29
29
30− property string defaultLayout: '
31− {
32− "schema-version": 1,
33− "template": {
34− "category-layout": "grid",
35− "card-layout": "vertical",
36− "card-size": "medium",
37− "overlay-mode": null,
38− "collapsed-rows": 2
39− },
40− "components": {
41− "title": null,
42− "art": {
43− "aspect-ratio": 1.0,
44− "fill-mode": "crop"
45− },
46− "subtitle": null,
47− "mascot": null,
48− "emblem": null,
49− "old-price": null,
50− "price": null,
51− "alt-price": null,
52− "rating": {
53− "type": "stars",
54− "range": [0, 5],
55− "full": "image://theme/rating-star-full",
56− "half": "image://theme/rating-star-half",
57− "empty": "image://theme/rating-star-empty"
58− },
59− "alt-rating": null,
60− "summary": null
61− },
62− "resources": {}
63− }'
64−
65 property string cardData: '
30 property string cardData: '
66 {
31 {
67 "art": "../../tests/qmltests/Dash/artwork/music-player-design.png",
32 "art": "../../tests/qmltests/Dash/artwork/music-player-design.png",
@@ -71,62 +36,61 @@ Rectangle {
71 "summary": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
36 "summary": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
72 }'
37 }'
73
38
74− property string fullMapping: '
75− {
76− "title": "title",
77− "art": "art",
78− "subtitle": "subtitle",
79− "mascot": "mascot",
80− "summary": "summary"
81− }'
82−
83 property var cardsModel: [
39 property var cardsModel: [
84 {
40 {
85− "name": "Art, header, summary - vertical",
41+ "name": "Art, header, summary",
86− "layout": { "components": JSON.parse(fullMapping) }
42+ "layout": { "components": JSON.parse(Helpers.fullMapping) }
87 },
43 },
88 {
44 {
89− "name": "Art, header, summary - vertical, small",
45+ "name": "Art, header, summary - small",
90− "layout": { "template": { "card-size": "small" }, "components": JSON.parse(fullMapping) }
46+ "layout": { "template": { "card-size": "small" }, "components": JSON.parse(Helpers.fullMapping) }
91 },
47 },
92 {
48 {
93− "name": "Art, header, summary - vertical, large",
49+ "name": "Art, header, summary - large",
94− "layout": { "template": { "card-size": "large" }, "components": JSON.parse(fullMapping) }
50+ "layout": { "template": { "card-size": "large" }, "components": JSON.parse(Helpers.fullMapping) }
95 },
51 },
96 {
52 {
97− "name": "Art, header, summary - vertical, wide",
53+ "name": "Art, header, summary - wide",
98− "layout": { "components": Helpers.update(JSON.parse(root.fullMapping), { "art": { "aspect-ratio": 2 } }) }
54+ "layout": { "components": Helpers.update(JSON.parse(Helpers.fullMapping), { "art": { "aspect-ratio": 2 } }) }
99 },
55 },
100 {
56 {
101− "name": "Art, title - vertical, fitted",
57+ "name": "Art, title - fitted",
102− "layout": { "components": Helpers.update(JSON.parse(root.fullMapping), { "art": { "fill-mode": "fit" } }) }
58+ "layout": { "components": Helpers.update(JSON.parse(Helpers.fullMapping), { "art": { "fill-mode": "fit" } }) }
103 },
59 },
104 {
60 {
105 "name": "Art, header, summary - horizontal",
61 "name": "Art, header, summary - horizontal",
106 "layout": { "template": { "card-layout": "horizontal" },
62 "layout": { "template": { "card-layout": "horizontal" },
107− "components": JSON.parse(fullMapping) }
63+ "components": JSON.parse(Helpers.fullMapping) }
108 },
64 },
109 {
65 {
110− "name": "Art, header - vertical",
66+ "name": "Art, header",
111− "layout": { "components": Helpers.update(JSON.parse(root.fullMapping), { "summary": undefined }) }
67+ "layout": { "components": Helpers.update(JSON.parse(Helpers.fullMapping), { "summary": undefined }) }
112 },
68 },
113 {
69 {
114− "name": "Art, summary - vertical",
70+ "name": "Art, summary",
115 "layout": { "components": { "art": "art", "summary": "summary" } }
71 "layout": { "components": { "art": "art", "summary": "summary" } }
116 },
72 },
117 {
73 {
118− "name": "Header title only - horizontal",
74+ "name": "Header title only",
119− "layout": { "template": { "card-layout": "horizontal" },
120− "components": { "title": "title" } }
75+ "layout": { "components": { "title": "title" } }
121 },
76 },
122 ]
77 ]
123
78
79+ CardTool {
80+ id: cardTool
81+ template: card.template
82+ components: card.components
83+ }
84+
124 Card {
85 Card {
125 id: card
86 id: card
126 anchors { top: parent.top; left: parent.left; margins: units.gu(1) }
87 anchors { top: parent.top; left: parent.left; margins: units.gu(1) }
127
88
89+ width: cardTool.cardWidth || implicitWidth
90+ height: cardTool.cardHeight || implicitHeight
91+
128− template: Helpers.update(JSON.parse(root.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['template'];
92+ template: Helpers.update(JSON.parse(Helpers.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['template'];
129− components: Helpers.update(JSON.parse(root.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['components'];
93+ components: Helpers.update(JSON.parse(Helpers.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['components'];
130 cardData: Helpers.mapData(dataArea.text, components, dataError)
94 cardData: Helpers.mapData(dataArea.text, components, dataError)
131 }
95 }
132
96
@@ -249,8 +213,8 @@ Rectangle {
249 function test_card_size_data() {
213 function test_card_size_data() {
250 return [
214 return [
251 { tag: "Medium", width: units.gu(18.5), index: 0 },
215 { tag: "Medium", width: units.gu(18.5), index: 0 },
252− { tag: "Small", width: units.gu(12), size: "small", index: 0 },
216+ { tag: "Small", width: units.gu(12), index: 1 },
253− { tag: "Large", width: units.gu(38), size: "large", index: 0 },
217+ { tag: "Large", width: units.gu(38), index: 2 },
254 { tag: "Wide", width: units.gu(18.5), aspect: 0.5, index: 0 },
218 { tag: "Wide", width: units.gu(18.5), aspect: 0.5, index: 0 },
255 { tag: "Horizontal", width: units.gu(38), index: 5 },
219 { tag: "Horizontal", width: units.gu(38), index: 5 },
256 // Make sure card ends with header when there's no summary
220 // Make sure card ends with header when there's no summary
tests/qmltests/Dash/tst_CardTool.qmlnew file+300
@@ -0,0 +1,300 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import Ubuntu.Components 0.1
20+import Unity.Test 0.1 as UT
21+import "../../../qml/Dash"
22+import "CardHelpers.js" as Helpers
23+
24+Rectangle {
25+ id: root
26+ width: units.gu(80)
27+ height: units.gu(72)
28+ color: "#88FFFFFF"
29+
30+ property var cardsModel: [
31+ {
32+ "name": "Art, header, summary",
33+ "layout": { "components": JSON.parse(Helpers.fullMapping) }
34+ },
35+ {
36+ "name": "Header, summary",
37+ "layout": { "components": Helpers.update(JSON.parse(Helpers.fullMapping), { "art": undefined }) }
38+ },
39+ {
40+ "name": "Art, header",
41+ "layout": { "components": Helpers.update(JSON.parse(Helpers.fullMapping), { "summary": undefined }) }
42+ },
43+ {
44+ "name": "Header only",
45+ "layout": { "components": Helpers.update(JSON.parse(Helpers.fullMapping), { "art": undefined, "summary": undefined }) }
46+ },
47+ {
48+ "name": "Header title only",
49+ "layout": { "components": { "title": "title" } }
50+ },
51+ {
52+ "name": "Header title and subtitle",
53+ "layout": { "components": { "title": "title", "subtitle": "subtitle" } }
54+ },
55+ {
56+ "name": "Header title and mascot",
57+ "layout": { "components": { "title": "title", "mascot": "mascot" } }
58+ },
59+ {
60+ "name": "Art, header, summary - small",
61+ "layout": { "template": { "card-size": "small" }, "components": JSON.parse(Helpers.fullMapping) }
62+ },
63+ {
64+ "name": "Art, header, summary - large",
65+ "layout": { "template": { "card-size": "large" }, "components": JSON.parse(Helpers.fullMapping) }
66+ },
67+ {
68+ "name": "Art, header, summary - horizontal",
69+ "layout": { "template": { "card-layout": "horizontal" }, "components": JSON.parse(Helpers.fullMapping) }
70+ },
71+ {
72+ "name": "Art, header - portrait",
73+ "layout": { "components": Helpers.update(JSON.parse(Helpers.fullMapping), { "art": {"aspect-ratio": 0.5, "summary": undefined }}) }
74+ },
75+ ]
76+
77+ CardTool {
78+ id: cardTool
79+
80+ template: Helpers.update(JSON.parse(Helpers.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['template'];
81+ components: Helpers.update(JSON.parse(Helpers.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['components'];
82+ viewWidth: units.gu(Math.round(widthSlider.value))
83+ }
84+
85+ Column {
86+ width: units.gu(38)
87+ anchors { left: parent.left; top: parent.top; margins: units.gu(1) }
88+
89+ Repeater {
90+ model: [
91+ { label: "View width", value: cardTool.viewWidth && cardTool.viewWidth / units.gu(1) },
92+ { label: "Card width", value: cardTool.cardWidth && cardTool.cardWidth / units.gu(1) },
93+ { label: "Card height", value: cardTool.cardHeight && cardTool.cardHeight / units.gu(1) },
94+ ]
95+
96+ delegate: Row {
97+ anchors { left: parent.left; right: parent.right; margins: units.gu(3) }
98+
99+ Label {
100+ height: units.gu(5)
101+ width: units.gu(25)
102+ text: modelData.label
103+ verticalAlignment: Text.AlignVCenter
104+ }
105+
106+ Label {
107+ height: units.gu(5)
108+ text: modelData.value || "undefined"
109+ verticalAlignment: Text.AlignVCenter
110+ }
111+ }
112+ }
113+
114+ Card {
115+ id: card
116+ template: cardTool.template;
117+ components: cardTool.components;
118+ cardData: Helpers.mapData(dataArea.text, components, dataError)
119+
120+ width: cardTool.cardWidth || implicitWidth
121+ height: cardTool.cardHeight || implicitHeight
122+
123+ clip: true
124+
125+ Rectangle {
126+ anchors.fill: parent
127+ color: "transparent"
128+ border.width: 1
129+ border.color: "green"
130+ }
131+ }
132+ }
133+
134+ Rectangle {
135+ anchors { top: parent.top; bottom: parent.bottom; right: parent.right}
136+ width: units.gu(40)
137+ color: "lightgrey"
138+
139+ Column {
140+ anchors { fill: parent; margins: units.gu(1) }
141+ spacing: units.gu(1)
142+
143+ OptionSelector {
144+ id: selector
145+ model: cardsModel
146+ delegate: OptionSelectorDelegate { text: modelData.name }
147+ onSelectedIndexChanged: updateAreas()
148+ Component.onCompleted: updateAreas()
149+
150+ function updateAreas() {
151+ var element = cardsModel[selectedIndex];
152+ if (element) {
153+ layoutArea.text = JSON.stringify(element.layout, undefined, 2) || "{}";
154+ } else {
155+ layoutArea.text = "";
156+ }
157+ }
158+ }
159+
160+ OptionSelector {
161+ id: layoutSelector
162+ model: [
163+ { label: "Grid", layout: "grid" },
164+ { label: "Organic Grid", layout: "organic-grid" },
165+ { label: "Horizontal Journal", layout: "journal" },
166+ { label: "Vertical Journal", layout: "vertical-journal" },
167+ { label: "Carousel", layout: "carousel" },
168+ ]
169+ delegate: OptionSelectorDelegate { text: modelData.label }
170+ onSelectedIndexChanged: {
171+ var element = model[selectedIndex];
172+ if (element && cardTool.template["category-layout"] !== element.layout) {
173+ var current = JSON.parse(layoutArea.text);
174+ var update = { "template": { "category-layout": element.layout }}
175+ layoutArea.text = JSON.stringify(Helpers.update(current, update), undefined, 2) || "{}";
176+ }
177+ }
178+ }
179+
180+ Label {
181+ anchors { left: parent.left; right: parent.right }
182+ height: units.gu(3)
183+ verticalAlignment: Text.AlignBottom
184+ text: "View width:"
185+ }
186+
187+ Slider {
188+ id: widthSlider
189+ minimumValue: 30
190+ maximumValue: 140
191+ live: true
192+ value: 40
193+ }
194+
195+ TextArea {
196+ id: layoutArea
197+ anchors { left: parent.left; right: parent.right }
198+ height: units.gu(25)
199+ }
200+
201+ Label {
202+ id: layoutError
203+ anchors { left: parent.left; right: parent.right }
204+ height: units.gu(4)
205+ color: "orange"
206+ }
207+
208+ TextArea {
209+ id: dataArea
210+ anchors { left: parent.left; right: parent.right }
211+ height: units.gu(25)
212+ text: JSON.stringify(cardTool.priv.cardData, undefined, 2)
213+ }
214+
215+ Label {
216+ id: dataError
217+ anchors { left: parent.left; right: parent.right }
218+ height: units.gu(4)
219+ color: "orange"
220+ }
221+ }
222+
223+ Connections {
224+ target: cardTool
225+ onTemplateChanged: {
226+ switch(cardTool.template["category-layout"]) {
227+ case "organic-grid": layoutSelector.selectedIndex = 1; break;
228+ case "journal": layoutSelector.selectedIndex = 2; break;
229+ case "vertical-journal": layoutSelector.selectedIndex = 3; break;
230+ case "carousel": layoutSelector.selectedIndex = 4; break;
231+ case "grid":
232+ default: layoutSelector.selectedIndex = 0; break;
233+ }
234+ }
235+ }
236+ }
237+
238+ UT.UnityTestCase {
239+ id: testCase
240+ name: "Card"
241+
242+ when: windowShown
243+
244+ function cleanup() {
245+ selector.selectedIndex = -1;
246+ layoutSelector.selectedIndex = -1;
247+ }
248+
249+ function test_card_size_data() {
250+ return [
251+ { tag: "Medium", width: units.gu(18.5), height: function() { return card.height }, index: 0 },
252+ { tag: "No art", width: units.gu(18.5), height: function() { return card.height }, index: 1 },
253+ { tag: "No summary", width: units.gu(18.5), height: function() { return card.height }, index: 2 },
254+ { tag: "Just header", width: units.gu(18.5), height: function() { return card.height }, index: 3 },
255+ { tag: "Just title", width: units.gu(18.5), height: function() { return card.height }, index: 4 },
256+ { tag: "Title and subtitle", width: units.gu(18.5), height: function() { return card.height }, index: 5 },
257+ { tag: "Title and mascot", width: units.gu(18.5), height: function() { return card.height }, index: 6 },
258+ { tag: "Small", width: units.gu(12), height: function() { return card.height }, index: 7 },
259+ { tag: "Large", width: units.gu(38), height: function() { return card.height }, index: 8 },
260+ { tag: "Horizontal", width: units.gu(38), height: function() { return card.height }, index: 9 },
261+ { tag: "OrganicGrid", width: undefined, height: undefined, index: 0, layout_index: 1},
262+ { tag: "Journal", width: undefined, height: units.gu(20), size: 20, index: 0, layout_index: 2 },
263+ { tag: "OversizedJournal", width: undefined, height: units.gu(18.5), size: 40, index: 0, layout_index: 2 },
264+ { tag: "VerticalJournal", width: units.gu(18.5), height: undefined, index: 0, layout_index: 3 },
265+ { tag: "SmallCarousel", width: units.gu(18), height: units.gu(18), viewWidth: 30, index: 0, layout_index: 4},
266+ { tag: "MediumCarousel", width: units.gu(22), height: units.gu(22), viewWidth: 84, index: 0, layout_index: 4},
267+ { tag: "LargeCarousel", width: units.gu(26), height: units.gu(26), viewWidth: 140, index: 0, layout_index: 4},
268+ { tag: "PortraitCarousel", width: units.gu(22), height: units.gu(44), viewWidth: 84, index: 10, layout_index: 4},
269+ ]
270+ }
271+
272+ function test_card_size(data) {
273+ selector.selectedIndex = data.index;
274+ if (data.hasOwnProperty("layout_index")) {
275+ layoutSelector.selectedIndex = data.layout_index;
276+ }
277+
278+ if (data.hasOwnProperty("viewWidth")) {
279+ widthSlider.value = data.viewWidth;
280+ }
281+
282+ if (data.hasOwnProperty("size")) {
283+ cardTool.template['card-size'] = data.size;
284+ cardTool.templateChanged();
285+ }
286+
287+ if (data.hasOwnProperty("width")) {
288+ if (typeof data.width === "function") {
289+ tryCompareFunction(function() { return cardTool.cardWidth === data.width() }, true);
290+ } else tryCompare(cardTool, "cardWidth", data.width);
291+ }
292+
293+ if (data.hasOwnProperty("height")) {
294+ if (typeof data.height === "function") {
295+ tryCompareFunction(function() { return cardTool.cardHeight === data.height() }, true);
296+ } else tryCompare(cardTool, "cardHeight", data.height);
297+ }
298+ }
299+ }
300+}