From 3b065655523a4f74612b03d0dd2ed303665295c7 Mon Sep 17 00:00:00 2001 From: Xiao Gui <xgui3783@gmail.com> Date: Tue, 14 Apr 2020 13:09:47 +0200 Subject: [PATCH] chore: added user doc chore: auto gen images --- common/constants.js | 11 ++++ docs/.gitignore | 1 + docs/Dockerfile | 26 ++++++++ docs/releases/v2.2.0.md | 5 ++ docs/usage/sharing.md | 24 ++++++++ e2e/screenshots/gen.js | 25 ++++++++ e2e/src/selecting/share.e2e-screenshot.js | 61 +++++++++++++++++++ e2e/src/util.js | 30 +++++++++ mkdocs.yml | 2 + package.json | 1 + .../statusCard/statusCard.component.ts | 13 +++- .../statusCard/statusCard.template.html | 9 ++- 12 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 common/constants.js create mode 100644 docs/.gitignore create mode 100644 docs/releases/v2.2.0.md create mode 100644 docs/usage/sharing.md create mode 100644 e2e/screenshots/gen.js create mode 100644 e2e/src/selecting/share.e2e-screenshot.js diff --git a/common/constants.js b/common/constants.js new file mode 100644 index 000000000..363efa6a8 --- /dev/null +++ b/common/constants.js @@ -0,0 +1,11 @@ +(function(exports){ + + exports.ARIA_LABELS = { + + // sharing module + SHARE_BTN: `Share this view`, + SHARE_COPY_URL_CLIPBOARD: `Copy URL to clipboard`, + SHARE_CUSTOM_URL: 'Create a custom URL', + SHARE_CUSTOM_URL_DIALOG: 'Dialog for creating a custom URL' + } +})(typeof exports === 'undefined' ? module.exports : exports) diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..ceb8b070b --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +autogen_images diff --git a/docs/Dockerfile b/docs/Dockerfile index 668554115..b017c579c 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,9 +1,35 @@ +# TODO using dockerfile will not work for readthedocs ... +# need to consider how to build for read the docs + +FROM node:12 as img_autogen + +COPY . /iav +WORKDIR /iav + +# build aot first && install webdriver & pptr +# parallel build-aot, update webdriver and install pptr +RUN npm i && \ + npm run build-aot & \ + npm run wd -- update --versions.chrome=80.0.3987.106 & \ + npm i --no-save puppeteer@2.1.0 + +# install deploy folder +RUN cd deploy && \ + npm i + +# auto generate screenshots +RUN npm run e2e -- --specs ./e2e/screenshots/gen.js + + FROM python:3.7 as builder COPY . /iav WORKDIR /iav RUN pip install mkdocs mkdocs-material mdx_truly_sane_lists + +COPY --from=img_autogen /iav/docs/autogen_images /iav/docs/autogen_images + RUN mkdocs build FROM nginx:alpine diff --git a/docs/releases/v2.2.0.md b/docs/releases/v2.2.0.md new file mode 100644 index 000000000..3db6c411e --- /dev/null +++ b/docs/releases/v2.2.0.md @@ -0,0 +1,5 @@ +# v2.2.0 + +## New features: + +- [sane url sharing](../usage/sharing.md) \ No newline at end of file diff --git a/docs/usage/sharing.md b/docs/usage/sharing.md new file mode 100644 index 000000000..ba0046c6f --- /dev/null +++ b/docs/usage/sharing.md @@ -0,0 +1,24 @@ +# Sharing + +Most of the viewer state are [saved in the URL](../advanced/url.md). Therefore, bookmarking / sending URL is a good way of saving / sharing a view of interest. Users may also generate a more human readable URL. + +A share button has been added in [v2.2.0](../releases/v2.2.0.md) to simplify this process. + +[](../autogen_images/share_highlightShareBtn.png) + +## Share URL + +`Share link to this view` will attempt to copy the current URL to the clipboard. It can then be sent or saved to restore the current view. + +[](../autogen_images/share_highlightShareURL.png) + +## Create custom URL + +`Create custom URL` will allow users to create a more human readable URL. + +!!! warning + Links generated by unauthenticated users will expire after 72 hours. + +[](../autogen_images/share_highlightShareURL.png) + +[](../autogen_images/share_shareCustomURLDialog.png) diff --git a/e2e/screenshots/gen.js b/e2e/screenshots/gen.js new file mode 100644 index 000000000..d81a922c7 --- /dev/null +++ b/e2e/screenshots/gen.js @@ -0,0 +1,25 @@ +const { spawn } = require("child_process") +const path = require('path') +const glob = require('glob') +describe('> generating screenshot', () => { + let childProcess + beforeAll(done => { + const cwdPath = path.join(__dirname, '../../deploy/') + childProcess = spawn('node', ['server.js'], { + cwd: cwdPath + }) + setTimeout(done, 1000) + }) + + require('../src/selecting/share.e2e-screenshot') + glob('../src/**/*.e2e-screenshot.js', (err, matches) => { + if (err) throw err + for (const match of matches) { + require(match) + } + }) + + afterAll(() => { + childProcess.kill() + }) +}) diff --git a/e2e/src/selecting/share.e2e-screenshot.js b/e2e/src/selecting/share.e2e-screenshot.js new file mode 100644 index 000000000..b3295553f --- /dev/null +++ b/e2e/src/selecting/share.e2e-screenshot.js @@ -0,0 +1,61 @@ +const { AtlasPage } = require('../util') +const { ARIA_LABELS } = require('../../../common/constants') +const fs = require('fs') +const path = require('path') + +const outputDir = path.join(__dirname, '../../../docs/autogen_images') +const exists = fs.existsSync(outputDir) +if (!exists) fs.mkdirSync(outputDir) + +describe('> share', () => { + let iavPage + beforeEach(async () => { + iavPage = new AtlasPage() + await iavPage.init() + await iavPage.goto() + await iavPage.selectTitleCard('Big Brain (Histology)') + await iavPage.wait(1000) + await iavPage.waitUntilAllChunksLoaded() + }) + + it('> generating highlight share btn', async () => { + const b64 = await iavPage.takeScreenshot(`[aria-label="${ARIA_LABELS.SHARE_BTN}"]`) + + const outputPath = path.join(outputDir, 'share_highlightShareBtn.png') + fs.writeFileSync(outputPath, b64, 'base64') + }) + + it('> generating highlight shareUrl btn', async () => { + await iavPage.click(`[aria-label="${ARIA_LABELS.SHARE_BTN}"]`) + await iavPage.wait(1000) + + const b64 = await iavPage.takeScreenshot(`[aria-label="${ARIA_LABELS.SHARE_COPY_URL_CLIPBOARD}"]`) + + const outputPath = path.join(outputDir, 'share_highlightShareURL.png') + fs.writeFileSync(outputPath, b64, 'base64') + }) + + it('> generating highlight custom URL', async () => { + + await iavPage.click(`[aria-label="${ARIA_LABELS.SHARE_BTN}"]`) + await iavPage.wait(1000) + + const b64 = await iavPage.takeScreenshot(`[aria-label="${ARIA_LABELS.SHARE_CUSTOM_URL}"]`) + + const outputPath = path.join(outputDir, 'share_highlightShareCustomURL.png') + fs.writeFileSync(outputPath, b64, 'base64') + }) + + it('> generating custom URL dialog', async () => { + + await iavPage.click(`[aria-label="${ARIA_LABELS.SHARE_BTN}"]`) + await iavPage.wait(1000) + await iavPage.click(`[aria-label="${ARIA_LABELS.SHARE_CUSTOM_URL}"]`) + await iavPage.wait(1000) + + const b64 = await iavPage.takeScreenshot(`[aria-label="${ARIA_LABELS.SHARE_CUSTOM_URL_DIALOG}"]`) + + const outputPath = path.join(outputDir, 'share_shareCustomURLDialog.png') + fs.writeFileSync(outputPath, b64, 'base64') + }) +}) diff --git a/e2e/src/util.js b/e2e/src/util.js index 679cbffe2..4956f0936 100644 --- a/e2e/src/util.js +++ b/e2e/src/util.js @@ -5,6 +5,7 @@ const USE_SELENIUM = !!process.env.SELENIUM_ADDRESS if (ATLAS_URL.length === 0) throw new Error(`ATLAS_URL must either be left unset or defined.`) if (ATLAS_URL[ATLAS_URL.length - 1] === '/') throw new Error(`ATLAS_URL should not trail with a slash: ${ATLAS_URL}`) const { By, WebDriver, Key } = require('selenium-webdriver') +const CITRUS_LIGHT_URL = `https://unpkg.com/citruslight@0.0.2/citruslight.js` function getActualUrl(url) { return /^http\:\/\//.test(url) ? url : `${ATLAS_URL}/${url.replace(/^\//, '')}` @@ -48,6 +49,35 @@ class WdBase{ return this._browser.driver } + // without image header + // output as b64 png + async takeScreenshot(cssSelector){ + + if(cssSelector) { + await this._browser.executeAsyncScript(async () => { + const cb = arguments[arguments.length - 1] + const moduleUrl = arguments[0] + const cssSelector = arguments[1] + + const el = document.querySelector(cssSelector) + if (!el) throw new Error(`css selector not fetching anything`) + import(moduleUrl) + .then(async m => { + m.citruslight(el) + cb() + }) + }, CITRUS_LIGHT_URL, cssSelector) + } + await this.wait(1000) + const result = await this._browser.takeScreenshot() + return result + } + + async click(cssSelector){ + if (!cssSelector) throw new Error(`click method needs to define a css selector`) + await this._browser.findElement( By.css(cssSelector) ).click() + } + historyBack() { return this._browser.navigate().back() } diff --git a/mkdocs.yml b/mkdocs.yml index 0a43acdc0..12153a9d1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -23,12 +23,14 @@ pages: - Searching: 'usage/search.md' - Exploring connectivity: 'usage/connectivity.md' - Miscellaneous: 'usage/misc.md' + - Sharing: 'usage/sharing.md' - Advanced usage: - Keyboard shortcuts: 'advanced/keyboard.md' - URL parsing: 'advanced/url.md' - Fetching datasets: 'advanced/datasets.md' - Display non-atlas volumes: 'advanced/otherVolumes.md' - Release notes: + - v2.2.0: 'releases/v2.2.0.md' - v2.1.0: 'releases/v2.1.0.md' - v2.0.2: 'releases/v2.0.2.md' - v2.0.1: 'releases/v2.0.1.md' diff --git a/package.json b/package.json index 6332a0cce..5028d50f7 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "eslint": "^6.8.0", "eslint-plugin-html": "^6.0.0", "file-loader": "^1.1.11", + "glob": "^7.1.6", "hammerjs": "^2.0.8", "html-webpack-plugin": "^3.2.0", "html2canvas": "^1.0.0-rc.1", diff --git a/src/ui/nehubaContainer/statusCard/statusCard.component.ts b/src/ui/nehubaContainer/statusCard/statusCard.component.ts index f64f9c3ee..e75437099 100644 --- a/src/ui/nehubaContainer/statusCard/statusCard.component.ts +++ b/src/ui/nehubaContainer/statusCard/statusCard.component.ts @@ -7,6 +7,7 @@ import { Observable, Subscription, of, combineLatest, BehaviorSubject } from "rx import { distinctUntilChanged, shareReplay, map, filter, startWith } from "rxjs/operators"; import { MatBottomSheet } from "@angular/material/bottom-sheet"; import { MatDialog } from "@angular/material/dialog"; +import { ARIA_LABELS } from 'common/constants' @Component({ selector : 'ui-status-card', @@ -26,6 +27,11 @@ export class StatusCardComponent implements OnInit, OnChanges{ public navVal$: Observable<string> public mouseVal$: Observable<string> + public SHARE_BTN_ARIA_LABEL = ARIA_LABELS.SHARE_BTN + public COPY_URL_TO_CLIPBOARD_ARIA_LABEL = ARIA_LABELS.SHARE_COPY_URL_CLIPBOARD + public SHARE_CUSTOM_URL_ARIA_LABEL = ARIA_LABELS.SHARE_CUSTOM_URL + public SHARE_CUSTOM_URL_DIALOG_ARIA_LABEL = ARIA_LABELS.SHARE_CUSTOM_URL_DIALOG + constructor( private store: Store<ViewerStateInterface>, private log: LoggingService, @@ -157,7 +163,10 @@ export class StatusCardComponent implements OnInit, OnChanges{ }) } - openDialog(tmpl: TemplateRef<any>) { - this.dialog.open(tmpl) + openDialog(tmpl: TemplateRef<any>, options) { + const { ariaLabel } = options + this.dialog.open(tmpl, { + ariaLabel + }) } } diff --git a/src/ui/nehubaContainer/statusCard/statusCard.template.html b/src/ui/nehubaContainer/statusCard/statusCard.template.html index fbb52fae9..b7baf4061 100644 --- a/src/ui/nehubaContainer/statusCard/statusCard.template.html +++ b/src/ui/nehubaContainer/statusCard/statusCard.template.html @@ -67,6 +67,7 @@ <div class="w-0 position-relative"> <button (click)="showBottomSheet(shareTmpl)" + [attr.aria-label]="SHARE_BTN_ARIA_LABEL" mat-icon-button class="position-absolute share-btn"> <i class="fas fa-share-square"></i> @@ -95,7 +96,9 @@ Share via </h4> <mat-nav-list> - <mat-list-item iav-clipboard-copy> + <mat-list-item iav-clipboard-copy + [attr.aria-label]="COPY_URL_TO_CLIPBOARD_ARIA_LABEL" + [attr.tab-index]="10"> <mat-icon class="mr-4" fontSet="fas" @@ -105,7 +108,9 @@ Copy link to this view </span> </mat-list-item> - <mat-list-item (click)="openDialog(shareSaneUrl)"> + <mat-list-item (click)="openDialog(shareSaneUrl, { ariaLabel: SHARE_CUSTOM_URL_DIALOG_ARIA_LABEL })" + [attr.aria-label]="SHARE_CUSTOM_URL_ARIA_LABEL" + [attr.tab-index]="10"> <mat-icon class="mr-4" fontSet="fas" -- GitLab