diff --git a/frontend/assets/github.png b/frontend/assets/github.png new file mode 100644 index 000000000..c61ab9d05 Binary files /dev/null and b/frontend/assets/github.png differ diff --git a/frontend/assets/moz-fav-bw-rgb.png b/frontend/assets/moz-fav-bw-rgb.png new file mode 100644 index 000000000..b0521009c Binary files /dev/null and b/frontend/assets/moz-fav-bw-rgb.png differ diff --git a/frontend/assets/moz-logo-black.png b/frontend/assets/moz-logo-black.png new file mode 100644 index 000000000..81cf0eef6 Binary files /dev/null and b/frontend/assets/moz-logo-black.png differ diff --git a/frontend/package.json b/frontend/package.json index ddd531ac6..c1fc6fd09 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -36,13 +36,16 @@ "eslint-plugin-mozilla": "^2.1.0", "eslint-plugin-no-unsanitized": "^3.0.2", "eslint-plugin-prettier": "^3.1.0", + "file-loader": "^4.2.0", "html-webpack-plugin": "^3.2.0", "mini-css-extract-plugin": "^0.8.0", + "node-sass": "^4.12.0", "optimize-css-assets-webpack-plugin": "^5.0.3", "prettier": "1.18.2", + "sass-loader": "^8.0.0", "standard": "^14.1.0", "terser-webpack-plugin": "^2.0.1", - "webpack": "^4.35.3", + "webpack": "^4.40.2", "webpack-cli": "^3.3.6", "webpack-dev-server": "^3.7.2", "webpack-merge": "^4.2.1" @@ -50,6 +53,7 @@ "dependencies": { "chartist": "^0.11.3", "mustache": "^3.0.1", + "normalize.css": "^8.0.1", "prismjs": "^1.16.0" } } diff --git a/frontend/src/base.html b/frontend/src/base.html index 980e97508..4bc6db6e0 100755 --- a/frontend/src/base.html +++ b/frontend/src/base.html @@ -3,10 +3,11 @@ mozilla-central coverage + - - +
+ + +
Loading...
@@ -146,9 +157,12 @@

