Add treeView to visualize changes in transaction

Test: N/A
Change-Id: Ida61733f1cc73b3bee4d3963b1bc17dfe2b626fc
This commit is contained in:
Pablo Gamito
2020-06-01 23:33:17 +01:00
parent d07c46dc6b
commit bd47ff7863
4 changed files with 142 additions and 22 deletions

View File

@@ -57,7 +57,7 @@ import LocalStore from './localstore.js'
import DataAdb from './DataAdb.vue' import DataAdb from './DataAdb.vue'
import FileType from './mixins/FileType.js' import FileType from './mixins/FileType.js'
const APP_NAME = "Winscope"; const APP_NAME = "Winscope"
export default { export default {
name: 'app', name: 'app',

View File

@@ -37,6 +37,7 @@
:filter="hierarchyFilter" :filter="hierarchyFilter"
:flattened="store.flattened" :flattened="store.flattened"
:items-clickable="true" :items-clickable="true"
:useGlobalCollapsedState="true"
ref="hierarchy" ref="hierarchy"
/> />
</md-card> </md-card>
@@ -52,6 +53,7 @@
:item="selectedTree" :item="selectedTree"
:filter="propertyFilter" :filter="propertyFilter"
:collapseChildren="true" :collapseChildren="true"
:useGlobalCollapsedState="true"
/> />
</md-card> </md-card>
</md-card-content> </md-card-content>

View File

@@ -31,7 +31,11 @@
</md-table-toolbar> </md-table-toolbar>
<div class="scrollBody" ref="tableBody"> <div class="scrollBody" ref="tableBody">
<md-table-row v-for="transaction in filteredData" :key="transaction.timestamp"> <md-table-row
v-for="transaction in filteredData"
:key="transaction.timestamp"
@click="transactionSelected(transaction)"
>
<md-table-cell>{{transaction.time}}</md-table-cell> <md-table-cell>{{transaction.time}}</md-table-cell>
<div v-if="transaction.type == 'transaction'"> <div v-if="transaction.type == 'transaction'">
@@ -49,9 +53,24 @@
</div> </div>
</md-table> </md-table>
<md-card class="changes">
<md-content md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
<h2 class="md-title" style="flex: 1">Changes</h2>
</md-content>
<div class="changes-content">
<tree-view :item="selectedTree" :useGlobalCollapsedState="true" />
</div>
</md-card>
</md-card-content> </md-card-content>
</template> </template>
<script> <script>
import TreeView from './TreeView.vue';
import { transform_json } from './transform.js';
import { stableIdCompatibilityFixup } from './utils/utils.js'
export default { export default {
name: 'transactionsview', name: 'transactionsview',
props: ['data'], props: ['data'],
@@ -71,6 +90,7 @@ export default {
transactionTypes: Array.from(transactionTypes), transactionTypes: Array.from(transactionTypes),
selectedTransactionTypes: [], selectedTransactionTypes: [],
searchInput: "", searchInput: "",
selectedTree: null,
}; };
}, },
computed: { computed: {
@@ -95,6 +115,47 @@ export default {
}, },
methods: { methods: {
removeNullFields(changeObject) {
for (const key in changeObject) {
if (changeObject[key] === null) {
delete changeObject[key];
}
}
return changeObject;
},
transactionSelected(transaction) {
let obj = this.removeNullFields(transaction.obj);
let name = transaction.type;
if (transaction.type == "transaction") {
name = "changes";
obj = {};
const [surfaceChanges, displayChanges] =
this.aggregateTransactions(transaction.transactions);
for (const changeId in surfaceChanges) {
this.removeNullFields(surfaceChanges[changeId]);
}
for (const changeId in displayChanges) {
this.removeNullFields(displayChanges[changeId]);
}
if (Object.keys(surfaceChanges).length > 0) {
obj.surfaceChanges = surfaceChanges;
}
if (Object.keys(displayChanges).length > 0) {
obj.displayChanges = displayChanges;
}
}
const transactionUnique = transaction.timestamp;
this.selectedTree = transform_json(obj, name, transactionUnique, {
formatter: () => {}
});
},
filterTransactions(condition) { filterTransactions(condition) {
return (entry) => { return (entry) => {
if (entry.type == "transaction") { if (entry.type == "transaction") {
@@ -110,7 +171,27 @@ export default {
} }
}; };
}, },
summarizeTranscations(transactions) { mergeChanges(a, b) {
const res = {};
for (const key in a) {
if (a[key] !== null && a.hasOwnProperty(key)) {
res[key] = a[key];
}
}
for (const key in b) {
if (b[key] !== null && key !== 'id' && b.hasOwnProperty(key)) {
if (res.hasOwnProperty(key)) {
throw new Error(`Merge failed key '${key}' already present`);
}
res[key] = b[key];
}
}
return res;
},
aggregateTransactions(transactions) {
const surfaceChanges = {}; const surfaceChanges = {};
const displayChanges = {}; const displayChanges = {};
@@ -120,12 +201,12 @@ export default {
switch (transaction.type) { switch (transaction.type) {
case "surfaceChange": case "surfaceChange":
surfaceChanges[obj.id] = surfaceChanges[obj.id] =
Object.assign(surfaceChanges[obj.id] ?? {}, obj); this.mergeChanges(surfaceChanges[obj.id] ?? {}, obj);
break; break;
case "displayChange": case "displayChange":
displayChanges[obj.id] = displayChanges[obj.id] =
Object.assign(displayChanges[obj.id] ?? {}, obj); this.mergeChanges(displayChanges[obj.id] ?? {}, obj);
break; break;
default: default:
@@ -133,31 +214,50 @@ export default {
} }
} }
const summary = []; return [surfaceChanges, displayChanges];
},
summarizeTranscations(transactions) {
const ids = {
"surfaceChange": new Set(),
"displayChange": new Set(),
};
const surfaceChangesId = Object.keys(surfaceChanges); for (const transaction of transactions) {
if (surfaceChangesId.length > 0) { ids[transaction.type].add(transaction.obj.id);
summary.push(`surfaceChanges: ${surfaceChangesId.join(', ')}`);
} }
const displayChangesIds = Object.keys(displayChanges); const summary = [];
if (displayChangesIds.length > 0) {
summary.push(`displayChanges: ${displayChangesIds.join(', ')}`); if (ids.surfaceChange.size > 0) {
summary.push(`surfaceChanges: ${[...ids.surfaceChange].join(', ')}`);
}
if (ids.displayChange.size > 0) {
summary.push(`displayChanges: ${[...ids.displayChange].join(', ')}`);
} }
return summary.join(" | "); return summary.join(" | ");
} },
},
components: {
'tree-view': TreeView,
} }
} }
</script> </script>
<style scoped> <style scoped>
/* .transaction-table { .container {
width: 100%; display: flex;
} */ flex-wrap: wrap;
}
.transaction-table,
.changes {
flex: 1;
margin: 8px;
}
.scrollBody { .scrollBody {
/* width: 100%; */
max-height: 75vh; max-height: 75vh;
overflow: scroll; overflow: scroll;
} }

