Skip to content

Commit

Permalink
Add diff view (#1286)
Browse files Browse the repository at this point in the history
  • Loading branch information
Andres Martinez Gotor authored Nov 14, 2019
1 parent 10e0b9b commit a3809e6
Show file tree
Hide file tree
Showing 12 changed files with 363 additions and 45 deletions.
3 changes: 3 additions & 0 deletions dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"homepage": "./",
"dependencies": {
"@types/diff": "^4.0.2",
"@types/js-yaml": "^3.10.1",
"@types/json-schema": "^7.0.3",
"@types/jsonwebtoken": "^8.3.4",
Expand All @@ -21,6 +22,8 @@
"ajv": "^6.10.2",
"axios": "^0.19.0",
"connected-react-router": "^4.5.0",
"diff": "^4.0.1",
"diff2html": "^2.12.1",
"enzyme-adapter-react-16": "^1.1.1",
"fast-json-patch": "^3.0.0-1",
"fstream": "^1.0.12",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ class AdvancedDeploymentForm extends React.Component<IAdvancedDeploymentForm> {
public render() {
return (
<div className="margin-t-normal">
<label htmlFor="values">Values (YAML)</label>
<AceEditor
mode="yaml"
theme="xcode"
name="values"
width="100%"
onChange={this.props.handleValuesChange}
setOptions={{ showPrintMargin: false }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import DeploymentFormBody, {
IDeploymentFormBodyProps,
IDeploymentFormBodyState,
} from "./DeploymentFormBody";
import Differential from "./Differential";

const defaultProps = {
chartID: "foo",
Expand Down Expand Up @@ -141,12 +142,6 @@ describe("when the basic form is enabled", () => {
expect(wrapper.find(Tabs)).toExist();
});

it("should not render the tabs if there are no basic parameters", () => {
const wrapper = shallow(<DeploymentFormBody {...props} />);
expect(wrapper.find(LoadingWrapper)).not.toExist();
expect(wrapper.find(Tabs)).not.toExist();
});

it("changes the parameter value", () => {
const basicFormParameters = [
{
Expand Down Expand Up @@ -200,7 +195,7 @@ describe("when the basic form is enabled", () => {

const tab = wrapper
.find(Tab)
.findWhere(t => !!t.text().match("Basic"))
.findWhere(t => !!t.text().match("Form"))
.first();
tab.simulate("click");

Expand Down Expand Up @@ -322,3 +317,55 @@ it("restores the default chart values when clicking on the button", () => {

expect(setValues).toHaveBeenCalledWith("foo: value");
});

describe("Changes tab", () => {
it("should show the differences between the default chart values when deploying", () => {
const selected = {
...defaultProps.selected,
versions: [chartVersion],
version: chartVersion,
values: "foo: bar",
schema: initialSchema,
};
const appValues = "bar: foo";
const wrapper = shallow(
<DeploymentFormBody {...props} selected={selected} appValues={appValues} />,
);

const Diff = wrapper.find(Differential);
expect(Diff.props()).toMatchObject({
emptyDiffText: "No changes detected from chart defaults.",
newValues: "bar: foo",
oldValues: "foo: bar",
title: "Difference from chart defaults",
});
});

it("should show the differences between the current release and the new one when upgrading", () => {
const selected = {
...defaultProps.selected,
versions: [chartVersion],
version: chartVersion,
values: "foo: bar",
schema: initialSchema,
};
const deployedValues = "a: b";
const appValues = "bar: foo";
const wrapper = shallow(
<DeploymentFormBody
{...props}
selected={selected}
appValues={appValues}
deployedValues={deployedValues}
/>,
);

const Diff = wrapper.find(Differential);
expect(Diff.props()).toMatchObject({
emptyDiffText: "The values for the new release are identical to the deployed version.",
newValues: "bar: foo",
oldValues: "a: b",
title: "Difference from deployed version",
});
});
});
90 changes: 59 additions & 31 deletions dashboard/src/components/DeploymentFormBody/DeploymentFormBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import AdvancedDeploymentForm from "./AdvancedDeploymentForm";
import BasicDeploymentForm from "./BasicDeploymentForm";

import "react-tabs/style/react-tabs.css";
import Differential from "./Differential";
import "./Tabs.css";

export interface IDeploymentFormBodyProps {
chartID: string;
chartVersion: string;
deployedValues?: string;
namespace: string;
releaseVersion?: string;
selected: IChartState["selected"];
Expand Down Expand Up @@ -73,7 +75,7 @@ class DeploymentFormBody extends React.Component<
};

public render() {
const { selected, chartID, chartVersion, goBack, appValues } = this.props;
const { selected, chartID, chartVersion, goBack } = this.props;
const { version, versions } = selected;
if (selected.error) {
return (
Expand Down Expand Up @@ -111,14 +113,7 @@ class DeploymentFormBody extends React.Component<
))}
</select>
</div>
{this.shouldRenderBasicForm() ? (
this.renderTabs()
) : (
<AdvancedDeploymentForm
appValues={appValues}
handleValuesChange={this.handleValuesChange}
/>
)}
{this.renderTabs()}
<div className="margin-t-big">
<button className="button button-primary" type="submit">
Submit
Expand Down Expand Up @@ -173,35 +168,41 @@ class DeploymentFormBody extends React.Component<
<div className="margin-t-normal">
<Tabs>
<TabList>
<Tab onClick={this.refreshBasicParameters}>
Basic{" "}
<Hint reactTooltipOpts={{ delayHide: 100 }} id="basicFormHelp">
<span>
This form has been automatically generated based on the chart schema.
<br />
This feature is currently in a beta state. If you find an issue please report it{" "}
<a target="_blank" href="https://github.com/kubeapps/kubeapps/issues/new">
here.
</a>
</span>
</Hint>
</Tab>
<Tab>Advanced</Tab>
{this.shouldRenderBasicForm() && (
<Tab onClick={this.refreshBasicParameters}>
Form{" "}
<Hint reactTooltipOpts={{ delayHide: 100 }} id="basicFormHelp">
<span>
This form has been automatically generated based on the chart schema.
<br />
This feature is currently in a beta state. If you find an issue please report it{" "}
<a target="_blank" href="https://github.com/kubeapps/kubeapps/issues/new">
here.
</a>
</span>
</Hint>
</Tab>
)}
<Tab>Values (YAML)</Tab>
<Tab>Changes</Tab>
</TabList>
<TabPanel>
<BasicDeploymentForm
params={this.state.basicFormParameters}
handleBasicFormParamChange={this.handleBasicFormParamChange}
appValues={this.props.appValues}
handleValuesChange={this.handleValuesChange}
/>
</TabPanel>
{this.shouldRenderBasicForm() && (
<TabPanel>
<BasicDeploymentForm
params={this.state.basicFormParameters}
handleBasicFormParamChange={this.handleBasicFormParamChange}
appValues={this.props.appValues}
handleValuesChange={this.handleValuesChange}
/>
</TabPanel>
)}
<TabPanel>
<AdvancedDeploymentForm
appValues={this.props.appValues}
handleValuesChange={this.handleValuesChange}
/>
</TabPanel>
<TabPanel>{this.renderDiff()}</TabPanel>
</Tabs>
</div>
);
Expand Down Expand Up @@ -247,6 +248,33 @@ class DeploymentFormBody extends React.Component<
}
this.setState({ restoreDefaultValuesModalIsOpen: false });
};

private renderDiff = () => {
let oldValues = "";
let title = "";
let emptyDiffText = "";
if (this.props.deployedValues) {
// If there are already some deployed values (upgrade scenario)
// We compare the values from the old release and the new one
oldValues = this.props.deployedValues;
title = "Difference from deployed version";
emptyDiffText = "The values for the new release are identical to the deployed version.";
} else {
// If it's a new deployment, we show the different from the default
// values for the selected version
oldValues = this.props.selected.values || "";
title = "Difference from chart defaults";
emptyDiffText = "No changes detected from chart defaults.";
}
return (
<Differential
title={title}
oldValues={oldValues}
newValues={this.props.appValues}
emptyDiffText={emptyDiffText}
/>
);
};
}

export default DeploymentFormBody;
18 changes: 18 additions & 0 deletions dashboard/src/components/DeploymentFormBody/Differential.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { shallow } from "enzyme";
import * as React from "react";
import Differential from "./Differential";

it("should render a diff between two strings", () => {
const wrapper = shallow(
<Differential title="test" oldValues="foo" newValues="bar" emptyDiffText="empty" />,
);
expect(wrapper).toMatchSnapshot();
});

it("should print the emptyDiffText if there are no changes", () => {
const wrapper = shallow(
<Differential title="test" oldValues="foo" newValues="foo" emptyDiffText="No differences!" />,
);
expect(wrapper.text()).toMatch("No differences!");
expect(wrapper.text()).not.toMatch("foo");
});
36 changes: 36 additions & 0 deletions dashboard/src/components/DeploymentFormBody/Differential.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as jsdiff from "diff";
import { Diff2Html } from "diff2html";
import * as React from "react";

import "diff2html/dist/diff2html.css";

export interface IDifferentialProps {
title: string;
oldValues: string;
newValues: string;
emptyDiffText: string;
}

class Differential extends React.Component<IDifferentialProps> {
public render = () => {
const { oldValues, newValues, title, emptyDiffText } = this.props;
const sdiff = jsdiff.createPatch(title, oldValues, newValues);
const outputHtml = Diff2Html.getPrettyHtml(sdiff, {
inputFormat: "diff",
showFiles: false,
matching: "lines",
maxLineSizeInBlockForComparison: 20,
});
return (
<div className="diff">
{oldValues === newValues ? (
<p>{emptyDiffText}</p>
) : (
<div dangerouslySetInnerHTML={{ __html: outputHtml }} />
)}
</div>
);
};
}

export default Differential;
25 changes: 25 additions & 0 deletions dashboard/src/components/DeploymentFormBody/Tabs.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,28 @@
.editor {
z-index: 0;
}

.diff {
height: 500px;
overflow: auto;
}

.diff pre {
background-color: inherit;
padding: 0;
font: 12px / normal "Monaco", "Menlo", "Ubuntu Mono", "Consolas", "source-code-pro", monospace;
line-height: 12px;
}

.diff table td {
padding: 0;
}

.diff table td[class*="linenumber"] {
position: inherit;
}

.diff table td div[class*="code-line"] {
padding: 0 0.3em;
min-width: 58em;
}
Loading

0 comments on commit a3809e6

Please sign in to comment.