From 0ad392d15948e7cc38fdb93a9ada56aa05f42970 Mon Sep 17 00:00:00 2001
From: Melissa Page <73hl10n@gmail.com>
Date: Wed, 24 Oct 2018 16:30:28 -0700
Subject: [PATCH] [Web UI] Check details page improvements (#2162)
* Wrap commands, allow scrolling on env vars
Signed-off-by: Melissa P <73hl10n@gmail.com>
* Add buttons for publish and unpublish
Signed-off-by: Melissa P <73hl10n@gmail.com>
* Added check publish and unpublish actions
Signed-off-by: Melissa P <73hl10n@gmail.com>
* Fix syntax
Signed-off-by: Melissa P <73hl10n@gmail.com>
* Render single button depending on state of publish
Signed-off-by: Melissa P <73hl10n@gmail.com>
* update changelog
Signed-off-by: Melissa P <73hl10n@gmail.com>
* Review tweaks
Signed-off-by: Melissa P <73hl10n@gmail.com>
* Review tweaks
Signed-off-by: Melissa P <73hl10n@gmail.com>
* Rework Monospaced to be more clear at it's intention
Signed-off-by: Melissa P <73hl10n@gmail.com>
* Set DV back to table-cell and give optional scrollable prop
Signed-off-by: Melissa P <73hl10n@gmail.com>
* Initial implementation of code highlighting
Signed-off-by: Melissa P <73hl10n@gmail.com>
* Implement code highlighting
Signed-off-by: Melissa P <73hl10n@gmail.com>
* Update eslint rule
Signed-off-by: Melissa P <73hl10n@gmail.com>
* update changelog
Signed-off-by: Melissa P <73hl10n@gmail.com>
* Fallback theme colour re-added
Signed-off-by: Melissa P <73hl10n@gmail.com>
---
CHANGELOG.md | 2 +
dashboard/.eslintrc.yml | 3 +
dashboard/config/webpack.config.js | 14 ++
dashboard/package.json | 6 +-
dashboard/src/components/Code.js | 13 +-
.../Monospaced.js => CodeBlock/CodeBlock.js} | 29 ++--
dashboard/src/components/CodeBlock/index.js | 1 +
.../components/CodeHighlight/CodeHighlight.js | 72 ++++++++++
.../CodeHighlight/CodeHighlight.worker.js | 14 ++
.../components/Dictionary/DictionaryValue.js | 10 ++
dashboard/src/components/Monospaced/index.js | 1 -
.../CheckDetailsConfiguration.js | 45 ++++--
.../CheckDetailsPublishAction.js | 53 +++++++
.../CheckDetailsToolbar.js | 16 +++
.../CheckDetailsUnpublishAction.js | 54 +++++++
.../partials/ChecksList/ChecksListItem.js | 5 +-
.../EntityDetailsInformation.js | 14 +-
.../EventDetailsCheckResult.js | 6 +-
.../EventDetailsSummary.js | 14 +-
dashboard/yarn.lock | 133 +++++++++++++++++-
20 files changed, 455 insertions(+), 50 deletions(-)
rename dashboard/src/components/{Monospaced/Monospaced.js => CodeBlock/CodeBlock.js} (76%)
create mode 100644 dashboard/src/components/CodeBlock/index.js
create mode 100644 dashboard/src/components/CodeHighlight/CodeHighlight.js
create mode 100644 dashboard/src/components/CodeHighlight/CodeHighlight.worker.js
delete mode 100644 dashboard/src/components/Monospaced/index.js
create mode 100644 dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsPublishAction.js
create mode 100644 dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsUnpublishAction.js
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bbbd85c625..879a676c97 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,8 @@ Versioning](http://semver.org/spec/v2.0.0.html).
- Added a debug log message for interval timer initial offset.
- Added a privilege escalation test for RBAC.
- Web ui allows publishing and unpublishing on checks page
+- Web ui allows publishing and unpublishing on check details page
+- Code highlighting added
### Removed
- Staging resources and configurations have been removed from sensu-go.
diff --git a/dashboard/.eslintrc.yml b/dashboard/.eslintrc.yml
index 3cd491daf9..d21aa83421 100644
--- a/dashboard/.eslintrc.yml
+++ b/dashboard/.eslintrc.yml
@@ -47,6 +47,9 @@ rules:
import/prefer-default-export: 0
+ # Code Highlighting requirees dangerously setting
+ react/no-danger: 0
+
# https://github.com/apollographql/eslint-plugin-graphql
graphql/template-strings:
- error
diff --git a/dashboard/config/webpack.config.js b/dashboard/config/webpack.config.js
index 463b438fcb..107d230d84 100644
--- a/dashboard/config/webpack.config.js
+++ b/dashboard/config/webpack.config.js
@@ -28,6 +28,8 @@ export default () => {
entry: [path.resolve(root, "src/index.js")],
output: {
+ globalObject: "self",
+
path: outputPath,
publicPath: "/",
@@ -111,6 +113,18 @@ export default () => {
name: "static/media/[name].[hash:8].[ext]",
},
},
+ {
+ test: /\.css$/,
+ use: ["style-loader", "css-loader"],
+ },
+ {
+ test: /\.worker\.js$/,
+ loader: "worker-loader",
+ options: {
+ name: "static/[hash].worker.js",
+ },
+ },
+
{
test: /\.macro\.js$/,
exclude: path.resolve(root, "node_modules"),
diff --git a/dashboard/package.json b/dashboard/package.json
index ea2a2df6cf..f3dac1215e 100644
--- a/dashboard/package.json
+++ b/dashboard/package.json
@@ -58,6 +58,7 @@
"connect-history-api-fallback": "^1.5.0",
"core-js": "^2.5.6",
"cronstrue": "^1.31.0",
+ "css-loader": "^1.0.0",
"debounce": "^1.2.0",
"deepmerge": "^2.0.1",
"enzyme": "^3.3.0",
@@ -83,6 +84,7 @@
"graphql": "0.13.0",
"graphql-config": "^2.0.1",
"graphql-tag": "^2.9.2",
+ "highlight.js": "^9.13.1",
"hoist-non-react-statics": "^2.5.0",
"html-webpack-plugin": "^3.2.0",
"http-proxy-middleware": "^0.18.0",
@@ -121,6 +123,7 @@
"regenerator-runtime": "^0.11.1",
"source-map-loader": "bryanforbes/source-map-loader#module-context",
"stacktrace-js": "^2.0.0",
+ "style-loader": "^0.23.1",
"sw-precache-webpack-plugin": "^0.11.5",
"typeface-roboto": "^0.0.54",
"uglifyjs-webpack-plugin": "^1.2.5",
@@ -130,7 +133,8 @@
"warning": "^3.0.0",
"webpack": "^4.8.1",
"webpack-stats-plugin": "^0.2.1",
- "whatwg-fetch": "^2.0.4"
+ "whatwg-fetch": "^2.0.4",
+ "worker-loader": "^2.0.0"
},
"jest": {
"collectCoverageFrom": [
diff --git a/dashboard/src/components/Code.js b/dashboard/src/components/Code.js
index 4fa81a7004..01d80c0669 100644
--- a/dashboard/src/components/Code.js
+++ b/dashboard/src/components/Code.js
@@ -5,7 +5,7 @@ import { fade } from "@material-ui/core/styles/colorManipulator";
const styles = theme => ({
root: {
- whiteSpace: "nowrap",
+ whiteSpace: "pre-wrap",
margin: "0 1px",
padding: `${2 / 16}em ${4 / 16}em`,
borderRadius: `${5 / 16}em`,
@@ -24,11 +24,18 @@ const styles = theme => ({
class Code extends React.PureComponent {
static propTypes = {
classes: PropTypes.object.isRequired,
+ children: PropTypes.node,
};
+ static defaultProps = { children: null };
+
render() {
- const { classes, ...props } = this.props;
- return
;
+ const { classes, children, ...props } = this.props;
+ return (
+
+ {children}
+
+ );
}
}
diff --git a/dashboard/src/components/Monospaced/Monospaced.js b/dashboard/src/components/CodeBlock/CodeBlock.js
similarity index 76%
rename from dashboard/src/components/Monospaced/Monospaced.js
rename to dashboard/src/components/CodeBlock/CodeBlock.js
index eadcad11e2..ef6c978cf6 100644
--- a/dashboard/src/components/Monospaced/Monospaced.js
+++ b/dashboard/src/components/CodeBlock/CodeBlock.js
@@ -7,48 +7,42 @@ import Typography from "@material-ui/core/Typography";
const styles = theme => ({
root: {
- // TODO: Move into theme so that it can be overridden.
- fontFamily: `"SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace`,
+ fontFamily: theme.typography.monospace.fontFamily,
overflowX: "scroll",
userSelect: "text",
tabSize: 2,
- },
- background: {
- backgroundColor: emphasize(theme.palette.background.paper, 0.01875),
- },
- highlight: {
color:
theme.palette.type === "dark"
? theme.palette.secondary.light
: theme.palette.secondary.dark,
- "& $background": {
- backgroundColor: emphasize(theme.palette.text.primary, 0.05),
- },
+ },
+ background: {
+ backgroundColor: emphasize(theme.palette.background.paper, 0.01875),
},
scaleFont: {
// Browsers tend to render monospaced fonts a little larger than intended.
// Attempt to scale accordingly.
fontSize: "0.8125rem", // TODO: Scale given fontSize from theme?
},
+ wrap: { whiteSpace: "pre-wrap" },
});
-class Monospaced extends React.Component {
+class CodeBlock extends React.Component {
static propTypes = {
background: PropTypes.bool,
classes: PropTypes.object.isRequired,
className: PropTypes.string,
component: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
children: PropTypes.node.isRequired,
- highlight: PropTypes.bool,
scaleFont: PropTypes.bool,
};
static defaultProps = {
- background: false,
+ background: true,
component: "pre",
className: "",
- highlight: false,
scaleFont: true,
+ highlight: null,
};
render() {
@@ -57,7 +51,6 @@ class Monospaced extends React.Component {
classes,
className: classNameProp,
children,
- highlight,
scaleFont,
...props
} = this.props;
@@ -65,14 +58,14 @@ class Monospaced extends React.Component {
const className = classnames(classes.root, classNameProp, {
[classes.background]: background,
[classes.scaleFont]: scaleFont,
- [classes.highlight]: highlight,
});
+
return (
- {children}
+ {children}
);
}
}
-export default withStyles(styles)(Monospaced);
+export default withStyles(styles)(CodeBlock);
diff --git a/dashboard/src/components/CodeBlock/index.js b/dashboard/src/components/CodeBlock/index.js
new file mode 100644
index 0000000000..45a9fcacda
--- /dev/null
+++ b/dashboard/src/components/CodeBlock/index.js
@@ -0,0 +1 @@
+export { default } from "./CodeBlock";
diff --git a/dashboard/src/components/CodeHighlight/CodeHighlight.js b/dashboard/src/components/CodeHighlight/CodeHighlight.js
new file mode 100644
index 0000000000..f6a62a04ba
--- /dev/null
+++ b/dashboard/src/components/CodeHighlight/CodeHighlight.js
@@ -0,0 +1,72 @@
+import "highlight.js/styles/github-gist.css";
+
+import React from "react";
+import PropTypes from "prop-types";
+import Worker from "./CodeHighlight.worker";
+
+const worker = new Worker();
+const pending = {};
+
+worker.onmessage = event => {
+ const [key, data] = event.data;
+ pending[key].call(this, data);
+};
+
+let idx = 0;
+function postMessage(msg, callback) {
+ const key = idx;
+ pending[idx] = data => {
+ callback(data);
+ delete pending[idx];
+ };
+ worker.postMessage({ key, msg });
+ idx += 1;
+}
+
+class CodeHighlight extends React.Component {
+ static propTypes = {
+ language: PropTypes.oneOf(["json", "bash", "properties"]).isRequired,
+ code: PropTypes.string.isRequired,
+ children: PropTypes.func.isRequired,
+ };
+
+ static getDerivedStateFromProps(props, state) {
+ if (props.code !== state.code) {
+ return { code: props.code, result: props.code };
+ }
+ return null;
+ }
+
+ state = {
+ code: this.props.code,
+ result: this.props.code,
+ };
+
+ componentDidMount() {
+ this.update();
+ }
+
+ componentDidUpdate() {
+ if (this.props.code === this.state.result) {
+ this.update();
+ }
+ }
+
+ componentWillUnmount() {
+ this.unmounted = true;
+ }
+
+ update = () => {
+ postMessage([this.props.language, this.props.code], result => {
+ if (!this.unmounted) {
+ this.setState({ result });
+ }
+ });
+ };
+
+ render() {
+ return this.props.children(this.state.result);
+ }
+}
+
+export default CodeHighlight;
diff --git a/dashboard/src/components/CodeHighlight/CodeHighlight.worker.js b/dashboard/src/components/CodeHighlight/CodeHighlight.worker.js
new file mode 100644
index 0000000000..9da67b0aea
--- /dev/null
+++ b/dashboard/src/components/CodeHighlight/CodeHighlight.worker.js
@@ -0,0 +1,14 @@
+import hljs from "highlight.js/lib/highlight";
+import bash from "highlight.js/lib/languages/bash";
+import json from "highlight.js/lib/languages/json";
+import properties from "highlight.js/lib/languages/properties";
+
+hljs.registerLanguage("bash", bash);
+hljs.registerLanguage("json", json);
+hljs.registerLanguage("properties", properties);
+
+onmessage = message => {
+ const [language, data] = message.data.msg;
+ const result = hljs.highlight(language, data);
+ postMessage([message.data.key, result.value]);
+};
diff --git a/dashboard/src/components/Dictionary/DictionaryValue.js b/dashboard/src/components/Dictionary/DictionaryValue.js
index c9e19f2a8f..295c57bb8f 100644
--- a/dashboard/src/components/Dictionary/DictionaryValue.js
+++ b/dashboard/src/components/Dictionary/DictionaryValue.js
@@ -13,6 +13,8 @@ const styles = theme => ({
limit: {
maxWidth: "60%",
},
+ scrollableContent: { display: "inline-grid" },
+ explicitRightMargin: { paddingRight: "24px" },
});
class DictionaryValue extends React.Component {
@@ -21,11 +23,15 @@ class DictionaryValue extends React.Component {
classes: PropTypes.object.isRequired,
className: PropTypes.string,
constrain: PropTypes.bool,
+ explicitRightMargin: PropTypes.bool,
+ scrollableContent: PropTypes.bool,
};
static defaultProps = {
className: null,
constrain: false,
+ explicitRightMargin: false,
+ scrollableContent: false,
};
render() {
@@ -34,10 +40,14 @@ class DictionaryValue extends React.Component {
classes,
children,
constrain,
+ explicitRightMargin,
+ scrollableContent,
...props
} = this.props;
const className = classnames(classes.root, classNameProp, {
[classes.limit]: constrain,
+ [classes.explicitRightMargin]: explicitRightMargin,
+ [classes.scrollableContent]: scrollableContent,
});
return (
diff --git a/dashboard/src/components/Monospaced/index.js b/dashboard/src/components/Monospaced/index.js
deleted file mode 100644
index e04b4f5523..0000000000
--- a/dashboard/src/components/Monospaced/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from "./Monospaced";
diff --git a/dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsConfiguration.js b/dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsConfiguration.js
index 2923c531b0..89339e53e3 100644
--- a/dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsConfiguration.js
+++ b/dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsConfiguration.js
@@ -16,7 +16,8 @@ import Grid from "@material-ui/core/Grid";
import List from "@material-ui/core/List";
import ListItem, { ListItemTitle } from "/components/DetailedListItem";
import Maybe from "/components/Maybe";
-import Monospaced from "/components/Monospaced";
+import CodeBlock from "/components/CodeBlock";
+import CodeHighlight from "/components/CodeHighlight/CodeHighlight";
import SilencedIcon from "/icons/Silence";
import Typography from "@material-ui/core/Typography";
import Tooltip from "@material-ui/core/Tooltip";
@@ -137,8 +138,12 @@ class CheckDetailsConfiguration extends React.PureComponent {
Command
-
- {check.command}
+
+
+ {code => (
+
+ )}
+
@@ -247,11 +252,18 @@ class CheckDetailsConfiguration extends React.PureComponent {
ENV Vars
-
+
{check.envVars.length > 0 ? (
-
- {check.envVars.join("\n")}
-
+
+
+ {code => (
+
+ )}
+
+
) : (
"None"
)}
@@ -324,15 +336,20 @@ class CheckDetailsConfiguration extends React.PureComponent {
{Object.keys(check.extendedAttributes).length > 0 && (
-
+
- {`# Extra\n\n${JSON.stringify(
- check.extendedAttributes,
- null,
- "\t",
- )}`}
+
+ {code =>
}
+
-
+
)}
diff --git a/dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsPublishAction.js b/dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsPublishAction.js
new file mode 100644
index 0000000000..fd51b5a08d
--- /dev/null
+++ b/dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsPublishAction.js
@@ -0,0 +1,53 @@
+import React from "react";
+import PropTypes from "prop-types";
+import gql from "graphql-tag";
+import { withApollo } from "react-apollo";
+
+import setCheckPublish from "/mutations/setCheckPublish";
+
+import ToastConnector from "/components/relocation/ToastConnector";
+import PublishCheckStatusToast from "/components/relocation/PublishCheckStatusToast";
+
+class CheckDetailsPublishAction extends React.PureComponent {
+ static propTypes = {
+ children: PropTypes.func.isRequired,
+ client: PropTypes.object.isRequired,
+ check: PropTypes.object.isRequired,
+ };
+
+ static fragments = {
+ check: gql`
+ fragment CheckDetailsPublishAction_check on CheckConfig {
+ id
+ name
+ publish
+ }
+ `,
+ };
+
+ render() {
+ const { children, client, check } = this.props;
+
+ return (
+
+ {({ addToast }) =>
+ children(() => {
+ const promise = setCheckPublish(client, {
+ id: check.id,
+ publish: true,
+ });
+ addToast(({ remove }) => (
+
+ ));
+ })
+ }
+
+ );
+ }
+}
+
+export default withApollo(CheckDetailsPublishAction);
diff --git a/dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsToolbar.js b/dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsToolbar.js
index 96f03c7e67..339d603d2b 100644
--- a/dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsToolbar.js
+++ b/dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsToolbar.js
@@ -3,14 +3,18 @@ import PropTypes from "prop-types";
import gql from "graphql-tag";
import DeleteMenuItem from "/components/partials/ToolbarMenuItems/Delete";
+import PublishMenuItem from "/components/partials/ToolbarMenuItems/Publish";
import SilenceMenuItem from "/components/partials/ToolbarMenuItems/Silence";
import Toolbar from "/components/partials/Toolbar";
import ToolbarMenu from "/components/partials/ToolbarMenu";
+import UnpublishMenuItem from "/components/partials/ToolbarMenuItems/Unpublish";
import UnsilenceMenuItem from "/components/partials/ToolbarMenuItems/Unsilence";
import QueueMenuItem from "/components/partials/ToolbarMenuItems/QueueExecution";
import DeleteAction from "./CheckDetailsDeleteAction";
import ExecuteAction from "./CheckDetailsExecuteAction";
+import PublishAction from "./CheckDetailsPublishAction";
+import UnpublishAction from "./CheckDetailsUnpublishAction";
import SilenceAction from "./CheckDetailsSilenceAction";
import ClearSilenceAction from "./CheckDetailsClearSilenceAction";
@@ -81,6 +85,18 @@ class CheckDetailsToolbar extends React.Component {
)}
+
+ {!check.publish && (
+
+ {handler => }
+
+ )}
+ {check.publish && (
+
+ {handler => }
+
+ )}
+
{handler => }
diff --git a/dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsUnpublishAction.js b/dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsUnpublishAction.js
new file mode 100644
index 0000000000..17e43a82d8
--- /dev/null
+++ b/dashboard/src/components/partials/CheckDetailsContainer/CheckDetailsUnpublishAction.js
@@ -0,0 +1,54 @@
+import React from "react";
+import PropTypes from "prop-types";
+import gql from "graphql-tag";
+import { withApollo } from "react-apollo";
+
+import setCheckPublish from "/mutations/setCheckPublish";
+
+import ToastConnector from "/components/relocation/ToastConnector";
+import PublishCheckStatusToast from "/components/relocation/PublishCheckStatusToast";
+
+class CheckDetailsUnpublishAction extends React.PureComponent {
+ static propTypes = {
+ children: PropTypes.func.isRequired,
+ client: PropTypes.object.isRequired,
+ check: PropTypes.object.isRequired,
+ };
+
+ static fragments = {
+ check: gql`
+ fragment CheckDetailsPublishAction_check on CheckConfig {
+ id
+ name
+ publish
+ }
+ `,
+ };
+
+ render() {
+ const { children, client, check } = this.props;
+
+ return (
+
+ {({ addToast }) =>
+ children(() => {
+ const promise = setCheckPublish(client, {
+ id: check.id,
+ publish: false,
+ });
+ addToast(({ remove }) => (
+
+ ));
+ })
+ }
+
+ );
+ }
+}
+
+export default withApollo(CheckDetailsUnpublishAction);
diff --git a/dashboard/src/components/partials/ChecksList/ChecksListItem.js b/dashboard/src/components/partials/ChecksList/ChecksListItem.js
index 6fa1c99247..c581e55344 100644
--- a/dashboard/src/components/partials/ChecksList/ChecksListItem.js
+++ b/dashboard/src/components/partials/ChecksList/ChecksListItem.js
@@ -4,6 +4,7 @@ import gql from "graphql-tag";
import Checkbox from "@material-ui/core/Checkbox";
import Code from "/components/Code";
+import CodeHighlight from "/components/CodeHighlight/CodeHighlight";
import ConfirmDelete from "/components/partials/ConfirmDelete";
import DeleteMenuItem from "/components/partials/ToolbarMenuItems/Delete";
import NamespaceLink from "/components/util/NamespaceLink";
@@ -82,7 +83,9 @@ class CheckListItem extends React.Component {
}
details={
- {check.command}
+
+ {code =>
}
+
diff --git a/dashboard/src/components/partials/EntityDetailsContainer/EntityDetailsInformation.js b/dashboard/src/components/partials/EntityDetailsContainer/EntityDetailsInformation.js
index ddc37dc581..06d83a52e6 100644
--- a/dashboard/src/components/partials/EntityDetailsContainer/EntityDetailsInformation.js
+++ b/dashboard/src/components/partials/EntityDetailsContainer/EntityDetailsInformation.js
@@ -17,7 +17,8 @@ import Dictionary, {
import List from "@material-ui/core/List";
import ListItem, { ListItemTitle } from "/components/DetailedListItem";
import Maybe from "/components/Maybe";
-import Monospaced from "/components/Monospaced";
+import CodeBlock from "/components/CodeBlock";
+import CodeHighlight from "/components/CodeHighlight/CodeHighlight";
import { RelativeToCurrentDate } from "/components/RelativeDate";
import StatusIcon from "/components/CheckStatusIcon";
import SilencedIcon from "/icons/Silence";
@@ -233,11 +234,16 @@ class EntityDetailsInformation extends React.PureComponent {
{Object.keys(entity.extendedAttributes).length > 0 && (
-
+
- {JSON.stringify(entity.extendedAttributes, null, "\t")}
+
+ {code =>
}
+
-
+
)}
diff --git a/dashboard/src/components/partials/EventDetailsContainer/EventDetailsCheckResult.js b/dashboard/src/components/partials/EventDetailsContainer/EventDetailsCheckResult.js
index b5879646a4..052a5ff7a4 100644
--- a/dashboard/src/components/partials/EventDetailsContainer/EventDetailsCheckResult.js
+++ b/dashboard/src/components/partials/EventDetailsContainer/EventDetailsCheckResult.js
@@ -21,7 +21,7 @@ import {
} from "/components/DateFormatter";
import Duration from "/components/Duration";
import StatusIcon from "/components/CheckStatusIcon";
-import Monospaced from "/components/Monospaced";
+import CodeBlock from "/components/CodeBlock";
import Maybe from "/components/Maybe";
import SilencedIcon from "/icons/Silence";
import Tooltip from "@material-ui/core/Tooltip";
@@ -154,9 +154,9 @@ class EventDetailsCheckResult extends React.PureComponent {
{check.output ? (
-
+
{check.output}
-
+
) : (
diff --git a/dashboard/src/components/partials/EventDetailsContainer/EventDetailsSummary.js b/dashboard/src/components/partials/EventDetailsContainer/EventDetailsSummary.js
index 899f5935cd..0420271fab 100644
--- a/dashboard/src/components/partials/EventDetailsContainer/EventDetailsSummary.js
+++ b/dashboard/src/components/partials/EventDetailsContainer/EventDetailsSummary.js
@@ -11,7 +11,8 @@ import Dictionary, {
DictionaryEntry,
} from "/components/Dictionary";
import { RelativeToCurrentDate } from "/components/RelativeDate";
-import Monospaced from "/components/Monospaced";
+import CodeBlock from "/components/CodeBlock";
+import CodeHighlight from "/components/CodeHighlight/CodeHighlight";
import Maybe from "/components/Maybe";
import InlineLink from "/components/InlineLink";
import Typography from "@material-ui/core/Typography";
@@ -127,11 +128,16 @@ class EventDetailsSummary extends React.Component {
{check.command && (
-
+
- {`# Executed command\n$ ${check.command}`}
+
+ {code =>
}
+
-
+
)}
diff --git a/dashboard/yarn.lock b/dashboard/yarn.lock
index 5e637335f5..70c049848c 100644
--- a/dashboard/yarn.lock
+++ b/dashboard/yarn.lock
@@ -372,6 +372,10 @@ address@1.0.3, address@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9"
+ajv-errors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.0.tgz#ecf021fa108fd17dfb5e6b383f2dd233e31ffc59"
+
ajv-keywords@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762"
@@ -2101,6 +2105,23 @@ crypto-random-string@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
+css-loader@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-1.0.0.tgz#9f46aaa5ca41dbe31860e3b62b8e23c42916bf56"
+ dependencies:
+ babel-code-frame "^6.26.0"
+ css-selector-tokenizer "^0.7.0"
+ icss-utils "^2.1.0"
+ loader-utils "^1.0.2"
+ lodash.camelcase "^4.3.0"
+ postcss "^6.0.23"
+ postcss-modules-extract-imports "^1.2.0"
+ postcss-modules-local-by-default "^1.2.0"
+ postcss-modules-scope "^1.1.0"
+ postcss-modules-values "^1.3.0"
+ postcss-value-parser "^3.3.0"
+ source-list-map "^2.0.0"
+
css-select@^1.1.0, css-select@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
@@ -2110,6 +2131,14 @@ css-select@^1.1.0, css-select@~1.2.0:
domutils "1.5.1"
nth-check "~1.0.1"
+css-selector-tokenizer@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86"
+ dependencies:
+ cssesc "^0.1.0"
+ fastparse "^1.1.1"
+ regexpu-core "^1.0.0"
+
css-vendor@^0.3.8:
version "0.3.8"
resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-0.3.8.tgz#6421cfd3034ce664fe7673972fd0119fc28941fa"
@@ -2120,6 +2149,10 @@ css-what@2.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd"
+cssesc@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
+
cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
version "0.3.2"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b"
@@ -3001,6 +3034,10 @@ fast-levenshtein@~2.0.4:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+fastparse@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
+
faye-websocket@~0.11.0:
version "0.11.1"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.1.tgz#f0efe18c4f56e4f40afc7e06c719fd5ee6188f38"
@@ -3601,6 +3638,10 @@ he@1.1.x:
version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
+highlight.js@^9.13.1:
+ version "9.13.1"
+ resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e"
+
history@^4.7.2:
version "4.7.2"
resolved "https://registry.yarnpkg.com/history/-/history-4.7.2.tgz#22b5c7f31633c5b8021c7f4a8a954ac139ee8d5b"
@@ -3779,6 +3820,16 @@ iconv-lite@~0.4.13:
dependencies:
safer-buffer ">= 2.1.2 < 3"
+icss-replace-symbols@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
+
+icss-utils@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962"
+ dependencies:
+ postcss "^6.0.1"
+
ieee754@^1.1.4:
version "1.1.11"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.11.tgz#c16384ffe00f5b7835824e67b6f2bd44a5229455"
@@ -4988,7 +5039,7 @@ loader-utils@^0.2.16:
json5 "^0.5.0"
object-assign "^4.0.1"
-loader-utils@^1.0.2, loader-utils@^1.1.0:
+loader-utils@^1.0.0, loader-utils@^1.0.2, loader-utils@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd"
dependencies:
@@ -5035,6 +5086,10 @@ lodash._reinterpolate@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
+lodash.camelcase@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
+
lodash.defaults@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
@@ -6017,10 +6072,49 @@ posix-character-classes@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
+postcss-modules-extract-imports@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85"
+ dependencies:
+ postcss "^6.0.1"
+
+postcss-modules-local-by-default@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069"
+ dependencies:
+ css-selector-tokenizer "^0.7.0"
+ postcss "^6.0.1"
+
+postcss-modules-scope@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90"
+ dependencies:
+ css-selector-tokenizer "^0.7.0"
+ postcss "^6.0.1"
+
+postcss-modules-values@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20"
+ dependencies:
+ icss-replace-symbols "^1.1.0"
+ postcss "^6.0.1"
+
postcss-value-parser@^3.2.3:
version "3.3.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15"
+postcss-value-parser@^3.3.0:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
+
+postcss@^6.0.1, postcss@^6.0.23:
+ version "6.0.23"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
+ dependencies:
+ chalk "^2.4.1"
+ source-map "^0.6.1"
+ supports-color "^5.4.0"
+
postcss@^6.0.17:
version "6.0.22"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.22.tgz#e23b78314905c3b90cbd61702121e7a78848f2a3"
@@ -6539,6 +6633,14 @@ regexpp@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab"
+regexpu-core@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b"
+ dependencies:
+ regenerate "^1.2.1"
+ regjsgen "^0.2.0"
+ regjsparser "^0.1.4"
+
regexpu-core@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240"
@@ -6837,6 +6939,13 @@ sax@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+schema-utils@^0.4.0:
+ version "0.4.7"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"
+ dependencies:
+ ajv "^6.1.0"
+ ajv-keywords "^3.1.0"
+
schema-utils@^0.4.3, schema-utils@^0.4.4, schema-utils@^0.4.5:
version "0.4.5"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e"
@@ -6844,6 +6953,14 @@ schema-utils@^0.4.3, schema-utils@^0.4.4, schema-utils@^0.4.5:
ajv "^6.1.0"
ajv-keywords "^3.1.0"
+schema-utils@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
+ dependencies:
+ ajv "^6.1.0"
+ ajv-errors "^1.0.0"
+ ajv-keywords "^3.1.0"
+
semver-diff@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
@@ -7257,6 +7374,13 @@ strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+style-loader@^0.23.1:
+ version "0.23.1"
+ resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.23.1.tgz#cb9154606f3e771ab6c4ab637026a1049174d925"
+ dependencies:
+ loader-utils "^1.1.0"
+ schema-utils "^1.0.0"
+
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
@@ -7974,6 +8098,13 @@ worker-farm@^1.5.2:
dependencies:
errno "~0.1.7"
+worker-loader@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/worker-loader/-/worker-loader-2.0.0.tgz#45fda3ef76aca815771a89107399ee4119b430ac"
+ dependencies:
+ loader-utils "^1.0.0"
+ schema-utils "^0.4.0"
+
wrap-ansi@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"