View File

@@ -65,6 +65,7 @@
:initial-depth="depth + 1" :initial-depth="depth + 1"
:collapse="collapseChildren" :collapse="collapseChildren"
:collapseChildren="collapseChildren" :collapseChildren="collapseChildren"
:useGlobalCollapsedState="useGlobalCollapsedState"
ref="children" ref="children"
/> />
</div> </div>
@@ -98,6 +99,14 @@ export default {
"initial-depth", "initial-depth",
"collapse", "collapse",
"collapseChildren", "collapseChildren",
// Allows collapse state to be tracked by Vuex so that collapse state of
// items with same stableId can remain consisten accross time and easily
// toggled from anywhere in the app.
// Should be true if you are using the same TreeView to display multiple
// trees throughout the component's lifetime to make sure same nodes are
// toggled when switching back and forth between trees.
// If true, requires all nodes in tree to have a stableId.
"useGlobalCollapsedState",
], ],
data() { data() {
const isCollapsedByDefault = this.collapse ?? false; const isCollapsedByDefault = this.collapse ?? false;
@@ -106,14 +115,19 @@ export default {
isChildSelected: false, isChildSelected: false,
clickTimeout: null, clickTimeout: null,
isCollapsedByDefault, isCollapsedByDefault,
localCollapsedState: isCollapsedByDefault,
}; };
}, },
methods: { methods: {
setCollapseValue(isCollapsed) { setCollapseValue(isCollapsed) {
this.$store.commit('setCollapsedState', { if (this.useGlobalCollapsedState) {
item: this.item, this.$store.commit('setCollapsedState', {
isCollapsed, item: this.item,
}); isCollapsed,
});
} else {
this.localCollapsedState = isCollapsed;
}
}, },
toggleTree() { toggleTree() {
this.setCollapseValue(!this.isCollapsed); this.setCollapseValue(!this.isCollapsed);
@@ -236,8 +250,12 @@ export default {
return false; return false;
} }
return this.$store.getters.collapsedStateStoreFor(this.item) ?? if (this.useGlobalCollapsedState) {
return this.$store.getters.collapsedStateStoreFor(this.item) ??
this.isCollapsedByDefault; this.isCollapsedByDefault;
}
return this.localCollapsedState;
}, },
isSelected() { isSelected() {
return this.selected === this.item; return this.selected === this.item;