diff --git a/frontend/src/common.js b/frontend/src/common.js index 152c56284..7e514c3f2 100644 --- a/frontend/src/common.js +++ b/frontend/src/common.js @@ -364,7 +364,7 @@ export function message(cssClass, message) { const box = document.getElementById("message"); box.className = "message " + cssClass; box.textContent = message; - box.style.display = "block"; + box.style.display = "inherit"; } export function hide(id) { @@ -382,7 +382,7 @@ export function show(id, node) { } const box = document.getElementById(id); - box.style.display = "block"; + box.style.display = "inherit"; if (node) { box.replaceWith(node); } @@ -400,6 +400,6 @@ export function render(template, data, target) { // eslint-disable-next-line no-unsanitized/property box.innerHTML = output; - box.style.display = "block"; + box.style.display = "inherit"; return box; } diff --git a/frontend/src/index.js b/frontend/src/index.js index 4ef25afef..d31172837 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -18,7 +18,8 @@ import { zeroCoverageDisplay, zeroCoverageMenu } from "./zero_coverage_report.js"; -import "./style.css"; +import "normalize.css/normalize.css"; +import "./style.scss"; import Prism from "prismjs"; import Chartist from "chartist"; import "chartist/dist/chartist.css"; @@ -112,6 +113,10 @@ async function showDirectory(dir, revision, files) { file.route = buildRoute({ path: file.path }); + + // Calc decimal range to make a nice coloration + file.coveragePercent = Math.floor(file.coveragePercent); + file.range = parseInt(file.coveragePercent / 10) * 10; return file; }), revision: revision || REV_LATEST, @@ -120,7 +125,7 @@ async function showDirectory(dir, revision, files) { return dir ? this.path.substring(dir.length + 1) : this.path; } }; - render("browser", context, "output"); + render("file_browser", context, "output"); } async function showFile(file, revision) { @@ -148,7 +153,7 @@ async function showFile(file, revision) { lines: source.split("\n").map((line, nb) => { const coverage = file.coverage[nb]; let cssClass = ""; - if (coverage !== -1) { + if (coverage && coverage !== -1) { cssClass = coverage > 0 ? "covered" : "uncovered"; } return { diff --git a/frontend/src/style.css b/frontend/src/style.css deleted file mode 100644 index cf8e9c454..000000000 --- a/frontend/src/style.css +++ /dev/null @@ -1,200 +0,0 @@ -body { - font-family: sans-serif; - background-color:#eff7fc; - color: #262626; - line-height: 1.3em; - margin: 1em 0; - text-align: center; -} - -a { - text-decoration: none; - color: #0081ad; -} - -a:hover { - color: #00a7e0; -} -label { - padding-right: 2em; -} - -header #browser input { - min-width: 350px; - font-family: monospace; -} - -#main { - background-color: white; - border-top: 1px solid darkgray; - border-bottom: 1px solid darkgray; - margin: auto; - text-align: left; -} - -#main div.message { - padding: 10px; - color: white; - width: 40%; - margin: 30px auto; -} - -#main div.message.loading { - border: 1px solid blue; - background: #209cee; -} - -#main div.message.warning { - border: 1px solid orange; - background: #c48404; -} - -#main div.message.error { - border: 1px solid red; - background: #ff3860; -} - -/* Directory navigation */ -#output.directory, #output > .directory { - padding: 1em 20%; -} - -#output .table { - display: table; - width: 100%; -} - -#output .table div { - display: table-row; - height: 1.5em; -} - -#output .table div > :first-child { - text-align: left; -} - -#output .table div > :last-child { - text-align: right; -} - -#output .table span { - text-align: center; - height: 1.5em; -} - -#output .table .row span, #output .table .header span { - display: table-cell; -} - -#output .table .row:nth-child(2n+3) { - background: AliceBlue; -} - -#output .table .row:hover { - background-color: #d8dfe5; -} - -#output h2 { - width: 100%; - display: block; - font-size: 1.3em; -} - -#output h2 * { - display: inline-block; - margin-right: 3px; -} - -/* File coverage */ -#output > .file, #output > .file table { - width: 100%; -} - -#output > .file nav { - margin: 5px; -} - -#output .filename { - text-align: left; -} - -#output .header { - font-weight: bold; -} - - -#output .file table { - border-collapse: collapse; - border-spacing: 0; -} - -#output tr * { - padding: 0; - margin: 0; -} - -#output tr pre { - padding: 0 2px; -} - -#output tr td:first-child { - color: grey; - font-size: 1em; - vertical-align: middle; - border-right: 1px solid #CCC; - font-family: monospace; - background: #f5f2f0; - padding: 0 2px; -} - -#output tr.covered td { - background: #c3fca2; -} - -#output tr.covered pre { - background: #e7ffd9; -} - -#output tr.uncovered td { - background: #ff8870; -} - -#output tr.uncovered pre { - background: #ffc8bd; -} - -/* Breadcrumb navigation */ -nav { - margin-right: 5px; -} - -nav a:not(:last-child)::after { - content: "/"; - color: #CCC; - margin: 0 2px; -} - -/* History graph */ -#history, #history svg { - width: 100%; - height: 35%; -} - -#history .ct-point { - stroke: #6fc0f2; - cursor: pointer; -} - -#history .ct-line { - stroke: #ccf2ff; -} - -#history .ct-point:hover { - stroke: #d91e44; -} - -#history #history_details { - color: darkgrey; - margin-right: 20px; - float: right; -} diff --git a/frontend/src/style.scss b/frontend/src/style.scss new file mode 100644 index 000000000..b14f0ec56 --- /dev/null +++ b/frontend/src/style.scss @@ -0,0 +1,360 @@ +// Configuration +$default_color: #4a4a4a; +$link_color: #0081ad; +$background_color: #eff7fc; +$row_height: 1.8em; +$header_height: 50px; +$footer_height: 60px; +$coverage_low: #d91a47; +$coverage_warn: #ff9a36; +$coverage_good: #438718; +$small_screen: 1900px; + +body { + // Setup base typography + color: $default_color; + font-size: 1em; + font-weight: 400; + line-height: 1.5; + text-rendering: optimizeLegibility; + font-family: BlinkMacSystemFont, -apple-system, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + + background-color: white; + + // For footer + display: flex; + min-height: 100vh; + flex-direction: column; +} + +a { + text-decoration: none; + color: $link_color; + + &:hover { + color: lighten($link_color, 10%); + } +} + +header { + background-color: $background_color; + border-bottom: 1px solid darkgray; + min-height: $header_height; + + div.logo { + display: inline-block; + img { + margin: $header_height * 0.1; + max-height: $header_height * 0.8; + } + a { + color: $default_color; + line-height: $header_height; + font-size: $header_height * 0.6; + vertical-align: top; + } + + // On smaller screens, hide "Code coverage" link & display a smaller logo + @media only screen and (max-width: $small_screen) { + img { + max-height: $header_height * 0.6; + } + a { + display: none; + } + } + } + + div#menu { + float: right; + height: $header_height; + line-height: $header_height; + margin-right: 10px; + } + + input.revision { + width: 170px; + font-family: monospace; + } +} + +footer { + text-align: center; + background: darken($background_color, 60%); + min-height: $footer_height; + line-height: $footer_height; + border-top: 1px solid darkgray; + + img { + //padding-top: 0.2 * $footer_height; + vertical-align: middle; + max-height: 0.75 * $footer_height; + } + + a { + color: lighten($link_color, 50%); + } +} + +#main { + // Main element on flex display + flex: 1; + + div.message { + border-radius: 5px; + padding: 10px; + color: white; + width: 40%; + margin: 30px auto; + + &.loading { + background: #209cee; + } + + &.warning { + background: #c48404; + } + + &.error { + background: #ff3860; + } + } +} + +// Breadcrumb navigation +nav { + margin-right: 5px; + + a:not(:last-child)::after { + content: "/"; + color: #CCC; + margin: 0 2px; + } +} + +// Directory navigation +#browser { + margin: 1em 20%; + + // On smaller screens, display table on full width + @media only screen and (max-width: $small_screen) { + margin: 0.5em; + } + + .table { + display: table; + width: 100%; + + div { + display: table-row; + height: $row_height; + line-height: $row_height; + + > { + :first-child { + text-align: left; + } + + :last-child { + text-align: right; + } + } + + &.header { + span { + font-weight: bold; + } + } + + span { + padding: 0 3px; + text-align: center; + height: $row_height; + line-height: $row_height; + display: table-cell; + + &.coverage_0 { + color: $coverage_low; + } + &.coverage_10 { + color: lighten($coverage_low, 10%); + } + &.coverage_20 { + color: lighten($coverage_low, 20%); + } + &.coverage_30 { + color: lighten($coverage_low, 30%); + } + &.coverage_40 { + color: darken($coverage_warn, 10%); + } + &.coverage_50 { + color: $coverage_warn; + } + &.coverage_60 { + color: lighten($coverage_warn, 10%); + } + &.coverage_70 { + color: lighten($coverage_good, 30%); + } + &.coverage_80 { + color: lighten($coverage_good, 20%); + } + &.coverage_90 { + color: lighten($coverage_good, 10%); + } + &.coverage_100 { + color: $coverage_good; + } + } + + &.row { + &:nth-child(2n+3) { + background: hsl(0, 0%, 98%); + } + + &:hover { + background-color: hsl(0, 0%, 96%); + } + } + } + } + + // Navbar with stats + h2 { + width: 100%; + + nav, span { + display: inline-block; + margin-right: 3px; + } + } +} + +// File coverage +$covered_color: #e7ffd9; +$uncovered_color: #ffc8bd; +$no_data_color: hsl(0, 0%, 98%); +$samp_size: 20px; +#file { + div.help { + position: fixed; + top: 70px; + right: 20px; + background: rgba(0, 0, 0, 0.2); + border-radius: 5px; + height: 2 * $samp_size; + line-height: 2 * $samp_size; + + p { + display: inline-block; + margin: 0 6px; + padding: 0; + + span { + margin-left: 2px; + vertical-align: top; + } + + samp { + display: inline-block; + margin-top: $samp_size / 2; + width: $samp_size; + height: $samp_size; + border: 1px solid darkgray; + } + + &.no_data samp { + background: $no_data_color; + } + &.uncovered samp { + background: $uncovered_color; + } + &.covered samp { + background: $covered_color; + } + } + } + + table { + width: 100%; + border-collapse: collapse; + border-spacing: 0; + + tr { + * { + padding: 0; + margin: 0; + } + + td { + font-size: 0.9em; + } + + pre { + padding: 1px 2px; + + // To override prism settings + border-radius: 0px 0px; + background: $no_data_color; + } + + td:first-child { + color: grey; + font-size: 1em; + vertical-align: middle; + border-right: 1px solid #CCC; + font-family: monospace; + background: #f5f2f0; + padding: 0 2px; + } + + &.covered { + td { + background: darken($covered_color, 10%); + } + + pre { + background: $covered_color; + } + } + + &.uncovered { + td { + background: darken($uncovered_color, 10%); + } + + pre { + background: $uncovered_color; + } + } + } + } +} + +// History graph +#history { + .ct-chart { + width: 100%; + height: 200px; + } + + .ct-point { + stroke: #6fc0f2; + cursor: pointer; + } + + .ct-line { + stroke: #ccf2ff; + } + + .ct-point:hover { + stroke: #d91e44; + } + + #history_details { + color: darkgrey; + margin-right: 20px; + float: right; + } +} diff --git a/frontend/webpack.common.js b/frontend/webpack.common.js index c48db7b7b..fbd69591c 100644 --- a/frontend/webpack.common.js +++ b/frontend/webpack.common.js @@ -41,10 +41,24 @@ module.exports = { } } }, + { + test: /\.s[ac]ss$/i, + use: [ + MiniCssExtractPlugin.loader, + 'css-loader', + 'sass-loader', + ], + }, { test: /\.css$/i, use: [MiniCssExtractPlugin.loader, 'css-loader'], }, + { + test: /\.(png|svg|jpg|gif)$/, + use: [ + 'file-loader' + ] + }, ], }, devServer: { @@ -55,6 +69,7 @@ module.exports = { resolve: { modules: [ path.join(__dirname, 'node_modules'), + path.join(__dirname, 'assets'), path.join(__dirname, 'src'), ], },