content/static: update version page symbol UI
Refactors versions page template to meet updated
design spec. The page now has a responsive layout
and symbol history content visibility can be toggled
with expand and collapse buttons at versions page
header or by clicking on the commit time text.
For golang/go#37102
Change-Id: I61487c51ac76ee02495c22d1d798bfa1712a9e35
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/306132
Trust: Jamal Carvalho <jamal@golang.org>
Run-TryBot: Jamal Carvalho <jamal@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/content/static/css/versions.css b/content/static/css/versions.css
new file mode 100644
index 0000000..2f6e554
--- /dev/null
+++ b/content/static/css/versions.css
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2021 The Go Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+.Unit-content .Versions {
+ margin-top: 1rem;
+ max-width: unset;
+}
+.Versions-title {
+ align-items: center;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1rem 2.5rem;
+ margin-bottom: 1rem;
+}
+.Versions-title h2 {
+ margin: 0;
+}
+.Versions-titleButtonGroup {
+ display: none;
+}
+.Versions-titleButtonGroup button {
+ background-color: transparent;
+ border: none;
+ bottom: 1rem;
+ color: var(--turq-dark);
+ cursor: pointer;
+ font-size: 0.875rem;
+ text-decoration: none;
+}
+.Versions-titleButtonGroup button:disabled {
+ color: var(--gray-8);
+ cursor: initial;
+}
+.Versions-list {
+ gap: 0 1rem;
+ line-height: 2.25rem;
+}
+@media only screen and (min-width: 37.5rem) {
+ .Versions-list {
+ display: grid;
+ grid-template-columns: fit-content(8rem) fit-content(20rem) min-content auto;
+ }
+}
+.Version-major {
+ margin-bottom: 1rem;
+ min-width: 4rem;
+}
+@media only screen and (min-width: 37.5rem) {
+ .Version-major {
+ margin-bottom: 0;
+ }
+}
+.Version-tag {
+ text-align: left;
+}
+@media only screen and (min-width: 37.5rem) {
+ .Version-tag {
+ text-align: right;
+ }
+}
+.Version-dot {
+ border: 0.0625rem solid var(--gray-9);
+ color: var(--gray-8);
+ display: none;
+ font-size: 2.75rem;
+ justify-content: center;
+ line-height: 1.75rem;
+ -webkit-text-stroke: 0.125rem var(--white);
+ width: 0;
+}
+.Version-dot::before {
+ content: '•';
+}
+@media only screen and (min-width: 37.5rem) {
+ .Version-dot {
+ display: flex;
+ }
+}
+.Version-dot--minor {
+ color: var(--turq-dark);
+}
+.Version-commitTime {
+ margin-left: 1rem;
+ white-space: nowrap;
+}
+.Version-details {
+ line-height: 1.25rem;
+ margin-left: -0.5rem;
+}
+.Version-summary {
+ align-items: center;
+ cursor: pointer;
+ display: flex;
+ line-height: 2.25rem;
+ padding-right: 0.5rem;
+ white-space: nowrap;
+ width: min-content;
+}
+details.Version-details img {
+ position: relative;
+ top: 0.0625rem;
+}
+details.Version-details[open] img {
+ transform: rotate(90deg);
+}
diff --git a/content/static/html/helpers/_versions.tmpl b/content/static/html/helpers/_versions.tmpl
index d8748cb..4f68034 100644
--- a/content/static/html/helpers/_versions.tmpl
+++ b/content/static/html/helpers/_versions.tmpl
@@ -8,15 +8,18 @@
{{define "versions"}}
<div class="Versions">
- <table>
- <tr><th colspan="3"><h2>Versions in this module</h2></th></tr>
- {{template "module_list" .ThisModule}}
- {{if .IncompatibleModules}}
- <tr><th colspan="3"><h2>Incompatible versions in this module</h2></th></tr>
- {{template "module_list_incompatible" .IncompatibleModules}}
- {{end}}
- </table>
-
+ <div class="Versions-title">
+ <h2>Versions in this module</h2>
+ <div class="Versions-titleButtonGroup js-buttonGroup">
+ <button class="js-versionsExpand">Expand all</button>
+ <button class="js-versionsCollapse">Collapse all</button>
+ </div>
+ </div>
+ {{template "version_list" .ThisModule}}
+ {{if .IncompatibleModules}}
+ <h2>Incompatible versions in this module</h2>
+ {{template "version_list" .IncompatibleModules}}
+ {{end}}
{{if .OtherModules}}
<h2>Other modules containing this package</h2>
{{range .OtherModules}}
@@ -26,87 +29,74 @@
</div>
{{end}}
-
{{/* . is []*internal/frontend.VersionList */}}
-{{define "module_list_incompatible"}}
- {{range $major := .}}
- {{range $i, $v := $major.Versions}}
- <tr>
- <td>
- {{if eq $i 0 }}
- <div class="Versions-major">
- {{if $major.Deprecated}}(Deprecated{{with $major.DeprecationComment}}: {{.}}{{end}}){{end}}
- </div>
- {{end}}
- </td>
- <td>
- <a href="{{$v.Link}}">{{$v.Version}}</a>
- {{if $v.Retracted}}(Retracted{{with .RetractionRationale}}: {{.}}){{end}}{{end}}
- </td>
- <td>
- <div class="Versions-commitTime">{{$v.CommitTime}}</div>
- </td>
- </tr>
- {{end}}
- {{end}}
+{{define "version_list"}}
+ <div class="Versions-list">
+ {{range $major := .}}
+ {{range $i, $v := $major.Versions}}
+ <div class="Version-major">
+ {{if and (eq $i 0) (not $major.Incompatible)}}
+ <strong>{{$major.Major}}</strong>
+ <div>{{if $major.Deprecated}}(Deprecated{{with $major.DeprecationComment}}: {{.}}{{end}}){{end}}</div>
+ {{end}}
+ </div>
+ <div class="Version-tag">
+ <a class="js-versionLink" href="{{$v.Link}}">{{$v.Version}}</a>
+ <div>{{if $v.Retracted}}(Retracted{{with $v.RetractionRationale}}: {{.}}{{end}}){{end}}</div>
+ </div>
+ <div class="Version-dot{{if and $v.IsMinor (not $major.Incompatible)}} Version-dot--minor{{end}}"></div>
+ {{if and $v.Symbols (not $major.Incompatible)}}
+ {{template "symbol_history" $v}}
+ {{else}}
+ <div class="Version-commitTime">{{$v.CommitTime}}</div>
+ {{end}}
+ {{end}}
+ {{end}}
+ </div>
{{end}}
-{{define "module_list"}}
- {{range $major := .}}
- {{range $i, $v := $major.Versions}}
- <tr>
- <td>
- {{if eq $i 0 }}
- <div class="Versions-major">
- {{$major.Major}}
- {{if $major.Deprecated}}(Deprecated{{with $major.DeprecationComment}}: {{.}}{{end}}){{end}}
- </div>
- {{end}}
- </td>
- <td>
- <a href="{{$v.Link}}">{{$v.Version}}</a>
- {{if $v.Retracted}}(Retracted{{with .RetractionRationale}}: {{.}}){{end}}{{end}}
- </td>
- <td>
- <div class="Versions-commitTime">{{$v.CommitTime}}</div>
- {{if $v.Symbols }}
- <div class="Versions-symbols">
- <div class="Versions-symbolsHeader">Changes in this version</div>
- {{range $v.Symbols}}
- <div class="Versions-symbolSection">
- {{range .}}
- {{if eq .Kind "Type"}}
- <div class="Versions-symbolType">
- {{template "symbol" .}}
- {{range .Children}}
- <div class="Versions-symbolChild">{{template "symbol" .}}</div>
- {{end}}
- </div>
- {{else}}
- <div>{{template "symbol" .}}</div>
- {{end}}
- {{end}}
- </div>
- {{end}}
- </div>
- {{end}}
- </td>
- </tr>
- {{end}}
- {{end}}
+{{define "symbol_history"}}
+ <details class="Version-details js-versionDetails">
+ <summary class="Version-summary">
+ <img alt="" height="24" width="24" src="/static/img/pkg-icon-arrowRight_24x24.svg">
+ {{.CommitTime}}
+ </summary>
+ <div class="Versions-symbols">
+ <div class="Versions-symbolsHeader">Changes in this version</div>
+ {{range .Symbols}}
+ <div class="Versions-symbolSection">
+ {{range .}}
+ {{if eq .Kind "Type"}}
+ <div class="Versions-symbolType">
+ {{template "symbol" .}}
+ {{range .Children}}
+ <div class="Versions-symbolChild">{{template "symbol" .}}</div>
+ {{end}}
+ </div>
+ {{else}}
+ <div>{{template "symbol" .}}</div>
+ {{end}}
+ {{end}}
+ </div>
+ {{end}}
+ </div>
+ </details>
{{end}}
{{define "symbol"}}
<div>
- {{if .New}}
- <span class="Versions-symbolBulletNew">+</span><a class="Versions-symbolSynopsis" href="{{.Link}}">{{.Synopsis}}</a>
- {{else}}
- <span class="Versions-symbolOld Versions-symbolSynopsis">{{.Synopsis}}</span>
- {{end}}
- {{if .Builds}}
- <span class="Versions-symbolBuildsDash">—</span>
- <span class="Versions-symbolBuilds">{{range $i, $b := .Builds}}{{if $i}}, {{end}}{{$b}}{{end}}</span>
- {{end}}
+ {{if .New}}
+ <span class="Versions-symbolBulletNew">+</span>
+ <a class="Versions-symbolSynopsis" href="{{.Link}}">{{.Synopsis}}</a>
+ {{else}}
+ <span class="Versions-symbolOld Versions-symbolSynopsis">{{.Synopsis}}</span>
+ {{end}}
+ {{if .Builds}}
+ <span class="Versions-symbolBuildsDash">—</span>
+ <span class="Versions-symbolBuilds">
+ {{range $i, $b := .Builds}}{{if $i}}, {{end}}{{$b}}{{end}}
+ </span>
+ {{end}}
</div>
{{end}}
diff --git a/content/static/html/pages/unit_versions.tmpl b/content/static/html/pages/unit_versions.tmpl
index c628773..c345499 100644
--- a/content/static/html/pages/unit_versions.tmpl
+++ b/content/static/html/pages/unit_versions.tmpl
@@ -4,8 +4,22 @@
license that can be found in the LICENSE file.
-->
+{{define "unit_pre_content"}}
+ <link href="/static/css/versions.css?version={{.AppVersionLabel}}" rel="stylesheet">
+{{end}}
+
{{define "unit_content"}}
<div class="Unit-content" role="main" data-test-id="UnitVersions">
- {{block "versions" .Details}}{{end}}
+ {{if (.Experiments.IsActive "symbol-history-versions-page")}}
+ {{block "versions" .Details}}{{end}}
+ {{else}}
+ {{block "legacy_versions" .Details}}{{end}}
+ {{end}}
</div>
{{end}}
+
+{{define "unit_post_content"}}
+ <script>
+ loadScript("/static/js/versions.js", {type: 'module', async: true, defer: true})
+ </script>
+{{end}}
diff --git a/content/static/js/versions.js b/content/static/js/versions.js
new file mode 100644
index 0000000..2165e32
--- /dev/null
+++ b/content/static/js/versions.js
@@ -0,0 +1,7 @@
+/*!
+ * @license
+ * Copyright 2021 The Go Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */class VersionsController{constructor(){this.expand=document.querySelector(".js-versionsExpand");this.collapse=document.querySelector(".js-versionsCollapse");this.details=[...document.querySelectorAll(".js-versionDetails")];if(!!this.expand?.parentElement){this.details.some(e=>e.tagName==="DETAILS")&&(this.expand.parentElement.style.display="block");for(const e of this.details)e.addEventListener("click",()=>{this.updateButtons()});this.expand?.addEventListener("click",()=>{this.details.map(e=>e.open=!0),this.updateButtons()}),this.collapse?.addEventListener("click",()=>{this.details.map(e=>e.open=!1),this.updateButtons()}),this.updateButtons(),this.setCurrent()}}setCurrent(){const e=document.querySelector(".js-canonicalURLPath")?.dataset?.canonicalUrlPath,t=document.querySelector(`.js-versionLink[href="${e}"]`);t&&(t.style.fontWeight="bold")}updateButtons(){setTimeout(()=>{if(!this.expand||!this.collapse)return;let e,t;for(const s of this.details)e=e||s.open,t=t||!s.open;this.expand.style.display=t?"inline-block":"none",this.collapse.style.display=t?"none":"inline-block"})}}new VersionsController;
+//# sourceMappingURL=versions.js.map
diff --git a/content/static/js/versions.js.map b/content/static/js/versions.js.map
new file mode 100644
index 0000000..028d44d
--- /dev/null
+++ b/content/static/js/versions.js.map
@@ -0,0 +1,7 @@
+{
+ "version": 3,
+ "sources": ["versions.ts"],
+ "sourcesContent": ["/*!\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * VersionsController encapsulates event listeners and UI updates\n * for the versions page. As the the expandable sections containing\n * the symbol history for a package are opened and closed it toggles\n * visiblity of the buttons to expand or collapse them. On page load\n * it adds an indicator to the version that matches the version request\n * by the user for the page or the canonical url path.\n */\nclass VersionsController {\n private expand = document.querySelector<HTMLButtonElement>('.js-versionsExpand');\n private collapse = document.querySelector<HTMLButtonElement>('.js-versionsCollapse');\n private details = [...document.querySelectorAll<HTMLDetailsElement>('.js-versionDetails')];\n\n constructor() {\n if (!this.expand?.parentElement) return;\n if (this.details.some(d => d.tagName === 'DETAILS')) {\n this.expand.parentElement.style.display = 'block';\n }\n\n for (const d of this.details) {\n d.addEventListener('click', () => {\n this.updateButtons();\n });\n }\n\n this.expand?.addEventListener('click', () => {\n this.details.map(d => (d.open = true));\n this.updateButtons();\n });\n\n this.collapse?.addEventListener('click', () => {\n this.details.map(d => (d.open = false));\n this.updateButtons();\n });\n\n this.updateButtons();\n this.setCurrent();\n }\n\n /**\n * setCurrent applies the active style to the version dot\n * for the version that matches the canonical URL path.\n */\n private setCurrent() {\n const canonicalPath = document.querySelector<HTMLElement>('.js-canonicalURLPath')?.dataset\n ?.canonicalUrlPath;\n const versionLink = document.querySelector<HTMLElement>(\n `.js-versionLink[href=\"${canonicalPath}\"]`\n );\n if (versionLink) {\n versionLink.style.fontWeight = 'bold';\n }\n }\n\n private updateButtons() {\n setTimeout(() => {\n if (!this.expand || !this.collapse) return;\n let someOpen, someClosed;\n for (const d of this.details) {\n someOpen = someOpen || d.open;\n someClosed = someClosed || !d.open;\n }\n this.expand.style.display = someClosed ? 'inline-block' : 'none';\n this.collapse.style.display = someClosed ? 'none' : 'inline-block';\n });\n }\n}\n\nnew VersionsController();\n"],
+ "mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAeA,wBAAyB,CAKvB,aAAc,CAJN,YAAS,SAAS,cAAiC,sBACnD,cAAW,SAAS,cAAiC,wBACrD,aAAU,CAAC,GAAG,SAAS,iBAAqC,uBAGlE,GAAI,EAAC,KAAK,QAAQ,cAClB,CAAI,KAAK,QAAQ,KAAK,GAAK,EAAE,UAAY,YACvC,MAAK,OAAO,cAAc,MAAM,QAAU,SAG5C,SAAW,KAAK,MAAK,QACnB,EAAE,iBAAiB,QAAS,IAAM,CAChC,KAAK,kBAIT,KAAK,QAAQ,iBAAiB,QAAS,IAAM,CAC3C,KAAK,QAAQ,IAAI,GAAM,EAAE,KAAO,IAChC,KAAK,kBAGP,KAAK,UAAU,iBAAiB,QAAS,IAAM,CAC7C,KAAK,QAAQ,IAAI,GAAM,EAAE,KAAO,IAChC,KAAK,kBAGP,KAAK,gBACL,KAAK,cAOC,YAAa,CACnB,KAAM,GAAgB,SAAS,cAA2B,yBAAyB,SAC/E,iBACE,EAAc,SAAS,cAC3B,yBAAyB,OAE3B,AAAI,GACF,GAAY,MAAM,WAAa,QAI3B,eAAgB,CACtB,WAAW,IAAM,CACf,GAAI,CAAC,KAAK,QAAU,CAAC,KAAK,SAAU,OACpC,GAAI,GAAU,EACd,SAAW,KAAK,MAAK,QACnB,EAAW,GAAY,EAAE,KACzB,EAAa,GAAc,CAAC,EAAE,KAEhC,KAAK,OAAO,MAAM,QAAU,EAAa,eAAiB,OAC1D,KAAK,SAAS,MAAM,QAAU,EAAa,OAAS,kBAK1D,GAAI",
+ "names": []
+}
diff --git a/content/static/js/versions.ts b/content/static/js/versions.ts
new file mode 100644
index 0000000..6ee0cd9
--- /dev/null
+++ b/content/static/js/versions.ts
@@ -0,0 +1,76 @@
+/*!
+ * @license
+ * Copyright 2021 The Go Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+/**
+ * VersionsController encapsulates event listeners and UI updates
+ * for the versions page. As the the expandable sections containing
+ * the symbol history for a package are opened and closed it toggles
+ * visiblity of the buttons to expand or collapse them. On page load
+ * it adds an indicator to the version that matches the version request
+ * by the user for the page or the canonical url path.
+ */
+class VersionsController {
+ private expand = document.querySelector<HTMLButtonElement>('.js-versionsExpand');
+ private collapse = document.querySelector<HTMLButtonElement>('.js-versionsCollapse');
+ private details = [...document.querySelectorAll<HTMLDetailsElement>('.js-versionDetails')];
+
+ constructor() {
+ if (!this.expand?.parentElement) return;
+ if (this.details.some(d => d.tagName === 'DETAILS')) {
+ this.expand.parentElement.style.display = 'block';
+ }
+
+ for (const d of this.details) {
+ d.addEventListener('click', () => {
+ this.updateButtons();
+ });
+ }
+
+ this.expand?.addEventListener('click', () => {
+ this.details.map(d => (d.open = true));
+ this.updateButtons();
+ });
+
+ this.collapse?.addEventListener('click', () => {
+ this.details.map(d => (d.open = false));
+ this.updateButtons();
+ });
+
+ this.updateButtons();
+ this.setCurrent();
+ }
+
+ /**
+ * setCurrent applies the active style to the version dot
+ * for the version that matches the canonical URL path.
+ */
+ private setCurrent() {
+ const canonicalPath = document.querySelector<HTMLElement>('.js-canonicalURLPath')?.dataset
+ ?.canonicalUrlPath;
+ const versionLink = document.querySelector<HTMLElement>(
+ `.js-versionLink[href="${canonicalPath}"]`
+ );
+ if (versionLink) {
+ versionLink.style.fontWeight = 'bold';
+ }
+ }
+
+ private updateButtons() {
+ setTimeout(() => {
+ if (!this.expand || !this.collapse) return;
+ let someOpen, someClosed;
+ for (const d of this.details) {
+ someOpen = someOpen || d.open;
+ someClosed = someClosed || !d.open;
+ }
+ this.expand.style.display = someClosed ? 'inline-block' : 'none';
+ this.collapse.style.display = someClosed ? 'none' : 'inline-block';
+ });
+ }
+}
+
+new VersionsController();
diff --git a/internal/middleware/secureheaders.go b/internal/middleware/secureheaders.go
index 2ff8b5f..b4a8379 100644
--- a/internal/middleware/secureheaders.go
+++ b/internal/middleware/secureheaders.go
@@ -27,6 +27,8 @@
"'sha256-nF5UdhqQFxB95DCaw1XdSQCEkIjoMhorTCQ+nQ4+Lq4='",
"'sha256-L+G1K2BEWa+o2vPy1pwdabLjINBByPWi1NkRwvASUq8='",
"'sha256-hb8VdkRSeBmkNlbshYmBnkYWC/BYHCPiz5s7liRcZNM='",
+ // From content/static/html/pages/unit_versions.tmpl
+ "'sha256-KBdPSv2Ajjw3jsa29qBhRW49nNx3jXxOLZIWX545FCA='",
}
// SecureHeaders adds a content-security-policy and other security-related