Skip to content

Commit

Permalink
Fix #365: Render a JSON diff for history entries. (#380)
Browse files Browse the repository at this point in the history
  • Loading branch information
n1k0 committed Feb 2, 2017
1 parent 1e18b07 commit c825ed0
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 6 deletions.
19 changes: 19 additions & 0 deletions css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ table.record-list td.load-more {
margin: 0;
}

.history-row-details .alert {
margin: 0;
border-radius: 0;
}

.sort-link {
float: right;
margin-top: .1em;
Expand Down Expand Up @@ -249,6 +254,20 @@ table.record-list td.load-more {
min-height: 300px;
}

.json-record > div {
padding: 1px 0 1px 4px;
}

.json-record > div.added {
background: #cbeecb;
color: green;
}

.json-record > div.removed {
background: #f0c9c9;
color: red;
}

.panel-heading > .glyphicon,
.list-group-item .glyphicon,
.nav-tabs li .glyphicon {
Expand Down
9 changes: 9 additions & 0 deletions interfaces/external-modules/diff.d.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
declare module "diff" {
declare type DiffEntry = {
added: boolean,
removed: boolean,
value: string,
};
declare function diffJson(a: Object, b: Object): DiffEntry[]
declare var exports: diffJson
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"bootstrap": "^3.3.6",
"btoa": "^1.1.2",
"codemirror": "^5.13.2",
"diff": "^3.2.0",
"express": "^4.14.0",
"filesize": "^3.3.0",
"kinto-http": "^2.7.0",
Expand Down
64 changes: 58 additions & 6 deletions src/components/HistoryTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,83 @@ import type { ResourceHistoryEntry, RouteLocation } from "../types";

import React, { Component } from "react";
import { Link } from "react-router";
import { diffJson } from "diff";

import { timeago, humanDate } from "../utils";
import AdminLink from "./AdminLink";
import PaginatedTable from "./PaginatedTable";
import { getClient } from "../client";


function Diff({source, target}) {
const diff = diffJson(target, source);
return (
<pre className="json-record">{
diff.map((chunk, i) => {
const className = chunk.added ? "added" : chunk.removed ? "removed" : "";
const prefixedChunk = chunk.value.split("\n")
.filter(part => part !== "")
.map((part) => {
const prefix = chunk.added ? "+ " : chunk.removed ? "- " : " ";
return prefix + part;
})
.join("\n");
return (
<div key={i} className={className}>
<code>{prefixedChunk}</code>
</div>
);
})
}</pre>
);
}

function fetchPreviousVersion(bid, entry: ResourceHistoryEntry): Promise<?ResourceHistoryEntry> {
const {uri, last_modified} = entry;
return getClient().bucket(bid).listHistory({
filters: {uri, _before: last_modified},
limit: 1
}).then(({data}) => data[0]);
}

class HistoryRow extends Component {
props: {
bid: string,
entry: ResourceHistoryEntry,
};

state: {
open: boolean
open: boolean,
previous: ?ResourceHistoryEntry,
error: ?Error,
};

constructor(props) {
super(props);
this.state = {open: false};
this.state = {open: false, previous: null, error: null};
}

toggle = (event) => {
const {bid, entry} = this.props;
event.preventDefault();
this.setState({open: !this.state.open});
if (entry.action !== "update") {
return this.setState({open: !this.state.open});
}
if (this.state.open) {
return this.setState({open: false});
}
if (this.state.previous) {
return this.setState({open: true});
}
// We don't leverage redux store and dedicated action as this behavior is
// contextually specific to this local component.
fetchPreviousVersion(bid, entry)
.then((previous) => this.setState({open: true, previous, error: null}))
.catch((error) => this.setState({open: true, previous: null, error}));
};

render() {
const {open} = this.state;
const {open, previous, error} = this.state;
const {entry, bid} = this.props;
const {
last_modified,
Expand Down Expand Up @@ -71,15 +120,18 @@ class HistoryRow extends Component {
<tr className="history-row-details"
style={{display: open ? "table-row" : "none"}}>
<td colSpan="6">
<pre>{JSON.stringify(entry, null, 2)}</pre>
{previous
? <Diff source={entry.target} target={previous.target} />
: error
? <p className="alert alert-danger">{error}</p>
: <pre>{JSON.stringify(entry.target, null, 2)}</pre>}
</td>
</tr>
</tbody>
);
}
}


function FilterInfo(props) {
const {location}: {location: RouteLocation} = props;
const {pathname, query: {since}} = location;
Expand Down

0 comments on commit c825ed0

Please sign in to comment.