Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ba2916cf5 | ||
|
|
4736ad5620 | ||
|
|
44f562e24c | ||
|
|
8683e1e8ff | ||
|
|
f86eb5e8f2 | ||
|
|
64fd724455 | ||
|
|
b2dfb6e0f7 | ||
|
|
7c043b46b4 | ||
|
|
62932b3e80 | ||
|
|
7a7b2774e6 | ||
|
|
1556b3bbf8 | ||
|
|
199db68674 | ||
|
|
d74b6fb826 | ||
|
|
aeac900496 | ||
|
|
835fec7f32 | ||
|
|
4c202ed064 | ||
|
|
e751e8fab8 | ||
|
|
ae795ea97d | ||
|
|
d90c70cab5 | ||
|
|
1a8fd13896 | ||
|
|
0026801b62 | ||
|
|
395315ea4c | ||
|
|
4664f673d6 | ||
|
|
a8c4534b33 | ||
|
|
e4a3510c86 | ||
|
|
d28f332312 | ||
|
|
37f60ba0af | ||
|
|
d64cdc429e | ||
|
|
e06e8304d3 | ||
|
|
0e862c69db | ||
|
|
fbdb61bcb2 | ||
|
|
3acea218fb | ||
|
|
a521ce31ce | ||
|
|
67e2fd39a3 | ||
|
|
253620650b | ||
|
|
31357ea9b5 | ||
|
|
b8baae7c25 | ||
|
|
5544c57c37 | ||
|
|
6b523cb50d | ||
|
|
579c2b41d1 | ||
|
|
9449ae4314 | ||
|
|
4386b5c960 | ||
|
|
fe3ee3010f | ||
|
|
ee5530183e | ||
|
|
413214786d | ||
|
|
0b62b720c5 | ||
|
|
c3fead1ca9 | ||
|
|
bc8d505a0a | ||
|
|
ab4067144f | ||
|
|
575d11ac5b | ||
|
|
aa53d3258c | ||
|
|
dc672bf38d | ||
|
|
65a855d99b | ||
|
|
99c1d0220d | ||
|
|
dab0b5e0e3 | ||
|
|
ea3f7d5657 | ||
|
|
3c812792bb | ||
|
|
ff21408440 | ||
|
|
fd720bd4d7 | ||
|
|
2279e555a3 | ||
|
|
91ee615aee | ||
|
|
f8d9bbef8a | ||
|
|
4fcf544abe | ||
|
|
2ba0e95fe8 | ||
|
|
91564bfa06 | ||
|
|
83e627e5c9 | ||
|
|
2fbbb4a8e2 | ||
|
|
c05058a0f5 | ||
|
|
2429294dda | ||
|
|
2234869b11 | ||
|
|
96686850cb | ||
|
|
35dc82e120 | ||
|
|
f5d6674b62 | ||
|
|
f3009c7a45 | ||
|
|
e8969fba11 | ||
|
|
f175d4b96b | ||
|
|
e250ac0c50 | ||
|
|
3e1e456cc2 | ||
|
|
e58ba39a60 | ||
|
|
b73ea99971 |
No files matched your search
@@ -1,6 +0,0 @@
|
|||||||
# Browsers that we support
|
|
||||||
|
|
||||||
> 1%
|
|
||||||
IE 11
|
|
||||||
not dead
|
|
||||||
not op_mini all
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Set directory to location of this script
|
|
||||||
# https://stackoverflow.com/a/3355423/1867984
|
|
||||||
cd "$(dirname "$0")"
|
|
||||||
|
|
||||||
yarn -v
|
|
||||||
node -v
|
|
||||||
|
|
||||||
echo 'Installing Gitbook CLI'
|
|
||||||
|
|
||||||
yarn global bin
|
|
||||||
yarn config get prefix
|
|
||||||
yarn config set prefix ~/.yarn
|
|
||||||
export PATH="$PATH:`yarn global bin`"
|
|
||||||
|
|
||||||
echo 'Running Gitbook installation'
|
|
||||||
|
|
||||||
# Generate all version's GitBook output
|
|
||||||
# For each directory in /docs ...
|
|
||||||
cd ./../docs/
|
|
||||||
for D in *; do
|
|
||||||
if [ -d "${D}" ]; then
|
|
||||||
|
|
||||||
echo "Generating output for: ${D}"
|
|
||||||
cd "${D}"
|
|
||||||
|
|
||||||
# Clear previous output, generate new
|
|
||||||
rm -rf _book
|
|
||||||
gitbook install
|
|
||||||
gitbook build
|
|
||||||
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Move CNAME File into `latest`
|
|
||||||
cp CNAME ./latest/_book/CNAME
|
|
||||||
|
|
||||||
# Create a history folder in our latest version's output
|
|
||||||
mkdir ./latest/_book/history
|
|
||||||
|
|
||||||
# Move each version's files to latest's history folder
|
|
||||||
for D in *; do
|
|
||||||
if [ -d "${D}" ]; then
|
|
||||||
if [ "${D}" == v* ] ; then
|
|
||||||
|
|
||||||
echo "Moving ${D} to the latest version's history folder"
|
|
||||||
|
|
||||||
mkdir "./latest/_book/history/${D}"
|
|
||||||
cp -v -r "./${D}/_book"/* "./latest/_book/history/${D}"
|
|
||||||
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Back to repo root
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
echo "Done generating documentation output"
|
|
||||||
echo 'STARTING PUBLISH'
|
|
||||||
|
|
||||||
# WILL ALWAYS FAIL IF INITIATED FROM PR BRANCH
|
|
||||||
npx gh-pages \
|
|
||||||
--silent \
|
|
||||||
--repo https://$GITHUB_TOKEN@github.com/OHIF/Viewers.git \
|
|
||||||
--message 'Autogenerated Message: [ci skip]' \
|
|
||||||
--dist docs/latest/_book
|
|
||||||
@@ -9,93 +9,18 @@ version: 2.1
|
|||||||
# create pull request previews and to update `https://docs.ohif.org`
|
# create pull request previews and to update `https://docs.ohif.org`
|
||||||
###
|
###
|
||||||
|
|
||||||
## https://github.com/cypress-io/circleci-orb
|
|
||||||
##
|
|
||||||
orbs:
|
orbs:
|
||||||
codecov: codecov/codecov@1.0.5
|
codecov: codecov/codecov@1.0.5
|
||||||
cypress: cypress-io/cypress@1.26.0
|
|
||||||
executors:
|
|
||||||
# Custom executor to override Cypress config
|
|
||||||
deploy-to-prod-executor:
|
|
||||||
docker:
|
|
||||||
- image: 'cypress/browsers:node14.15.0-chrome86-ff82'
|
|
||||||
environment:
|
|
||||||
CYPRESS_BASE_URL: https://ohif-staging.netlify.com/
|
|
||||||
chrome-and-pacs:
|
|
||||||
docker:
|
|
||||||
# Primary container image where all steps run.
|
|
||||||
- image: 'cypress/browsers:node14.15.0-chrome86-ff82'
|
|
||||||
defaults: &defaults
|
defaults: &defaults
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:14.19
|
- image: circleci/node:10.16.0
|
||||||
environment:
|
|
||||||
TERM: xterm # Enable colors in term
|
|
||||||
QUICK_BUILD: true
|
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
###
|
PR_UNIT_TESTS:
|
||||||
# Workflow: PR_CHECKS
|
|
||||||
###
|
|
||||||
UNIT_TESTS:
|
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
steps:
|
|
||||||
# Update yarn
|
|
||||||
- run: yarn -v
|
|
||||||
# Checkout code and ALL Git Tags
|
|
||||||
- checkout:
|
|
||||||
post:
|
|
||||||
- git fetch --all
|
|
||||||
# - restore_cache:
|
|
||||||
# name: Restore Yarn and Cypress Package Cache
|
|
||||||
# keys:
|
|
||||||
# # when lock file changes, use increasingly general patterns to restore cache
|
|
||||||
# - yarn-packages-{{ checksum "yarn.lock" }}
|
|
||||||
# - yarn-packages-
|
|
||||||
- run:
|
|
||||||
name: Install Dependencies
|
|
||||||
command: yarn install --frozen-lockfile
|
|
||||||
- save_cache:
|
|
||||||
name: Save Yarn Package Cache
|
|
||||||
paths:
|
|
||||||
- ~/.cache ## Cache yarn and Cypress
|
|
||||||
key: yarn-packages-{{ checksum "yarn.lock" }}
|
|
||||||
# RUN TESTS
|
|
||||||
- run:
|
|
||||||
name: 'JavaScript Test Suite'
|
|
||||||
command: yarn run test:unit:ci
|
|
||||||
# PLATFORM/VIEWER
|
|
||||||
- run:
|
|
||||||
name: 'VIEWER: Combine report output'
|
|
||||||
command: |
|
|
||||||
viewerCov="/home/circleci/repo/platform/viewer/coverage"
|
|
||||||
touch "${viewerCov}/reports"
|
|
||||||
cat "${viewerCov}/clover.xml" >> "${viewerCov}/reports"
|
|
||||||
echo "\<<\<<\<< EOF" >> "${viewerCov}/reports"
|
|
||||||
cat "${viewerCov}/lcov.info" >>"${viewerCov}/reports"
|
|
||||||
echo "\<<\<<\<< EOF" >> "${viewerCov}/reports"
|
|
||||||
- codecov/upload:
|
|
||||||
file: '/home/circleci/repo/platform/viewer/coverage/reports'
|
|
||||||
flags: 'viewer'
|
|
||||||
# PLATFORM/CORE
|
|
||||||
- run:
|
|
||||||
name: 'CORE: Combine report output'
|
|
||||||
command: |
|
|
||||||
coreCov="/home/circleci/repo/platform/core/coverage"
|
|
||||||
touch "${coreCov}/reports"
|
|
||||||
cat "${coreCov}/clover.xml" >> "${coreCov}/reports"
|
|
||||||
echo "\<<\<<\<< EOF" >> "${coreCov}/reports"
|
|
||||||
cat "${coreCov}/lcov.info" >> "${coreCov}/reports"
|
|
||||||
echo "\<<\<<\<< EOF" >> "${coreCov}/reports"
|
|
||||||
- codecov/upload:
|
|
||||||
file: '/home/circleci/repo/platform/core/coverage/reports'
|
|
||||||
flags: 'core'
|
|
||||||
|
|
||||||
###
|
|
||||||
# Workflow: PR_OPTIONAL_DOCKER_PUBLISH
|
|
||||||
###
|
|
||||||
DOCKER_PR_PUBLISH:
|
|
||||||
<<: *defaults
|
|
||||||
steps:
|
steps:
|
||||||
# Enable yarn workspaces
|
# Enable yarn workspaces
|
||||||
- run: yarn config set workspaces-experimental true
|
- run: yarn config set workspaces-experimental true
|
||||||
@@ -104,50 +29,13 @@ jobs:
|
|||||||
- checkout:
|
- checkout:
|
||||||
post:
|
post:
|
||||||
- git fetch --all
|
- git fetch --all
|
||||||
|
- restore_cache:
|
||||||
# - restore_cache:
|
name: Restore Yarn and Cypress Package Cache
|
||||||
# name: Restore Yarn and Cypress Package Cache
|
keys:
|
||||||
# keys:
|
# when lock file changes, use increasingly general patterns to restore cache
|
||||||
# # when lock file changes, use increasingly general patterns to restore cache
|
- yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||||
# - yarn-packages-{{ checksum "yarn.lock" }}
|
- yarn-packages-v1-{{ .Branch }}-
|
||||||
# - yarn-packages-
|
- yarn-packages-v1-
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Install Dependencies
|
|
||||||
command: yarn install --frozen-lockfile
|
|
||||||
|
|
||||||
- setup_remote_docker:
|
|
||||||
version: 19.03.13
|
|
||||||
docker_layer_caching: false
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Build and push Docker image
|
|
||||||
command: |
|
|
||||||
# Remove npm config
|
|
||||||
rm -f ./.npmrc
|
|
||||||
# Set our version number using vars
|
|
||||||
echo $CIRCLE_BUILD_NUM
|
|
||||||
# Build our image, auth, and push
|
|
||||||
docker build --tag ohif/viewer:PR_BUILD-$CIRCLE_BUILD_NUM .
|
|
||||||
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
|
|
||||||
docker push ohif/viewer:PR_BUILD-$CIRCLE_BUILD_NUM
|
|
||||||
|
|
||||||
###
|
|
||||||
# Workflow: DEPLOY
|
|
||||||
###
|
|
||||||
BUILD:
|
|
||||||
<<: *defaults
|
|
||||||
steps:
|
|
||||||
# Checkout code and ALL Git Tags
|
|
||||||
- checkout:
|
|
||||||
post:
|
|
||||||
- git fetch --all
|
|
||||||
# - restore_cache:
|
|
||||||
# name: Restore Yarn and Cypress Package Cache
|
|
||||||
# keys:
|
|
||||||
# # when lock file changes, use increasingly general patterns to restore cache
|
|
||||||
# - yarn-packages-{{ checksum "yarn.lock" }}
|
|
||||||
# - yarn-packages-
|
|
||||||
- run:
|
- run:
|
||||||
name: Install Dependencies
|
name: Install Dependencies
|
||||||
command: yarn install --frozen-lockfile
|
command: yarn install --frozen-lockfile
|
||||||
@@ -155,160 +43,188 @@ jobs:
|
|||||||
name: Save Yarn Package Cache
|
name: Save Yarn Package Cache
|
||||||
paths:
|
paths:
|
||||||
- ~/.cache ## Cache yarn and Cypress
|
- ~/.cache ## Cache yarn and Cypress
|
||||||
key: yarn-packages-{{ checksum "yarn.lock" }}
|
key: yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||||
# Build & Test
|
|
||||||
- run:
|
|
||||||
name: 'Build the OHIF Viewer'
|
|
||||||
command: yarn run build
|
|
||||||
no_output_timeout: 45m
|
|
||||||
# - run:
|
|
||||||
# name: 'Upload SourceMaps, Send Deploy Notification'
|
|
||||||
# command: |
|
|
||||||
# # export FILE_1=$(find ./build/static/js -type f -name "2.*.js" -exec basename {} \;)
|
|
||||||
# # export FILE_MAIN=$(find ./build/static/js -type f -name "main.*.js" -exec basename {} \;)
|
|
||||||
# # export FILE_RUNTIME_MAIN=$(find ./build/static/js -type f -name "runtime~main.*.js" -exec basename {} \;)
|
|
||||||
# # curl https://api.rollbar.com/api/1/sourcemap -F source_map=@build/static/js/$FILE_1.map -F access_token=$ROLLBAR_TOKEN -F version=$CIRCLE_SHA1 -F minified_url=https://$GOOGLE_STORAGE_BUCKET/static/js/$FILE_1
|
|
||||||
# # curl https://api.rollbar.com/api/1/sourcemap -F source_map=@build/static/js/$FILE_MAIN.map -F access_token=$ROLLBAR_TOKEN -F version=$CIRCLE_SHA1 -F minified_url=https://$GOOGLE_STORAGE_BUCKET/static/js/$FILE_MAIN
|
|
||||||
# # curl https://api.rollbar.com/api/1/sourcemap -F source_map=@build/static/js/$FILE_RUNTIME_MAIN.map -F access_token=$ROLLBAR_TOKEN -F version=$CIRCLE_SHA1 -F minified_url=https://$GOOGLE_STORAGE_BUCKET/static/js/$FILE_RUNTIME_MAIN
|
|
||||||
# curl --request POST https://api.rollbar.com/api/1/deploy/ -F access_token=$ROLLBAR_TOKEN -F environment=$GOOGLE_STORAGE_BUCKET -F revision=$CIRCLE_SHA1 -F local_username=CircleCI
|
|
||||||
# Persist :+1:
|
|
||||||
- persist_to_workspace:
|
|
||||||
root: ~/repo
|
|
||||||
paths:
|
|
||||||
- platform/viewer/dist
|
|
||||||
- netlify.toml
|
|
||||||
- .netlify
|
|
||||||
|
|
||||||
DEPLOY_TO_DEV:
|
# RUN TESTS
|
||||||
docker:
|
|
||||||
- image: circleci/node:14.15.0
|
|
||||||
environment:
|
|
||||||
TERM: xterm
|
|
||||||
NETLIFY_SITE_ID: 32708787-c9b0-4634-b50f-7ca41952da77
|
|
||||||
working_directory: ~/repo
|
|
||||||
steps:
|
|
||||||
- attach_workspace:
|
|
||||||
at: ~/repo
|
|
||||||
- run: cd .netlify && npm install
|
|
||||||
- run:
|
- run:
|
||||||
cp .netlify/deploy-workflow/_redirects platform/viewer/dist/_redirects
|
name: "JavaScript Test Suite"
|
||||||
- run: cd .netlify && npm run deploy
|
command: yarn run test:unit:ci
|
||||||
|
|
||||||
DEPLOY_TO_STAGING:
|
# PLATFORM/VIEWER
|
||||||
docker:
|
|
||||||
- image: circleci/node:14.15.0
|
|
||||||
environment:
|
|
||||||
TERM: xterm
|
|
||||||
NETLIFY_SITE_ID: c7502ae3-b150-493c-8422-05701e44a969
|
|
||||||
working_directory: ~/repo
|
|
||||||
steps:
|
|
||||||
- attach_workspace:
|
|
||||||
at: ~/repo
|
|
||||||
- run: cd .netlify && npm install
|
|
||||||
- run:
|
- run:
|
||||||
cp .netlify/deploy-workflow/_redirects platform/viewer/dist/_redirects
|
name: "VIEWER: Combine report output"
|
||||||
- run: cd .netlify && npm run deploy
|
command: |
|
||||||
|
viewerCov="/home/circleci/repo/platform/viewer/coverage"
|
||||||
|
touch "${viewerCov}/reports"
|
||||||
|
cat "${viewerCov}/clover.xml" >> "${viewerCov}/reports"
|
||||||
|
echo "\<<\<<\<< EOF" >> "${viewerCov}/reports"
|
||||||
|
cat "${viewerCov}/lcov.info" >>"${viewerCov}/reports"
|
||||||
|
echo "\<<\<<\<< EOF" >> "${viewerCov}/reports"
|
||||||
|
- codecov/upload:
|
||||||
|
file: "/home/circleci/repo/platform/viewer/coverage/reports"
|
||||||
|
flags: "viewer"
|
||||||
|
|
||||||
DEPLOY_TO_PRODUCTION:
|
# PLATFORM/CORE
|
||||||
docker:
|
|
||||||
- image: circleci/node:14.15.0
|
|
||||||
environment:
|
|
||||||
TERM: xterm
|
|
||||||
NETLIFY_SITE_ID: 79c4a5da-5c95-4dc9-84f7-45fd9dfe21b0
|
|
||||||
working_directory: ~/repo
|
|
||||||
steps:
|
|
||||||
- attach_workspace:
|
|
||||||
at: ~/repo
|
|
||||||
- run: cd .netlify && npm install
|
|
||||||
- run:
|
- run:
|
||||||
cp .netlify/deploy-workflow/_redirects platform/viewer/dist/_redirects
|
name: "CORE: Combine report output"
|
||||||
- run: cd .netlify && npm run deploy
|
command: |
|
||||||
|
coreCov="/home/circleci/repo/platform/core/coverage"
|
||||||
|
touch "${coreCov}/reports"
|
||||||
|
cat "${coreCov}/clover.xml" >> "${coreCov}/reports"
|
||||||
|
echo "\<<\<<\<< EOF" >> "${coreCov}/reports"
|
||||||
|
cat "${coreCov}/lcov.info" >> "${coreCov}/reports"
|
||||||
|
echo "\<<\<<\<< EOF" >> "${coreCov}/reports"
|
||||||
|
- codecov/upload:
|
||||||
|
file: "/home/circleci/repo/platform/core/coverage/reports"
|
||||||
|
flags: "core"
|
||||||
|
|
||||||
###
|
MERGE_UNIT_TESTS:
|
||||||
# Workflow: RELEASE
|
|
||||||
###
|
|
||||||
NPM_PUBLISH:
|
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- run: yarn -v
|
# Enable yarn workspaces
|
||||||
|
- run: yarn config set workspaces-experimental true
|
||||||
|
|
||||||
# Checkout code and ALL Git Tags
|
# Checkout code and ALL Git Tags
|
||||||
- checkout:
|
- checkout:
|
||||||
post:
|
post:
|
||||||
- git fetch --all
|
- git fetch --all
|
||||||
# Use increasingly general patterns to restore cache
|
- restore_cache:
|
||||||
# - restore_cache:
|
name: Restore Yarn and Cypress Package Cache
|
||||||
# name: Restore Yarn and Cypress Package Cache
|
keys:
|
||||||
# keys:
|
# when lock file changes, use increasingly general patterns to restore cache
|
||||||
# - yarn-packages-{{ checksum "yarn.lock" }}
|
- yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||||
# - yarn-packages-
|
- yarn-packages-v1-{{ .Branch }}-
|
||||||
|
- yarn-packages-v1-
|
||||||
- run:
|
- run:
|
||||||
name: Install Dependencies
|
name: Install Dependencies
|
||||||
command: yarn install --frozen-lockfile
|
command: yarn install --frozen-lockfile
|
||||||
- save_cache:
|
- save_cache:
|
||||||
name: Save Yarn Package Cache
|
name: Save Yarn Package Cache
|
||||||
paths:
|
paths:
|
||||||
- ~/.cache/yarn
|
- ~/.cache ## Cache yarn and Cypress
|
||||||
key: yarn-packages-{{ checksum "yarn.lock" }}
|
key: yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||||
|
|
||||||
|
# RUN TESTS
|
||||||
- run:
|
- run:
|
||||||
name: Avoid hosts unknown for github
|
name: "JavaScript Test Suite"
|
||||||
|
command: yarn run test:unit:ci
|
||||||
|
|
||||||
|
# PLATFORM/VIEWER
|
||||||
|
- run:
|
||||||
|
name: "VIEWER: Combine report output"
|
||||||
command: |
|
command: |
|
||||||
rm -rf ~/.ssh
|
viewerCov="/home/circleci/repo/platform/viewer/coverage"
|
||||||
mkdir ~/.ssh/
|
touch "${viewerCov}/reports"
|
||||||
echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config
|
cat "${viewerCov}/clover.xml" >> "${viewerCov}/reports"
|
||||||
git config --global user.email "danny.ri.brown+ohif-bot@gmail.com"
|
echo "\<<\<<\<< EOF" >> "${viewerCov}/reports"
|
||||||
git config --global user.name "ohif-bot"
|
cat "${viewerCov}/lcov.info" >>"${viewerCov}/reports"
|
||||||
|
echo "\<<\<<\<< EOF" >> "${viewerCov}/reports"
|
||||||
|
- codecov/upload:
|
||||||
|
file: "/home/circleci/repo/platform/viewer/coverage/reports"
|
||||||
|
flags: "viewer"
|
||||||
|
|
||||||
|
# PLATFORM/CORE
|
||||||
- run:
|
- run:
|
||||||
name: Authenticate with NPM registry
|
name: "CORE: Combine report output"
|
||||||
command:
|
command: |
|
||||||
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc
|
coreCov="/home/circleci/repo/platform/core/coverage"
|
||||||
- run: npx lerna version
|
touch "${coreCov}/reports"
|
||||||
- run: npx lerna publish from-package
|
cat "${coreCov}/clover.xml" >> "${coreCov}/reports"
|
||||||
|
echo "\<<\<<\<< EOF" >> "${coreCov}/reports"
|
||||||
|
cat "${coreCov}/lcov.info" >> "${coreCov}/reports"
|
||||||
|
echo "\<<\<<\<< EOF" >> "${coreCov}/reports"
|
||||||
|
- codecov/upload:
|
||||||
|
file: "/home/circleci/repo/platform/core/coverage/reports"
|
||||||
|
flags: "core"
|
||||||
|
|
||||||
|
# Persist :+1:
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: ~/repo
|
root: ~/repo
|
||||||
paths: .
|
paths: .
|
||||||
|
|
||||||
DOCS_PUBLISH:
|
e2e_test:
|
||||||
docker:
|
|
||||||
# Gitbook fails due to graceful-fs updates above this node version :-(
|
|
||||||
- image: circleci/node:12.9.1
|
|
||||||
environment:
|
|
||||||
TERM: xterm # Enable colors in term
|
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
|
docker:
|
||||||
|
- image: cypress/base:8
|
||||||
|
environment:
|
||||||
|
## this enables colors in the output
|
||||||
|
TERM: xterm
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- attach_workspace:
|
||||||
|
at: ~/repo
|
||||||
|
- restore_cache:
|
||||||
|
name: Restore Yarn and Cypress Package Cache
|
||||||
|
keys:
|
||||||
|
# when lock file changes, use increasingly general patterns to restore cache
|
||||||
|
- yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||||
|
- yarn-packages-v1-{{ .Branch }}-
|
||||||
|
- yarn-packages-v1-
|
||||||
|
# - run: $(yarn bin)/cypress run --record
|
||||||
|
# Shouldn't be needed if we're using a package cache that contains Cypress
|
||||||
|
# Could do a check here, if it doesn't exist, install cypress?
|
||||||
|
- run: yarn install
|
||||||
|
- run: yarn run test:e2e:ci
|
||||||
|
|
||||||
|
npm_publish:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- attach_workspace:
|
||||||
|
at: ~/repo
|
||||||
- run:
|
- run:
|
||||||
name: Avoid hosts unknown for github
|
name: Avoid hosts unknown for github
|
||||||
command: |
|
command:
|
||||||
rm -rf ~/.ssh
|
mkdir ~/.ssh/ && echo -e "Host github.com\n\tStrictHostKeyChecking
|
||||||
mkdir ~/.ssh/
|
no\n" > ~/.ssh/config
|
||||||
echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config
|
# --no-ci argument is not ideal; however, semantic-rlease thinks we're
|
||||||
git config --global user.email "danny.ri.brown+ohif-bot@gmail.com"
|
# attempting to run it from a `pr`, which is not the case
|
||||||
git config --global user.name "ohif-bot"
|
- run:
|
||||||
- run: yarn global add gitbook-cli gh-pages
|
name: Publish using Semantic Release
|
||||||
- run: chmod +x ~/repo/.circleci/build-and-publish-docs.sh
|
command: npx semantic-release --debug
|
||||||
- run: ~/repo/.circleci/build-and-publish-docs.sh
|
# Persist :+1:
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: ~/repo
|
||||||
|
paths: .
|
||||||
|
|
||||||
DOCKER_MASTER_PUBLISH:
|
docs_publish:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- attach_workspace:
|
||||||
|
at: ~/repo
|
||||||
|
- run:
|
||||||
|
name: Avoid hosts unknown for github
|
||||||
|
command:
|
||||||
|
mkdir ~/.ssh/ && echo -e "Host github.com\n\tStrictHostKeyChecking
|
||||||
|
no\n" > ~/.ssh/config
|
||||||
|
- run: git config --global user.email "gh-pages@localhost"
|
||||||
|
- run: git config --global user.name "npm gh-pages"
|
||||||
|
- run: yarn global add gh-pages
|
||||||
|
- run:
|
||||||
|
name: Generate Docs
|
||||||
|
command: yarn run staticDeploy
|
||||||
|
- run:
|
||||||
|
name: Publish Docs
|
||||||
|
command: yarn run docs:publish
|
||||||
|
|
||||||
|
docker_publish:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
steps:
|
steps:
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: ~/repo
|
at: ~/repo
|
||||||
- setup_remote_docker:
|
- setup_remote_docker:
|
||||||
version: 19.03.13
|
docker_layer_caching: true
|
||||||
docker_layer_caching: false
|
|
||||||
- run:
|
- run:
|
||||||
name: Build and push Docker image
|
name: Build and push Docker image
|
||||||
command: |
|
command: |
|
||||||
# This file will exist if a new version was published by
|
# This file will exist if a new version was published by
|
||||||
# our command in the previous job. Created in npm postpublish hook
|
# our `semantic-release` command in the previous job
|
||||||
# in the `platform/viewer` project.
|
if [[ ! -e tmp/updated-version.txt ]]; then
|
||||||
if [[ ! -e platform/viewer/success_version.txt ]]; then
|
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
# Remove npm config
|
# Remove npm config
|
||||||
rm -f ./.npmrc
|
rm -f ./.npmrc
|
||||||
# Set our version number using vars
|
# Set our version number using vars
|
||||||
export IMAGE_VERSION=$(cat platform/viewer/success_version.txt)
|
export IMAGE_VERSION=$(cat tmp/updated-version.txt)
|
||||||
export IMAGE_VERSION_FULL=v$IMAGE_VERSION.${CIRCLE_BUILD_NUM}
|
export IMAGE_VERSION_FULL=v$IMAGE_VERSION.${CIRCLE_BUILD_NUM}
|
||||||
echo $IMAGE_VERSION
|
echo $IMAGE_VERSION
|
||||||
echo $IMAGE_VERSION_FULL
|
echo $IMAGE_VERSION_FULL
|
||||||
@@ -319,190 +235,109 @@ jobs:
|
|||||||
docker push ohif/$IMAGE_NAME:latest
|
docker push ohif/$IMAGE_NAME:latest
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
build_demo_site:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
# Download and cache dependencies
|
||||||
|
- checkout
|
||||||
|
- restore_cache:
|
||||||
|
name: Restore Yarn Package Cache
|
||||||
|
keys:
|
||||||
|
# when lock file changes, use increasingly general patterns to restore cache
|
||||||
|
- yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||||
|
- yarn-packages-v1-{{ .Branch }}-
|
||||||
|
- yarn-packages-v1-
|
||||||
|
- run:
|
||||||
|
name: Install Dependencies
|
||||||
|
command: yarn install --frozen-lockfile
|
||||||
|
- save_cache:
|
||||||
|
name: Save Yarn Package Cache
|
||||||
|
paths:
|
||||||
|
- ~/.cache/yarn
|
||||||
|
key: yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||||
|
# Build & Test
|
||||||
|
- run:
|
||||||
|
name: "Build Demo Site, Upload SourceMaps, Send Deploy Notification"
|
||||||
|
command: |
|
||||||
|
yarn build:demo:ci
|
||||||
|
perl -i -pe 's#</head>#`cat .circleci/rollbar.html` #e' build/index.html
|
||||||
|
export FILE_1=$(find ./build/static/js -type f -name "2.*.js" -exec basename {} \;)
|
||||||
|
export FILE_MAIN=$(find ./build/static/js -type f -name "main.*.js" -exec basename {} \;)
|
||||||
|
export FILE_RUNTIME_MAIN=$(find ./build/static/js -type f -name "runtime~main.*.js" -exec basename {} \;)
|
||||||
|
curl https://api.rollbar.com/api/1/sourcemap -F source_map=@build/static/js/$FILE_1.map -F access_token=$ROLLBAR_TOKEN -F version=$CIRCLE_SHA1 -F minified_url=https://$GOOGLE_STORAGE_BUCKET/static/js/$FILE_1
|
||||||
|
curl https://api.rollbar.com/api/1/sourcemap -F source_map=@build/static/js/$FILE_MAIN.map -F access_token=$ROLLBAR_TOKEN -F version=$CIRCLE_SHA1 -F minified_url=https://$GOOGLE_STORAGE_BUCKET/static/js/$FILE_MAIN
|
||||||
|
curl https://api.rollbar.com/api/1/sourcemap -F source_map=@build/static/js/$FILE_RUNTIME_MAIN.map -F access_token=$ROLLBAR_TOKEN -F version=$CIRCLE_SHA1 -F minified_url=https://$GOOGLE_STORAGE_BUCKET/static/js/$FILE_RUNTIME_MAIN
|
||||||
|
curl --request POST https://api.rollbar.com/api/1/deploy/ -F access_token=$ROLLBAR_TOKEN -F environment=$GOOGLE_STORAGE_BUCKET -F revision=$CIRCLE_SHA1 -F local_username=CircleCI
|
||||||
|
# Persist :+1:
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: ~/repo
|
||||||
|
paths: .
|
||||||
|
|
||||||
|
demo_site_publish:
|
||||||
|
working_directory: ~/repo
|
||||||
|
docker:
|
||||||
|
- image: google/cloud-sdk
|
||||||
|
steps:
|
||||||
|
- attach_workspace:
|
||||||
|
at: ~/repo
|
||||||
|
- setup_remote_docker:
|
||||||
|
docker_layer_caching: true
|
||||||
|
- run:
|
||||||
|
name: Deploy latest version to viewer.ohif.org
|
||||||
|
command: |
|
||||||
|
# This file will exist if a new version was published by
|
||||||
|
# our `semantic-release` command in the previous job
|
||||||
|
#if [[ ! -e tmp/updated-version.txt ]]; then
|
||||||
|
# exit 0
|
||||||
|
#else
|
||||||
|
echo $GCLOUD_SERVICE_KEY | gcloud auth activate-service-account --key-file=-
|
||||||
|
gcloud --quiet config set project ${GOOGLE_PROJECT_ID}
|
||||||
|
gcloud --quiet config set compute/zone ${GOOGLE_COMPUTE_ZONE}
|
||||||
|
|
||||||
|
gsutil -m rm gs://$GOOGLE_STORAGE_BUCKET/**
|
||||||
|
gsutil -m rsync -R build gs://$GOOGLE_STORAGE_BUCKET
|
||||||
|
#fi
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
|
|
||||||
PR_CHECKS:
|
# PULL REQUESTS
|
||||||
|
pull_requests:
|
||||||
jobs:
|
jobs:
|
||||||
- UNIT_TESTS:
|
- PR_UNIT_TESTS:
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
ignore:
|
ignore:
|
||||||
- master
|
- master
|
||||||
- feature/*
|
- feature/*
|
||||||
- hotfix/*
|
- hotfix/*
|
||||||
# E2E: PWA
|
|
||||||
- cypress/run:
|
|
||||||
name: 'E2E: PWA'
|
|
||||||
executor: chrome-and-pacs
|
|
||||||
browser: chrome
|
|
||||||
pre-steps:
|
|
||||||
- run: |
|
|
||||||
# Clear yarn cache; update to latest
|
|
||||||
rm -rf ~/.yarn
|
|
||||||
npm i -g yarn
|
|
||||||
yarn -v
|
|
||||||
yarn: true
|
|
||||||
record: true
|
|
||||||
store_artifacts: true
|
|
||||||
working_directory: platform/viewer
|
|
||||||
build:
|
|
||||||
git submodule update --init && npx cross-env QUICK_BUILD=true
|
|
||||||
APP_CONFIG=config/e2e.js yarn run build
|
|
||||||
start: yarn run test:e2e:serve
|
|
||||||
spec: 'cypress/integration/common/**/*,cypress/integration/pwa/**/*'
|
|
||||||
wait-on: 'http://localhost:3000'
|
|
||||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
|
||||||
no-workspace: true # Don't persist workspace
|
|
||||||
post-steps:
|
|
||||||
- store_artifacts:
|
|
||||||
path: platform/viewer/cypress/screenshots
|
|
||||||
- store_artifacts:
|
|
||||||
path: platform/viewer/cypress/videos
|
|
||||||
- store_test_results:
|
|
||||||
path: platform/viewer/cypress/results
|
|
||||||
requires:
|
|
||||||
- UNIT_TESTS
|
|
||||||
# E2E: script-tag
|
|
||||||
- cypress/run:
|
|
||||||
name: 'E2E: Script Tag'
|
|
||||||
executor: chrome-and-pacs
|
|
||||||
browser: chrome
|
|
||||||
pre-steps:
|
|
||||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v' # Use yarn latest
|
|
||||||
yarn: true
|
|
||||||
record: true
|
|
||||||
store_artifacts: true
|
|
||||||
working_directory: platform/viewer
|
|
||||||
build:
|
|
||||||
git submodule update --init && npx cross-env QUICK_BUILD=true
|
|
||||||
APP_CONFIG=config/e2e.js yarn run build:package
|
|
||||||
start: yarn run test:e2e:serve
|
|
||||||
spec: 'cypress/integration/common/**/*,cypress/integration/script-tag/**/*'
|
|
||||||
wait-on: 'http://localhost:3000'
|
|
||||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
|
||||||
no-workspace: true # Don't persist workspace
|
|
||||||
post-steps:
|
|
||||||
- store_artifacts:
|
|
||||||
path: platform/viewer/cypress/screenshots
|
|
||||||
- store_artifacts:
|
|
||||||
path: platform/viewer/cypress/videos
|
|
||||||
- store_test_results:
|
|
||||||
path: platform/viewer/cypress/results
|
|
||||||
requires:
|
|
||||||
- UNIT_TESTS
|
|
||||||
|
|
||||||
PR_OPTIONAL_VISUAL_TESTS:
|
# MERGE TO MASTER
|
||||||
|
cut_release:
|
||||||
jobs:
|
jobs:
|
||||||
- AWAIT_APPROVAL:
|
- MERGE_UNIT_TESTS:
|
||||||
type: approval
|
filters:
|
||||||
|
branches:
|
||||||
|
only: master
|
||||||
|
- e2e_test:
|
||||||
|
requires:
|
||||||
|
- MERGE_UNIT_TESTS
|
||||||
|
# Update NPM
|
||||||
|
- npm_publish:
|
||||||
|
requires:
|
||||||
|
- e2e_test
|
||||||
|
# Update docs.ohif.org
|
||||||
|
- docs_publish:
|
||||||
|
requires:
|
||||||
|
- e2e_test
|
||||||
# Update hub.docker.org
|
# Update hub.docker.org
|
||||||
- cypress/run:
|
- docker_publish:
|
||||||
name: 'Generate Percy Snapshots'
|
|
||||||
executor: chrome-and-pacs
|
|
||||||
browser: chrome
|
|
||||||
pre-steps:
|
|
||||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
|
||||||
add wait-on' # Use yarn latest
|
|
||||||
yarn: true
|
|
||||||
store_artifacts: false
|
|
||||||
working_directory: platform/viewer
|
|
||||||
build:
|
|
||||||
git submodule update --init && npx cross-env QUICK_BUILD=true
|
|
||||||
APP_CONFIG=config/e2e.js yarn run build
|
|
||||||
# start server --> verify running --> percy + chrome + cypress
|
|
||||||
command: yarn run test:e2e:dist
|
|
||||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
|
||||||
no-workspace: true # Don't persist workspace
|
|
||||||
post-steps:
|
|
||||||
- store_artifacts:
|
|
||||||
path: platform/viewer/cypress/screenshots
|
|
||||||
- store_artifacts:
|
|
||||||
path: platform/viewer/cypress/videos
|
|
||||||
requires:
|
requires:
|
||||||
- AWAIT_APPROVAL
|
- npm_publish
|
||||||
|
# Update viewer.ohif.org
|
||||||
PR_OPTIONAL_DOCKER_PUBLISH:
|
- build_demo_site:
|
||||||
jobs:
|
|
||||||
# https://circleci.com/docs/2.0/workflows/#holding-a-workflow-for-a-manual-approval
|
|
||||||
- AWAIT_APPROVAL:
|
|
||||||
type: approval
|
|
||||||
# Update hub.docker.org
|
|
||||||
- DOCKER_PR_PUBLISH:
|
|
||||||
context: Docker Hub
|
|
||||||
requires:
|
requires:
|
||||||
- AWAIT_APPROVAL
|
- e2e_test
|
||||||
|
- demo_site_publish:
|
||||||
###
|
|
||||||
# Our workflow for building, deploying, and promoting builds across our
|
|
||||||
# development, staging, and production environments.
|
|
||||||
###
|
|
||||||
DEPLOY:
|
|
||||||
jobs:
|
|
||||||
- BUILD:
|
|
||||||
filters:
|
|
||||||
branches:
|
|
||||||
only: master
|
|
||||||
- DEPLOY_TO_DEV:
|
|
||||||
requires:
|
requires:
|
||||||
- BUILD
|
- build_demo_site
|
||||||
- PROMOTE_TO_STAGING:
|
|
||||||
type: approval
|
|
||||||
requires:
|
|
||||||
- DEPLOY_TO_DEV
|
|
||||||
- DEPLOY_TO_STAGING:
|
|
||||||
requires:
|
|
||||||
- PROMOTE_TO_STAGING
|
|
||||||
- PROMOTE_TO_PRODUCTION:
|
|
||||||
type: approval
|
|
||||||
requires:
|
|
||||||
- DEPLOY_TO_STAGING
|
|
||||||
- DEPLOY_TO_PRODUCTION:
|
|
||||||
requires:
|
|
||||||
- PROMOTE_TO_PRODUCTION
|
|
||||||
###
|
|
||||||
# Unit and E2E tests have already run for PR_CHECKS
|
|
||||||
# Re-running should not gain us any confidence here
|
|
||||||
###
|
|
||||||
RELEASE:
|
|
||||||
jobs:
|
|
||||||
- NPM_PUBLISH:
|
|
||||||
filters:
|
|
||||||
branches:
|
|
||||||
only: master
|
|
||||||
- DOCS_PUBLISH:
|
|
||||||
filters:
|
|
||||||
branches:
|
|
||||||
only: master
|
|
||||||
# Update base branch snapshots
|
|
||||||
# and record a Cypress dashboard test run
|
|
||||||
- cypress/run:
|
|
||||||
name: 'Generate Percy Snapshots'
|
|
||||||
executor: chrome-and-pacs
|
|
||||||
browser: chrome
|
|
||||||
pre-steps:
|
|
||||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
|
||||||
add wait-on' # Use yarn latest
|
|
||||||
yarn: true
|
|
||||||
store_artifacts: false
|
|
||||||
working_directory: platform/viewer
|
|
||||||
build:
|
|
||||||
git submodule update --init && npx cross-env QUICK_BUILD=true
|
|
||||||
APP_CONFIG=config/e2e.js yarn run build
|
|
||||||
# start server --> verify running --> percy + chrome + cypress
|
|
||||||
command: yarn run test:e2e:dist
|
|
||||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
|
||||||
no-workspace: true # Don't persist workspace
|
|
||||||
post-steps:
|
|
||||||
- store_artifacts:
|
|
||||||
path: platform/viewer/cypress/screenshots
|
|
||||||
- store_artifacts:
|
|
||||||
path: platform/viewer/cypress/videos
|
|
||||||
- store_test_results:
|
|
||||||
path: platform/viewer/cypress/results
|
|
||||||
filters:
|
|
||||||
branches:
|
|
||||||
only: master
|
|
||||||
- DOCKER_MASTER_PUBLISH:
|
|
||||||
requires:
|
|
||||||
- NPM_PUBLISH
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<script>
|
||||||
|
// Note: This is a client access token so there is no issue with having it publicly available
|
||||||
|
var _rollbarConfig = {
|
||||||
|
accessToken: "739c64cd39e84592ba062bc29aac9769",
|
||||||
|
captureUncaught: true,
|
||||||
|
captureUnhandledRejections: true,
|
||||||
|
payload: {
|
||||||
|
environment: "viewer.ohif.org"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Rollbar Snippet
|
||||||
|
!function(r){var e={};function o(n){if(e[n])return e[n].exports;var t=e[n]={i:n,l:!1,exports:{}};return r[n].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.m=r,o.c=e,o.d=function(r,e,n){o.o(r,e)||Object.defineProperty(r,e,{enumerable:!0,get:n})},o.r=function(r){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(r,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(r,"__esModule",{value:!0})},o.t=function(r,e){if(1&e&&(r=o(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var t in r)o.d(n,t,function(e){return r[e]}.bind(null,t));return n},o.n=function(r){var e=r&&r.__esModule?function(){return r.default}:function(){return r};return o.d(e,"a",e),e},o.o=function(r,e){return Object.prototype.hasOwnProperty.call(r,e)},o.p="",o(o.s=0)}([function(r,e,o){var n=o(1),t=o(4);_rollbarConfig=_rollbarConfig||{},_rollbarConfig.rollbarJsUrl=_rollbarConfig.rollbarJsUrl||"https://cdnjs.cloudflare.com/ajax/libs/rollbar.js/2.7.1/rollbar.min.js",_rollbarConfig.async=void 0===_rollbarConfig.async||_rollbarConfig.async;var a=n.setupShim(window,_rollbarConfig),l=t(_rollbarConfig);window.rollbar=n.Rollbar,a.loadFull(window,document,!_rollbarConfig.async,_rollbarConfig,l)},function(r,e,o){var n=o(2);function t(r){return function(){try{return r.apply(this,arguments)}catch(r){try{console.error("[Rollbar]: Internal error",r)}catch(r){}}}}var a=0;function l(r,e){this.options=r,this._rollbarOldOnError=null;var o=a++;this.shimId=function(){return o},"undefined"!=typeof window&&window._rollbarShims&&(window._rollbarShims[o]={handler:e,messages:[]})}var i=o(3),d=function(r,e){return new l(r,e)},c=function(r){return new i(d,r)};function s(r){return t(function(){var e=Array.prototype.slice.call(arguments,0),o={shim:this,method:r,args:e,ts:new Date};window._rollbarShims[this.shimId()].messages.push(o)})}l.prototype.loadFull=function(r,e,o,n,a){var l=!1,i=e.createElement("script"),d=e.getElementsByTagName("script")[0],c=d.parentNode;i.crossOrigin="",i.src=n.rollbarJsUrl,o||(i.async=!0),i.onload=i.onreadystatechange=t(function(){if(!(l||this.readyState&&"loaded"!==this.readyState&&"complete"!==this.readyState)){i.onload=i.onreadystatechange=null;try{c.removeChild(i)}catch(r){}l=!0,function(){var e;if(void 0===r._rollbarDidLoad){e=new Error("rollbar.js did not load");for(var o,n,t,l,i=0;o=r._rollbarShims[i++];)for(o=o.messages||[];n=o.shift();)for(t=n.args||[],i=0;i<t.length;++i)if("function"==typeof(l=t[i])){l(e);break}}"function"==typeof a&&a(e)}()}}),c.insertBefore(i,d)},l.prototype.wrap=function(r,e,o){try{var n;if(n="function"==typeof e?e:function(){return e||{}},"function"!=typeof r)return r;if(r._isWrap)return r;if(!r._rollbar_wrapped&&(r._rollbar_wrapped=function(){o&&"function"==typeof o&&o.apply(this,arguments);try{return r.apply(this,arguments)}catch(o){var e=o;throw e&&("string"==typeof e&&(e=new String(e)),e._rollbarContext=n()||{},e._rollbarContext._wrappedSource=r.toString(),window._rollbarWrappedError=e),e}},r._rollbar_wrapped._isWrap=!0,r.hasOwnProperty))for(var t in r)r.hasOwnProperty(t)&&(r._rollbar_wrapped[t]=r[t]);return r._rollbar_wrapped}catch(e){return r}};for(var p="log,debug,info,warn,warning,error,critical,global,configure,handleUncaughtException,handleUnhandledRejection,captureEvent,captureDomContentLoaded,captureLoad".split(","),u=0;u<p.length;++u)l.prototype[p[u]]=s(p[u]);r.exports={setupShim:function(r,e){if(r){var o=e.globalAlias||"Rollbar";if("object"==typeof r[o])return r[o];r._rollbarShims={},r._rollbarWrappedError=null;var a=new c(e);return t(function(){e.captureUncaught&&(a._rollbarOldOnError=r.onerror,n.captureUncaughtExceptions(r,a,!0),n.wrapGlobals(r,a,!0)),e.captureUnhandledRejections&&n.captureUnhandledRejections(r,a,!0);var t=e.autoInstrument;return!1!==e.enabled&&(void 0===t||!0===t||"object"==typeof t&&t.network)&&r.addEventListener&&(r.addEventListener("load",a.captureLoad.bind(a)),r.addEventListener("DOMContentLoaded",a.captureDomContentLoaded.bind(a))),r[o]=a,a})()}},Rollbar:c}},function(r,e){function o(r,e,o){if(e.hasOwnProperty&&e.hasOwnProperty("addEventListener")){for(var n=e.addEventListener;n._rollbarOldAdd&&n.belongsToShim;)n=n._rollbarOldAdd;var t=function(e,o,t){n.call(this,e,r.wrap(o),t)};t._rollbarOldAdd=n,t.belongsToShim=o,e.addEventListener=t;for(var a=e.removeEventListener;a._rollbarOldRemove&&a.belongsToShim;)a=a._rollbarOldRemove;var l=function(r,e,o){a.call(this,r,e&&e._rollbar_wrapped||e,o)};l._rollbarOldRemove=a,l.belongsToShim=o,e.removeEventListener=l}}r.exports={captureUncaughtExceptions:function(r,e,o){if(r){var n;if("function"==typeof e._rollbarOldOnError)n=e._rollbarOldOnError;else if(r.onerror){for(n=r.onerror;n._rollbarOldOnError;)n=n._rollbarOldOnError;e._rollbarOldOnError=n}var t=function(){var o=Array.prototype.slice.call(arguments,0);!function(r,e,o,n){r._rollbarWrappedError&&(n[4]||(n[4]=r._rollbarWLine truncated
|
||||||
|
// End Rollbar Snippet
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
@@ -8,13 +8,11 @@ coverage:
|
|||||||
status:
|
status:
|
||||||
project:
|
project:
|
||||||
default:
|
default:
|
||||||
threshold: 0.5%
|
threshold: 0.10%
|
||||||
core:
|
core:
|
||||||
flags: core
|
flags: core
|
||||||
threshold: 0.5%
|
|
||||||
viewer:
|
viewer:
|
||||||
flags: viewer
|
flags: viewer
|
||||||
threshold: 0.5%
|
|
||||||
patch: off
|
patch: off
|
||||||
flags:
|
flags:
|
||||||
core:
|
core:
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
if [ -n "$CLIENT_ID" ] || [ -n "$HEALTHCARE_API_ENDPOINT" ]
|
|
||||||
then
|
|
||||||
# If CLIENT_ID is specified, use the google.js configuration with the modified ID
|
|
||||||
if [ -n "$CLIENT_ID" ]
|
|
||||||
then
|
|
||||||
echo "Google Cloud Healthcare \$CLIENT_ID has been provided: "
|
|
||||||
echo "$CLIENT_ID"
|
|
||||||
echo "Updating config..."
|
|
||||||
|
|
||||||
# - Use SED to replace the CLIENT_ID that is currently in google.js
|
|
||||||
sed -i -e "s/YOURCLIENTID.apps.googleusercontent.com/$CLIENT_ID/g" /usr/share/nginx/html/google.js
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If HEALTHCARE_API_ENDPOINT is specified, use the google.js configuration with the modified endpoint
|
|
||||||
if [ -n "$HEALTHCARE_API_ENDPOINT" ]
|
|
||||||
then
|
|
||||||
echo "Google Cloud Healthcare \$HEALTHCARE_API_ENDPOINT has been provided: "
|
|
||||||
echo "$HEALTHCARE_API_ENDPOINT"
|
|
||||||
echo "Updating config..."
|
|
||||||
|
|
||||||
# - Use SED to replace the HEALTHCARE_API_ENDPOINT that is currently in google.js
|
|
||||||
sed -i -e "s+https://healthcare.googleapis.com/v1beta1+$HEALTHCARE_API_ENDPOINT+g" /usr/share/nginx/html/google.js
|
|
||||||
fi
|
|
||||||
|
|
||||||
# - Copy google.js to overwrite app-config.js
|
|
||||||
cp /usr/share/nginx/html/google.js /usr/share/nginx/html/app-config.js
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "${PORT}" ]
|
|
||||||
then
|
|
||||||
echo "Changing port to ${PORT}..."
|
|
||||||
sed -i -e "s/listen 80/listen ${PORT}/g" /etc/nginx/conf.d/default.conf
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Starting Nginx to serve the OHIF Viewer..."
|
|
||||||
|
|
||||||
exec "$@"
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
# Reduces size of context and hides
|
|
||||||
# files from Docker (can't COPY or ADD these)
|
|
||||||
|
|
||||||
# Output
|
|
||||||
dist/
|
|
||||||
build/
|
|
||||||
|
|
||||||
# Dependencies
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# Root
|
|
||||||
README.md
|
|
||||||
Dockerfile
|
|
||||||
dockerfile
|
|
||||||
|
|
||||||
# Misc. Config
|
|
||||||
.git
|
|
||||||
.DS_Store
|
|
||||||
.gitignore
|
|
||||||
.vscode
|
|
||||||
.circleci
|
|
||||||
|
|
||||||
# Unnecessary things to pull into container
|
|
||||||
.circleci/
|
|
||||||
.github/
|
|
||||||
.netlify/
|
|
||||||
.scripts/
|
|
||||||
.vscode/
|
|
||||||
coverage/
|
|
||||||
docs/
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
PERCY_TOKEN=<your token here>
|
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"cy": true,
|
"cy": true,
|
||||||
"before": true,
|
|
||||||
"context": true,
|
"context": true,
|
||||||
"Cypress": true,
|
"Cypress": true,
|
||||||
"assert": true
|
"assert": true
|
||||||
@@ -1 +0,0 @@
|
|||||||
custom: https://giving.massgeneral.org/ohif
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
---
|
|
||||||
name: "\U0001F41B Bug report"
|
|
||||||
about: Create a report to help us improve
|
|
||||||
title: ''
|
|
||||||
labels: 'Community: Report :bug:, Awaiting Reproduction, Triage :white_flag:'
|
|
||||||
assignees: ''
|
|
||||||
---
|
|
||||||
|
|
||||||
> **Before Creating an issue**
|
|
||||||
>
|
|
||||||
> - Are you running the latest version?
|
|
||||||
> - Are you reporting to the correct repository?
|
|
||||||
> - Did you search existing issues?
|
|
||||||
|
|
||||||
## Bug Report
|
|
||||||
|
|
||||||
### Describe the Bug
|
|
||||||
|
|
||||||
_A clear and concise description of what the bug is._
|
|
||||||
|
|
||||||
### What steps can we follow to reproduce the bug?
|
|
||||||
|
|
||||||
1. First step
|
|
||||||
2. Second step
|
|
||||||
3. ...
|
|
||||||
|
|
||||||
```js
|
|
||||||
Please use code blocks to show formatted errors or code snippets
|
|
||||||
```
|
|
||||||
|
|
||||||
> :warning: Reports we cannot reproduce are at risk of being marked stale and
|
|
||||||
> closed. The more information you can provide, the more likely we are to look
|
|
||||||
> into and address your issue.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
name: "\U0001F680 Feature request"
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: ''
|
|
||||||
labels: 'Community: Request :hand:, Triage :white_flag:'
|
|
||||||
assignees: ''
|
|
||||||
---
|
|
||||||
|
|
||||||
> :hand: Many people requests features. Tell us why yours is important to the
|
|
||||||
> community. How does it add value? Why _this feature_?
|
|
||||||
>
|
|
||||||
> Is your request very specific to your needs? Consider
|
|
||||||
> [contributing it](https://docs.ohif.org/contributing.html) yourself! Or reach
|
|
||||||
> out to a community member that offers
|
|
||||||
> [consulting services](https://docs.ohif.org/help.html#paid--commercial).
|
|
||||||
|
|
||||||
## Request
|
|
||||||
|
|
||||||
**What feature or change would you like to see made?**
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
**Why should we prioritize this feature?**
|
|
||||||
|
|
||||||
...
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: 🤗 Support Question
|
|
||||||
url: https://community.ohif.org/
|
|
||||||
about: Please use our forum if you have questions or need help.
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
### PR Checklist
|
|
||||||
|
|
||||||
- [ ] Brief description of changes
|
|
||||||
- [ ] Links to any relevant issues
|
|
||||||
- [ ] Required status checks are passing
|
|
||||||
- [ ] User cases if changes impact the user's experience
|
|
||||||
- [ ] `@mention` a maintainer to request a review
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Links
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
[blog]: https://circleci.com/blog/triggering-trusted-ci-jobs-on-untrusted-forks/
|
|
||||||
[script]: https://github.com/jklukas/git-push-fork-to-upstream-branch
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
# GitHub App: Stale
|
|
||||||
# https://github.com/apps/stale
|
|
||||||
#
|
|
||||||
# Number of days of inactivity before an issue becomes stale
|
|
||||||
daysUntilStale: 180
|
|
||||||
# Number of days of inactivity before a stale issue is closed
|
|
||||||
daysUntilClose: 9000
|
|
||||||
# Issues with these labels will never be considered stale
|
|
||||||
exemptLabels:
|
|
||||||
- 'Story :raised_hands:'
|
|
||||||
- 'Bug: Verified :bug:'
|
|
||||||
- 'Task: CI/Tooling :robot:'
|
|
||||||
- 'Task: Docs 📖'
|
|
||||||
- 'Task: Docs :book:'
|
|
||||||
- 'Task: Refactor :hammer_and_wrench:'
|
|
||||||
- 'Task: Tests :microscope:'
|
|
||||||
- 'PR: Awaiting Review 👀'
|
|
||||||
- 'Triage :white_flag:'
|
|
||||||
- 'Extension: Discussion'
|
|
||||||
- 'Announcement 🎉'
|
|
||||||
- 'IDC:priority'
|
|
||||||
- 'IDC:candidate'
|
|
||||||
- 'IDC:collaboration'
|
|
||||||
# Label to use when marking an issue as stale
|
|
||||||
staleLabel: 'Stale :baguette_bread:'
|
|
||||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
|
||||||
markComment: >
|
|
||||||
This issue has been automatically marked as stale because it has not had
|
|
||||||
recent activity. It will be closed if no further activity occurs. Thank you
|
|
||||||
for your contributions.
|
|
||||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
|
||||||
closeComment: false
|
|
||||||
@@ -8,10 +8,6 @@ docs/_book
|
|||||||
src/version.js
|
src/version.js
|
||||||
junit.xml
|
junit.xml
|
||||||
coverage/
|
coverage/
|
||||||
*.js.map
|
|
||||||
.yarn
|
|
||||||
platform/docs/.docusaurus
|
|
||||||
platform/ui/storybook-static
|
|
||||||
|
|
||||||
# YALC (for Erik)
|
# YALC (for Erik)
|
||||||
.yalc
|
.yalc
|
||||||
@@ -24,7 +20,6 @@ npm-debug.log
|
|||||||
package-lock.json
|
package-lock.json
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.env
|
|
||||||
|
|
||||||
# Common Example Data Directories
|
# Common Example Data Directories
|
||||||
sampledata/
|
sampledata/
|
||||||
@@ -32,8 +27,4 @@ example/deps/
|
|||||||
docker/dcm4che/dcm4che-arc
|
docker/dcm4che/dcm4che-arc
|
||||||
|
|
||||||
# Cypress test results
|
# Cypress test results
|
||||||
videos/
|
cypress/videos/
|
||||||
screenshots/
|
|
||||||
|
|
||||||
# Locize settings
|
|
||||||
.locize
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
[submodule "testdata"]
|
|
||||||
path = testdata
|
|
||||||
url = https://github.com/OHIF/viewer-testdata-dicomweb.git
|
|
||||||
@@ -3,29 +3,79 @@
|
|||||||
# Set directory to location of this script
|
# Set directory to location of this script
|
||||||
# https://stackoverflow.com/a/3355423/1867984
|
# https://stackoverflow.com/a/3355423/1867984
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
cd .. # Up to project root
|
|
||||||
|
|
||||||
# Helpful to verify which versions we're using
|
# Helpful to verify which versions we're using
|
||||||
yarn -v
|
yarn -v
|
||||||
node -v
|
node -v
|
||||||
|
|
||||||
|
# Install GitBook CLI
|
||||||
|
echo 'Installing Gitbook CLI'
|
||||||
|
yarn global add gitbook-cli
|
||||||
|
npm install -g gitbook-cli
|
||||||
|
|
||||||
|
echo 'Running Gitbook installation'
|
||||||
|
# Generate all version's GitBook output
|
||||||
|
# For each directory in /docs ...
|
||||||
|
cd ./../docs/
|
||||||
|
for D in *; do
|
||||||
|
if [ -d "${D}" ]; then
|
||||||
|
|
||||||
|
echo "Generating output for: ${D}"
|
||||||
|
cd "${D}"
|
||||||
|
|
||||||
|
# Clear previous output, generate new
|
||||||
|
rm -rf _book
|
||||||
|
node /opt/buildhome/.nvm/versions/node/v10.16.0/lib/node_modules/gitbook-cli/bin/gitbook.js install
|
||||||
|
node /opt/buildhome/.nvm/versions/node/v10.16.0/lib/node_modules/gitbook-cli/bin/gitbook.js build
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Move CNAME File into `latest`
|
||||||
|
cp CNAME ./latest/_book/CNAME
|
||||||
|
|
||||||
|
# Create a history folder in our latest version's output
|
||||||
|
mkdir ./latest/_book/history
|
||||||
|
|
||||||
|
# Move each version's files to latest's history folder
|
||||||
|
for D in *; do
|
||||||
|
# If it's a directory
|
||||||
|
if [ -d "${D}" ]; then
|
||||||
|
# If the directory name starts with `v` (v1, v2, etc.)
|
||||||
|
if [ "${D}" == v* ] ; then
|
||||||
|
|
||||||
|
echo "Moving ${D} to the latest version's history folder"
|
||||||
|
|
||||||
|
mkdir "./latest/_book/history/${D}"
|
||||||
|
cp -v -r "./${D}/_book"/* "./latest/_book/history/${D}"
|
||||||
|
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Build and copy the PWA Viewer into the demo directory
|
||||||
|
mkdir ./docs/latest/_book/demo/
|
||||||
|
|
||||||
# Install build deps and all monorepo package dependencies. Yarn Workspaces
|
# Install build deps and all monorepo package dependencies. Yarn Workspaces
|
||||||
# should also symlink all projects appropriately
|
# should also symlink all projects appropriately
|
||||||
yarn run lerna:restore
|
|
||||||
yarn install --no-ignore-optional --pure-lockfile
|
yarn install --no-ignore-optional --pure-lockfile
|
||||||
|
|
||||||
# Build && Move PWA Output
|
# Navigate to our Viewer project
|
||||||
yarn run build:ci
|
cd ./platform/viewer/
|
||||||
mkdir -p ./.netlify/www/pwa
|
|
||||||
mv platform/viewer/dist/* .netlify/www/pwa -v
|
|
||||||
|
|
||||||
# Build && Move script output
|
|
||||||
# yarn run build:package
|
|
||||||
|
|
||||||
# Cache all of the node_module dependencies in
|
|
||||||
# extensions, modules, and platform packages
|
|
||||||
yarn run lerna:cache
|
|
||||||
echo 'Nothing left to see here. Go home, folks.'
|
|
||||||
|
|
||||||
|
# Create a Versions File
|
||||||
|
# node -p -e '"export default "' + require(\"./../package.json\").version + '";"' > src/version.js
|
||||||
|
yarn run version
|
||||||
|
# Copy over wado-image-loader codecs and worker file
|
||||||
|
cp ./../../node_modules/cornerstone-wado-image-loader/dist/*.min.js* public -v
|
||||||
# Build using react-scripts
|
# Build using react-scripts
|
||||||
# npx cross-env PUBLIC_URL=/demo APP_CONFIG=config/netlify.js react-scripts --max_old_space_size=4096 build
|
# npx cross-env PUBLIC_URL=/demo REACT_APP_CONFIG=config/netlify.js react-scripts --max_old_space_size=4096 build
|
||||||
|
# Build using WebPack
|
||||||
|
# TODO: consume public/config correctly instead of hardcode
|
||||||
|
npx webpack --config config/webpack.prod.js --mode production --env.production
|
||||||
|
# Copy output to the folder that is our publish target
|
||||||
|
npx cpx "dist/**/*" ./../../docs/latest/_book/demo --verbose
|
||||||
|
|
||||||
|
echo 'Nothing left to see here. Go home, folks.'
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
# Specific to our non-deploy-preview deploys
|
|
||||||
# Confgure redirects using netlify.toml
|
|
||||||
|
|
||||||
# PWA Redirect
|
|
||||||
/* /index.html 200
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "root",
|
|
||||||
"private": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10",
|
|
||||||
"npm": ">=6",
|
|
||||||
"yarn": ">=1.16.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"deploy": "netlify deploy --prod --dir ./../platform/viewer/dist"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"netlify-cli": "^2.21.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Set directory to location of this script
|
||||||
|
# https://stackoverflow.com/a/3355423/1867984
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
echo 'PUBLISHING'
|
||||||
|
|
||||||
|
./node_modules/.bin/gh-pages \
|
||||||
|
--silent \
|
||||||
|
--repo https://$GITHUB_TOKEN@github.com/OHIF/Viewers.git \
|
||||||
|
--message 'Autogenerated Message: [ci skip]' \
|
||||||
|
--dist docs/latest/_book
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
# Specific to our deploy-preview
|
|
||||||
# Our docs are published using CircleCI + GitBook
|
|
||||||
# Confgure redirects using netlify.toml
|
|
||||||
|
|
||||||
# PWA Demo
|
|
||||||
/pwa/* /pwa/index.html 200
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>OHIF Viewer: Deploy Preview</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Index of Previews</h1>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a href="/pwa">Progressive Web App</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
14.18.0
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"trailingComma": "es5",
|
|
||||||
"printWidth": 80,
|
|
||||||
"proseWrap": "always",
|
|
||||||
"tabWidth": 2,
|
|
||||||
"semi": true,
|
|
||||||
"singleQuote": true
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# https://github.com/shelljs/shelljs
|
|
||||||
# https://github.com/shelljs/shelljs#exclude-options
|
|
||||||
PROJECT=$1
|
|
||||||
|
|
||||||
if [ -z "$PROJECT" ]
|
|
||||||
then
|
|
||||||
# Default
|
|
||||||
npx lerna run dev:viewer
|
|
||||||
else
|
|
||||||
eval "npx lerna run dev:$PROJECT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
read -p 'Press [Enter] key to continue...'
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "pwa-chrome",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Launch Chrome against localhost",
|
|
||||||
"url": "http://localhost:3000",
|
|
||||||
"webRoot": "${workspaceFolder}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
{
|
{
|
||||||
"editor.rulers": [80, 120],
|
"editor.rulers": [80, 120],
|
||||||
|
|
||||||
// ===
|
// ===
|
||||||
// Spacing
|
// Spacing
|
||||||
// ===
|
// ===
|
||||||
|
|
||||||
"editor.insertSpaces": true,
|
"editor.insertSpaces": true,
|
||||||
"editor.tabSize": 2,
|
"editor.tabSize": 2,
|
||||||
"editor.trimAutoWhitespace": true,
|
"editor.trimAutoWhitespace": true,
|
||||||
@@ -10,26 +12,19 @@
|
|||||||
"files.eol": "\n",
|
"files.eol": "\n",
|
||||||
"files.insertFinalNewline": true,
|
"files.insertFinalNewline": true,
|
||||||
"files.trimFinalNewlines": true,
|
"files.trimFinalNewlines": true,
|
||||||
|
|
||||||
// ===
|
// ===
|
||||||
// Event Triggers
|
// Event Triggers
|
||||||
// ===
|
// ===
|
||||||
|
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"eslint.autoFixOnSave": true,
|
"eslint.autoFixOnSave": true,
|
||||||
"eslint.run": "onSave",
|
"eslint.run": "onSave",
|
||||||
"eslint.validate": [
|
"eslint.validate": [
|
||||||
{
|
{ "language": "javascript", "autoFix": true },
|
||||||
"language": "javascript",
|
{ "language": "javascriptreact", "autoFix": true }
|
||||||
"autoFix": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"language": "javascriptreact",
|
|
||||||
"autoFix": true
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"prettier.disableLanguages": ["html"],
|
"prettier.disableLanguages": [],
|
||||||
"prettier.endOfLine": "lf",
|
"prettier.endOfLine": "lf",
|
||||||
"workbench.colorCustomizations": {},
|
"workbench.colorCustomizations": {}
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.fixAll.eslint": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
|
|
||||||
function excludeNodeModulesExcept(modules) {
|
|
||||||
var pathSep = path.sep;
|
|
||||||
if (pathSep == '\\')
|
|
||||||
// must be quoted for use in a regexp:
|
|
||||||
pathSep = '\\\\';
|
|
||||||
var moduleRegExps = modules.map(function(modName) {
|
|
||||||
return new RegExp('node_modules' + pathSep + modName);
|
|
||||||
});
|
|
||||||
|
|
||||||
return function(modulePath) {
|
|
||||||
if (/node_modules/.test(modulePath)) {
|
|
||||||
for (var i = 0; i < moduleRegExps.length; i++)
|
|
||||||
if (moduleRegExps[i].test(modulePath)) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = excludeNodeModulesExcept;
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
const autoprefixer = require('autoprefixer');
|
|
||||||
|
|
||||||
const cssToJavaScript = {
|
|
||||||
test: /\.css$/,
|
|
||||||
use: [
|
|
||||||
'style-loader',
|
|
||||||
{ loader: 'css-loader', options: { importLoaders: 1 } },
|
|
||||||
{
|
|
||||||
loader: 'postcss-loader',
|
|
||||||
options: {
|
|
||||||
plugins: () => [autoprefixer('last 2 version', 'ie >= 11')],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = cssToJavaScript;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
/**
|
|
||||||
* This is exclusively used by `vtk.js` to bundle glsl files.
|
|
||||||
*/
|
|
||||||
const loadShaders = {
|
|
||||||
test: /\.glsl$/i,
|
|
||||||
include: /vtk\.js[\/\\]Sources/,
|
|
||||||
loader: 'shader-loader',
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = loadShaders;
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* This allows us to include web workers in our bundle, and VTK.js
|
|
||||||
* web workers in our bundle. While this increases bundle size, it
|
|
||||||
* cuts down on the number of includes we need for `script tag` usage.
|
|
||||||
*/
|
|
||||||
const loadWebWorkers = {
|
|
||||||
test: /\.worker\.js$/,
|
|
||||||
include: /vtk\.js[\/\\]Sources/,
|
|
||||||
use: [
|
|
||||||
{
|
|
||||||
loader: 'worker-loader',
|
|
||||||
options: { inline: true, fallback: false },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = loadWebWorkers;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
const stylusToJavaScript = {
|
|
||||||
test: /\.styl$/,
|
|
||||||
use: [
|
|
||||||
{ loader: 'style-loader' }, // 3. Style nodes from JS Strings
|
|
||||||
{ loader: 'css-loader' }, // 2. CSS to CommonJS
|
|
||||||
{ loader: 'stylus-loader' }, // 1. Stylus to CSS
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = stylusToJavaScript;
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
const excludeNodeModulesExcept = require('./../helpers/excludeNodeModulesExcept.js');
|
|
||||||
|
|
||||||
function transpileJavaScript(mode) {
|
|
||||||
const exclude =
|
|
||||||
mode === 'production'
|
|
||||||
? excludeNodeModulesExcept([
|
|
||||||
'vtk.js',
|
|
||||||
// 'dicomweb-client',
|
|
||||||
// https://github.com/react-dnd/react-dnd/blob/master/babel.config.js
|
|
||||||
'react-dnd',
|
|
||||||
// https://github.com/dcmjs-org/dcmjs/blob/master/.babelrc
|
|
||||||
// https://github.com/react-dnd/react-dnd/issues/1342
|
|
||||||
// 'dcmjs', // contains: loglevelnext
|
|
||||||
// https://github.com/shellscape/loglevelnext#browser-support
|
|
||||||
// 'loglevelnext',
|
|
||||||
// https://github.com/dcmjs-org/dicom-microscopy-viewer/issues/35
|
|
||||||
// 'dicom-microscopy-viewer',
|
|
||||||
// https://github.com/openlayers/openlayers#supported-browsers
|
|
||||||
// 'ol', --> Should be fine
|
|
||||||
])
|
|
||||||
: excludeNodeModulesExcept([]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
test: /\.jsx?$/,
|
|
||||||
// These are packages that are not transpiled to our lowest supported
|
|
||||||
// JS version (currently ES5). Most of these leverage ES6+ features,
|
|
||||||
// that we need to transpile to a different syntax.
|
|
||||||
exclude,
|
|
||||||
loader: 'babel-loader',
|
|
||||||
options: {
|
|
||||||
// Find babel.config.js in monorepo root
|
|
||||||
// https://babeljs.io/docs/en/options#rootmode
|
|
||||||
rootMode: 'upward',
|
|
||||||
envName: mode,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = transpileJavaScript;
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
// ~~ ENV
|
|
||||||
const dotenv = require('dotenv');
|
|
||||||
//
|
|
||||||
const path = require('path');
|
|
||||||
const webpack = require('webpack');
|
|
||||||
// ~~ RULES
|
|
||||||
const loadShadersRule = require('./rules/loadShaders.js');
|
|
||||||
const loadWebWorkersRule = require('./rules/loadWebWorkers.js');
|
|
||||||
const transpileJavaScriptRule = require('./rules/transpileJavaScript.js');
|
|
||||||
// ~~ PLUGINS
|
|
||||||
const TerserJSPlugin = require('terser-webpack-plugin');
|
|
||||||
// ~~ ENV VARS
|
|
||||||
const NODE_ENV = process.env.NODE_ENV;
|
|
||||||
const QUICK_BUILD = process.env.QUICK_BUILD;
|
|
||||||
const BUILD_NUM = process.env.CIRCLE_BUILD_NUM || '0';
|
|
||||||
|
|
||||||
//
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
|
||||||
if (!process.env.NODE_ENV) {
|
|
||||||
throw new Error('process.env.NODE_ENV not set');
|
|
||||||
}
|
|
||||||
|
|
||||||
const mode = NODE_ENV === 'production' ? 'production' : 'development';
|
|
||||||
const isProdBuild = NODE_ENV === 'production';
|
|
||||||
const isQuickBuild = QUICK_BUILD === 'true';
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
mode: isProdBuild ? 'production' : 'development',
|
|
||||||
devtool: isProdBuild ? 'source-map' : 'eval-cheap-module-source-map',
|
|
||||||
entry: {
|
|
||||||
app: `${SRC_DIR}/index.js`,
|
|
||||||
},
|
|
||||||
optimization: {
|
|
||||||
minimize: isProdBuild,
|
|
||||||
sideEffects: true,
|
|
||||||
},
|
|
||||||
context: SRC_DIR,
|
|
||||||
stats: {
|
|
||||||
colors: true,
|
|
||||||
hash: true,
|
|
||||||
timings: true,
|
|
||||||
assets: true,
|
|
||||||
chunks: false,
|
|
||||||
chunkModules: false,
|
|
||||||
modules: false,
|
|
||||||
children: false,
|
|
||||||
warnings: true,
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
transpileJavaScriptRule(mode),
|
|
||||||
loadWebWorkersRule,
|
|
||||||
loadShadersRule,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
// Which directories to search when resolving modules
|
|
||||||
modules: [
|
|
||||||
// Modules specific to this package
|
|
||||||
path.resolve(__dirname, '../node_modules'),
|
|
||||||
// Hoisted Yarn Workspace Modules
|
|
||||||
path.resolve(__dirname, '../../../node_modules'),
|
|
||||||
SRC_DIR,
|
|
||||||
],
|
|
||||||
// Attempt to resolve these extensions in order.
|
|
||||||
extensions: ['.js', '.jsx', '.json', '*'],
|
|
||||||
// symlinked resources are resolved to their real path, not their symlinked location
|
|
||||||
symlinks: true,
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new webpack.DefinePlugin({
|
|
||||||
/* Application */
|
|
||||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
|
||||||
'process.env.DEBUG': JSON.stringify(process.env.DEBUG),
|
|
||||||
'process.env.APP_CONFIG': JSON.stringify(process.env.APP_CONFIG || ''),
|
|
||||||
'process.env.PUBLIC_URL': JSON.stringify(process.env.PUBLIC_URL || '/'),
|
|
||||||
'process.env.VERSION_NUMBER': webpack.DefinePlugin.runtimeValue(() => {
|
|
||||||
const package = require('../platform/viewer/package.json');
|
|
||||||
return JSON.stringify(package.version || '');
|
|
||||||
}, ['../platform/viewer/package.json']),
|
|
||||||
'process.env.BUILD_NUM': JSON.stringify(BUILD_NUM),
|
|
||||||
/* i18n */
|
|
||||||
'process.env.USE_LOCIZE': JSON.stringify(process.env.USE_LOCIZE || ''),
|
|
||||||
'process.env.LOCIZE_PROJECTID': JSON.stringify(process.env.LOCIZE_PROJECTID || ''),
|
|
||||||
'process.env.LOCIZE_API_KEY': JSON.stringify(process.env.LOCIZE_API_KEY || ''),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
// Fix: https://github.com/webpack-contrib/css-loader/issues/447#issuecomment-285598881
|
|
||||||
// For issue in cornerstone-wado-image-loader
|
|
||||||
node: {
|
|
||||||
fs: 'empty',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isProdBuild) {
|
|
||||||
config.optimization.minimizer = [
|
|
||||||
new TerserJSPlugin({
|
|
||||||
// Supports:
|
|
||||||
// source-map and inline-source-map
|
|
||||||
sourceMap: isProdBuild && !isQuickBuild,
|
|
||||||
parallel: true,
|
|
||||||
terserOptions: {},
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isQuickBuild) {
|
|
||||||
config.optimization.minimize = false;
|
|
||||||
config.devtool = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
const path = require("path");
|
||||||
|
const ExtractCssChunks = require("extract-css-chunks-webpack-plugin");
|
||||||
|
|
||||||
|
const SRC_DIR = path.join(__dirname, "../src");
|
||||||
|
const PUBLIC_DIR = path.join(__dirname, "../public");
|
||||||
|
const DIST_DIR = path.join(__dirname, "../dist");
|
||||||
|
|
||||||
|
module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
||||||
|
return {
|
||||||
|
entry: {
|
||||||
|
app: `${SRC_DIR}/index.js`
|
||||||
|
},
|
||||||
|
context: SRC_DIR,
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.jsx?$/,
|
||||||
|
exclude: [/node_modules/, /packages\\extension/],
|
||||||
|
loader: "babel-loader",
|
||||||
|
options: {
|
||||||
|
// Find babel.config.js in monorepo root
|
||||||
|
// https://babeljs.io/docs/en/options#rootmode
|
||||||
|
rootMode: "upward",
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
"@babel/preset-env",
|
||||||
|
{
|
||||||
|
// Do not transform ES6 modules to another format.
|
||||||
|
// Webpack will take care of that.
|
||||||
|
modules: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: [
|
||||||
|
"style-loader",
|
||||||
|
ExtractCssChunks.loader,
|
||||||
|
{ loader: "css-loader", options: { importLoaders: 1 } },
|
||||||
|
{
|
||||||
|
loader: "postcss-loader",
|
||||||
|
options: {
|
||||||
|
config: {
|
||||||
|
path: "./postcss.config.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* This allows us to include web workers in our bundle, and VTK.js
|
||||||
|
* web workers in our bundle. While this increases bundle size, it
|
||||||
|
* cuts down on the number of includes we need for `script tag` usage.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
test: /\.worker\.js$/,
|
||||||
|
include: /vtk\.js[\/\\]Sources/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: "worker-loader",
|
||||||
|
options: { inline: true, fallback: false }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* This is exclusively used by `vtk.js` to bundle glsl files.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
test: /\.glsl$/i,
|
||||||
|
include: /vtk\.js[\/\\]Sources/,
|
||||||
|
loader: "shader-loader"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
// Which directories to search when resolving modules
|
||||||
|
modules: [
|
||||||
|
path.resolve(__dirname, "../node_modules"),
|
||||||
|
path.resolve(__dirname, "../../../node_modules"),
|
||||||
|
SRC_DIR
|
||||||
|
],
|
||||||
|
// Attempt to resolve these extensions in order.
|
||||||
|
extensions: [".js", ".jsx", ".json", "*"],
|
||||||
|
// symlinked resources are resolved to their real path, not their symlinked location
|
||||||
|
symlinks: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
const merge = require('webpack-merge');
|
|
||||||
const webpackBase = require('./webpack.base.js');
|
|
||||||
const cssToJavaScriptRule = require('./rules/cssToJavaScript.js');
|
|
||||||
const stylusToJavaScriptRule = require('./rules/stylusToJavaScript.js');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WebPack configuration for CommonJS Bundles. Extends rules of BaseConfig by making
|
|
||||||
* sure we're bundling styles and other files that would normally be split in a
|
|
||||||
* PWA.
|
|
||||||
*/
|
|
||||||
module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
|
||||||
const baseConfig = webpackBase(env, argv, { SRC_DIR, DIST_DIR });
|
|
||||||
|
|
||||||
return merge(baseConfig, {
|
|
||||||
module: {
|
|
||||||
rules: [cssToJavaScriptRule, stylusToJavaScriptRule],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
cff-version: 1.2.0
|
|
||||||
message: "If you use this software, please cite it as below."
|
|
||||||
authors:
|
|
||||||
- family-names: "Ziegler"
|
|
||||||
given-names: "Erik"
|
|
||||||
orcid: "https://orcid.org/0000-0003-1857-8129"
|
|
||||||
- family-names: "Urban"
|
|
||||||
given-names: "Trinity"
|
|
||||||
- family-names: "Brown"
|
|
||||||
given-names: "Danny"
|
|
||||||
- family-names: "Petts"
|
|
||||||
given-names: "James"
|
|
||||||
- family-names: "Pieper"
|
|
||||||
given-names: "Steve D"
|
|
||||||
- family-names: "Lewis"
|
|
||||||
given-names: "Rob"
|
|
||||||
- family-names: "Hafey"
|
|
||||||
given-names: "Chris"
|
|
||||||
- family-names: "Harris"
|
|
||||||
given-names: "Gordon J"
|
|
||||||
title: "Open Health Imaging Foundation Viewer: An Extensible Open-Source Framework for Building Web-Based Imaging Applications to Support Cancer Research"
|
|
||||||
doi: 10.1200/CCI.19.00131
|
|
||||||
version: 1.0.0
|
|
||||||
url: "https://github.com/OHIF/Viewers"
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
In the interest of fostering an open and welcoming environment, we as
|
|
||||||
contributors and maintainers pledge to making participation in our project and
|
|
||||||
our community a harassment-free experience for everyone, regardless of age, body
|
|
||||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
|
||||||
level of experience, education, socio-economic status, nationality, personal
|
|
||||||
appearance, race, religion, or sexual identity and orientation.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment
|
|
||||||
include:
|
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
|
||||||
* Being respectful of differing viewpoints and experiences
|
|
||||||
* Gracefully accepting constructive criticism
|
|
||||||
* Focusing on what is best for the community
|
|
||||||
* Showing empathy towards other community members
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
|
||||||
advances
|
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or electronic
|
|
||||||
address, without explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
|
||||||
professional setting
|
|
||||||
|
|
||||||
## Our Responsibilities
|
|
||||||
|
|
||||||
Project maintainers are responsible for clarifying the standards of acceptable
|
|
||||||
behavior and are expected to take appropriate and fair corrective action in
|
|
||||||
response to any instances of unacceptable behavior.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or
|
|
||||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
||||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
|
||||||
permanently any contributor for other behaviors that they deem inappropriate,
|
|
||||||
threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies both within project spaces and in public spaces
|
|
||||||
when an individual is representing the project or its community. Examples of
|
|
||||||
representing a project or community include using an official project e-mail
|
|
||||||
address, posting via an official social media account, or acting as an appointed
|
|
||||||
representative at an online or offline event. Representation of a project may be
|
|
||||||
further defined and clarified by project maintainers.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
||||||
reported by contacting the project team at danny.ri.brown+OHIFcoc@gmail.com. All
|
|
||||||
complaints will be reviewed and investigated and will result in a response that
|
|
||||||
is deemed necessary and appropriate to the circumstances. The project team is
|
|
||||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
||||||
Further details of specific enforcement policies may be posted separately.
|
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
|
||||||
faith may face temporary or permanent repercussions as determined by other
|
|
||||||
members of the project's leadership.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
|
||||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
|
||||||
|
|
||||||
For answers to common questions about this code of conduct, see
|
|
||||||
https://www.contributor-covenant.org/faq
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
See our contributing guidelines at [`https://docs.ohif.org`](https://docs.ohif.org/development/contributing.html)
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2018 Open Health Imaging Foundation
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,185 +1,38 @@
|
|||||||
<!-- prettier-ignore-start -->
|
# OHIF Medical Imaging Platform
|
||||||
<!-- markdownlint-disable -->
|
|
||||||
<div align="center">
|
|
||||||
<h1>OHIF Medical Imaging Viewer</h1>
|
|
||||||
<p><strong>The OHIF Viewer</strong> is a zero-footprint medical image viewer provided by the <a href="http://ohif.org/">Open Health Imaging Foundation (OHIF)</a>. It is a configurable and extensible progressive web application with out-of-the-box support for image archives which support <a href="https://www.dicomstandard.org/dicomweb/">DICOMweb</a>.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<a href="https://docs.ohif.org/"><strong>Read The Docs</strong></a> |
|
|
||||||
<a href="https://github.com/OHIF/Viewers/tree/master/docs/latest">Edit the docs</a>
|
|
||||||
</div>
|
|
||||||
<div align="center">
|
|
||||||
<a href="https://viewer.ohif.org/">Live Demo</a> |
|
|
||||||
<a href="https://react.ohif.org/">Component Library</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
[![NPM version][npm-version-image]][npm-url]
|
|
||||||
[![NPM downloads][npm-downloads-image]][npm-url]
|
|
||||||
[![Pulls][docker-pulls-img]][docker-image-url]
|
|
||||||
[![MIT License][license-image]][license-url]
|
|
||||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2FOHIF%2FViewers?ref=badge_shield)
|
|
||||||
|
|
||||||
|
[![lerna][lerna-image]][lerna-url]
|
||||||
[![Netlify Status][netlify-image]][netlify-url]
|
[![Netlify Status][netlify-image]][netlify-url]
|
||||||
[![CircleCI][circleci-image]][circleci-url]
|
|
||||||
[![codecov][codecov-image]][codecov-url]
|
|
||||||
[![This project is using Percy.io for visual regression testing.][percy-image]](percy-url)
|
|
||||||
[](#contributors)
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
|
|
||||||
## About
|
## The Problem
|
||||||
|
|
||||||
The OHIF Medical Imaging Viewer is for viewing medical images. It can retrieve
|
...
|
||||||
and load images from most sources and formats; render sets in 2D, 3D, and
|
|
||||||
reconstructed representations; allows for the manipulation, annotation, and
|
|
||||||
serialization of observations; supports internationalization, OpenID Connect,
|
|
||||||
offline use, hotkeys, and many more features.
|
|
||||||
|
|
||||||
Almost everything offers some degree of customization and configuration. If it
|
|
||||||
doesn't support something you need, we accept pull requests and have an ever
|
|
||||||
improving Extension System.
|
|
||||||
|
|
||||||
## Why Choose Us
|
## Why Choose Us
|
||||||
|
|
||||||
### Community & Experience
|
...
|
||||||
|
|
||||||
The OHIF Viewer is a collaborative effort that has served as the basis for many
|
## Quick Start
|
||||||
active, production, and FDA Cleared medical imaging viewers. It benefits from
|
|
||||||
our extensive community's collective experience, and from the sponsored
|
|
||||||
contributions of individuals, research groups, and commercial organizations.
|
|
||||||
|
|
||||||
### Built to Adapt
|
...
|
||||||
|
|
||||||
After more than 5-years of integrating with many companies and organizations,
|
|
||||||
The OHIF Viewer has been rebuilt from the ground up to better address the
|
|
||||||
varying workflow and configuration needs of its many users. All of the Viewer's
|
|
||||||
core features are built using it's own extension system. The same extensibility
|
|
||||||
that allows us to offer:
|
|
||||||
|
|
||||||
- 2D and 3D medical image viewing
|
|
||||||
- Multiplanar Reconstruction (MPR)
|
|
||||||
- Maximum Intensity Project (MIP)
|
|
||||||
- Whole slide microscopy viewing
|
|
||||||
- PDF and Dicom Structured Report rendering
|
|
||||||
- User Access Control (UAC)
|
|
||||||
- Context specific toolbar and side panel content
|
|
||||||
- and many others
|
|
||||||
|
|
||||||
Can be leveraged by you to customize the viewer for your workflow, and to add
|
|
||||||
any new functionality you may need (and wish to maintain privately without
|
|
||||||
forking).
|
|
||||||
|
|
||||||
### Support
|
|
||||||
|
|
||||||
We offer support through
|
|
||||||
[GitHub Issues](https://github.com/OHIF/Viewers/issues/new/choose). You can:
|
|
||||||
|
|
||||||
- [Report a Bug 🐛](https://github.com/OHIF/Viewers/issues/new?assignees=&labels=Community%3A+Report+%3Abug%3A&template=---bug-report.md)
|
|
||||||
- [Request a Feature 🚀](https://github.com/OHIF/Viewers/issues/new?assignees=&labels=Community%3A+Request+%3Ahand%3A&template=---feature-request.md)
|
|
||||||
- [Ask a Question 🤗](https://github.com/OHIF/Viewers/issues/new?assignees=&labels=Community%3A+Question+%3Aquestion%3A&template=---support-question.md)
|
|
||||||
|
|
||||||
For commercial support, academic collaberations, and answers to common
|
|
||||||
questions; please read our
|
|
||||||
[documented FAQ](https://docs.ohif.org/faq/index.html#does-ohif-offer-commercial-support).
|
|
||||||
|
|
||||||
## Quick Start Deployment
|
|
||||||
|
|
||||||
> This is only one of many ways to configure and deploy the OHIF Viewer. To
|
|
||||||
> learn more about your options, and how to choose the best one for your
|
|
||||||
> requirements, check out
|
|
||||||
> [our deployment recipes and documentation](https://docs.ohif.org/deployment/).
|
|
||||||
|
|
||||||
The fastest and easiest way to get started is to include the OHIF Viewer with a
|
|
||||||
script tag. In practice, this is as simple as:
|
|
||||||
|
|
||||||
- Including the following dependencies with script tags:
|
|
||||||
- [React](https://unpkg.com/react@16/umd/react.production.min.js)
|
|
||||||
- [React Dom](https://unpkg.com/react-dom@16/umd/react-dom.production.min.js)
|
|
||||||
- The [OHIF Viewer](https://unpkg.com/@ohif/viewer)
|
|
||||||
- Have an element with an ID of `root` on the page
|
|
||||||
- Configure the OHIF Viewer at `window.config`:
|
|
||||||
|
|
||||||
```js
|
|
||||||
window.config = {
|
|
||||||
routerBasename: '/',
|
|
||||||
servers: {
|
|
||||||
dicomWeb: [
|
|
||||||
{
|
|
||||||
name: 'DCM4CHEE',
|
|
||||||
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
|
||||||
wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
|
||||||
qidoSupportsIncludeField: true,
|
|
||||||
imageRendering: 'wadors',
|
|
||||||
thumbnailRendering: 'wadors',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
- Install the viewer: `window.OHIFViewer.installViewer(window.config);`
|
|
||||||
|
|
||||||
This exact setup is demonstrated in this
|
|
||||||
[CodeSandbox](https://codesandbox.io/s/viewer-script-tag-tprch) and in our
|
|
||||||
[Embedding The Viewer](https://docs.ohif.org/deployment/recipes/embedded-viewer.html)
|
|
||||||
deployment recipe.
|
|
||||||
|
|
||||||
## Developing
|
|
||||||
|
|
||||||
### Requirements
|
|
||||||
|
|
||||||
- [Yarn 1.17.3+](https://yarnpkg.com/en/docs/install)
|
|
||||||
- [Node 10+](https://nodejs.org/en/)
|
|
||||||
- Yarn Workspaces should be enabled on your machine:
|
|
||||||
- `yarn config set workspaces-experimental true`
|
|
||||||
|
|
||||||
### Getting Started
|
|
||||||
|
|
||||||
1. [Fork this repository][how-to-fork]
|
|
||||||
2. [Clone your forked repository][how-to-clone]
|
|
||||||
- `git clone https://github.com/YOUR-USERNAME/Viewers.git`
|
|
||||||
3. Navigate to the cloned project's directory
|
|
||||||
4. Add this repo as a `remote` named `upstream`
|
|
||||||
- `git remote add upstream https://github.com/OHIF/Viewers.git`
|
|
||||||
5. `yarn install` to restore dependencies and link projects
|
|
||||||
|
|
||||||
#### To Develop
|
|
||||||
|
|
||||||
_From this repository's root directory:_
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Enable Yarn Workspaces
|
|
||||||
yarn config set workspaces-experimental true
|
|
||||||
|
|
||||||
# Restore dependencies
|
|
||||||
yarn install
|
|
||||||
```
|
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
These commands are available from the root directory. Each project directory
|
### Global
|
||||||
also supports a number of commands that can be found in their respective
|
|
||||||
`README.md` and `project.json` files.
|
|
||||||
|
|
||||||
| Yarn Commands | Description |
|
| Commands | | |
|
||||||
| ---------------------------- | ------------------------------------------------------------- |
|
| --------------- | --- | ----------------------------------------------------- |
|
||||||
| **Develop** | |
|
| `build:package` | | Builds commonjs bundles for all projects |
|
||||||
| `dev` or `start` | Default development experience for Viewer |
|
| `test:unit` | | Jest multi-project test runner; overall coverage |
|
||||||
| `dev:project <package-name>` | Replace with `core`, `ui`, `i18n`, `cornerstone`, `vtk`, etc. |
|
| `test:unit:ci` | | Runs tests in parallel. Reports coverage per project. |
|
||||||
| `test:unit` | Jest multi-project test runner; overall coverage |
|
|
||||||
| **Deploy** | |
|
|
||||||
| `build`\* | Builds production output for our PWA Viewer |
|
|
||||||
| `build:package`\* | Builds production `commonjs` output for our Viewer |
|
|
||||||
| `build:package-all`\* | Builds commonjs bundles for all projects |
|
|
||||||
|
|
||||||
\* - For more information on our different builds, check out our [Deploy
|
### Local
|
||||||
Docs][deployment-docs]
|
|
||||||
|
|
||||||
## Projects
|
| Commands | | |
|
||||||
|
| -------------- | --- | ------------------------------------------------- |
|
||||||
|
| `test:unit` | | Runs tests while watching for changes |
|
||||||
|
| `test:unit:ci` | | Runs tests, collects coverage, reports to codecov |
|
||||||
|
|
||||||
|
## Developing
|
||||||
|
|
||||||
The OHIF Medical Image Viewing Platform is maintained as a
|
The OHIF Medical Image Viewing Platform is maintained as a
|
||||||
[`monorepo`][monorepo]. This means that this repository, instead of containing a
|
[`monorepo`][monorepo]. This means that this repository, instead of containing a
|
||||||
@@ -209,85 +62,72 @@ you'll see the following:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Want to better understand why and how we've structured this repository? Read
|
Want to better understand why and how we've structured this repository? Read
|
||||||
more about it in our [Architecture Documentation][ohif-architecture].
|
more about it in our [Architecture Documentation](#todo).
|
||||||
|
|
||||||
### Platform
|
### Requirements
|
||||||
|
|
||||||
These projects comprise the
|
- [Yarn 1.17.3+](https://yarnpkg.com/en/docs/install)
|
||||||
|
- [Node 8+](https://nodejs.org/en/)
|
||||||
|
- Yarn Workspaces should be enabled on your machine:
|
||||||
|
- `yarn config set workspaces-experimental true`
|
||||||
|
|
||||||
| Name | Description | Links |
|
### Getting Started
|
||||||
| ------------------------------- | ---------------------------------------------------------------------------------------------------- | ----------------- |
|
|
||||||
| [@ohif/core][platform-core] | Business logic and classes that model the data, services, and extensions that are framework agnostic | [NPM][core-npm] |
|
|
||||||
| [@ohif/i18n][platform-i18n] | Language files and small API for wrapping component/ui text for translations | [NPM][i18n-npm] |
|
|
||||||
| [@ohif/viewer][platform-viewer] | The OHIF Viewer. Where we consume and configure all platform library's and extensions | [NPM][viewer-npm] |
|
|
||||||
| [@ohif/ui][platform-ui] | Reusable React components we consume and compose to build our Viewer's UI | [NPM][ui-npm] |
|
|
||||||
|
|
||||||
### Extensions
|
1. [Fork this repository][how-to-fork]
|
||||||
|
2. [Clone your forked repository][how-to-clone]
|
||||||
|
- `git clone https://github.com/YOUR-USERNAME/Viewers.git`
|
||||||
|
3. Navigate to the cloned project's directory
|
||||||
|
4. Add this repo as a `remote` named `upstream`
|
||||||
|
- `git remote add upstream https://github.com/OHIF/Viewers.git`
|
||||||
|
5. `yarn install` to restore dependencies and link projects
|
||||||
|
|
||||||
This is a list of Extensions maintained by the OHIF Core team. It's possible to
|
#### To Develop
|
||||||
customize and configure these extensions, and you can even create your own. You
|
|
||||||
can [read more about extensions here][ohif-extensions].
|
|
||||||
|
|
||||||
| Name | Description | Links |
|
```bash
|
||||||
| -------------------------------------------------------------- | ------------------------------------------------------- | ---------------------- |
|
# Force install
|
||||||
| [@ohif/extension-cornestone][extension-cornerstone] | 2D image viewing, annotation, and segementation tools | [NPM][cornerstone-npm] |
|
yarn install --force
|
||||||
| [@ohif/extension-dicom-html][extension-dicom-html] | Support for viewing DICOM SR as rendered HTML | [NPM][html-npm] |
|
|
||||||
| [@ohif/extension-dicom-microscopy][extension-dicom-microscopy] | Whole slide microscopy viewing | [NPM][microscopy-npm] |
|
|
||||||
| [@ohif/extension-dicom-pdf][extension-dicom-pdf] | View DICOM wrapped PDFs in a viewport | [NPM][pdf-npm] |
|
|
||||||
| [@ohif/extension-vtk][extension-vtk] | Volume rendering, reconstruction, and 3D visualizations | [NPM][vtk-npm] |
|
|
||||||
|
|
||||||
## Acknowledgments
|
# Link
|
||||||
|
# Redundent with yarn workspaces
|
||||||
|
npx lerna bootstrap
|
||||||
|
```
|
||||||
|
|
||||||
To acknowledge the OHIF Viewer in an academic publication, please cite
|
```bash
|
||||||
|
# Link for local dev
|
||||||
|
cd ./extensions/my-package
|
||||||
|
npx lerna add @ohif/my-package --scope=@ohif/my-package-consumer
|
||||||
|
```
|
||||||
|
|
||||||
> _LesionTracker: Extensible Open-Source Zero-Footprint Web Viewer for Cancer
|
```bash
|
||||||
> Imaging Research and Clinical Trials_
|
# Add shared dev dependency for workspace
|
||||||
>
|
yarn add --dev -W package-name
|
||||||
> Trinity Urban, Erik Ziegler, Rob Lewis, Chris Hafey, Cheryl Sadow, Annick D.
|
```
|
||||||
> Van den Abbeele and Gordon J. Harris
|
|
||||||
>
|
|
||||||
> _Cancer Research_, November 1 2017 (77) (21) e119-e122 DOI:
|
|
||||||
> [10.1158/0008-5472.CAN-17-0334](https://www.doi.org/10.1158/0008-5472.CAN-17-0334)
|
|
||||||
|
|
||||||
**Note:** If you use or find this repository helpful, please take the time to
|
// module vs main vs jsnext:main vs browser
|
||||||
star this repository on Github. This is an easy way for us to assess adoption
|
https://babeljs.io/blog/2018/06/26/on-consuming-and-publishing-es2015+-packages
|
||||||
and it can help us obtain future funding for the project.
|
|
||||||
|
|
||||||
This work is supported primarily by the National Institutes of Health, National
|
Webpack: https://webpack.js.org/configuration/resolve/
|
||||||
Cancer Institute, Informatics Technology for Cancer Research (ITCR) program,
|
|
||||||
under a
|
|
||||||
[grant to Dr. Gordon Harris at Massachusetts General Hospital (U24 CA199460)](https://projectreporter.nih.gov/project_info_description.cfm?aid=8971104).
|
|
||||||
|
|
||||||
## Projects that contributed to OHIF Viewer
|
UMD builds go through
|
||||||
|
|
||||||
The following is a (partial) list of projects that contributed resources towards
|
- index.umd.js
|
||||||
development of OHIF Viewer:
|
- Extensions passed in via window config
|
||||||
|
|
||||||
- [NCI Imaging Data Commons (IDC) project](https://imaging.datacommons.cancer.gov/)
|
PWA builds go through
|
||||||
supported development of new features and bug fixes marked with
|
|
||||||
["IDC:priority"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Apriority),
|
|
||||||
["IDC:candidate"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Acandidate)
|
|
||||||
or
|
|
||||||
["IDC:collaboration"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Acollaboration).
|
|
||||||
NCI Imaging Data Commons is supported by the contract number 19X037Q from
|
|
||||||
Leidos Biomedical Research under Task Order HHSN26100071 from NCI.
|
|
||||||
[IDC Viewer](https://learn.canceridc.dev/portal/visualization) is a customized
|
|
||||||
version of the OHIF Viewer.
|
|
||||||
|
|
||||||
### Research notice
|
- index.js
|
||||||
Please note that this repository is participating in a study into sustainability
|
- Extensions specified in file or by window config
|
||||||
of open source projects. Data will be gathered about this repository for
|
|
||||||
approximately the next 12 months, starting from June 2021.
|
|
||||||
|
|
||||||
Data collected will include number of contributors, number of PRs, time taken to
|
> The module property should point to a script that utilizes ES2015 module
|
||||||
close/merge these PRs, and issues closed.
|
> syntax but no other syntax features that aren't yet supported by browsers or
|
||||||
|
> node. This enables webpack to parse the module syntax itself, allowing for
|
||||||
|
> lighter bundles via tree shaking if users are only consuming certain parts of
|
||||||
|
> the library.
|
||||||
|
|
||||||
For more information, please visit
|
## Notes
|
||||||
[our informational page](https://sustainable-open-science-and-software.github.io/) or download our [participant information sheet](https://sustainable-open-science-and-software.github.io/assets/PIS_sustainable_software.pdf).
|
|
||||||
|
|
||||||
## License
|
Lerna requires `GH_TOKEN` for GitHub authentication token
|
||||||
|
`git remote update` 1st call, need to verify host key
|
||||||
MIT © [OHIF](https://github.com/OHIF)
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Links
|
Links
|
||||||
@@ -297,59 +137,12 @@ MIT © [OHIF](https://github.com/OHIF)
|
|||||||
<!-- Badges -->
|
<!-- Badges -->
|
||||||
[lerna-image]: https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg
|
[lerna-image]: https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg
|
||||||
[lerna-url]: https://lerna.js.org/
|
[lerna-url]: https://lerna.js.org/
|
||||||
[netlify-image]: https://api.netlify.com/api/v1/badges/32708787-c9b0-4634-b50f-7ca41952da77/deploy-status
|
[netlify-image]: https://api.netlify.com/api/v1/badges/a5d369ab-18a6-41c3-bcde-83805205ac7f/deploy-status
|
||||||
[netlify-url]: https://app.netlify.com/sites/ohif-dev/deploys
|
[netlify-url]: https://app.netlify.com/sites/ohif/deploys
|
||||||
[all-contributors-image]: https://img.shields.io/badge/all_contributors-0-orange.svg?style=flat-square
|
|
||||||
[circleci-image]: https://circleci.com/gh/OHIF/Viewers.svg?style=svg
|
|
||||||
[circleci-url]: https://circleci.com/gh/OHIF/Viewers
|
|
||||||
[codecov-image]: https://codecov.io/gh/OHIF/Viewers/branch/master/graph/badge.svg
|
|
||||||
[codecov-url]: https://codecov.io/gh/OHIF/Viewers/branch/master
|
|
||||||
[prettier-image]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square
|
|
||||||
[prettier-url]: https://github.com/prettier/prettier
|
|
||||||
[semantic-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
|
|
||||||
[semantic-url]: https://github.com/semantic-release/semantic-release
|
|
||||||
<!-- ROW -->
|
|
||||||
[npm-url]: https://npmjs.org/package/@ohif/viewer
|
|
||||||
[npm-downloads-image]: https://img.shields.io/npm/dm/@ohif/viewer.svg?style=flat-square
|
|
||||||
[npm-version-image]: https://img.shields.io/npm/v/@ohif/viewer.svg?style=flat-square
|
|
||||||
[docker-pulls-img]: https://img.shields.io/docker/pulls/ohif/viewer.svg?style=flat-square
|
|
||||||
[docker-image-url]: https://hub.docker.com/r/ohif/viewer
|
|
||||||
[license-image]: https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square
|
|
||||||
[license-url]: LICENSE
|
|
||||||
[percy-image]: https://percy.io/static/images/percy-badge.svg
|
|
||||||
[percy-url]: https://percy.io/Open-Health-Imaging-Foundation/OHIF-Viewer
|
|
||||||
<!-- Links -->
|
<!-- Links -->
|
||||||
[monorepo]: https://en.wikipedia.org/wiki/Monorepo
|
[monorepo]: https://en.wikipedia.org/wiki/Monorepo
|
||||||
[how-to-fork]: https://help.github.com/en/articles/fork-a-repo
|
[how-to-fork]: https://help.github.com/en/articles/fork-a-repo
|
||||||
[how-to-clone]: https://help.github.com/en/articles/fork-a-repo#step-2-create-a-local-clone-of-your-fork
|
[how-to-clone]: https://help.github.com/en/articles/fork-a-repo#step-2-create-a-local-clone-of-your-fork
|
||||||
[ohif-architecture]: https://docs.ohif.org/architecture/index.html
|
|
||||||
[ohif-extensions]: https://docs.ohif.org/architecture/index.html
|
|
||||||
[deployment-docs]: https://docs.ohif.org/deployment/
|
|
||||||
[react-url]: https://reactjs.org/
|
|
||||||
[pwa-url]: https://developers.google.com/web/progressive-web-apps/
|
|
||||||
[ohif-viewer-url]: https://www.npmjs.com/package/@ohif/viewer
|
|
||||||
[configuration-url]: https://docs.ohif.org/configuring/
|
|
||||||
[extensions-url]: https://docs.ohif.org/extensions/
|
|
||||||
<!-- Platform -->
|
|
||||||
[platform-core]: platform/core/README.md
|
|
||||||
[core-npm]: https://www.npmjs.com/package/@ohif/core
|
|
||||||
[platform-i18n]: platform/i18n/README.md
|
|
||||||
[i18n-npm]: https://www.npmjs.com/package/@ohif/i18n
|
|
||||||
[platform-ui]: platform/ui/README.md
|
|
||||||
[ui-npm]: https://www.npmjs.com/package/@ohif/ui
|
|
||||||
[platform-viewer]: platform/viewer/README.md
|
|
||||||
[viewer-npm]: https://www.npmjs.com/package/@ohif/viewer
|
|
||||||
<!-- Extensions -->
|
|
||||||
[extension-cornerstone]: extensions/cornerstone/README.md
|
|
||||||
[cornerstone-npm]: https://www.npmjs.com/package/@ohif/extension-cornerstone
|
|
||||||
[extension-dicom-html]: extensions/dicom-html/README.md
|
|
||||||
[html-npm]: https://www.npmjs.com/package/@ohif/extension-dicom-html
|
|
||||||
[extension-dicom-microscopy]: extensions/dicom-microscopy/README.md
|
|
||||||
[microscopy-npm]: https://www.npmjs.com/package/@ohif/extension-dicom-microscopy
|
|
||||||
[extension-dicom-pdf]: extensions/dicom-pdf/README.md
|
|
||||||
[pdf-npm]: https://www.npmjs.com/package/@ohif/extension-dicom-pdf
|
|
||||||
[extension-vtk]: extensions/vtk/README.md
|
|
||||||
[vtk-npm]: https://www.npmjs.com/package/@ohif/extension-vtk
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
|
|
||||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2FOHIF%2FViewers?ref=badge_large)
|
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
@@ -1,63 +1,50 @@
|
|||||||
const aliases = require('./aliases.config');
|
const aliases = require("./aliases.config");
|
||||||
const path = require('path');
|
const path = require("path");
|
||||||
|
|
||||||
// https://babeljs.io/docs/en/options#babelrcroots
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
babelrcRoots: ['./platform/*', './extensions/*'],
|
// https://babeljs.io/docs/en/options#babelrcroots
|
||||||
plugins: ['inline-react-svg', '@babel/plugin-proposal-class-properties'],
|
|
||||||
env: {
|
|
||||||
test: {
|
|
||||||
presets: [
|
presets: [
|
||||||
[
|
[
|
||||||
// TODO: https://babeljs.io/blog/2019/03/19/7.4.0#migration-from-core-js-2
|
"@babel/preset-env",
|
||||||
'@babel/preset-env',
|
|
||||||
{
|
{
|
||||||
modules: 'commonjs',
|
targets: {
|
||||||
debug: false,
|
ie: "11"
|
||||||
},
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
'@babel/preset-react',
|
"@babel/preset-react"
|
||||||
],
|
],
|
||||||
|
babelrcRoots: ["./platform/*", "./extensions/*"],
|
||||||
plugins: [
|
plugins: [
|
||||||
'@babel/plugin-proposal-object-rest-spread',
|
"inline-react-svg",
|
||||||
'@babel/plugin-syntax-dynamic-import',
|
"@babel/plugin-proposal-class-properties",
|
||||||
'@babel/plugin-transform-regenerator',
|
"@babel/plugin-proposal-object-rest-spread",
|
||||||
'@babel/plugin-transform-runtime',
|
"@babel/plugin-syntax-dynamic-import",
|
||||||
|
"@babel/plugin-transform-regenerator",
|
||||||
|
"@babel/plugin-transform-runtime",
|
||||||
|
[
|
||||||
|
"module-resolver",
|
||||||
|
{
|
||||||
|
// https://github.com/tleunen/babel-plugin-module-resolver/issues/338
|
||||||
|
// There seem to be a bug with module-resolver with a mono-repo setup:
|
||||||
|
// It doesn't resolve paths correctly when using root/alias combo, so we
|
||||||
|
// use this function instead.
|
||||||
|
resolvePath(sourcePath, currentFile, opts) {
|
||||||
|
// This will return undefined if aliases has no key for the sourcePath,
|
||||||
|
// in which case module-resolver will fallback on its default behaviour.
|
||||||
|
return aliases[sourcePath];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
],
|
],
|
||||||
|
env: {
|
||||||
|
debug: {
|
||||||
|
sourceMaps: "inline",
|
||||||
|
retainLines: true
|
||||||
},
|
},
|
||||||
production: {
|
build: {
|
||||||
presets: [
|
ignore: ["**/*.test.jsx", "**/*.test.js", "__snapshots__", "__tests__"]
|
||||||
// WebPack handles ES6 --> Target Syntax
|
}
|
||||||
['@babel/preset-env', { modules: false }],
|
}
|
||||||
'@babel/preset-react',
|
// ignore: ["node_modules"]
|
||||||
],
|
|
||||||
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
|
|
||||||
},
|
|
||||||
development: {
|
|
||||||
presets: [
|
|
||||||
// WebPack handles ES6 --> Target Syntax
|
|
||||||
['@babel/preset-env', { modules: false }],
|
|
||||||
'@babel/preset-react',
|
|
||||||
],
|
|
||||||
plugins: ['react-hot-loader/babel'],
|
|
||||||
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Plugins; Aliases
|
|
||||||
// We don't currently use aliases, but this is a nice snippet that would help
|
|
||||||
// [
|
|
||||||
// 'module-resolver',
|
|
||||||
// {
|
|
||||||
// // https://github.com/tleunen/babel-plugin-module-resolver/issues/338
|
|
||||||
// // There seem to be a bug with module-resolver with a mono-repo setup:
|
|
||||||
// // It doesn't resolve paths correctly when using root/alias combo, so we
|
|
||||||
// // use this function instead.
|
|
||||||
// resolvePath(sourcePath, currentFile, opts) {
|
|
||||||
// // This will return undefined if aliases has no key for the sourcePath,
|
|
||||||
// // in which case module-resolver will fallback on its default behaviour.
|
|
||||||
// return aliases[sourcePath];
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Set directory to location of this script
|
|
||||||
# https://stackoverflow.com/a/3355423/1867984
|
|
||||||
cd "$(dirname "$0")"
|
|
||||||
|
|
||||||
yarn -v
|
|
||||||
node -v
|
|
||||||
|
|
||||||
echo 'Installing Gitbook CLI'
|
|
||||||
yarn global add gitbook-cli
|
|
||||||
|
|
||||||
echo 'Running Gitbook installation'
|
|
||||||
|
|
||||||
# Generate all version's GitBook output
|
|
||||||
# For each directory in /docs ...
|
|
||||||
cd ./../docs/
|
|
||||||
for D in *; do
|
|
||||||
if [ -d "${D}" ]; then
|
|
||||||
|
|
||||||
echo "Generating output for: ${D}"
|
|
||||||
cd "${D}"
|
|
||||||
|
|
||||||
# Clear previous output, generate new
|
|
||||||
rm -rf _book
|
|
||||||
gitbook install
|
|
||||||
gitbook build
|
|
||||||
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Move CNAME File into `latest`
|
|
||||||
cp CNAME ./latest/_book/CNAME
|
|
||||||
|
|
||||||
# Create a history folder in our latest version's output
|
|
||||||
mkdir ./latest/_book/history
|
|
||||||
|
|
||||||
# Move each version's files to latest's history folder
|
|
||||||
for D in *; do
|
|
||||||
if [ -d "${D}" ]; then
|
|
||||||
if [ "${D}" == v* ] ; then
|
|
||||||
|
|
||||||
echo "Moving ${D} to the latest version's history folder"
|
|
||||||
|
|
||||||
mkdir "./latest/_book/history/${D}"
|
|
||||||
cp -v -r "./${D}/_book"/* "./latest/_book/history/${D}"
|
|
||||||
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Back to repo root
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
echo "Done generating documentation output"
|
|
||||||
echo 'PUBLISHING'
|
|
||||||
|
|
||||||
./node_modules/.bin/gh-pages \
|
|
||||||
--silent \
|
|
||||||
--repo https://$GITHUB_TOKEN@github.com/OHIF/Viewers.git \
|
|
||||||
--message 'Autogenerated Message: [ci skip]' \
|
|
||||||
--dist docs/latest/_book
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Netlify redirects
|
|
||||||
# SPA rules for our docs
|
|
||||||
/* /index.html 200
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class='row'>
|
<div class='row'>
|
||||||
<div class='column' style='text-align: right; padding: 0 20px'>
|
<div class='column' style='text-align: right; padding: 0 20px'>
|
||||||
<strong>Looking for a Live Demo?</strong>
|
<strong>Looking for a Deploy Preview?</strong>
|
||||||
<a href="http://viewer.ohif.org/">Preview The OHIF Viewer</a>
|
<a onclick="function redirect() { window.location.href='/demo/'; } redirect();">Deploy Preview for Viewer</a>
|
||||||
</div>
|
</div>
|
||||||
<div class='column' style='text-align: left; padding: 0 20px'>
|
<div class='column' style='text-align: left; padding: 0 20px'>
|
||||||
<a href="https://www.netlify.com">
|
<a href="https://www.netlify.com">
|
||||||
@@ -33,14 +33,14 @@ yourself unable to extend the viewer for your purposes, please reach out via our
|
|||||||
[GitHub issues][gh-issues]. We are actively seeking feedback on ways to improve
|
[GitHub issues][gh-issues]. We are actively seeking feedback on ways to improve
|
||||||
our integration and extension points.
|
our integration and extension points.
|
||||||
|
|
||||||
## Where to next?
|
## Where to Next?
|
||||||
|
|
||||||
Check out these helpful links:
|
Check out these helpful links:
|
||||||
|
|
||||||
- Ready to dive into some code? Check out our
|
- Ready to dive into some code? Check out our
|
||||||
[Getting Started Guide](./development/getting-started.md).
|
[Getting Started Guide](./essentials/getting-started.md).
|
||||||
- We're an active, vibrant community.
|
- We're an active, vibrant community.
|
||||||
[Learn how you can be more involved.](./development/contributing.md)
|
[Learn how you can be more involved.](./contributing.md)
|
||||||
- Feeling lost? Read our [help page](./help.md).
|
- Feeling lost? Read our [help page](./help.md).
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
@@ -48,7 +48,7 @@ Check out these helpful links:
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
<!-- prettier-ignore-start -->
|
||||||
[ohif-org]: http://www.ohif.org
|
[ohif-org]: https://www.ohif.org
|
||||||
[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb
|
[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb
|
||||||
[gh-issues]: https://github.com/OHIF/Viewers/issues
|
[gh-issues]: https://github.com/OHIF/Viewers/issues
|
||||||
<!-- prettier-ignore-end -->
|
<!-- prettier-ignore-end -->
|
||||||
@@ -1,40 +1,31 @@
|
|||||||
# OHIF Viewers
|
# OHIF Viewers
|
||||||
|
|
||||||
- [Our Process](our-process.md)
|
- Essentials
|
||||||
- Development
|
- [Getting Started](essentials/getting-started.md)
|
||||||
- [Getting Started](development/getting-started.md)
|
- [Installation](essentials/installation.md)
|
||||||
- [Contributing](development/contributing.md)
|
- [Data Source](essentials/data-source.md)
|
||||||
- [Continuous Integration](development/continous-integration.md)
|
- [Configuration](essentials/configuration.md)
|
||||||
- [Testing](development/testing.md)
|
- [Themeing](essentials/themeing.md)
|
||||||
- [Configuring](configuring/index.md)
|
- [Translating](essentials/translating.md)
|
||||||
- [Data Source](configuring/data-source.md)
|
- [Troubleshooting](essentials/troubleshooting.md)
|
||||||
|
- [Scope of Project](essentials/scope-of-project.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
- [Architecture](architecture/index.md)
|
- [Advanced](advanced/index.md)
|
||||||
- [Viewer](viewer/index.md)
|
- [Architecture](advanced/architecture.md)
|
||||||
- [Configuration](viewer/configuration.md)
|
- [Overview](advanced/architecture.md#overview)
|
||||||
- [Themeing](viewer/themeing.md)
|
- [Business Logic](advanced/architecture.md#business-logic)
|
||||||
- [Internationalization](viewer/internationalization.md)
|
- [Component Library](advanced/architecture.md#react-component-library)
|
||||||
- [Extensions](extensions/index.md)
|
- [Extensions](advanced/architecture.md#misc-extensions)
|
||||||
- [Registering](extensions/index.md#registering-an-extension)
|
- [Diagram](advanced/architecture.md#diagram)
|
||||||
- [Lifecycle Hooks](extensions/index.md#lifecycle-hooks)
|
- [Common Questions](advanced/architecture.md#common-questions)
|
||||||
- [preRegistration](extensions/lifecycle/pre-registration.md)
|
- [Extensions](advanced/extensions.md)
|
||||||
- [Modules](extensions/index.md#modules)
|
- [Overview](advanced/extensions.md#overview)
|
||||||
- [Commands](extensions/modules/commands.md)
|
- [Modules](advanced/extensions.md#modules)
|
||||||
- [Panel](extensions/modules/panel.md)
|
- [Registering](advanced/extensions.md#registering-extensions)
|
||||||
- [SOP Class Handler](extensions/modules/sop-class-handler.md)
|
- [OHIF Maintained](advanced/extensions.md#ohif-maintained-extensions)
|
||||||
- [Toolbar](extensions/modules/toolbar.md)
|
- [Custom Tools](advanced/custom-tools.md)
|
||||||
- [Viewport](extensions/modules/viewport.md)
|
|
||||||
- [Contexts](extensions/index.md#contexts)
|
|
||||||
- [ExtensionManager](extensions/index.md#extensionmanager)
|
|
||||||
- [OHIF Maintained](extensions/index.md#maintained-extensions)
|
|
||||||
- [Services](services/index.md)
|
|
||||||
- [Default](services/default/index.md)
|
|
||||||
- [UI](services/ui/index.md)
|
|
||||||
- [Dialog Service](services/ui/ui-dialog-service.md)
|
|
||||||
- [Modal Service](services/ui/ui-modal-service.md)
|
|
||||||
- [Notification Service](services/ui/ui-notification-service.md)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -54,8 +45,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
- [FAQ](faq/index.md)
|
- [Contributing](contributing.md)
|
||||||
- [Scope of Project](faq/scope-of-project.md)
|
- [FAQ](frequently-asked-questions.md)
|
||||||
- [Browser Support](faq/browser-support.md)
|
- [Roadmap](roadmap.md)
|
||||||
- [PWA vs Packaged](faq/pwa-vs-packaged.md)
|
|
||||||
- [Help](help.md)
|
- [Help](help.md)
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<!-- CORNERSTONE.js -->
|
<!-- CORNERSTONE.js -->
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="https://www.npmjs.com/package/@ohif/extension-cornerstone">
|
<a href="https://www.npmjs.com/package/ohif-cornerstone-extension">
|
||||||
Cornerstone
|
Cornerstone
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<!-- VTK.js -->
|
<!-- VTK.js -->
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="https://www.npmjs.com/package/@ohif/extension-vtk">
|
<a href="https://www.npmjs.com/package/ohif-vtk-extension">
|
||||||
VTK.js
|
VTK.js
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
@@ -31,45 +31,32 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>Viewport, Toolbar</td>
|
<td>Viewport, Toolbar</td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- dicom-html -->
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="https://www.npmjs.com/package/@ohif/extension-dicom-html">DICOM HTML</a>
|
<a href="">HTML</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
Renders text and HTML content for <a href="https://github.com/OHIF/Viewers/blob/master/extensions/dicom-html/src/OHIFDicomHtmlSopClassHandler.js#L4-L12">specific SopClassUIDs</a>.
|
Renders text and HTML content for <a href="https://github.com/OHIF/Viewers/blob/react/extensions/ohif-dicom-html-extension/src/OHIFDicomHtmlSopClassHandler.js#L7-L15">specific SopClassUIDs</a>.
|
||||||
</td>
|
</td>
|
||||||
<td>Viewport, SopClassHandler</td>
|
<td>Viewport, SopClassHandler</td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- dicom-pdf -->
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="https://www.npmjs.com/package/@ohif/extension-dicom-pdf">DICOM PDF</a>
|
<a href="https://www.npmjs.com/package/ohif-dicom-pdf-extension">PDF</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
Renders PDFs for a <a href="https://github.com/OHIF/Viewers/blob/master/extensions/dicom-pdf/src/OHIFDicomPDFSopClassHandler.js#L4-L6">specific SopClassUID</a>.
|
Renders PDFs for a <a href="https://github.com/OHIF/Viewers/blob/react/extensions/ohif-dicom-pdf-extension/src/OHIFDicomPDFSopClassHandler.js#L8">specific SopClassUID</a>.
|
||||||
</td>
|
</td>
|
||||||
<td>Viewport, SopClassHandler</td>
|
<td>Viewport, SopClassHandler</td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- dicom-microscopy -->
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="https://www.npmjs.com/package/@ohif/extension-dicom-microscopy">DICOM Microscopy</a>
|
<a href="https://www.npmjs.com/package/ohif-dicom-microscopy-extension">Microscopy</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
Renders Microscopy images for a <a href="https://github.com/OHIF/Viewers/blob/master/extensions/dicom-microscopy/src/DicomMicroscopySopClassHandler.js#L5-L7">specific SopClassUID</a>.
|
Renders Microscopy images for a <a href="https://github.com/OHIF/Viewers/blob/react/extensions/ohif-dicom-microscopy-extension/src/DicomMicroscopySopClassHandler.js#L6">specific SopClassUID</a>.
|
||||||
</td>
|
</td>
|
||||||
<td>Viewport, SopClassHandler</td>
|
<td>Viewport, SopClassHandler</td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- dicom-segmentation -->
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="https://www.npmjs.com/package/@ohif/extension-dicom-segmentation">DICOM Segmentation</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
Renders segmentation images for a <a href="https://github.com/OHIF/Viewers/blob/master/extensions/dicom-segmentation/src/OHIFDicomSegSopClassHandler.js#L5-L7">specific SopClassUID</a>.
|
|
||||||
</td>
|
|
||||||
<td>Panel, Toolbar</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
# Architecture
|
||||||
|
|
||||||
|
Looking to extend your instance of the OHIF Viewer? Want learn how to reuse _a
|
||||||
|
portion_ of the Viewer in your own application? Or maybe you want to get
|
||||||
|
involved and draft or suggest a new feature? Regardless, you're in the right
|
||||||
|
place!
|
||||||
|
|
||||||
|
The OHIF Viewer aims to be decoupled, configurable, and extensible; while this
|
||||||
|
allows our code to be used in more ways, it also increases complexity. Below, we
|
||||||
|
aim to demistify that complexity by providing insight into how our Viewer is
|
||||||
|
architected, and the role each of it's dependent libraries plays.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The [`OHIF/Viewers`][viewers-project] project contains the source code for the
|
||||||
|
OHIF Medical Imaging Viewer. It is effectively a React [progressive web
|
||||||
|
app][pwa] (PWA) that combines the business logic housed in
|
||||||
|
[`OHIF/ohif-core`][core] and the components in our React Component library
|
||||||
|
[`OHIF/react-viewerbase`][component-library]. It provides customization for
|
||||||
|
common use cases through [configuration][configuration] and for adding
|
||||||
|
functionality via [extensions][extensions].
|
||||||
|
|
||||||
|
### Business Logic
|
||||||
|
|
||||||
|
Our goal is to maintain the majority of our business logic in
|
||||||
|
[`OHIF/ohif-core`](https://github.com/OHIF/ohif-core). `ohif-core` offers
|
||||||
|
pre-packaged solutions for features common to Web-based medical imaging viewers.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
- Hotkeys
|
||||||
|
- DICOM Web
|
||||||
|
- Hanging Protocols
|
||||||
|
- Managing a study's measurements
|
||||||
|
- Managing a study's DICOM metadata
|
||||||
|
- A flexible pattern for extensions
|
||||||
|
- [And many others](https://github.com/OHIF/ohif-core/blob/master/src/index.js#L49-L69)
|
||||||
|
|
||||||
|
It does this while remaining decoupled from any particular view library or
|
||||||
|
rendering logic. While we use it to power our React Viewer, it can be used with
|
||||||
|
Vue, React, Vanilla JS, or any number of other frameworks.
|
||||||
|
|
||||||
|
### React Component Library
|
||||||
|
|
||||||
|
[`OHIF/react-viewerbase`](https://github.com/OHIF/react-viewerbase) is a React
|
||||||
|
Component library that contains the reusable components that power the OHIF
|
||||||
|
Viewer. It allows us to build, compose, and test components in isolation; easing
|
||||||
|
the development process by reducing the need to stand-up a local PACS with test
|
||||||
|
case data.
|
||||||
|
|
||||||
|
[Check out our component library!](https://react.ohif.org/)
|
||||||
|
|
||||||
|
### Misc. Extensions
|
||||||
|
|
||||||
|
Want to add custom logic or UI Components to the OHIF Viewer, but don't want to
|
||||||
|
maintain a fork? We expose common integration points via
|
||||||
|
[extensions](./extensions.md) to make that possible. For a list of extensions
|
||||||
|
maintained by OHIF,
|
||||||
|
[check out this helpful table](./extensions.html#ohif-maintained-extensions).
|
||||||
|
|
||||||
|
If you find yourself thinking "I wish the Viewer could do X", and you can't
|
||||||
|
accomplish it with an extension today, create a GitHub issue! We're actively
|
||||||
|
looking for ways to improve our extensibility ^\_^
|
||||||
|
|
||||||
|
[Click here to read more about extensions!](./extensions.md)
|
||||||
|
|
||||||
|
### Diagram
|
||||||
|
|
||||||
|
This diagram is a conceptual illustration of how the Viewer is architected.
|
||||||
|
|
||||||
|
1. (optional) `extensions` can be registered with `ohif-core`'s extension
|
||||||
|
manager
|
||||||
|
2. `ohif-core` provides bussiness logic and a way for `viewer` to access
|
||||||
|
registered extensions
|
||||||
|
3. The `viewer` composes and provides data to components from our component
|
||||||
|
library (`react-viewerbase`)
|
||||||
|
4. The `viewer` can be built and served as a stand-alone PWA, or as an
|
||||||
|
embeddable package
|
||||||
|
([`ohif-viewer`](https://www.npmjs.com/package/ohif-viewer))
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<center><i>architecture diagram</i></center>
|
||||||
|
|
||||||
|
## Common Questions
|
||||||
|
|
||||||
|
> When should I use the packaged source `ohif-viewer` versus building a PWA from
|
||||||
|
> the source?
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
> Can I create my own Viewer using Vue.js or Angular.js?
|
||||||
|
|
||||||
|
You can, but you will not be able to leverage as much of the existing code and
|
||||||
|
components. `ohif-core` could still be used for business logic, and to provide a
|
||||||
|
model for extensions. `react-viewerbase` would then become a guide for the
|
||||||
|
components you would need to recreate.
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Links
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
[viewers-project]: https://github.com/OHIF/Viewers
|
||||||
|
[pwa]: https://developers.google.com/web/progressive-web-apps/
|
||||||
|
[core]: https://github.com/OHIF/ohif-core
|
||||||
|
[component-library]: https://github.com/OHIF/react-viewerbase
|
||||||
|
[configuration]: ../essentials/configuration.md
|
||||||
|
[extensions]: ./extensions.m
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Tool Management
|
||||||
|
|
||||||
|
This is not yet exposed in an easy/convenient way. Most tools are currently
|
||||||
|
added by creating new Viewport, Toolbar, and SOPInstanceHandler extension
|
||||||
|
modules. You can read more about that approach in [extensions](./extensions.md).
|
||||||
|
|
||||||
|
In the near future, we intend to improve the extensibility of tools for existing
|
||||||
|
Viewports (like our Cornerstone.js and VTK.js viewports).
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
# Extensions
|
||||||
|
|
||||||
|
Extensions add new functionality to the viewer by registering one or more
|
||||||
|
modules. They go one step further than configuration in that they allow us to
|
||||||
|
inject custom React components, so long as they adhere to the module's
|
||||||
|
interface. This can be something as simple as adding a new button to the
|
||||||
|
toolbar, or as complex as a new viewport capable of rendering volumes in 3D.
|
||||||
|
|
||||||
|
- [Overview](#overview)
|
||||||
|
- [Modules](#modules)
|
||||||
|
- [Commands](#commands)
|
||||||
|
- [Hotkeys](#hotkeys)
|
||||||
|
- [Toolbar](#toolbar)
|
||||||
|
- [Panel](#panel)
|
||||||
|
- [Viewport](#viewport)
|
||||||
|
- [SOP Class Handler](#sopclasshandler)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
At a glance, an extension is a javascript object that has an `id` property, and
|
||||||
|
one or more "module" methods. You can find an abbreviated extension below, or
|
||||||
|
[view the source][example-ext-src] of our example extension.
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* Only required property. Should be a unique value across all extensions.
|
||||||
|
*/
|
||||||
|
id: 'example-extension',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers one or more named commands scoped to a context. Commands are
|
||||||
|
* the primary means for...
|
||||||
|
*/
|
||||||
|
getCommandsModule() {
|
||||||
|
return {
|
||||||
|
defaultContext: 'VIEWER'
|
||||||
|
actions: { ... },
|
||||||
|
definitions: { ... }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows you to provide toolbar definitions that will be merged with any
|
||||||
|
* existing application toolbar configuration. Used to determine which
|
||||||
|
* buttons should be visible when, their order, what happens when they're
|
||||||
|
* clicked, etc.
|
||||||
|
*/
|
||||||
|
getToolbarModule() {
|
||||||
|
return {
|
||||||
|
definitions: [ ... ],
|
||||||
|
defaultContext: 'ACTIVE_VIEWPORT::CORNERSTONE'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not yet implemented
|
||||||
|
*/
|
||||||
|
getPanelModule: () => null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a ReactComponent that should be used to render data in a
|
||||||
|
* Viewport. The first registered viewport is our "default viewport". If
|
||||||
|
* more than one viewport is registered, we use `SopClassHandlers` to
|
||||||
|
* determine which viewport should be used.
|
||||||
|
*/
|
||||||
|
getViewportModule: () => reactViewportComponent,
|
||||||
|
|
||||||
|
/** Provides a whitelist of SOPClassUIDs the viewport is capable of rendering.
|
||||||
|
* Can modify default behavior for methods like `getDisplaySetFromSeries` */
|
||||||
|
getSopClassHandler: () => {
|
||||||
|
id: 'some-other-unique-id',
|
||||||
|
sopClassUids: [ ... ],
|
||||||
|
getDisplaySetFromSeries: (series, study, dicomWebClient, authorizationHeaders) => { ... }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modules
|
||||||
|
|
||||||
|
There are a few different module types. Each module type allows us to extend the
|
||||||
|
viewer in a different way, and provides a consistent API for us to do so. You
|
||||||
|
can find a full list of the different types of modules
|
||||||
|
[`in ohif-core`][module-types]. Information on each type of module, it's API,
|
||||||
|
and how we determine when/where it should be used is included below.
|
||||||
|
|
||||||
|
> NOTE: Modifying the extensions/modules registered to the OHIF Viewer currently
|
||||||
|
> requires us to import and pass extensions to the ExtensionManager in
|
||||||
|
> `src/App.js`, then rebuild the application. Long-term, we intend to make it
|
||||||
|
> possible to accomplish this without a build step.
|
||||||
|
|
||||||
|
#### Commands
|
||||||
|
|
||||||
|
The Commands Module allows us to register one or more commands scoped to
|
||||||
|
specific contexts. Commands can be run by [hotkeys][#], [toolbar buttons][#],
|
||||||
|
and any registered custom react component (like a [viewport][#] or [panel][#]).
|
||||||
|
Here is a simple example commands module:
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
getCommandsModule() {
|
||||||
|
return {
|
||||||
|
actions: {
|
||||||
|
speak: ({ viewports, words }) => {
|
||||||
|
console.log(viewports, words);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
definitions: {
|
||||||
|
rotateViewportCW: {
|
||||||
|
commandFn: actions.rotateViewport,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: { rotation: 90 }
|
||||||
|
},
|
||||||
|
rotateViewportCCW: {
|
||||||
|
commandFn: actions.rotateViewport,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: { rotation: -90 },
|
||||||
|
context: 'ACTIVE_VIEWER::CORNERSTONE'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultContext: 'VIEWER'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Viewport
|
||||||
|
|
||||||
|
An extension can register a Viewport Module by providing a `getViewportModule()`
|
||||||
|
method that returns a React Component. The React component will receive the
|
||||||
|
following props:
|
||||||
|
|
||||||
|
```js
|
||||||
|
children: PropTypes.arrayOf(PropTypes.element)
|
||||||
|
studies: PropTypes.object,
|
||||||
|
displaySet: PropTypes.object,
|
||||||
|
viewportData: PropTypes.object, // { studies, displaySet }
|
||||||
|
viewportIndex: PropTypes.number,
|
||||||
|
children: PropTypes.node,
|
||||||
|
customProps: PropTypes.object
|
||||||
|
```
|
||||||
|
|
||||||
|
Viewport components are managed by the `LayoutManager`. Which Viewport component
|
||||||
|
is used depends on:
|
||||||
|
|
||||||
|
- The Layout Configuration
|
||||||
|
- Registered SopClassHandlers
|
||||||
|
- The SopClassUID for visible/selected datasets
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<center><i>An example of three Viewports</i></center>
|
||||||
|
|
||||||
|
For a complete example implementation,
|
||||||
|
[check out the OHIFCornerstoneViewport](https://github.com/OHIF/Viewers/blob/react/extensions/ohif-cornerstone-extension/src/OHIFCornerstoneViewport.js).
|
||||||
|
|
||||||
|
#### Toolbar
|
||||||
|
|
||||||
|
An extension can register a Toolbar Module by providing a `getToolbarModule()`
|
||||||
|
method that returns a React Component. The component does not receive any props.
|
||||||
|
If you want to modify or react to state, you will need to connect to the redux
|
||||||
|
store.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<center><i>A toolbar extension example</i></center>
|
||||||
|
|
||||||
|
Toolbar components are rendered in the `ToolbarRow` component.
|
||||||
|
|
||||||
|
For a complete example implementation,
|
||||||
|
[check out the OHIFCornerstoneViewport's Toolbar Module](https://github.com/OHIF/Viewers/blob/react/extensions/ohif-cornerstone-extension/src/ToolbarModule.js).
|
||||||
|
|
||||||
|
#### SopClassHandler
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
#### Panel
|
||||||
|
|
||||||
|
> The panel module is not yet in use.
|
||||||
|
|
||||||
|
#### Hotkeys
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
### Registering Extensions
|
||||||
|
|
||||||
|
Extensions are registered for the application at startup. The
|
||||||
|
`ExtensionManager`, exposed by `ohif-core`, registers a list of extensions with
|
||||||
|
our application's store. Each module provided by the extension becomes available
|
||||||
|
via `state.plugins.availablePlugins`, and consists of three parts: id, type
|
||||||
|
([PLUGIN_TYPE](https://github.com/OHIF/ohif-core/blob/43c08a29eff3fb646a0e83a03a236ddd84f4a6e8/src/plugins.js#L1-L6)),
|
||||||
|
and the return value of the module method.
|
||||||
|
|
||||||
|
In a future version, we will likely expose a way to provide the extensions you
|
||||||
|
would like included at startup.
|
||||||
|
|
||||||
|
_app.js_
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createStore, combineReducers } from 'redux';
|
||||||
|
import OHIF from 'ohif-core';
|
||||||
|
import OHIFCornerstoneExtension from 'ohif-cornerstone-extension';
|
||||||
|
|
||||||
|
const combined = combineReducers(OHIF.redux.reducers);
|
||||||
|
const store = createStore(combined);
|
||||||
|
const extensions = [new OHIFCornerstoneExtension()];
|
||||||
|
|
||||||
|
// Dispatches the `addPlugin` action to the store
|
||||||
|
// Adding extension modules to `state.plugins.availablePlugins`
|
||||||
|
ExtensionManager.registerExtensions(store, extensions);
|
||||||
|
```
|
||||||
|
|
||||||
|
## OHIF Maintained Extensions
|
||||||
|
|
||||||
|
A small number of powerful extensions for popular use cases are maintained by
|
||||||
|
OHIF. They're co-located in the
|
||||||
|
[`OHIF/Viewers`](https://github.com/OHIF/Viewers/tree/react/) repository, in the
|
||||||
|
top level [`extensions/`](https://github.com/OHIF/Viewers/tree/react/extensions)
|
||||||
|
directory.
|
||||||
|
|
||||||
|
{% include "./_maintained-extensions-table.md" %}
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Links
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
[example-ext-src]: https://github.com/OHIF/Viewers/blob/master/extensions/_ohif-example-extension/src/index.js)
|
||||||
|
[module-types]: https://github.com/OHIF/ohif-core/blob/43c08a29eff3fb646a0e83a03a236ddd84f4a6e8/src/plugins.js#L1-L6
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Advanced
|
||||||
|
|
||||||
|
Advanced topics go beyond basic configuration and deployment. Their goal is to provide insight into this project's architecture and guidance on leveraging extensions.
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# MonoRepos: A Crash Course
|
||||||
|
|
||||||
|
- [Lerna][lerna]
|
||||||
|
|
||||||
|
Solutions:
|
||||||
|
|
||||||
|
## Semantic-Release
|
||||||
|
|
||||||
|
- [Semantic-Release](https://github.com/semantic-release/semantic-release/issues/193#issuecomment-462063871)
|
||||||
|
- [Multi-semantic-release](https://github.com/dhoulb/multi-semantic-release)
|
||||||
|
- [semantic-release-monorepo](https://github.com/Updater/semantic-release-monorepo)
|
||||||
|
|
||||||
|
# Netlify
|
||||||
|
|
||||||
|
- https://community.netlify.com/t/best-practices-for-deploying-sites-from-monorepos/818
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Links
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
[lerna]: https://github.com/lerna/lerna
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
# Architecture
|
|
||||||
|
|
||||||
Looking to extend your instance of the OHIF Viewer? Want learn how to reuse _a
|
|
||||||
portion_ of the Viewer in your own application? Or maybe you want to get
|
|
||||||
involved and draft or suggest a new feature? Regardless, you're in the right
|
|
||||||
place!
|
|
||||||
|
|
||||||
The OHIF Viewer aims to be decoupled, configurable, and extensible; while this
|
|
||||||
allows our code to be used in more ways, it also increases complexity. Below, we
|
|
||||||
aim to demistify that complexity by providing insight into how our Viewer is
|
|
||||||
architected, and the role each of it's dependent libraries plays.
|
|
||||||
|
|
||||||
- [Overview](#overview)
|
|
||||||
- [Business Logic](#business-logic)
|
|
||||||
- [Component Library](#react-component-library)
|
|
||||||
- [Extensions & Configuration](#extensions--configuration)
|
|
||||||
- [Common Questions](#common-questions)
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The [OHIF Medical Image Viewing Platform][viewers-project] is maintained as a
|
|
||||||
[`monorepo`][monorepo]. This means that this repository, instead of containing a
|
|
||||||
single project, contains many projects. If you explore our project structure,
|
|
||||||
you'll see the following:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
.
|
|
||||||
├── extensions
|
|
||||||
│ ├── _example # Skeleton of example extension
|
|
||||||
│ ├── cornerstone # 2D images w/ Cornerstone.js
|
|
||||||
│ ├── dicom-html # Structured Reports as HTML in viewport
|
|
||||||
│ ├── dicom-microscopy # Whole slide microscopy viewing
|
|
||||||
│ ├── dicom-pdf # View DICOM wrapped PDFs in viewport
|
|
||||||
│ └── vtk # MPR and Volume support w/ VTK.js
|
|
||||||
│
|
|
||||||
├── platform
|
|
||||||
│ ├── core # Business Logic
|
|
||||||
│ ├── i18n # Internationalization Support
|
|
||||||
│ ├── ui # React component library
|
|
||||||
│ └── viewer # Connects platform and extension projects
|
|
||||||
│
|
|
||||||
├── ... # misc. shared configuration
|
|
||||||
├── lerna.json # MonoRepo (Lerna) settings
|
|
||||||
├── package.json # Shared devDependencies and commands
|
|
||||||
└── README.md
|
|
||||||
```
|
|
||||||
|
|
||||||
The `platform` directory contains the business logic library, component library,
|
|
||||||
and the application library that combines them to create a powerful medical
|
|
||||||
imaging viewer.
|
|
||||||
|
|
||||||
The `extensions` directory contains many packages that can be registered with
|
|
||||||
`@ohif/core`'s `ExtensionManager` to expand an application's supported features
|
|
||||||
and functionality.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<center><i>architecture diagram</i></center>
|
|
||||||
|
|
||||||
This diagram is a conceptual illustration of how the Viewer is architected.
|
|
||||||
|
|
||||||
1. (optional) `extensions` can be registered with `@ohif/core`'s
|
|
||||||
`ExtensionManager`
|
|
||||||
2. `@ohif/core` provides bussiness logic and a way for `@ohif/viewer` to access
|
|
||||||
registered extensions
|
|
||||||
3. The `@ohif/viewer` composes and provides data to components from our
|
|
||||||
component library (`@ohif/ui`)
|
|
||||||
4. The `@ohif/viewer` can be built and served as a stand-alone PWA, or as an
|
|
||||||
embeddable package ([`@ohif/viewer`][viewer-npm])
|
|
||||||
|
|
||||||
## Business Logic
|
|
||||||
|
|
||||||
The [`@ohif/core`][core-github] project offers pre-packaged solutions for
|
|
||||||
features common to Web-based medical imaging viewers. For example:
|
|
||||||
|
|
||||||
- Hotkeys
|
|
||||||
- DICOM Web requests
|
|
||||||
- Hanging Protocols
|
|
||||||
- Managing a study's measurements
|
|
||||||
- Managing a study's DICOM metadata
|
|
||||||
- [A flexible pattern for extensions](../extensions/index.md)
|
|
||||||
- And many others
|
|
||||||
|
|
||||||
It does this while remaining decoupled from any particular view library or
|
|
||||||
rendering logic. While we use it to power our React Viewer, it can be used with
|
|
||||||
Vue, React, Vanilla JS, or any number of other frameworks.
|
|
||||||
|
|
||||||
## React Component Library
|
|
||||||
|
|
||||||
[`@ohif/ui`][ui-github] is a React Component library that contains the reusable
|
|
||||||
components that power the OHIF Viewer. It allows us to build, compose, and test
|
|
||||||
components in isolation; easing the development process by reducing the need to
|
|
||||||
stand-up a local PACS with test case data.
|
|
||||||
|
|
||||||
Extension authors can also use these same components when building their
|
|
||||||
extension's UI; allowing for a consistent look and feel with the rest of the
|
|
||||||
application.
|
|
||||||
|
|
||||||
[Check out our component library!](https://react.ohif.org/)
|
|
||||||
|
|
||||||
## Extensions & Configuration
|
|
||||||
|
|
||||||
While OHIF maintains several high value and commonly requested features in its
|
|
||||||
own extensions, there are many instances where one may wish to further extend
|
|
||||||
the viewer. Some common use cases include:
|
|
||||||
|
|
||||||
- Adding AI/ML tools and insights
|
|
||||||
- Custom workflows for guided diagnosis
|
|
||||||
- Collecting specific annotations for training data or reports
|
|
||||||
- Authentication and granular permissions
|
|
||||||
- Teleconsultation workflow, image comments, and tracking
|
|
||||||
- Adding surgical templating tools and reports
|
|
||||||
- and many others
|
|
||||||
|
|
||||||
We expose common integration points via [extensions](../extensions/index.md) to
|
|
||||||
make this possible. The viewer and many of our own extensions also offer
|
|
||||||
[configuration][configuration]. For a list of extensions maintained by OHIF,
|
|
||||||
[check out this helpful table](../extensions/index.md#maintained-extensions).
|
|
||||||
|
|
||||||
If you find yourself thinking "I wish the Viewer could do X", and you can't
|
|
||||||
accomplish it with an extension today, create a GitHub issue! We're actively
|
|
||||||
looking for ways to improve our extensibility ^\_^
|
|
||||||
|
|
||||||
[Click here to read more about extensions!](../extensions/index.md)
|
|
||||||
|
|
||||||
## Common Questions
|
|
||||||
|
|
||||||
> When should I use the packaged source `@ohif/viewer` versus building a PWA
|
|
||||||
> from the source?
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
> Can I create my own Viewer using Vue.js or Angular.js?
|
|
||||||
|
|
||||||
You can, but you will not be able to leverage as much of the existing code and
|
|
||||||
components. `@ohif/core` could still be used for business logic, and to provide
|
|
||||||
a model for extensions. `@ohif/ui` would then become a guide for the components
|
|
||||||
you would need to recreate.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Links
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
[monorepo]: https://github.com/OHIF/Viewers/issues/768
|
|
||||||
[viewers-project]: https://github.com/OHIF/Viewers
|
|
||||||
[viewer-npm]: https://www.npmjs.com/package/@ohif/viewer
|
|
||||||
[pwa]: https://developers.google.com/web/progressive-web-apps/
|
|
||||||
[configuration]: ../configuring/index.md
|
|
||||||
[extensions]: ../extensions/index.md
|
|
||||||
[core-github]: https://github.com/OHIF/viewers/platform/core
|
|
||||||
[ui-github]: https://github.com/OHIF/Viewers/tree/master/platform/ui
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 260.4 82.9" enable-background="new 0 0 260.4 82.9" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<path fill="#525DF9" d="M31.7,8.9c9.6,0,16.5,4.9,19,13.4l0.1,0.4h9.6l-0.1-0.6C57.6,8.5,46.7,0,31.8,0C13.7,0,0,14.1,0,32.9 s13.7,32.9,31.8,32.9c14.9,0,25.8-8.5,28.6-22.1l0.1-0.6h-9.6l-0.1,0.4c-2.5,8.5-9.5,13.4-19,13.4c-13.1,0-21.9-9.6-21.9-24 C9.8,18.6,18.6,8.9,31.7,8.9z"/>
|
||||||
|
<path fill="#525DF9" d="M110.1,58c-2.3,0-3.5-1.4-3.5-4.2V32.7c0-8.8-7.2-14.7-17.9-14.7c-7.5,0-16.7,3.9-18.3,14.9l-0.1,0.6h8.4 l0.1-0.4c1.4-5.7,5.8-6.9,9.3-6.9c6,0,9.5,2.7,9.5,7.4v2.6l-13.6,1.4c-7.6,0.8-15.7,5-15.7,14.6c0,8.1,6,13.5,14.8,13.5 c6.5,0,11.8-3.4,15-6.8c1.3,4.2,4.5,6.5,9.1,6.5c1.7,0,3.2-0.3,5.2-0.9l0.3-0.1v-7l-0.6,0.2C111.5,57.9,110.9,58,110.1,58z M97.7,43.5v7.3c-4.6,4.6-8.9,6.7-13.5,6.7c-2,0-6.8-0.6-6.8-5.7c0-3.8,2.8-6.3,7.4-6.9L97.7,43.5z"/>
|
||||||
|
<path fill="#525DF9" d="M146.5,18c-6.5,0-12,3.1-15.2,6.1v-5.2h-8.9v46h8.9V34.3c2.2-3.2,6.7-8.3,13-8.3c5.6,0,8.7,3.1,8.7,8.8 v30.1h8.9V33.3C161.9,23.7,156.1,18,146.5,18z"/>
|
||||||
|
<path fill="#525DF9" d="M195.8,18c-6.5,0-12,3.1-15.2,6.1v-5.2h-8.9v46h8.9V34.3c2.2-3.2,6.7-8.3,13-8.3c5.6,0,8.7,3.1,8.7,8.8 v30.1h8.9V33.3C211.2,23.7,205.4,18,195.8,18z"/>
|
||||||
|
<polygon fill="#525DF9" points="251.3,18.9 238.6,51.8 237.8,47.9 225.6,18.9 216.2,18.9 234.3,62.4 226,82.9 235,82.9 260.4,18.9 "/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 445.9 638" enable-background="new 0 0 445.9 638" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path fill="#5F5DF9" d="M224.5,638C101.1,638,0,537.8,0,414.7V223.3C0,100.2,101.1,0,224.5,0c109.6,0,202.8,78.3,220.9,186.1
|
||||||
|
c2.9,17.4-8.6,33.8-26,36.8c-17.4,2.9-33.7-8.8-36.7-26.2c-13-77-78.6-132.9-157-132.9c-88.2,0-159.3,71.6-159.3,159.5v191.4
|
||||||
|
c0,88,71.1,159.5,159.3,159.5c78.3,0,144.2-55.9,157.1-132.9c2.9-17.4,19.3-29.1,36.6-26.2c17.4,2.9,28.6,19.4,25.7,36.7
|
||||||
|
C427.2,559.7,334,638,224.5,638z"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.5">
|
||||||
|
<path fill="#5F5DF9" d="M153.6,347.7c-17.6,0-30.7-14.3-30.7-31.9v-92.3c0-56.8,45.8-103,102.8-103c38.9,0,73.9,21.6,91.7,56.4
|
||||||
|
c8,15.7,1.7,34.9-14,42.9c-15.7,8-35,1.8-43-13.9c-6.8-13.4-21-21.7-35.8-21.7c-21.8,0-40.2,17.6-40.2,39.2v92.3
|
||||||
|
C184.3,333.4,171.2,347.7,153.6,347.7z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 422 KiB |
|
Before Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 230 KiB |
|
Before Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 26 KiB |
@@ -8,6 +8,7 @@
|
|||||||
"-fontsettings",
|
"-fontsettings",
|
||||||
"github",
|
"github",
|
||||||
"ga",
|
"ga",
|
||||||
|
"sitemap",
|
||||||
"anchors",
|
"anchors",
|
||||||
"versions"
|
"versions"
|
||||||
],
|
],
|
||||||
@@ -23,8 +24,7 @@
|
|||||||
"token": "UA-110573590-2"
|
"token": "UA-110573590-2"
|
||||||
},
|
},
|
||||||
"sitemap": {
|
"sitemap": {
|
||||||
"hostname": "https://docs.ohif.org",
|
"hostname": "https://docs.ohif.org"
|
||||||
"todo": "sitemap plugin removed; causing build errors"
|
|
||||||
},
|
},
|
||||||
"versions": {
|
"versions": {
|
||||||
"gitbookConfigURL": "https://raw.githubusercontent.com/OHIF/Viewers/master/docs/book.json",
|
"gitbookConfigURL": "https://raw.githubusercontent.com/OHIF/Viewers/master/docs/book.json",
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
# Configuration
|
|
||||||
|
|
||||||
> This step assumes you have an imaging archive. If you need assistance setting
|
|
||||||
> one up, check out the [`Data Source` Guide](./data-source.md) or a deployment
|
|
||||||
> recipe that contains an open Image Archive
|
|
||||||
|
|
||||||
- [Overview](#overview)
|
|
||||||
- [Configuration Files](#configuration-files)
|
|
||||||
- [Environment Variables](#environment-variables)
|
|
||||||
- [How do I configure my project?](#how-do-i-configure-my-project)
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
### Configuration Files
|
|
||||||
|
|
||||||
The configuration for our viewer is in the `<root>platform/viewer/public/config`
|
|
||||||
directory. Our build process knows which configuration file to use based on the
|
|
||||||
`APP_CONFIG` environment variable. By default, its value is
|
|
||||||
[`config/default.js`][default-config]. The majority of the viewer's features,
|
|
||||||
and registered extension's features, are configured using this file.
|
|
||||||
|
|
||||||
**Embedded Use Note:**
|
|
||||||
|
|
||||||
Alternatively, when using the `umd` bundle for embedded use cases, these same
|
|
||||||
values are what you'll pass to `installViewer` method:
|
|
||||||
|
|
||||||
`OHIFStandaloneViewer.installViewer(window.config)`
|
|
||||||
|
|
||||||
### Environment Variables
|
|
||||||
|
|
||||||
We use environment variables at build and dev time to change the Viewer's
|
|
||||||
behavior. We can update the `HTML_TEMPLATE` to easily change which extensions
|
|
||||||
are registered, and specify a different `APP_CONFIG` to connect to an
|
|
||||||
alternative data source (or even specify different default hotkeys).
|
|
||||||
|
|
||||||
| Environment Variable | Description | Default |
|
|
||||||
| -------------------- | -------------------------------------------------------------------------------------------------- | ------------------- |
|
|
||||||
| `HTML_TEMPLATE` | Which [HTML template][html-templates] to use as our web app's entry point. Specific to PWA builds. | `index.html` |
|
|
||||||
| `PUBLIC_URL` | The route relative to the host that the app will be served from. Specific to PWA builds. | `/` |
|
|
||||||
| `APP_CONFIG` | Which [configuration file][config-file] to copy to output as `app-config.js` | `config/default.js` |
|
|
||||||
| `PROXY_TARGET` | When developing, proxy requests that match this pattern to `PROXY_DOMAIN` | `undefined` |
|
|
||||||
| `PROXY_DOMAIN` | When developing, proxy requests from `PROXY_TARGET` to `PROXY_DOMAIN` | `undefined` |
|
|
||||||
|
|
||||||
## How do I configure my project?
|
|
||||||
|
|
||||||
The simplest way is to update the existing default config:
|
|
||||||
|
|
||||||
_/platform/viewer/public/config/default.js_
|
|
||||||
|
|
||||||
```js
|
|
||||||
window.config = {
|
|
||||||
routerBasename: '/',
|
|
||||||
servers: {
|
|
||||||
dicomWeb: [
|
|
||||||
{
|
|
||||||
name: 'DCM4CHEE',
|
|
||||||
wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
|
|
||||||
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
|
||||||
wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
|
||||||
qidoSupportsIncludeField: true,
|
|
||||||
imageRendering: 'wadors',
|
|
||||||
thumbnailRendering: 'wadors',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
The configuration can also be written as a JS Function in case you need to inject dependencies like external services:
|
|
||||||
|
|
||||||
```js
|
|
||||||
window.config = ({ servicesManager } = {}) => {
|
|
||||||
const { UIDialogService } = servicesManager.services;
|
|
||||||
return {
|
|
||||||
cornerstoneExtensionConfig: {
|
|
||||||
tools: {
|
|
||||||
ArrowAnnotate: {
|
|
||||||
configuration: {
|
|
||||||
getTextCallback: (callback, eventDetails) => UIDialogService.create({...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
routerBasename: '/',
|
|
||||||
servers: {
|
|
||||||
dicomWeb: [
|
|
||||||
{
|
|
||||||
name: 'DCM4CHEE',
|
|
||||||
wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
|
|
||||||
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
|
||||||
wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
|
||||||
qidoSupportsIncludeField: true,
|
|
||||||
imageRendering: 'wadors',
|
|
||||||
thumbnailRendering: 'wadors',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also create a new config file and specify its path relative to the build
|
|
||||||
output's root by setting the `APP_CONFIG` environment variable. You can set the
|
|
||||||
value of this environment variable a few different ways:
|
|
||||||
|
|
||||||
- ~[Add a temporary environment variable in your shell](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#adding-temporary-environment-variables-in-your-shell)~
|
|
||||||
- Previous `react-scripts` functionality that we need to duplicate with
|
|
||||||
`dotenv-webpack`
|
|
||||||
- ~[Add environment specific variables in `.env` file(s)](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#adding-development-environment-variables-in-env)~
|
|
||||||
- Previous `react-scripts` functionality that we need to duplicate with
|
|
||||||
`dotenv-webpack`
|
|
||||||
- Using the `cross-env` package in an npm script:
|
|
||||||
- `"build": "cross-env APP_CONFIG=config/my-config.js react-scripts build"`
|
|
||||||
|
|
||||||
After updating the configuration, `yarn run build` to generate updated build
|
|
||||||
output.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Links
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
[default-config]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/public/config/default.js
|
|
||||||
[html-templates]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/public/html-templates
|
|
||||||
[config-files]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/public/config
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
# Google Cloud Healthcare
|
# Google Cloud Healthcare
|
||||||
|
|
||||||
|
> ATTENTION: The original documentation for this integration lives in the legacy
|
||||||
|
> `version 1` Meteor documentation. You can
|
||||||
|
> [find it here](/history/v1/connecting-to-image-archives/google-cloud-healthcare.html).
|
||||||
|
> These docs will mirror the Meteor documentation until our `React`
|
||||||
|
> implementation has been updated to work with Google Cloud Healthcare.
|
||||||
|
|
||||||
> The [Google Cloud Healthcare API](https://cloud.google.com/healthcare/) is a
|
> The [Google Cloud Healthcare API](https://cloud.google.com/healthcare/) is a
|
||||||
> powerful option for storing medical imaging data in the cloud.
|
> powerful option for storing medical imaging data in the cloud.
|
||||||
|
|
||||||
@@ -10,7 +16,10 @@ store their data in the cloud. It offers an
|
|||||||
[almost-entirely complete DICOMWeb API](https://cloud.google.com/healthcare/docs/dicom)
|
[almost-entirely complete DICOMWeb API](https://cloud.google.com/healthcare/docs/dicom)
|
||||||
which requires tokens generated via the
|
which requires tokens generated via the
|
||||||
[OAuth 2.0 Sign In flow](https://developers.google.com/identity/sign-in/web/sign-in).
|
[OAuth 2.0 Sign In flow](https://developers.google.com/identity/sign-in/web/sign-in).
|
||||||
Images can even be transcoded on the fly if this is desired.
|
Images can even be transcoded on the fly if this is desired. The Cloud
|
||||||
|
Healthcare API is a very attractive option because it allows us to avoid
|
||||||
|
deploying the Meteor server entirely. We can just deploy OHIF as a client-only
|
||||||
|
static site application.
|
||||||
|
|
||||||
## Setup a Google Cloud Healthcare Project
|
## Setup a Google Cloud Healthcare Project
|
||||||
|
|
||||||
@@ -37,8 +46,8 @@ Images can even be transcoded on the fly if this is desired.
|
|||||||
[OAuth 2.0 Client ID](https://support.google.com/cloud/answer/6158849?hl=en)
|
[OAuth 2.0 Client ID](https://support.google.com/cloud/answer/6158849?hl=en)
|
||||||
- Add your domain (e.g. `http://localhost:3000`) to Authorized JavaScript
|
- Add your domain (e.g. `http://localhost:3000`) to Authorized JavaScript
|
||||||
origins.
|
origins.
|
||||||
- Add your domain, plus `callback` (e.g. `http://localhost:3000/callback`) to
|
- Add your domain, plus `_oauth/google` (e.g.
|
||||||
Authorized Redirect URIs.
|
`http://localhost:3000/_oauth/google`) to Authorized Redirect URIs.
|
||||||
- Save your Client ID for later.
|
- Save your Client ID for later.
|
||||||
|
|
||||||
- (Optional): Enable Public Datasets that are being hosted by Google:
|
- (Optional): Enable Public Datasets that are being hosted by Google:
|
||||||
@@ -46,26 +55,70 @@ Images can even be transcoded on the fly if this is desired.
|
|||||||
|
|
||||||
## Run the viewer with your OAuth Client ID
|
## Run the viewer with your OAuth Client ID
|
||||||
|
|
||||||
1. Open the `config/google.js` file and change `YOURCLIENTID` to your Client ID
|
1. Open the `config/oidc-googleCloud.json` file and change `YOURCLIENTID` to
|
||||||
value.
|
your Client ID value.
|
||||||
1. Run the OHIF Viewer using the config/google.js configuration file
|
1. Run the OHIF Viewer using the oidc-googleCloud.json configuration file
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd OHIFViewer
|
cd OHIFViewer
|
||||||
yarn install
|
METEOR_PACKAGE_DIRS="../Packages" meteor npm install
|
||||||
APP_CONFIG=config/google.js yarn run dev
|
METEOR_PACKAGE_DIRS="../Packages" meteor --settings ../config/oidc-googleCloud.json
|
||||||
```
|
```
|
||||||
|
|
||||||
## Running via Docker
|
## Running via Docker
|
||||||
|
|
||||||
The OHIF Viewer Docker container can be connected to Google Cloud Healthcare by
|
OHIF is also providing a Docker container which can connect to Google Cloud
|
||||||
providing a Client ID at runtime. This is a very simple method to get up and
|
Healthcare with a Client ID which is provided at runtime. This is a very simple
|
||||||
running.
|
method to get up and running. Internally, the container is running
|
||||||
|
[Nginx](https://nginx.org/) to serve the
|
||||||
|
[Standalone Viewer](../standalone-viewer/usage.md).
|
||||||
|
|
||||||
1. Install Docker (https://www.docker.com/)
|
1. Install Docker (https://www.docker.com/)
|
||||||
1. Run the Docker container, providing a Client ID as an environment variable.
|
1. Run the Docker container, providing a Client ID as an environment variable.
|
||||||
Client IDs look like `xyz.apps.googleusercontent.com`.
|
Client IDs look like `xyz.apps.googleusercontent.com`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --env CLIENT_ID=$CLIENT_ID --publish 5000:80 ohif/viewer:latest
|
docker run --env CLIENT_ID=$CLIENT_ID --publish 3000:80 ohif/viewer-google-cloud:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building the ohif/viewer-google-cloud Docker Image
|
||||||
|
|
||||||
|
The
|
||||||
|
[ohif/viewer-google-cloud](https://cloud.docker.com/u/ohif/repository/docker/ohif/viewer-google-cloud)
|
||||||
|
Docker image is built as follows. The Dockerfile and nginx.conf are in the
|
||||||
|
`/dockersupport/viewer-google-cloud` folder.
|
||||||
|
|
||||||
|
1. [Install Meteor](https://www.meteor.com/install)
|
||||||
|
1. Clone the repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/OHIF/Viewers.git
|
||||||
|
cd Viewers
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Install meteor-build-client-fixed2 so you can build the Standalone Viewer
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install -g meteor-build-client-fixed2
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Build the Standalone client-only OHIF Viewer
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd OHIFViewer/
|
||||||
|
METEOR_PACKAGE_DIRS="../Packages" meteor npm install
|
||||||
|
METEOR_PACKAGE_DIRS="../Packages" meteor-build-client-fixed2 ../dockersupport/viewer-google-cloud/build -s ../config/oidc.json
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Build the Docker image
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ../dockersupport/viewer-google-cloud
|
||||||
|
docker build -t ohif/viewer-google-cloud .
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Run the Docker image using an OAuth Client ID
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --env CLIENT_ID={$someID}.apps.googleusercontent.com --publish 3000:80 ohif/viewer-google-cloud
|
||||||
```
|
```
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
## I would like to contribute code - how do I do this?
|
||||||
|
|
||||||
|
Fork the repository, make your change and submit a pull request.
|
||||||
|
|
||||||
|
- The OHIF Viewer consists of code from three different repositories. Make sure
|
||||||
|
your change is modifying the appropriate one:
|
||||||
|
- `ohif-core`: Business Logic
|
||||||
|
- `react-viewerbase`: Reusable React Component Library
|
||||||
|
- `Viewers`: The glue, PWA, and primary extension point
|
||||||
|
- At a minimum, you may want to read the following documentation:
|
||||||
|
- [Essentials: Getting Started](./essentials/getting-started.md)
|
||||||
|
- [Advanced: Architecture](./advanced/architecture.md)
|
||||||
|
|
||||||
|
### When changes impact multiple repositories
|
||||||
|
|
||||||
|
This is a particularly tricky scenario. We don't want to publish code in one
|
||||||
|
repository, just so we can test and complete the other half of its requirements
|
||||||
|
in another. Thankfully, there are a couple of ways you can test unpublished
|
||||||
|
dependent changes locally before publishing:
|
||||||
|
|
||||||
|
- [Use `yarn link`](https://yarnpkg.com/en/docs/cli/link)
|
||||||
|
|
||||||
|
For example if you are working on `ohif-core` and would like to use your local
|
||||||
|
version to debug a problem in `Viewers`, simply run yarn link inside of the
|
||||||
|
`ohif-core` project.
|
||||||
|
|
||||||
|
- If you're experiencing issues with `yarn link`,
|
||||||
|
[try `yalc`](https://github.com/whitecolor/yalc)
|
||||||
|
|
||||||
|
Yalc provides an improved workflow as we add more and more dependent packages
|
||||||
|
that are "in-progress". This comes into play as we begin working on extensions
|
||||||
|
and their dependencies.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Install yalc for the first time
|
||||||
|
yarn global add yalc
|
||||||
|
|
||||||
|
// EXAMPLE: using an in-development version of ohif-core w/ Viewers locally
|
||||||
|
// 1. Navigate to ohif-core's project root
|
||||||
|
yarn install
|
||||||
|
yalc publish
|
||||||
|
|
||||||
|
// 2. Run the following after each change to ohif-core
|
||||||
|
yarn build
|
||||||
|
yalc push .
|
||||||
|
|
||||||
|
// 3. Use the local package in our Viewers project. Navigate to the Viewers
|
||||||
|
// Project root.
|
||||||
|
yarn install
|
||||||
|
yalc add ohif-core
|
||||||
|
yarn run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Any guidance on submitting changes?
|
||||||
|
|
||||||
|
While we do appreciate code contributions, triaging and integrating contributed
|
||||||
|
code changes can be very time consuming. Please consider the following tips when
|
||||||
|
working on your pull requests:
|
||||||
|
|
||||||
|
- Functionality is appropriate for the repository. Consider creating a GitHub
|
||||||
|
issue to discuss your suggested changes.
|
||||||
|
- The scope of the pull request is not too large. Please consider separate pull
|
||||||
|
requests for each feature as big pull requests are very time consuming to
|
||||||
|
understand.
|
||||||
|
|
||||||
|
We will provide feedback on your pull requests as soon as possible. Following
|
||||||
|
the tips above will help ensure your changes are reviewed.
|
||||||
|
|
||||||
|
## Testing contribution pull requests
|
||||||
|
|
||||||
|
OHIF uses [netlify](netlify.com) so that pull requests are autogenerated and
|
||||||
|
available for testing.
|
||||||
|
|
||||||
|
For example, [this url][example-url] allows you to test [pull request 237, the
|
||||||
|
request that created this FAQ entry,][pr-237] using data pulled from Amazon S3.
|
||||||
|
|
||||||
|
Replacing the number 237 in the link below with your pull request number should
|
||||||
|
let you test it as well and you can use this link for discussions on github
|
||||||
|
without requiring reviewers to download and build your branch.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
https://deploy-preview-237--ohif.netlify.com/viewer/?url=https://s3.eu-central-1.amazonaws.com/ohif-viewer/sampleDICOM.json
|
||||||
|
```
|
||||||
|
|
||||||
|
If you have made a documentation change, a link like this will let you preview
|
||||||
|
the gitbook generated by the pull request:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
https://deploy-preview-237--ohif.netlify.com/contributing.html
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Links
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
|
[example-url]: https://deploy-preview-237--ohif.netlify.com/viewer/?url=https://s3.eu-central-1.amazonaws.com/ohif-viewer/sampleDICOM.json
|
||||||
|
[pr-237]: https://github.com/OHIF/Viewers/pull/237
|
||||||
|
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
# Deployment
|
# Deployment
|
||||||
|
|
||||||
The OHIF Viewer can be embedded in other web applications via it's [packaged
|
The OHIF Viewer can be embedded in other web applications via it's [packaged
|
||||||
script source][viewer-npm], or served up as a stand-alone PWA ([progressive web
|
script source][ohif-viewer-npm], or served up as a stand-alone PWA ([progressive
|
||||||
application][pwa-url]) by building and hosting a collection of static assets. In
|
web application][pwa-url]) by building and hosting a collection of static
|
||||||
either case, you will need to configure your instance of the Viewer so that it
|
assets. In either case, you will need to configure your instance of the Viewer
|
||||||
can connect to your data source (the database or PACS that provides the data
|
so that it can connect to your data source (the database or PACS that provides
|
||||||
your Viewer will display).
|
the data your Viewer will display).
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Our goal is to make deployment as simple and painless as possible; however,
|
Our goal is to make deployment as simple and painless as possible; however,
|
||||||
there is an inherent amount of complexity in configuring and deploying web
|
there is an inherent amount of complexity in customizing, optimizing, and
|
||||||
applications. If you find yourself a little lost, please don't hesitate to
|
deploying web applications. If you find yourself a little lost, please don't
|
||||||
[reach out for help](/help.md)
|
hesitate to [reach out for help](/help.md)
|
||||||
|
|
||||||
## Deployment Scenarios
|
## Deployment Scenarios
|
||||||
|
|
||||||
@@ -35,7 +35,8 @@ benefits, but comes at the cost of time and complexity. Some benefits include:
|
|||||||
|
|
||||||
_Today:_
|
_Today:_
|
||||||
|
|
||||||
- Leverage [extensions](/extensions/index.md) to drop-in powerful new features
|
- Leverage [extensions](/advanced/extensions.md) to drop-in powerful new
|
||||||
|
features
|
||||||
- Add routes and customize the viewer's workflow
|
- Add routes and customize the viewer's workflow
|
||||||
- Finer control over styling and whitelabeling
|
- Finer control over styling and whitelabeling
|
||||||
|
|
||||||
@@ -96,12 +97,12 @@ support it yet, but it is gaining wider adoption.
|
|||||||
If you have an existing archive and intend to host the OHIF Viewer at the same
|
If you have an existing archive and intend to host the OHIF Viewer at the same
|
||||||
domain name as your archive, then connecting the two is as simple as following
|
domain name as your archive, then connecting the two is as simple as following
|
||||||
the steps layed out in our
|
the steps layed out in our
|
||||||
[Configuration Essentials Guide](./../configuring/index.md).
|
[Configuration Essentials Guide](./../essentials/configuration.md).
|
||||||
|
|
||||||
#### What if I don't have an imaging archive?
|
#### What if I don't have an imaging archive?
|
||||||
|
|
||||||
We provide some guidance on configuring a local image archive in our
|
We provide some guidance on configuring a local image archive in our
|
||||||
[Data Source Essentials](./../configuring/data-source.md) guide. Hosting an
|
[Data Source Essentials](./../essentials/data-source.md) guide. Hosting an
|
||||||
archive remotely is a little trickier. You can check out some of our
|
archive remotely is a little trickier. You can check out some of our
|
||||||
[advanced recipes](#recipes) for modeled setups that may work for you.
|
[advanced recipes](#recipes) for modeled setups that may work for you.
|
||||||
|
|
||||||
@@ -123,111 +124,14 @@ There are two important steps to making sure this setup works:
|
|||||||
Most image archives do not provide either of these features "out of the box".
|
Most image archives do not provide either of these features "out of the box".
|
||||||
It's common to use IIS, Nginx, or Apache to route incoming requests and append
|
It's common to use IIS, Nginx, or Apache to route incoming requests and append
|
||||||
appropriate headers. You can find an example of this setup in our
|
appropriate headers. You can find an example of this setup in our
|
||||||
[Nginx + Image Archive Deployment Recipe](./recipes/nginx--image-archive.md).
|
[Nginx + Image Archive Deployment Recipe](deployment/recipes/nginx--image-archive.md).
|
||||||
|
|
||||||
#### What if my archive doesn't support DicomWeb?
|
#### What if my archive doesn't support DicomWeb?
|
||||||
|
|
||||||
It's possible to supply all Study data via JSON format, in the event you do not have a DicomWeb endpoint.
|
> This is possible to do with the OHIF Viewer, but not as straightforward. Look
|
||||||
You can host all of the relevant files on any web accessible server (Amazon S3, Azure Blob Storage, Local file server etc.)
|
> out for documentation on this subject in the near future.
|
||||||
|
|
||||||
This JSON is supplied via the '?url=' query parameter.
|
|
||||||
It should reference an endpoint that returns **application/json** formatted text.
|
|
||||||
|
|
||||||
If you do not have an API, you can simply return a text file containing the JSON from any web server.
|
|
||||||
|
|
||||||
|
|
||||||
You tell the OHIF viewer to use JSON by appending the `'?url='` query to the `/Viewer` route:
|
|
||||||
|
|
||||||
eg. `https://my-test-ohif-server/viewer?url=https://my-json-server/study-uid.json`
|
|
||||||
|
|
||||||
The returned JSON object must contain a single root object with a 'studies' array.
|
|
||||||
|
|
||||||
|
|
||||||
*Sample JSON format:*
|
|
||||||
```JSON
|
|
||||||
{
|
|
||||||
"studies": [
|
|
||||||
{
|
|
||||||
"StudyInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.78",
|
|
||||||
"StudyDescription": "BRAIN SELLA",
|
|
||||||
"StudyDate": "20010108",
|
|
||||||
"StudyTime": "120022",
|
|
||||||
"PatientName": "MISTER^MR",
|
|
||||||
"PatientId": "832040",
|
|
||||||
"series": [
|
|
||||||
{
|
|
||||||
"SeriesDescription": "SAG T-1",
|
|
||||||
"SeriesInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.121",
|
|
||||||
"SeriesNumber": 2,
|
|
||||||
"SeriesDate": "20010108",
|
|
||||||
"SeriesTime": "120318",
|
|
||||||
"Modality": "MR",
|
|
||||||
"instances": [
|
|
||||||
{
|
|
||||||
"metadata": {
|
|
||||||
"Columns": 512,
|
|
||||||
"Rows": 512,
|
|
||||||
"InstanceNumber": 3,
|
|
||||||
"AcquisitionNumber": 0,
|
|
||||||
"PhotometricInterpretation": "MONOCHROME2",
|
|
||||||
"BitsAllocated": 16,
|
|
||||||
"BitsStored": 16,
|
|
||||||
"PixelRepresentation": 1,
|
|
||||||
"SamplesPerPixel": 1,
|
|
||||||
"PixelSpacing": [0.390625, 0.390625],
|
|
||||||
"HighBit": 15,
|
|
||||||
"ImageOrientationPatient": [0,1,0,0,0,-1],
|
|
||||||
"ImagePositionPatient": [11.600000,-92.500000, 98.099998],
|
|
||||||
"FrameOfReferenceUID": "1.2.840.113619.2.5.1762583153.223134.978956938.470",
|
|
||||||
"ImageType": ["ORIGINAL","PRIMARY","OTHER"],
|
|
||||||
"Modality": "MR",
|
|
||||||
"SOPInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.124",
|
|
||||||
"SeriesInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.121",
|
|
||||||
"StudyInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.78"
|
|
||||||
},
|
|
||||||
"url": "dicomweb://s3.amazonaws.com/lury/MRStudy/1.2.840.113619.2.5.1762583153.215519.978957063.124.dcm"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
More info on this JSON format can be found here [Issue #1500](https://github.com/OHIF/Viewers/issues/1500)
|
|
||||||
|
|
||||||
|
|
||||||
**Implementation Notes:**
|
|
||||||
|
|
||||||
1. When hosting the viewer, you will also need to host a /viewer route on the server - or the browser may not be able to find the route.
|
|
||||||
2. For each instance url (dicom object) in the returned JSON, you must prefix the `url` with `dicomweb:` in order for the cornerstone image loader to retrieve it correctly.
|
|
||||||
eg. `https://image-server/my-image.dcm` ---> `dicomweb:https://image-server/my-image.dcm`
|
|
||||||
3. The JSON format above is compatible with >= v3.7.8 of the application. Older versions of the viewer used a different JSON format. As of 20/04/20 the public [https://viewer.ohif.org/] is a pre 3.0 version that does not support this format yet.
|
|
||||||
4. The JSON format is case-sensitive. Please ensure you have matched casing with the naturalised Dicom format referenced in [Issue #1500](https://github.com/OHIF/Viewers/issues/1500).
|
|
||||||
|
|
||||||
*CORS Issues (Cross-Origin Resource Sharing)*
|
|
||||||
|
|
||||||
If you host a JSON API or Images on a different domain from the the app itself, you will likely have CORS issues. This will also happen when testing from Localhost and reaching out to remote servers.
|
|
||||||
Even if the domain is the same, different ports, subdomains or protocols (https vs http) will also cause CORS errors.
|
|
||||||
You will to need add a configuration on each server hosting these assets to allow your App server origin.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
Lets assume your application is hosted on `https://my-ohif-server.com`.
|
|
||||||
|
|
||||||
Your JSON API is hosted on `https://my-json-api.aws.com`
|
|
||||||
|
|
||||||
And your images are stored on Amazon S3 at `https://my-s3-bucket.aws.com`
|
|
||||||
|
|
||||||
When you first start your application, browsing to `https://my-ohif-server.com/viewer?url=https://my-json-api.aws.com/api/my-json-study-info.json`, you will likely get a CORS error in the browser console as it tries to connect to `https://my-json-api.aws.com`.
|
|
||||||
|
|
||||||
Adding a setting on the JSON server to allow the CORS origin = `https://my-ohif-server.com` should solve this.
|
|
||||||
|
|
||||||
Next, you will likely get a similar CORS error, as the browser tries to go to `https://my-s3-bucket.aws.com`.
|
|
||||||
You will need to go to the S3 bucket configuration, and add a CORS setting to allow origin = `https://my-ohif-server.com`.
|
|
||||||
|
|
||||||
Essentially, whenever the application connects to a remote resource, you will need to add the applications url to the allowed CORS Origins on that resource. Adding an origin similar to https://localhost:3000 will also allow for local testing.
|
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
### Securing Your Data
|
### Securing Your Data
|
||||||
|
|
||||||
@@ -239,7 +143,7 @@ The OHIF Viewer can be configured to work with authorization servers that
|
|||||||
support one or more of the OpenID-Connect authorization flows. The Viewer finds
|
support one or more of the OpenID-Connect authorization flows. The Viewer finds
|
||||||
it's OpenID-Connect settings on the `oidc` configuration key. You can set these
|
it's OpenID-Connect settings on the `oidc` configuration key. You can set these
|
||||||
values following the instructions laid out in the
|
values following the instructions laid out in the
|
||||||
[Configuration Essentials Guide](./../configuring/index.md).
|
[Configuration Essentials Guide](./../essentials/configuration.md).
|
||||||
|
|
||||||
_Example OpenID-Connect Settings:_
|
_Example OpenID-Connect Settings:_
|
||||||
|
|
||||||
@@ -263,7 +167,7 @@ window.config = {
|
|||||||
```
|
```
|
||||||
|
|
||||||
You can find an example of this setup in our
|
You can find an example of this setup in our
|
||||||
[User Account Control Deployment Recipe](./recipes/user-account-control.md).
|
[User Account Control Deployment Recipe](deployment/recipes/user-account-control.md).
|
||||||
|
|
||||||
#### Choosing a Flow for the Viewer
|
#### Choosing a Flow for the Viewer
|
||||||
|
|
||||||
@@ -280,19 +184,20 @@ many possible configurations, so please don't feel limited to these setups.
|
|||||||
Please feel free to suggest or contribute your own recipes.
|
Please feel free to suggest or contribute your own recipes.
|
||||||
|
|
||||||
- Script Include
|
- Script Include
|
||||||
- [Embedding the Viewer](./recipes/embedded-viewer.md)
|
- [Embedding the Viewer](deployment/recipes/embedded-viewer.md)
|
||||||
- Stand-Alone
|
- Stand-Alone
|
||||||
- [Build for Production](./recipes/build-for-production.md)
|
- [Build for Production](deployment/recipes/build-for-production.md)
|
||||||
- [Static](./recipes/static-assets.md)
|
- [Static](deployment/recipes/static-assets.md)
|
||||||
- [Nginx + Image Archive](./recipes/nginx--image-archive.md)
|
- [Nginx + Image Archive](deployment/recipes/nginx--image-archive.md)
|
||||||
- [User Account Control](./recipes/user-account-control.md)
|
- [User Account Control](deployment/recipes/user-account-control.md)
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Links
|
Links
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
<!-- prettier-ignore-start -->
|
||||||
[viewer-npm]: https://www.npmjs.com/package/@ohif/viewer
|
|
||||||
|
[ohif-viewer-npm]: https://www.npmjs.com/package/ohif-viewer
|
||||||
[pwa-url]: https://developers.google.com/web/progressive-web-apps/
|
[pwa-url]: https://developers.google.com/web/progressive-web-apps/
|
||||||
[static-assets-url]: https://www.maxcdn.com/one/visual-glossary/static-content/
|
[static-assets-url]: https://www.maxcdn.com/one/visual-glossary/static-content/
|
||||||
[app-store]: https://medium.freecodecamp.org/i-built-a-pwa-and-published-it-in-3-app-stores-heres-what-i-learned-7cb3f56daf9b
|
[app-store]: https://medium.freecodecamp.org/i-built-a-pwa-and-published-it-in-3-app-stores-heres-what-i-learned-7cb3f56daf9b
|
||||||
@@ -301,5 +206,5 @@ Please feel free to suggest or contribute your own recipes.
|
|||||||
[host-static-assets]: https://www.netlify.com/blog/2016/05/18/9-reasons-your-site-should-be-static/
|
[host-static-assets]: https://www.netlify.com/blog/2016/05/18/9-reasons-your-site-should-be-static/
|
||||||
[cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
[cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
||||||
[code-flows]: https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660
|
[code-flows]: https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660
|
||||||
[code-sandbox]: https://codesandbox.io/s/viewer-script-tag-tprch
|
[code-sandbox]: https://codesandbox.io/s/ohif-viewer-script-tag-usage-b3st9
|
||||||
<!-- prettier-ignore-end -->
|
<!-- prettier-ignore-end -->
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# Build for Production
|
# Build for Production
|
||||||
|
|
||||||
> If you've already followed the
|
> If you've already followed the
|
||||||
> ["Getting Started" Guide](/development/getting-started.md), you can skip ahead
|
> ["Getting Started" Guide](/essentials/getting-started.md), you can skip ahead
|
||||||
> to [Configuration](#configuration)
|
> to [Configuration](#configuration)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
@@ -19,6 +19,9 @@ _With Git:_
|
|||||||
```bash
|
```bash
|
||||||
# Clone the remote repository to your local machine
|
# Clone the remote repository to your local machine
|
||||||
git clone https://github.com/OHIF/Viewers.git
|
git clone https://github.com/OHIF/Viewers.git
|
||||||
|
|
||||||
|
# Make sure the local code reflects the `react` version of the OHIF Viewer
|
||||||
|
git checkout react
|
||||||
```
|
```
|
||||||
|
|
||||||
More on: _[`git clone`](https://git-scm.com/docs/git-clone),
|
More on: _[`git clone`](https://git-scm.com/docs/git-clone),
|
||||||
@@ -26,7 +29,7 @@ More on: _[`git clone`](https://git-scm.com/docs/git-clone),
|
|||||||
|
|
||||||
_From .zip:_
|
_From .zip:_
|
||||||
|
|
||||||
[OHIF/Viewers: react.zip](https://github.com/OHIF/Viewers/archive/master.zip)
|
[OHIF/Viewers: react.zip](https://github.com/OHIF/Viewers/archive/react.zip)
|
||||||
|
|
||||||
### Restore Dependencies & Build
|
### Restore Dependencies & Build
|
||||||
|
|
||||||
@@ -34,24 +37,20 @@ Open your terminal, and navigate to the directory containing the source files.
|
|||||||
Next run these commands:
|
Next run these commands:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// If you haven't already, enable yarn workspaces
|
|
||||||
yarn config set workspaces-experimental true
|
|
||||||
|
|
||||||
// Restore dependencies
|
// Restore dependencies
|
||||||
yarn install
|
yarn install
|
||||||
|
|
||||||
// Build source code for production
|
// Build source code for production
|
||||||
yarn run build
|
yarn run build:web
|
||||||
```
|
```
|
||||||
|
|
||||||
If everything worked as expected, you should have a new `dist/` directory in the
|
If everything worked as expected, you should have a new `build/` directory in
|
||||||
project's folder. It should roughly resemble the following:
|
the project's folder. It should roughly resemble the following:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
<root>platform/viewer/dist/
|
build
|
||||||
├── app-config.js
|
├── config/
|
||||||
├── app.bundle.js
|
├── static/
|
||||||
├── app.css
|
|
||||||
├── index.html
|
├── index.html
|
||||||
├── manifest.json
|
├── manifest.json
|
||||||
├── service-worker.js
|
├── service-worker.js
|
||||||
@@ -65,15 +64,61 @@ how to configure the project for your own imaging archive below.
|
|||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
The configuration for our viewer is in the `<root>platform/viewer/public/config`
|
> This step assumes you have an imaging archive. If you need assistance setting
|
||||||
directory. Our build process knows which configuration file to use based on the
|
> one up, check out the [`Data Source` Guide](./../../essentials/data-source.md)
|
||||||
`APP_CONFIG` environment variable. By default, its value is
|
> or a deployment recipe that contains an open source Image Archive
|
||||||
[`config/default.js`][default-config]. The majority of the viewer's features,
|
|
||||||
and registered extension's features, are configured using this file.
|
|
||||||
|
|
||||||
The easiest way to apply your own configuration is to modify the `default.js`
|
#### How it Works
|
||||||
file. For more advanced cofiguration options, check out our
|
|
||||||
[configuration essentials guide](/configuring/index.md).
|
The configuration for our project is in the `/public/config` directory. Our
|
||||||
|
build process knows which configuration file to use based on the
|
||||||
|
`REACT_APP_CONFIG` environment variable. By default, its value is
|
||||||
|
[`default.js`](https://github.com/OHIF/Viewers/blob/react/public/config/default.js).
|
||||||
|
When we build, the `%REACT_APP_CONFIG%` value in
|
||||||
|
our[`/public/index.html`](https://github.com/OHIF/Viewers/blob/react/public/index.html#L12-L15)
|
||||||
|
file is substituted for the correct configuration file's name. This sets
|
||||||
|
the`window.config` equal to our configuration file's value.
|
||||||
|
|
||||||
|
#### How do I configure my project?
|
||||||
|
|
||||||
|
The simplest way is to update the existing default config:
|
||||||
|
|
||||||
|
_/public/config/default.js_
|
||||||
|
|
||||||
|
```js
|
||||||
|
window.config = {
|
||||||
|
routerBasename: '/',
|
||||||
|
relativeWebWorkerScriptsPath: '',
|
||||||
|
servers: {
|
||||||
|
dicomWeb: [
|
||||||
|
{
|
||||||
|
name: 'DCM4CHEE',
|
||||||
|
wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
|
||||||
|
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||||
|
wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||||
|
qidoSupportsIncludeField: true,
|
||||||
|
imageRendering: 'wadors',
|
||||||
|
thumbnailRendering: 'wadors',
|
||||||
|
requestOptions: {
|
||||||
|
requestFromBrowser: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also create a new config file and specify its path relative to the build
|
||||||
|
output's root by setting the `REACT_APP_CONFIG` environment variable. You can
|
||||||
|
set the value of this environment variable a few different ways:
|
||||||
|
|
||||||
|
- [Add a temporary environment variable in your shell](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#adding-temporary-environment-variables-in-your-shell)
|
||||||
|
- [Add environment specific variables in `.env` file(s)](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#adding-development-environment-variables-in-env)
|
||||||
|
- Using the `cross-env` package in an npm script:
|
||||||
|
- `"build": "cross-env REACT_APP_CONFIG=config/my-config.js react-scripts build"`
|
||||||
|
|
||||||
|
After updating the configuration, `yarn run build:web` to generate updated build
|
||||||
|
output.
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
@@ -97,7 +142,7 @@ _Advanced_
|
|||||||
### Testing Build Output Locally
|
### Testing Build Output Locally
|
||||||
|
|
||||||
A quick way to test your build output locally is to spin up a small webserver.
|
A quick way to test your build output locally is to spin up a small webserver.
|
||||||
You can do this by running the following commands in the `dist/` output
|
You can do this by running the following commands in the `build/` output
|
||||||
directory:
|
directory:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@@ -118,7 +163,8 @@ web application. For a starting point, check out this repository's own use of:
|
|||||||
|
|
||||||
- [CircleCI][circleci]: [config.yaml][circleci-config]
|
- [CircleCI][circleci]: [config.yaml][circleci-config]
|
||||||
- [Netlify][netlify]: [netlify.toml][netlify.toml] |
|
- [Netlify][netlify]: [netlify.toml][netlify.toml] |
|
||||||
[build-deploy-preview.sh][build-deploy-preview.sh]
|
[generateStaticSite.sh][generatestaticsite.sh]
|
||||||
|
- [Semantic-Release][semantic-release]: [.releaserc][releaserc]
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
@@ -128,8 +174,10 @@ web application. For a starting point, check out this repository's own use of:
|
|||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
<!-- prettier-ignore-start -->
|
||||||
[circleci]: https://circleci.com/gh/OHIF/Viewers
|
[circleci]: https://circleci.com/gh/OHIF/Viewers
|
||||||
[circleci-config]: https://github.com/OHIF/Viewers/blob/master/.circleci/config.yml
|
[circleci-config]: https://github.com/OHIF/Viewers/blob/react/.circleci/config.yml
|
||||||
[netlify]: https://app.netlify.com/sites/ohif/deploys
|
[netlify]: https://app.netlify.com/sites/ohif/deploys
|
||||||
[netlify.toml]: https://github.com/OHIF/Viewers/blob/master/netlify.toml
|
[netlify.toml]: https://github.com/OHIF/Viewers/blob/react/netlify.toml
|
||||||
[build-deploy-preview.sh]: https://github.com/OHIF/Viewers/blob/master/.netlify/build-deploy-preview.sh
|
[generateStaticSite.sh]: https://github.com/OHIF/Viewers/blob/react/generateStaticSite.sh
|
||||||
|
[semantic-release]: https://semantic-release.gitbook.io/semantic-release/
|
||||||
|
[releaserc]: https://github.com/OHIF/Viewers/blob/react/.releaserc
|
||||||
<!-- prettier-ignore-end -->
|
<!-- prettier-ignore-end -->
|
||||||
@@ -12,27 +12,46 @@ include tags. Here's how it works:
|
|||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700&display=swap">
|
<a href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
|
||||||
<code>Google Font: Roboto</code>
|
<code>bootstrap@3.3.7</code>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://unpkg.com/@ohif/viewer">
|
<a href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700|Sanchez&display=swap">
|
||||||
<code>@ohif/viewer@latest</code>
|
<code>Google Fonts, Sanchez & Roboto</code>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://unpkg.com/react@16/umd/react.production.min.js">
|
||||||
|
<code>react@16.8.6</code>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js">
|
||||||
|
<code>react-dom@16.8.6</code>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://unpkg.com/ohif-viewer/dist/index.umd.js">
|
||||||
|
<code>ohif-viewer@latest</code>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ol start="2">
|
<ol start="2">
|
||||||
<li>Create a JS Object or Function to hold the OHIF Viewer's configuration. Here are some
|
<li>The <a href="">WADO Image Loader Codecs and Web Worker source code</a>
|
||||||
|
should be accessible from your server's root</li>
|
||||||
|
<li>Create a JS Object to hold the OHIF Viewer's configuration. Here are some
|
||||||
example values that would allow the viewer to hit our public PACS:</li>
|
example values that would allow the viewer to hit our public PACS:</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Set before importing `ohif-viewer` (JS Object)
|
// Set before importing `ohif-viewer`
|
||||||
window.config = {
|
window.config = {
|
||||||
// default: '/'
|
// default: '/'
|
||||||
routerBasename: '/',
|
routerBasename: '/',
|
||||||
|
// default: ''
|
||||||
|
relativeWebWorkerScriptsPath: '',
|
||||||
servers: {
|
servers: {
|
||||||
dicomWeb: [
|
dicomWeb: [
|
||||||
{
|
{
|
||||||
@@ -43,67 +62,28 @@ window.config = {
|
|||||||
qidoSupportsIncludeField: true,
|
qidoSupportsIncludeField: true,
|
||||||
imageRendering: 'wadors',
|
imageRendering: 'wadors',
|
||||||
thumbnailRendering: 'wadors',
|
thumbnailRendering: 'wadors',
|
||||||
|
requestOptions: {
|
||||||
|
requestFromBrowser: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
To learn more about how you can configure the OHIF Viewer, check out our
|
<ol start="5"><li>
|
||||||
[Configuration Guide](../../configuring/index.md).
|
|
||||||
|
|
||||||
<ol start="3"><li>
|
|
||||||
Render the viewer in the web page's target <code>div</code>
|
Render the viewer in the web page's target <code>div</code>
|
||||||
</li></ol>
|
</li></ol>
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Made available by the `@ohif/viewer` script included in step 1
|
// Made available by the `ohif-viewer` script included in step 1
|
||||||
var containerId = 'id-of-div-to-render-component-to';
|
var Viewer = window.OHIFStandaloneViewer.App;
|
||||||
var componentRenderedOrUpdatedCallback = function() {
|
var app = React.createElement(Viewer, window.config, null);
|
||||||
console.log('OHIF Viewer rendered/updated');
|
|
||||||
};
|
ReactDOM.render(app, document.getElementById('ohif-viewer-target'));
|
||||||
window.OHIFViewer.installViewer(
|
|
||||||
window.config,
|
|
||||||
containerId,
|
|
||||||
componentRenderedOrUpdatedCallback
|
|
||||||
);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can see a live example of this recipe in [this CodeSandbox][code-sandbox].
|
#### Tips & Tricks
|
||||||
|
|
||||||
## Add Extensions
|
|
||||||
|
|
||||||
The UMD build of the OHIF Viewer is a "light weight" build that only contains
|
|
||||||
the core extensions required for basic 2D image viewing. It's possible to add
|
|
||||||
other extensions at runtime.
|
|
||||||
|
|
||||||
This only requires us to include a single script tag, and add it using the
|
|
||||||
`extensions` key to our config. In this practical example, we register our
|
|
||||||
popular whole slide microscopy extension:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<script
|
|
||||||
src="https://unpkg.com/@ohif/extension-dicom-microscopy@0.50.5/dist/index.umd.js"
|
|
||||||
crossorigin
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<!-- --->
|
|
||||||
<script>
|
|
||||||
window.config = {
|
|
||||||
// ...
|
|
||||||
extensions: [OHIFExtDicomMicroscopy],
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
You can see an example of a slide microscopy study in the viewer [with the
|
|
||||||
extension enabled here][whole-slide-ext-demo] ([source code][ext-code-sandbox])
|
|
||||||
and [without it here][whole-slide-base-demo] ([source code][code-sandbox]).
|
|
||||||
|
|
||||||
You can read more about extensions and how to create your own in our
|
|
||||||
[extensions guide](/extensions/index.md).
|
|
||||||
|
|
||||||
#### FAQ
|
|
||||||
|
|
||||||
> I'm having trouble getting this to work. Where can I go for help?
|
> I'm having trouble getting this to work. Where can I go for help?
|
||||||
|
|
||||||
@@ -111,61 +91,16 @@ First, check out this fully functional [CodeSandbox][code-sandbox] example. If
|
|||||||
you're still having trouble, feel free to search or GitHub issues. Can't find
|
you're still having trouble, feel free to search or GitHub issues. Can't find
|
||||||
anything related your problem? Create a new one.
|
anything related your problem? Create a new one.
|
||||||
|
|
||||||
> My application's styles are impacting the OHIF Viewer's look and feel. What
|
> When I include bootstrap, other styles on my page no longer work correctly.
|
||||||
> can I do?
|
> What can I do?
|
||||||
|
|
||||||
When you include stylesheets and scripts, they are added globally. This has the
|
When we include `bootsrap` (and the other dependencies), they are added
|
||||||
potential of causing conflicts with other scripts and styles on the page. To
|
globally. This has the potential of causing conflicts with other scripts and
|
||||||
prevent this, `embed` the viewer in a new/empty web page. Have that working?
|
styles on the page. To prevent this, `embed` the viewer in a new/empty web page.
|
||||||
Good. Now `embed` that new page using an
|
Have that working? Good. Now `embed` that new page using an
|
||||||
[`<iframe>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe).
|
[`<iframe>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe).
|
||||||
|
|
||||||
This should produce the expected result while also protecting your page from any
|
This should produce the expected result while also protecting your page from any
|
||||||
globally defined styles/scripts.
|
globally defined styles/scripts.
|
||||||
|
|
||||||
> We're trying to embed the OHIF Viewer into an existing React App, but seeing
|
[code-sandbox]: https://codesandbox.io/s/ohif-viewer-script-tag-usage-b3st9
|
||||||
> react-dom and react conflicts. What can we do?
|
|
||||||
|
|
||||||
If you are installing OHIF viewer inside another react app, you may use `installViewer` as follows:
|
|
||||||
```
|
|
||||||
import { installViewer } from '@ohif/viewer'
|
|
||||||
|
|
||||||
const ohifViewerConfig = window.config // or set it here
|
|
||||||
const containerId = 'ohif'
|
|
||||||
const componentRenderedOrUpdatedCallback = function() {
|
|
||||||
console.log('OHIF Viewer rendered/updated');
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
installViewer(
|
|
||||||
ohifViewerConfig,
|
|
||||||
containerId,
|
|
||||||
componentRenderedOrUpdatedCallback
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
...
|
|
||||||
//you can render in any element you wish
|
|
||||||
<AnyTag id={containerId}/>
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
`installViewer` is a convenience method that pulls in some dependencies that may
|
|
||||||
not be compatible with existing `react` apps. `@ohif/viewer` also exports `App`
|
|
||||||
which is a react component that takes the `configuration` outlined above as
|
|
||||||
props. You can use it as a reusable component, and to avoid `react` version
|
|
||||||
conflict issues.
|
|
||||||
|
|
||||||
|
|
||||||
<!--
|
|
||||||
LINKS
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
[code-sandbox]: https://codesandbox.io/s/viewer-script-tag-tprch
|
|
||||||
[whole-slide-base-demo]: https://tprch.csb.app/viewer/1.2.392.200140.2.1.1.1.2.799008771.2020.1519719354.757
|
|
||||||
[ext-code-sandbox]: https://codesandbox.io/s/viewer-script-tag-microscopy-extension-44unk
|
|
||||||
[whole-slide-ext-demo]: https://44unk.csb.app/viewer/1.2.392.200140.2.1.1.1.2.799008771.2448.1519719572.518
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
@@ -8,12 +8,10 @@ OHIF Viewer and its medical imaging data. This post covers one of many potential
|
|||||||
setups that accomplish that. Please note, noticably absent is user account
|
setups that accomplish that. Please note, noticably absent is user account
|
||||||
control.
|
control.
|
||||||
|
|
||||||
> **Do not use this recipe to host sensitive medical data on the open web!**
|
Do not use this recipe to host sensitive medical data on the open web. Depending
|
||||||
|
|
||||||
Depending
|
|
||||||
on your company's policies, this may be an appropriate setup on an internal
|
on your company's policies, this may be an appropriate setup on an internal
|
||||||
network when protected with a server's basic authentication. For a more robust
|
network when protected with a server's basic authentication. For a more robust
|
||||||
setup, check out our [user account control recipe](./user-account-control.md)
|
setup, check out our [user account control recpie](./user-account-control.md)
|
||||||
that builds on the lessons learned here.
|
that builds on the lessons learned here.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
@@ -25,7 +23,7 @@ Our two biggest hurdles when hosting our image archive and web client are:
|
|||||||
|
|
||||||
### Handling Web Requests
|
### Handling Web Requests
|
||||||
|
|
||||||
We mitigate our first issue by allowing [Nginx][nginx] to handle incoming web
|
We mittigate our first issue by allowing [Nginx][nginx] to handle incoming web
|
||||||
requests. Nginx is open source software for web serving, reverse proxying,
|
requests. Nginx is open source software for web serving, reverse proxying,
|
||||||
caching, and more. It's designed for maximum performance and stability --
|
caching, and more. It's designed for maximum performance and stability --
|
||||||
allowing us to more reliably serve content than Orthanc's built-in server can.
|
allowing us to more reliably serve content than Orthanc's built-in server can.
|
||||||
@@ -40,7 +38,7 @@ resources from our image archive (Orthanc), and when accessing its web admin.
|
|||||||
|
|
||||||
### CORS Issues
|
### CORS Issues
|
||||||
|
|
||||||
[Cross-Origin Resource Sharing][understanding-cors] (CORS) is a mechanism that uses HTTP headers to
|
Cross-Origin Resource Sharing (CORS) is a mechanism that uses HTTP headers to
|
||||||
tell a browser which web applications have permission to access selected
|
tell a browser which web applications have permission to access selected
|
||||||
resources from a server at a different origin (domain, protocol, port). IE. By
|
resources from a server at a different origin (domain, protocol, port). IE. By
|
||||||
default, a Web App located at `http://my-website.com` can't access resources
|
default, a Web App located at `http://my-website.com` can't access resources
|
||||||
@@ -51,7 +49,12 @@ We can solve this one of two ways:
|
|||||||
1. Have our Image Archive located at the same domain as our Web App
|
1. Have our Image Archive located at the same domain as our Web App
|
||||||
2. Add appropriate `Access-Control-Allow-*` HTTP headers
|
2. Add appropriate `Access-Control-Allow-*` HTTP headers
|
||||||
|
|
||||||
**This solution uses the first approach.**
|
This solution uses the first approach, but you can see an example of the second
|
||||||
|
in the `docker-compose` bundled with this project for local development:
|
||||||
|
[HERE](#)
|
||||||
|
|
||||||
|
You can read more about CORS in this Medium article: [Understanding
|
||||||
|
CORS][understanding-cors]
|
||||||
|
|
||||||
### Diagram
|
### Diagram
|
||||||
|
|
||||||
@@ -72,8 +75,6 @@ This setup allows us to create a setup similar to the one pictured below:
|
|||||||
- Docker
|
- Docker
|
||||||
- [Docker for Mac](https://docs.docker.com/docker-for-mac/)
|
- [Docker for Mac](https://docs.docker.com/docker-for-mac/)
|
||||||
- [Docker for Windows](https://docs.docker.com/docker-for-windows/)
|
- [Docker for Windows](https://docs.docker.com/docker-for-windows/)
|
||||||
- Node 16.x with optional Python and Visual Studio Build Tools ([webpack 4 has an issue](https://github.com/webpack/webpack/issues/14532) with Node 17.x)
|
|
||||||
- [Node.js](https://nodejs.org/en/download/)
|
|
||||||
|
|
||||||
_Not sure if you have `docker` installed already? Try running `docker --version`
|
_Not sure if you have `docker` installed already? Try running `docker --version`
|
||||||
in command prompt or terminal_
|
in command prompt or terminal_
|
||||||
@@ -82,7 +83,7 @@ in command prompt or terminal_
|
|||||||
|
|
||||||
_Spin Things Up_
|
_Spin Things Up_
|
||||||
|
|
||||||
- Navigate to `./platform/viewer/.recipes/OpenResty-Orthanc` in your shell
|
- Navigate to `<project-root>/docker/OpenResty-Orthanc` in your shell
|
||||||
- Run `docker-compose up`
|
- Run `docker-compose up`
|
||||||
|
|
||||||
_Upload Your First Study_
|
_Upload Your First Study_
|
||||||
@@ -95,16 +96,16 @@ _Upload Your First Study_
|
|||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
|
|
||||||
#### Exit code 137
|
_Exit code 137_
|
||||||
|
|
||||||
This means Docker ran out of memory. Open Docker Desktop, go to the `advanced`
|
This means Docker ran out of memory. Open Docker Desktop, go to the `advanced`
|
||||||
tab, and increase the amount of Memory available.
|
tab, and increase the amount of Memory available.
|
||||||
|
|
||||||
#### Cannot create container for service X
|
_Cannot create container for service X_
|
||||||
|
|
||||||
Use this one with caution: `docker system prune`
|
Use this one with caution: `docker system prune`
|
||||||
|
|
||||||
#### X is already running
|
_X is already running_
|
||||||
|
|
||||||
Stop running all containers:
|
Stop running all containers:
|
||||||
|
|
||||||
@@ -120,12 +121,16 @@ likely want to update:
|
|||||||
|
|
||||||
#### OHIF Viewer
|
#### OHIF Viewer
|
||||||
|
|
||||||
The OHIF Viewer's configuration is imported from a static `.js` file. The
|
The OHIF Viewer's configuration is imported from a static `.js` file and made
|
||||||
configuration we use is set to [a specific file][config-ohif] when we build the viewer, and
|
available globally at `window.config`. The configuration we use is set to a
|
||||||
determined by the env variable: `APP_CONFIG`. You can see where we set its value
|
specific file when we build the viewer, and determined by the env variable:
|
||||||
in the `dockerfile` for this solution:
|
`REACT_APP_CONFIG`. You can see where we set its value in the `dockerfile` for
|
||||||
|
this solution:
|
||||||
|
|
||||||
`ENV APP_CONFIG=config/docker_openresty-orthanc.js`
|
`ENV REACT_APP_CONFIG=config/docker_openresty-orthanc.js`
|
||||||
|
|
||||||
|
You can find the configuration we're using here:
|
||||||
|
`/public/config/docker_openresty-orthanc.js`
|
||||||
|
|
||||||
To rebuild the `webapp` image created by our `dockerfile` after updating the
|
To rebuild the `webapp` image created by our `dockerfile` after updating the
|
||||||
Viewer's configuration, you can run:
|
Viewer's configuration, you can run:
|
||||||
@@ -135,11 +140,11 @@ Viewer's configuration, you can run:
|
|||||||
|
|
||||||
#### Other
|
#### Other
|
||||||
|
|
||||||
All other files in this directory include
|
All other files are found in: `/docker/OpenResty-Orthanc/`
|
||||||
|
|
||||||
| Service | Configuration | Docs |
|
| Service | Configuration | Docs |
|
||||||
| ----------------- | --------------------------------- | ------------------------------------------- |
|
| ----------------- | ---------------------------------------------- | ------------------------------------------- |
|
||||||
| OHIF Viewer | [dockerfile][dockerfile] | You're reading them now! |
|
| OHIF Viewer | [dockerfile][dockerfile] / [config.js][config] | You're reading them now! |
|
||||||
| OpenResty (Nginx) | [`/nginx.conf`][config-nginx] | [lua-resty-openidc][lua-resty-openidc-docs] |
|
| OpenResty (Nginx) | [`/nginx.conf`][config-nginx] | [lua-resty-openidc][lua-resty-openidc-docs] |
|
||||||
| Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] |
|
| Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] |
|
||||||
|
|
||||||
@@ -217,11 +222,10 @@ following resources helpful:
|
|||||||
- [OpenResty Guide](http://www.staticshin.com/programming/definitely-an-open-resty-guide/)
|
- [OpenResty Guide](http://www.staticshin.com/programming/definitely-an-open-resty-guide/)
|
||||||
- [Lua Ngx API](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/)
|
- [Lua Ngx API](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/)
|
||||||
|
|
||||||
For a different take on this setup, check out the repositories our community
|
For a different take on this setup, check out the repository one of our
|
||||||
members put together:
|
community members put together:
|
||||||
|
|
||||||
- [mjstealey/ohif-orthanc-dimse-docker](https://github.com/mjstealey/ohif-orthanc-dimse-docker)
|
- [mjstealey/ohif-orthanc-dimse-docker](https://github.com/mjstealey/ohif-orthanc-dimse-docker)
|
||||||
- [trypag/ohif-orthanc-postgres-docker](https://github.com/trypag/ohif-orthanc-postgres-docker)
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Links
|
Links
|
||||||
@@ -234,9 +238,8 @@ members put together:
|
|||||||
[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration
|
[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration
|
||||||
[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc
|
[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc
|
||||||
<!-- SRC -->
|
<!-- SRC -->
|
||||||
|
[config]: #
|
||||||
[config-ohif]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/public/config/docker_openresty-orthanc.js
|
[dockerfile]: #
|
||||||
[dockerfile]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc/dockerfile
|
[config-nginx]: #
|
||||||
[config-nginx]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc/config/nginx.conf
|
[config-orthanc]: #
|
||||||
[config-orthanc]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc/config/orthanc.json
|
|
||||||
<!-- prettier-ignore-end -->
|
<!-- prettier-ignore-end -->
|
||||||
@@ -122,12 +122,13 @@ likely want to update:
|
|||||||
|
|
||||||
#### OHIF Viewer
|
#### OHIF Viewer
|
||||||
|
|
||||||
The OHIF Viewer's configuration is imported from a static `.js` file. The
|
The OHIF Viewer's configuration is imported from a static `.js` file and made
|
||||||
configuration we use is set to a specific file when we build the viewer, and
|
available globally at `window.config`. The configuration we use is set to a
|
||||||
determined by the env variable: `APP_CONFIG`. You can see where we set its value
|
specific file when we build the viewer, and determined by the env variable:
|
||||||
in the `dockerfile` for this solution:
|
`REACT_APP_CONFIG`. You can see where we set its value in the `dockerfile` for
|
||||||
|
this solution:
|
||||||
|
|
||||||
`ENV APP_CONFIG=config/docker_openresty-orthanc-keycloak.js`
|
`ENV REACT_APP_CONFIG=config/docker_openresty-orthanc-keycloak.js`
|
||||||
|
|
||||||
You can find the configuration we're using here:
|
You can find the configuration we're using here:
|
||||||
`/public/config/docker_openresty-orthanc-keycloak.js`
|
`/public/config/docker_openresty-orthanc-keycloak.js`
|
||||||
@@ -149,8 +150,8 @@ All other files are found in: `/docker/OpenResty-Orthanc-Keycloak/`
|
|||||||
| Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] |
|
| Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] |
|
||||||
| Keycloak | [`/ohif-keycloak-realm.json`][config-keycloak]\* | |
|
| Keycloak | [`/ohif-keycloak-realm.json`][config-keycloak]\* | |
|
||||||
|
|
||||||
\* These are the seed values for Keycloak. They can be manually updated at
|
- \* These are the seed values for Keycloak. They can be manually updated at
|
||||||
`http://127.0.0.1/auth/admin`
|
`http://127.0.0.1/auth/admin`
|
||||||
|
|
||||||
#### Keycloak Themeing
|
#### Keycloak Themeing
|
||||||
|
|
||||||
@@ -264,12 +265,12 @@ for OAuth:
|
|||||||
|
|
||||||
- [Diagrams of OpenID Connect Flows](https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660)
|
- [Diagrams of OpenID Connect Flows](https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660)
|
||||||
- [KeyCloak: OpenID Connect Flows](https://www.keycloak.org/docs/latest/securing_apps/index.html#authorization-code)
|
- [KeyCloak: OpenID Connect Flows](https://www.keycloak.org/docs/latest/securing_apps/index.html#authorization-code)
|
||||||
|
- [Good description on SSO Protocols](https://www.keycloak.org/docs/2.5/server_admin/topics/sso-protocols/oidc.html)
|
||||||
|
|
||||||
For a different take on this setup, check out the repositories our community
|
For a different take on this setup, check out the repository one of our
|
||||||
members put together:
|
community members put together:
|
||||||
|
|
||||||
- [mjstealey/ohif-orthanc-dimse-docker](https://github.com/mjstealey/ohif-orthanc-dimse-docker)
|
- [mjstealey/ohif-orthanc-dimse-docker](https://github.com/mjstealey/ohif-orthanc-dimse-docker)
|
||||||
- [trypag/ohif-orthanc-postgres-docker](https://github.com/trypag/ohif-orthanc-postgres-docker)
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Links
|
Links
|
||||||
@@ -280,9 +281,9 @@ members put together:
|
|||||||
[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration
|
[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration
|
||||||
[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc
|
[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc
|
||||||
<!-- SRC -->
|
<!-- SRC -->
|
||||||
[config]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/src/config.js
|
[config]: #
|
||||||
[dockerfile]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/dockerfile
|
[dockerfile]: #
|
||||||
[config-nginx]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/nginx.conf
|
[config-nginx]: #
|
||||||
[config-orthanc]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/orthanc.json
|
[config-orthanc]: #
|
||||||
[config-keycloak]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/ohif-keycloak-realm.json
|
[config-keycloak]: #
|
||||||
<!-- prettier-ignore-end -->
|
<!-- prettier-ignore-end -->
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
# Continous Integration (CI)
|
|
||||||
|
|
||||||
This repository uses `CircleCI` and `Netlify` for continous integration.
|
|
||||||
|
|
||||||
## Deploy Previews
|
|
||||||
|
|
||||||
[Netlify Deploy previews][deploy-previews] are generated for every pull request.
|
|
||||||
They allow pull request authors and reviewers to "Preview" the OHIF Viewer as if
|
|
||||||
the changes had been merged.
|
|
||||||
|
|
||||||
Deploy previews can be configured by modifying the `netlify.toml` file in the
|
|
||||||
root of the repository. Some additional scripts/assets for netlify are included
|
|
||||||
in the root `.netlify` directory.
|
|
||||||
|
|
||||||
## Workflows
|
|
||||||
|
|
||||||
[CircleCI Workflows][circleci-workflows] are a set of rules for defining a
|
|
||||||
collection of jobs and their run order. They are self-documenting and their
|
|
||||||
configuration can be found in our CircleCI configuration file:
|
|
||||||
`.circleci/config.yml`.
|
|
||||||
|
|
||||||
### Workflow: PR_CHECKS
|
|
||||||
|
|
||||||
The PR_CHECKS workflow (Pull Request Checks) runs our automated unit and
|
|
||||||
end-to-end tests for every code check-in. These tests must all pass before code
|
|
||||||
can be merged to our `master` branch.
|
|
||||||
|
|
||||||
<div style="text-align: center;">
|
|
||||||
<a href="/assets/img/WORKFLOW_PR_CHECKS.png">
|
|
||||||
<img src="/assets/img/WORKFLOW_PR_CHECKS.png" alt="workflow diagram" style="margin: 0 auto; max-width: 500px;" />
|
|
||||||
</a>
|
|
||||||
<div><i>Workflow diagram for PR_CHECKS</i></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
### Workflow: PR_OPTIONAL_DOCKER_PUBLISH
|
|
||||||
|
|
||||||
The PR_OPTIONAL_DOCKER_PUBLISH workflow allows for "manual approval" to publish
|
|
||||||
the pull request as a tagged docker image. This is helpful when changes need to
|
|
||||||
be tested with the Google Adapter before merging to `master`.
|
|
||||||
|
|
||||||
<div style="text-align: center;">
|
|
||||||
<a href="/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png">
|
|
||||||
<img src="/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png" alt="workflow diagram" style="margin: 0 auto; max-width: 500px;" />
|
|
||||||
</a>
|
|
||||||
<div><i>Workflow diagram for PR_WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH</i></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
> NOTE: This workflow will fail unless it's for a branch on our `upstream`
|
|
||||||
> repository. If you need this functionality, but the branch is from a fork,
|
|
||||||
> merge the changes to a short-lived `feature/` branch on `upstream`
|
|
||||||
|
|
||||||
### Workflow: DEPLOY
|
|
||||||
|
|
||||||
The DEPLOY workflow deploys the OHIF Viewer when changes are merged to master.
|
|
||||||
It uses the Netlify CLI to deploy assets created as part of the repository's PWA
|
|
||||||
Build process (`yarn run build`). The workflow allows for "Manual Approval" to
|
|
||||||
promote the build to `STAGING` and `PRODUCTION` environments.
|
|
||||||
|
|
||||||
<div style="text-align: center;">
|
|
||||||
<a href="/assets/img/WORKFLOW_DEPLOY.png">
|
|
||||||
<img src="/assets/img/WORKFLOW_DEPLOY.png" alt="workflow diagram" style="margin: 0 auto; max-width: 500px;" />
|
|
||||||
</a>
|
|
||||||
<div><i>Workflow diagram for WORKFLOW_DEPLOY</i></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
| Environment | Description | URL |
|
|
||||||
| ----------- | ---------------------------------------------------------------------------------- | --------------------------------------------- |
|
|
||||||
| Development | Always reflects latest changes on `master` branch. | [Netlify][netlify-dev] / [OHIF][ohif-dev] |
|
|
||||||
| Staging | For manual testing before promotion to prod. Keeps development workflow unblocked. | [Netlify][netlify-stage] / [OHIF][ohif-stage] |
|
|
||||||
| Production | Stable, tested, updated less frequently. | [Netlify][netlify-prod] / [OHIF][ohif-prod] |
|
|
||||||
|
|
||||||
### Workflow: RELEASE
|
|
||||||
|
|
||||||
The RELEASE workflow publishes our `npm` packages, updated documentation, and
|
|
||||||
`docker` image when changes are merged to master. `Lerna` and "Semantic Commit
|
|
||||||
Syntax" are used to independently version and publish the many packages in our
|
|
||||||
monorepository. If a new version is cut/released, a Docker image is created.
|
|
||||||
Documentation is generated with `gitbook` and pushed to our `gh-pages` branch.
|
|
||||||
GitHub hosts the `gh-pages` branch with GitHub Pages.
|
|
||||||
|
|
||||||
- Platform Packages: https://github.com/ohif/viewers/#platform
|
|
||||||
- Extension Packages: https://github.com/ohif/viewers/#extensions
|
|
||||||
- Documentation: https://docs.ohif.org/
|
|
||||||
|
|
||||||
<div style="text-align: center;">
|
|
||||||
<a href="/assets/img/WORKFLOW_RELEASE.png">
|
|
||||||
<img src="/assets/img/WORKFLOW_RELEASE.png" alt="workflow diagram" style="margin: 0 auto; max-width: 500px;" />
|
|
||||||
</a>
|
|
||||||
<div><i>Workflow diagram for WORKFLOW_RELEASE</i></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
### HOTFIX
|
|
||||||
|
|
||||||
_Not yet implemented_
|
|
||||||
|
|
||||||
<!--
|
|
||||||
LINKS
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
[deploy-previews]: https://www.netlify.com/blog/2016/07/20/introducing-deploy-previews-in-netlify/
|
|
||||||
[circleci-workflows]: https://circleci.com/docs/2.0/workflows/
|
|
||||||
[netlify-dev]: https://ohif-dev.netlify.com
|
|
||||||
[netlify-stage]: https://ohif-stage.netlify.com
|
|
||||||
[netlify-prod]: https://ohif-prod.netlify.com
|
|
||||||
[ohif-dev]: https://viewer-dev.ohif.org
|
|
||||||
[ohif-stage]: https://viewer-stage.ohif.org
|
|
||||||
[ohif-prod]: https://viewer-prod.ohif.org
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
# Contributing
|
|
||||||
|
|
||||||
## How can I help?
|
|
||||||
|
|
||||||
Fork the repository, make your change and submit a pull request. If you would
|
|
||||||
like to discuss the changes you intend to make to clarify where or how they
|
|
||||||
should be implemented, please don't hesitate to create a new issue. At a
|
|
||||||
minimum, you may want to read the following documentation:
|
|
||||||
|
|
||||||
- [Getting Started](/development/getting-started.md)
|
|
||||||
- [Architecture](/architecture/index.md)
|
|
||||||
|
|
||||||
Pull requests that are:
|
|
||||||
|
|
||||||
- Small
|
|
||||||
- [Well tested](./testing.md)
|
|
||||||
- Decoupled
|
|
||||||
|
|
||||||
Are much more likely to get reviewed and merged in a timely manner.
|
|
||||||
|
|
||||||
## When changes impact multiple repositories
|
|
||||||
|
|
||||||
While this can be tricky, we've tried to reduce how often this situation crops
|
|
||||||
up this with our [recent switch to a monorepo][monorepo]. Our maintained
|
|
||||||
extensions, ui components, internationalization library, and business logic can
|
|
||||||
all be developed by simply running `yarn run dev` from the repository root.
|
|
||||||
|
|
||||||
Testing the viewer with locally developed, unpublished package changes from a
|
|
||||||
package outside of the monorepo is most common with extension development. Let's
|
|
||||||
demonstrate how to accomplish this with two commonly forked extension
|
|
||||||
dependencies:
|
|
||||||
|
|
||||||
### `cornerstone-tools`
|
|
||||||
|
|
||||||
On your local file system:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# code/my-projects/
|
|
||||||
.
|
|
||||||
├── cornerstonejs/cornerstone-tools
|
|
||||||
└── ohif/viewers
|
|
||||||
```
|
|
||||||
|
|
||||||
- Open a terminal/shell
|
|
||||||
- Navigate to `cornerstonejs/cornerstone-tools`
|
|
||||||
- `npm install`
|
|
||||||
- [`yarn link`](https://yarnpkg.com/en/docs/cli/link)
|
|
||||||
- `npm run dev`
|
|
||||||
- Open a new terminal/shell
|
|
||||||
- Navigate to `ohif/viewers`.
|
|
||||||
- `yarn install`
|
|
||||||
- [`yarn link cornerstone-tools`](https://yarnpkg.com/en/docs/cli/link)
|
|
||||||
- `yarn run dev`
|
|
||||||
|
|
||||||
As you make changed to `cornerstone-tools`, and it's output is rebuilt, you
|
|
||||||
should see the following behavior:
|
|
||||||
|
|
||||||
<div style="text-align: center;">
|
|
||||||
<a href="/assets/img/cornerstone-tools-link.gif">
|
|
||||||
<img src="/assets/img/cornerstone-tools-link.gif" alt="Example of linked cornerstone-tools package" style="margin: 0 auto; max-width: 500px;" />
|
|
||||||
</a>
|
|
||||||
<div><i>example of linked cornerstone-tools package</i></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
If you wish to stop using your local package, run the following commands in the
|
|
||||||
`ohif/viewers` repository root:
|
|
||||||
|
|
||||||
- `yarn unlink cornerstone-tools`
|
|
||||||
- `yarn install --force`
|
|
||||||
|
|
||||||
### `react-vtkjs-viewport`
|
|
||||||
|
|
||||||
On your local file system:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# code/my-projects/
|
|
||||||
.
|
|
||||||
├── ohif/react-vtkjs-viewport
|
|
||||||
└── ohif/viewers
|
|
||||||
```
|
|
||||||
|
|
||||||
- Open a terminal/shell
|
|
||||||
- Navigate to `ohif/react-vtkjs-viewport`
|
|
||||||
- `yarn install`
|
|
||||||
- [`yarn link`](https://yarnpkg.com/en/docs/cli/link)
|
|
||||||
- `yarn run start`
|
|
||||||
- Open a new terminal/shell
|
|
||||||
- Navigate to `ohif/viewers`.
|
|
||||||
- `yarn install`
|
|
||||||
- [`yarn link react-vtkjs-viewport`](https://yarnpkg.com/en/docs/cli/link)
|
|
||||||
- `yarn run dev`
|
|
||||||
|
|
||||||
#### Other linkage notes
|
|
||||||
|
|
||||||
We're still working out some of the kinks with local package development as
|
|
||||||
there are a lot of factors that can influence the behavior of our development
|
|
||||||
server and bundler. If you encounter issues not addressed here, please don't
|
|
||||||
hesitate to reach out on GitHub.
|
|
||||||
|
|
||||||
## Any guidance on submitting changes?
|
|
||||||
|
|
||||||
While we do appreciate code contributions, triaging and integrating contributed
|
|
||||||
code changes can be very time consuming. Please consider the following tips when
|
|
||||||
working on your pull requests:
|
|
||||||
|
|
||||||
- Functionality is appropriate for the repository. Consider creating a GitHub
|
|
||||||
issue to discuss your suggested changes.
|
|
||||||
- The scope of the pull request is not too large. Please consider separate pull
|
|
||||||
requests for each feature as big pull requests are very time consuming to
|
|
||||||
understand.
|
|
||||||
|
|
||||||
We will provide feedback on your pull requests as soon as possible. Following
|
|
||||||
the tips above will help ensure your changes are reviewed.
|
|
||||||
|
|
||||||
## Testing contribution pull requests
|
|
||||||
|
|
||||||
OHIF uses [netlify](https://www.netlify.com/) so that pull requests are
|
|
||||||
autogenerated and available for testing.
|
|
||||||
|
|
||||||
For example, [this url][example-url] allows you to test [pull request 237, the
|
|
||||||
request that created this FAQ entry,][pr-237] using data pulled from Amazon S3.
|
|
||||||
|
|
||||||
Replacing the number 237 in the link below with your pull request number should
|
|
||||||
let you test it as well and you can use this link for discussions on github
|
|
||||||
without requiring reviewers to download and build your branch.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
https://deploy-preview-237--ohif.netlify.com/viewer/?url=https://s3.eu-central-1.amazonaws.com/ohif-viewer/sampleDICOM.json
|
|
||||||
```
|
|
||||||
|
|
||||||
If you have made a documentation change, a link like this will let you preview
|
|
||||||
the gitbook generated by the pull request:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
https://deploy-preview-237--ohif.netlify.com/contributing.html
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Links
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
[example-url]: https://deploy-preview-237--ohif.netlify.com/viewer/?url=https://s3.eu-central-1.amazonaws.com/ohif-viewer/sampleDICOM.json
|
|
||||||
[pr-237]: https://github.com/OHIF/Viewers/pull/237
|
|
||||||
[monorepo]: https://github.com/OHIF/Viewers/issues/768
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
# Contributing: Tests
|
|
||||||
|
|
||||||
> Testing is an opinionated topic. Here is a rough overview of our testing
|
|
||||||
> philosiphy. See something you want to discuss or think should be changed? Open
|
|
||||||
> a PR and let's discuss.
|
|
||||||
|
|
||||||
You're an engineer. You know how to write code, and writing tests isn't all that
|
|
||||||
different. But do you know why we write tests? Do you know when to write one, or
|
|
||||||
what kind of test to write? How do you know if a test is a _"good"_ test? This
|
|
||||||
document's goal is to give you the tools you need to make those determinations.
|
|
||||||
|
|
||||||
Okay. So why do we write tests? To increase our... **CONFIDENCE**
|
|
||||||
|
|
||||||
- If I do a large refactor, does everything still work?
|
|
||||||
- If I changed some critical piece of code, is it safe to push to production?
|
|
||||||
|
|
||||||
Gaining the confidence we need to answer these questions after every change is
|
|
||||||
costly. Good tests allow us to answer them without manual regression testing.
|
|
||||||
What and how we choose to test to increase that confidence is nuanced.
|
|
||||||
|
|
||||||
## Kinds of Tests
|
|
||||||
|
|
||||||
Test's buy us confidence, but not all tests are created equal. Each kind of test
|
|
||||||
has a different cost to write and maintain. An expensive test is worth it if it
|
|
||||||
gives us confidence that a payment is processed, but it may not be the best
|
|
||||||
choice for asserting an element's border color.
|
|
||||||
|
|
||||||
| Test Type | Example | Speed | Cost |
|
|
||||||
| ----------- | ------------------------------------------------------------------------ | ---------------- | ------------------------------------------------------------------------ |
|
|
||||||
| Static | `addNums(1, '2')` called with `string`, expected `int`. | :rocket: Instant | :money_with_wings: |
|
|
||||||
| Unit | `addNums(1, 2)` returns expected result `3` | :airplane: Fast | :money_with_wings::money_with_wings: |
|
|
||||||
| Integration | Clicking "Sign In", navigates to the dashboard (mocked network requests) | :running: Okay | :money_with_wings::money_with_wings::money_with_wings: |
|
|
||||||
| End-to-end | Clicking "Sign In", navigates to the dashboard (no mocks) | :turtle: Slow | :money_with_wings::money_with_wings::money_with_wings::money_with_wings: |
|
|
||||||
|
|
||||||
- :rocket: Speed: How quickly tests run
|
|
||||||
- :money_with_wings: Cost: Time to write, and to debug when broken (more points
|
|
||||||
of failure)
|
|
||||||
|
|
||||||
### Static Code Analysis
|
|
||||||
|
|
||||||
Modern tooling gives us this "for free". It can catch invalid regular
|
|
||||||
expressions, unused variables, and guarantee we're calling methods/functions
|
|
||||||
with the expected paramater types.
|
|
||||||
|
|
||||||
Example Tooling:
|
|
||||||
|
|
||||||
- [ESLint][eslint-rules]
|
|
||||||
- [TypeScript][typescript-docs] or [Flow][flow-org]
|
|
||||||
|
|
||||||
### Unit Tests
|
|
||||||
|
|
||||||
The building blocks of our libraries and applications. For these, you'll often
|
|
||||||
be testing a single function or method. Conceptually, this equates to:
|
|
||||||
|
|
||||||
_Pure Function Test:_
|
|
||||||
|
|
||||||
- If I call `sum(2, 2)`, I expect the output to be `4`
|
|
||||||
|
|
||||||
_Side Effect Test:_
|
|
||||||
|
|
||||||
- If I call `resetViewport(viewport)`, I expect `cornerstone.reset` to be called
|
|
||||||
with `viewport`
|
|
||||||
|
|
||||||
#### When to use
|
|
||||||
|
|
||||||
Anything that is exposed as public API should have unit tests.
|
|
||||||
|
|
||||||
#### When to avoid
|
|
||||||
|
|
||||||
You're actually testing implementation details. You're testing implementation
|
|
||||||
details if:
|
|
||||||
|
|
||||||
- Your test does something that the consumer of your code would never do.
|
|
||||||
- IE. Using a private function
|
|
||||||
- A refactor can break your tests
|
|
||||||
|
|
||||||
### Integration Tests
|
|
||||||
|
|
||||||
We write integration tests to gain confidence that several units work together.
|
|
||||||
Generally, we want to mock as little as possible for these tests. In practice,
|
|
||||||
this means only mocking network requests.
|
|
||||||
|
|
||||||
#### When to use
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
### End-to-End Tests
|
|
||||||
|
|
||||||
These are the most expensive tests to write and maintain. Largely because, when
|
|
||||||
they fail, they have the largest number of potential points of failure. So why
|
|
||||||
do we write them? Because they also buy us the most confidence.
|
|
||||||
|
|
||||||
#### When to use
|
|
||||||
|
|
||||||
Mission critical features and functionality, or to cover a large breadth of
|
|
||||||
functionality until unit tests catch up. Unsure if we should have a test for
|
|
||||||
feature `X` or scenario `Y`? Open an issue and let's discuss.
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
- Does your test increase confidence?
|
|
||||||
- Does the test type chosen balance the cost-to-confidence ratio?
|
|
||||||
|
|
||||||
## Further Reading
|
|
||||||
|
|
||||||
### General
|
|
||||||
|
|
||||||
- [Assert(js) Conf 2018 Talks][assert-js-talks]
|
|
||||||
- [Write tests. Not too many. Mostly integration.][kent-talk] - Kent C. Dodds
|
|
||||||
- [I see your point, but…][gleb-talk] - Gleb Bahmutov
|
|
||||||
- [Static vs Unit vs Integration vs E2E Testing][kent-blog] - Kent C. Dodds
|
|
||||||
(Blog)
|
|
||||||
|
|
||||||
### End-to-end Testing w/ Cypress
|
|
||||||
|
|
||||||
- [Getting Started](https://docs.cypress.io/guides/overview/why-cypress.html)
|
|
||||||
- Be sure to check out `Getting Started` and `Core Concepts`
|
|
||||||
- [Best Practices](https://docs.cypress.io/guides/references/best-practices.html)
|
|
||||||
- [Example Recipes](https://docs.cypress.io/examples/examples/recipes.html)
|
|
||||||
|
|
||||||
## Testing Dorito
|
|
||||||
|
|
||||||
[![testing dorito][testing-dorito-img]][testing-dorito]
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Links
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
[eslint-rules]: https://eslint.org/docs/rules/
|
|
||||||
[typescript-docs]: https://www.typescriptlang.org/docs/home.html
|
|
||||||
[flow-org]: https://flow.org/
|
|
||||||
<!-- Talks -->
|
|
||||||
[assert-js-talks]: https://www.youtube.com/playlist?list=PLZ66c9_z3umNSrKSb5cmpxdXZcIPNvKGw
|
|
||||||
[kent-talk]: https://www.youtube.com/watch?v=Fha2bVoC8SE
|
|
||||||
[gleb-talk]: https://www.youtube.com/watch?v=5FnalKRjpZk
|
|
||||||
[kent-blog]: https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests
|
|
||||||
<!-- Images -->
|
|
||||||
[testing-trophy]: https://twitter.com/kentcdodds/status/960723172591992832?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E960723172591992832&ref_url=https%3A%2F%2Fkentcdodds.com%2Fblog%2Fwrite-tests
|
|
||||||
[aaron-square]: https://twitter.com/Carofine247/status/966727489274961920
|
|
||||||
[gleb-pyramid]: https://twitter.com/Carofine247/status/966764532046684160/photo/3
|
|
||||||
[testing-pyramid]: https://dojo.ministryoftesting.com/dojo/lessons/the-mobile-test-pyramid
|
|
||||||
[testing-dorito]: https://twitter.com/denvercoder/status/960752578198843392
|
|
||||||
[testing-dorito-img]: https://pbs.twimg.com/media/DVVHXycUMAAcN-F?format=jpg&name=4096x4096
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# Configuration
|
||||||
|
|
||||||
|
> This step assumes you have an imaging archive. If you need assistance setting
|
||||||
|
> one up, check out the [`Data Source` Guide](./data-source.md) or a deployment
|
||||||
|
> recipe that contains an open source Image Archive
|
||||||
|
|
||||||
|
## How it Works
|
||||||
|
|
||||||
|
The configuration for our project is in the `/public/config` directory. Our
|
||||||
|
build process knows which configuration file to use based on the
|
||||||
|
`REACT_APP_CONFIG` environment variable. By default, its value is
|
||||||
|
[`default.js`](https://github.com/OHIF/Viewers/blob/react/public/config/default.js).
|
||||||
|
When we build, the `%REACT_APP_CONFIG%` value in
|
||||||
|
our[`/public/index.html`](https://github.com/OHIF/Viewers/blob/react/public/index.html#L12-L15)
|
||||||
|
file is substituted for the correct configuration file's name. This sets the
|
||||||
|
`window.config` equal to our configuration file's value.
|
||||||
|
|
||||||
|
## How do I configure my project?
|
||||||
|
|
||||||
|
The simplest way is to update the existing default config:
|
||||||
|
|
||||||
|
_/public/config/default.js_
|
||||||
|
|
||||||
|
```js
|
||||||
|
window.config = {
|
||||||
|
routerBasename: '/',
|
||||||
|
relativeWebWorkerScriptsPath: '',
|
||||||
|
servers: {
|
||||||
|
dicomWeb: [
|
||||||
|
{
|
||||||
|
name: 'DCM4CHEE',
|
||||||
|
wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
|
||||||
|
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||||
|
wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||||
|
qidoSupportsIncludeField: true,
|
||||||
|
imageRendering: 'wadors',
|
||||||
|
thumbnailRendering: 'wadors',
|
||||||
|
requestOptions: {
|
||||||
|
requestFromBrowser: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also create a new config file and specify its path relative to the build
|
||||||
|
output's root by setting the `REACT_APP_CONFIG` environment variable. You can
|
||||||
|
set the value of this environment variable a few different ways:
|
||||||
|
|
||||||
|
- [Add a temporary environment variable in your shell](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#adding-temporary-environment-variables-in-your-shell)
|
||||||
|
- [Add environment specific variables in `.env` file(s)](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#adding-development-environment-variables-in-env)
|
||||||
|
- Using the `cross-env` package in an npm script:
|
||||||
|
- `"build": "cross-env REACT_APP_CONFIG=config/my-config.js react-scripts build"`
|
||||||
|
|
||||||
|
After updating the configuration, `yarn run build:web` to generate updated build
|
||||||
|
output.
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
# Data Source
|
# Data Source
|
||||||
|
|
||||||
After following the steps outlined in
|
After following the steps outlined in [Getting Started](./getting-started.md),
|
||||||
[Getting Started](./../development/getting-started.md), you'll notice that the
|
you'll notice that the OHIF Viewer has data for several studies and their
|
||||||
OHIF Viewer has data for several studies and their images. You didn't add this
|
images. You didn't add this data, so where is it coming from?
|
||||||
data, so where is it coming from?
|
|
||||||
|
|
||||||
By default, the viewer is configured to connect to a remote server hosted by the
|
By default, the viewer is configured to connect to a remote server hosted by the
|
||||||
nice folks over at [dcmjs.org][dcmjs-org]. While convenient for getting started,
|
nice folks over at [dcmjs.org][dcmjs-org]. While convenient for getting started,
|
||||||
@@ -36,10 +35,10 @@ For our purposes, we will be using `Orthanc`, but you can see a list of
|
|||||||
_Not sure if you have `docker` installed already? Try running `docker --version`
|
_Not sure if you have `docker` installed already? Try running `docker --version`
|
||||||
in command prompt or terminal_
|
in command prompt or terminal_
|
||||||
|
|
||||||
> If you are using `Docker Toolbox` you need to change the _PROXY_DOMAIN_
|
> If you are using `Docker Toolbox` you need to change the _proxy_ parameter in
|
||||||
> parameter in _platform/viewer/package.json_ to http://192.168.99.100:8042 or
|
> _package.json_ to http://192.168.99.100:8042 or the ip docker-machine ip
|
||||||
> the ip docker-machine ip throws. This is the value [`WebPack`][webpack-proxy]
|
> throws. This is the value [`react-scripts`][react-proxy] uses to proxy
|
||||||
> uses to proxy requests
|
> requests
|
||||||
|
|
||||||
### Running Orthanc
|
### Running Orthanc
|
||||||
|
|
||||||
@@ -52,9 +51,8 @@ yarn run orthanc:up
|
|||||||
|
|
||||||
_Upload your first Study:_
|
_Upload your first Study:_
|
||||||
|
|
||||||
1. Navigate to
|
1. Navigate to [Orthanc's web interface](http://localhost:8899) at
|
||||||
[Orthanc's web interface](http://localhost:8042/app/explorer.html) at
|
`http://localhost:8899` in a web browser.
|
||||||
`http://localhost:8042/app/explorer.html` in a web browser.
|
|
||||||
2. In the top right corner, click "Upload"
|
2. In the top right corner, click "Upload"
|
||||||
3. Click "Select files to upload..." and select one or more DICOM files
|
3. Click "Select files to upload..." and select one or more DICOM files
|
||||||
4. Click "Start the upload"
|
4. Click "Start the upload"
|
||||||
@@ -62,20 +60,17 @@ _Upload your first Study:_
|
|||||||
#### Orthanc: Learn More
|
#### Orthanc: Learn More
|
||||||
|
|
||||||
You can see the `docker-compose.yml` file this command runs at
|
You can see the `docker-compose.yml` file this command runs at
|
||||||
[`<project-root>/.docker/Nginx-Orthanc/`][orthanc-docker-compose], and more on
|
[`<project-root>/docker/Nginx-Docker/`](#), and more on Orthanc for Docker in
|
||||||
Orthanc for Docker in [Orthanc's documentation][orthanc-docker].
|
[Orthanc's documentation][orthanc-docker].
|
||||||
|
|
||||||
### Connecting to Orthanc
|
### Connecting to Orthanc
|
||||||
|
|
||||||
Now that we have a local Orthanc instance up and running, we need to configure
|
Now that we have a local Orthanc instance up and running, we need to configure
|
||||||
our web application to connect to it. Open a new terminal window, navigate to
|
our web application to connect to it. Open a new terminal window, navigate to
|
||||||
this repository's root directory, and run:
|
this project's root directory, and run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# If you haven't already, enable yarn workspaces
|
# If you haven't already, restore dependencies
|
||||||
yarn config set workspaces-experimental true
|
|
||||||
|
|
||||||
# Restore dependencies
|
|
||||||
yarn install
|
yarn install
|
||||||
|
|
||||||
# Run our dev command, but with the local orthanc config
|
# Run our dev command, but with the local orthanc config
|
||||||
@@ -85,35 +80,31 @@ yarn run dev:orthanc
|
|||||||
#### Configuration: Learn More
|
#### Configuration: Learn More
|
||||||
|
|
||||||
> For more configuration fun, check out the
|
> For more configuration fun, check out the
|
||||||
> [Essentials Configuration](./index.md) guide.
|
> [Essentials Configuration](./configuration.md) guide.
|
||||||
|
|
||||||
Let's take a look at what's going on under the hood here. `yarn run dev:orthanc`
|
Let's take a look at what's going on under the hood here. `yarn run dev:orthanc`
|
||||||
is running the `dev:orthanc` script in our project's `package.json`. That script
|
is running the `dev:orthanc` script in our project's `package.json`. That script
|
||||||
is:
|
is:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
cross-env NODE_ENV=development PROXY_TARGET=/dicom-web PROXY_DOMAIN=http://localhost:8042 APP_CONFIG=config/docker_nginx-orthanc.js webpack-dev-server --config .webpack/webpack.pwa.js -w
|
cross-env PORT=5000 REACT_APP_CONFIG=config/docker_nginx-orthanc.js react-scripts start
|
||||||
```
|
```
|
||||||
|
|
||||||
- `cross-env` sets three environment variables
|
- `cross-env` sets two environment variables
|
||||||
- PROXY_TARGET: `/dicom-web`
|
- PORT: 5000
|
||||||
- PROXY_DOMAIN: `http://localhost:8042`
|
- REACT_APP_CONFIG: `config/docker_nginx-orthanc.js`
|
||||||
- APP_CONFIG: `config/docker_nginx-orthanc.js`
|
- `react-scripts` runs it's `start` script. This is [the de-facto
|
||||||
- `webpack-dev-server` runs using the `.webpack/webpack.pwa.js` configuration
|
way][cra-start] to run a "Create React App" in development mode.
|
||||||
file. It will watch for changes and update as we develop.
|
|
||||||
|
|
||||||
`PROXY_TARGET` and `PROXY_DOMAIN` tell our development server to proxy requests
|
The `REACT_APP_CONFIG` value tells our app which file to load on to
|
||||||
to `Orthanc`. This allows us to bypass CORS issues that normally occur when
|
`window.config`. By default, our app uses the file at
|
||||||
requesting resources that live at a different domain.
|
`<project-root>/public/config/default.js`. Here is what that configuration looks
|
||||||
|
like:
|
||||||
The `APP_CONFIG` value tells our app which file to load on to `window.config`.
|
|
||||||
By default, our app uses the file at
|
|
||||||
`<project-root>/platform/viewer/public/config/default.js`. Here is what that
|
|
||||||
configuration looks like:
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
window.config = {
|
window.config = {
|
||||||
routerBasename: '/',
|
routerBasename: '/',
|
||||||
|
relativeWebWorkerScriptsPath: '',
|
||||||
servers: {
|
servers: {
|
||||||
dicomWeb: [
|
dicomWeb: [
|
||||||
{
|
{
|
||||||
@@ -131,7 +122,7 @@ window.config = {
|
|||||||
```
|
```
|
||||||
|
|
||||||
To learn more about how you can configure the OHIF Viewer, check out our
|
To learn more about how you can configure the OHIF Viewer, check out our
|
||||||
[Configuration Guide](./index.md).
|
[Configuration Guide](./configuration.md).
|
||||||
|
|
||||||
## Open Source DICOM Image Archives
|
## Open Source DICOM Image Archives
|
||||||
|
|
||||||
@@ -156,8 +147,8 @@ _Feel free to make a Pull Request if you want to add to this list._
|
|||||||
[dcmjs-org]: https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado
|
[dcmjs-org]: https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado
|
||||||
[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb
|
[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb
|
||||||
[storescu]: http://support.dcmtk.org/docs/storescu.html
|
[storescu]: http://support.dcmtk.org/docs/storescu.html
|
||||||
[webpack-proxy]: https://webpack.js.org/configuration/dev-server/#devserverproxy
|
[cra-start]: https://github.com/facebook/create-react-app#npm-start-or-yarn-start
|
||||||
[orthanc-docker-compose]: https://github.com/OHIF/Viewers/tree/master/.docker/Nginx-Orthanc
|
[react-proxy]: https://facebook.github.io/create-react-app/docs/proxying-api-requests-in-development#configuring-the-proxy-manually
|
||||||
<!-- Archives -->
|
<!-- Archives -->
|
||||||
[dcm4chee]: https://github.com/dcm4che/dcm4chee-arc-light
|
[dcm4chee]: https://github.com/dcm4che/dcm4chee-arc-light
|
||||||
[dcm4chee-docker]: https://github.com/dcm4che/dcm4chee-arc-light/wiki/Running-on-Docker
|
[dcm4chee-docker]: https://github.com/dcm4che/dcm4chee-arc-light/wiki/Running-on-Docker
|
||||||
@@ -24,7 +24,8 @@ graphic that illustrates this setup][triangular-workflow].
|
|||||||
Alternatively, if you intend to use the OHIF Viewer as a starting point, and you
|
Alternatively, if you intend to use the OHIF Viewer as a starting point, and you
|
||||||
aren't as concerned with syncing updates, then follow these steps:
|
aren't as concerned with syncing updates, then follow these steps:
|
||||||
|
|
||||||
1. Navigate to the [OHIF/Viewers][ohif-viewers] repository
|
1. Navigate to the [OHIF/Viewers/tree/react][ohif-viewers-react-repo] repository
|
||||||
|
and branch
|
||||||
2. Click `Clone or download`, and then `Download ZIP`
|
2. Click `Clone or download`, and then `Download ZIP`
|
||||||
3. Use the contents of the `.zip` file as a starting point for your viewer
|
3. Use the contents of the `.zip` file as a starting point for your viewer
|
||||||
|
|
||||||
@@ -38,8 +39,6 @@ aren't as concerned with syncing updates, then follow these steps:
|
|||||||
|
|
||||||
- [Node.js & NPM](https://nodejs.org/en/)
|
- [Node.js & NPM](https://nodejs.org/en/)
|
||||||
- [Yarn](https://yarnpkg.com/en/)
|
- [Yarn](https://yarnpkg.com/en/)
|
||||||
- Yarn workspaces should be enabled:
|
|
||||||
- `yarn config set workspaces-experimental true`
|
|
||||||
|
|
||||||
### Kick the tires
|
### Kick the tires
|
||||||
|
|
||||||
@@ -51,18 +50,21 @@ following commands:
|
|||||||
yarn install
|
yarn install
|
||||||
|
|
||||||
# Start local development server
|
# Start local development server
|
||||||
yarn run dev
|
yarn start
|
||||||
```
|
```
|
||||||
|
|
||||||
You should see the following output:
|
You should see the following output:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ohif/viewer: i 「wds」: Project is running at http://localhost:3000/
|
Compiled successfully!
|
||||||
@ohif/viewer: i 「wds」: webpack output is served from /
|
|
||||||
@ohif/viewer: i 「wds」: Content not from webpack is served from D:\code\ohif\Viewers\platform\viewer
|
|
||||||
@ohif/viewer: i 「wds」: 404s will fallback to /index.html
|
|
||||||
|
|
||||||
# And a list of all generated files
|
You can now view ohif-viewer in the browser.
|
||||||
|
|
||||||
|
Local: http://localhost:5000/
|
||||||
|
On Your Network: http://10.74.20.83:5000/
|
||||||
|
|
||||||
|
Note that the development build is not optimized.
|
||||||
|
To create a production build, use yarn build.
|
||||||
```
|
```
|
||||||
|
|
||||||
### 🎉 Celebrate 🎉
|
### 🎉 Celebrate 🎉
|
||||||
@@ -79,18 +81,24 @@ You should see the following output:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build static assets to host a PWA
|
# Build static assets to host a PWA
|
||||||
yarn run build
|
yarn run build:web
|
||||||
|
|
||||||
# Build packaged output (script-tag use)
|
# Build packaged output
|
||||||
yarn run build:package
|
yarn run build:package
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
- If you receive a _"No Studies Found"_ message and do not see your studies, try
|
- If you receive a _"No Studies Found"_ message and do not see your studies, try
|
||||||
changing the Study Date filters to a wider range.
|
changing the Study Date filters to a wider range.
|
||||||
- If you see a 'Loading' message which never resolves, check your browser
|
- If you see a 'Loading' message which never resolves, check your browser
|
||||||
JavaScript console inside the Developer Tools to identify any errors.
|
JavaScript console inside the Developer Tools to identify any errors.
|
||||||
|
- If you see any errors in your server console, check the
|
||||||
|
[Troubleshooting](./troubleshooting.md) page for more in depth advice.
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Links
|
Links
|
||||||
@@ -103,5 +111,5 @@ yarn run build:package
|
|||||||
[sync-changes]: https://help.github.com/en/articles/syncing-a-fork
|
[sync-changes]: https://help.github.com/en/articles/syncing-a-fork
|
||||||
[triangular-workflow]: https://github.blog/2015-07-29-git-2-5-including-multiple-worktrees-and-triangular-workflows/#improved-support-for-triangular-workflows
|
[triangular-workflow]: https://github.blog/2015-07-29-git-2-5-including-multiple-worktrees-and-triangular-workflows/#improved-support-for-triangular-workflows
|
||||||
[ohif-viewers-repo]: https://github.com/OHIF/Viewers
|
[ohif-viewers-repo]: https://github.com/OHIF/Viewers
|
||||||
[ohif-viewers]: https://github.com/OHIF/Viewers
|
[ohif-viewers-react-repo]: https://github.com/OHIF/Viewers/tree/react
|
||||||
<!-- prettier-ignore-end -->
|
<!-- prettier-ignore-end -->
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Installation
|
||||||
|
|
||||||
|
It's important to know that the OHIF Viewer project provides two different build
|
||||||
|
processes:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Static Asset output: For deploying PWAs
|
||||||
|
yarn run build:web
|
||||||
|
|
||||||
|
# Single `.js` script, for embedding viewer into existing apps
|
||||||
|
yarn run build:package
|
||||||
|
```
|
||||||
|
|
||||||
|
## create-react-app (PWA)
|
||||||
|
|
||||||
|
> [create-react-app](https://github.com/facebook/create-react-app) provides
|
||||||
|
> pre-configured build process for developing front-end applications with
|
||||||
|
> [React](https://reactjs.org/).
|
||||||
|
|
||||||
|
The ohif-viewer package can be run as a create-react-app application. This is
|
||||||
|
useful for development, debugging, or evolving the OHIF Viewer into your own
|
||||||
|
custom imaging application.
|
||||||
|
|
||||||
|
You can read more about this particular strategy in our
|
||||||
|
[Build for Production Deployment Guide](./../deployment/recipes/build-for-production.md)
|
||||||
|
|
||||||
|
## Rollup (Packaged Script)
|
||||||
|
|
||||||
|
> [Rollup](https://rollupjs.org/guide/en) is a module bundler for JavaScript. It
|
||||||
|
> uses the new standardized format for code modules included in the ES6 revision
|
||||||
|
> of JavaScript.
|
||||||
|
|
||||||
|
The [ohif-viewer](https://www.npmjs.com/package/ohif-viewer) package can be
|
||||||
|
built with Rollup to provide a set of React components which can be dropped into
|
||||||
|
a larger application. Specifically, the ohif-viewer package provides a React
|
||||||
|
component named `OHIFViewer` which is the entire viewer, configurable via React
|
||||||
|
`props`. This is useful for including the OHIF Viewer in a larger web
|
||||||
|
application, as the entire application can be provided via a `<script>` tag with
|
||||||
|
no build process required.
|
||||||
|
|
||||||
|
You can read more about this particular strategy in our
|
||||||
|
[Embedded Viewer Deployment Guide](./../deployment/recipes/embedded-viewer.md)
|
||||||
@@ -36,7 +36,7 @@ many data sources. The OHIF Viewer's scope **DOES** include configuration and
|
|||||||
support for services that are protected with OpenID-Connect.
|
support for services that are protected with OpenID-Connect.
|
||||||
|
|
||||||
In an effort to aide our users and contributors, we attempt to provide several
|
In an effort to aide our users and contributors, we attempt to provide several
|
||||||
[deployment and hosting recipes](./deployment/index.md) as potential starting
|
[deployment and hosting recipes](./../deployment/index.md) as potential starting
|
||||||
points. These are not meant to be rock solid, production ready, solutions; like
|
points. These are not meant to be rock solid, production ready, solutions; like
|
||||||
most recipes, they should be augmented to best fit you and your organization's
|
most recipes, they should be augmented to best fit you and your organization's
|
||||||
taste, preferences, etc.
|
taste, preferences, etc.
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# Viewer: Themeing
|
# Themeing
|
||||||
|
|
||||||
Themeing is currently accomplished with color variables that are defined within
|
Themeing is currently accomplished with color variables that are defined within
|
||||||
the [`:root`](https://css-tricks.com/almanac/selectors/r/root/) selector
|
the [`:root`](https://css-tricks.com/almanac/selectors/r/root/) selector
|
||||||
(allowing them to cascade across all elements). This repository's components,
|
(allowing them to cascade across all elements). This repository's components,
|
||||||
and the ones we consume from our
|
and the ones we consume from our
|
||||||
[`@ohif/ui` component library](https://react.ohif.org/styling-and-theming)
|
[React Viewerbase component library](https://react.ohif.org/styling-and-theming)
|
||||||
utilize them. We are interested in pursuing more robust themeing options, and
|
utilize them. We are interested in pursuing more robust themeing options, and
|
||||||
open to pull requests and discussion issues.
|
open to pull requests and discussion issues.
|
||||||
|
|
||||||
@@ -66,11 +66,11 @@ open to pull requests and discussion issues.
|
|||||||
|
|
||||||
Current white-labeling options are limited. We expose the ability to replace the
|
Current white-labeling options are limited. We expose the ability to replace the
|
||||||
"Logo" section of the application with a custom "Logo" component. You can do
|
"Logo" section of the application with a custom "Logo" component. You can do
|
||||||
this by adding a `whiteLabeling` key to your
|
this by adding a `whiteLabelling` key to your
|
||||||
[configuration file](./configuration.md).
|
[configuration file](./configuration.md).
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function RadicalImagingLogo(React) {
|
function RadicalImagingLogo() {
|
||||||
return React.createElement(
|
return React.createElement(
|
||||||
'a',
|
'a',
|
||||||
{
|
{
|
||||||
@@ -80,12 +80,12 @@ function RadicalImagingLogo(React) {
|
|||||||
href: 'http://radicalimaging.com',
|
href: 'http://radicalimaging.com',
|
||||||
},
|
},
|
||||||
React.createElement('h5', {}, 'RADICAL IMAGING')
|
React.createElement('h5', {}, 'RADICAL IMAGING')
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
props.whiteLabeling = {
|
props.whiteLabelling = {
|
||||||
createLogoComponentFn: RadicalImagingLogo,
|
logoComponent: RadicalImagingLogo(),
|
||||||
};
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Viewer: Internationalization
|
# Translating
|
||||||
|
|
||||||
OHIF supports internationalization using [i18next](https://www.i18next.com/)
|
OHIF supports internationalization using [i18next](https://www.i18next.com/)
|
||||||
through the npm package [@ohif/i18n](https://www.npmjs.com/package/@ohif/i18n),
|
through the npm package [@ohif/i18n](https://www.npmjs.com/package/@ohif/i18n),
|
||||||
@@ -15,7 +15,7 @@ where is the main instance of i18n containing several languages and tools.
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Installing
|
### Installing
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn add @ohif/i18n
|
yarn add @ohif/i18n
|
||||||
@@ -25,7 +25,7 @@ yarn add @ohif/i18n
|
|||||||
npm install --save @ohif/i18n
|
npm install --save @ohif/i18n
|
||||||
```
|
```
|
||||||
|
|
||||||
## How it works
|
### How it works
|
||||||
|
|
||||||
After installing `@ohif/i18n` npm package, the translation function
|
After installing `@ohif/i18n` npm package, the translation function
|
||||||
[t](https://www.i18next.com/overview/api#t) can be used [with](#with-react) or
|
[t](https://www.i18next.com/overview/api#t) can be used [with](#with-react) or
|
||||||
@@ -57,13 +57,13 @@ If the translation.json file contains a key that matches the HTML content e.g.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### With React
|
#### With React
|
||||||
|
|
||||||
This section will introduce you to [react-i18next](https://react.i18next.com/)
|
This section will introduce you to [react-i18next](https://react.i18next.com/)
|
||||||
basics and show how to implement the [t](https://www.i18next.com/overview/api#t)
|
basics and show how to implement the [t](https://www.i18next.com/overview/api#t)
|
||||||
function easily.
|
function easily.
|
||||||
|
|
||||||
#### Using HOCs
|
##### Using HOCs
|
||||||
|
|
||||||
In most cases we used
|
In most cases we used
|
||||||
[High Order Components](https://react.i18next.com/latest/withtranslation-hoc) to
|
[High Order Components](https://react.i18next.com/latest/withtranslation-hoc) to
|
||||||
@@ -86,13 +86,13 @@ export default withTranslation('MyNameSpace')(MyComponent);
|
|||||||
> [I18nextProvider](#using-outside-of-ohif-viewer) section, `withTranslation`
|
> [I18nextProvider](#using-outside-of-ohif-viewer) section, `withTranslation`
|
||||||
> HOC doesnt works without a I18nextProvider
|
> HOC doesnt works without a I18nextProvider
|
||||||
|
|
||||||
#### Using Hooks
|
##### Using Hooks
|
||||||
|
|
||||||
Also, it's possible to get the `t` tool using
|
Also, it's possible to get the `t` tool using
|
||||||
[React Hooks](https://react.i18next.com/latest/usetranslation-hook), but it
|
[React Hooks](https://react.i18next.com/latest/usetranslation-hook), but it
|
||||||
requires at least React > 16.8 😉
|
requires at least React > 16.8 😉
|
||||||
|
|
||||||
### Using outside of OHIF viewer
|
#### Using outside of OHIF viewer
|
||||||
|
|
||||||
OHIF Viewer already sets a main
|
OHIF Viewer already sets a main
|
||||||
[I18nextProvider](https://react.i18next.com/latest/i18nextprovider) connected to
|
[I18nextProvider](https://react.i18next.com/latest/i18nextprovider) connected to
|
||||||
@@ -119,7 +119,7 @@ usage.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Without React
|
#### Without React
|
||||||
|
|
||||||
When needed, you can also use available translations _without React_.
|
When needed, you can also use available translations _without React_.
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ console.log(T('$t(Common:Play) my translated text'));
|
|||||||
|
|
||||||
# Main Concepts While Translating
|
# Main Concepts While Translating
|
||||||
|
|
||||||
## Namespaces
|
## - Namespaces
|
||||||
|
|
||||||
Namespaces are being used to organize translations in smaller portions, combined
|
Namespaces are being used to organize translations in smaller portions, combined
|
||||||
semantically or by use. Each `.json` file inside `@ohif/i18n` npm package
|
semantically or by use. Each `.json` file inside `@ohif/i18n` npm package
|
||||||
@@ -145,8 +145,8 @@ becomes a new namespace automatically.
|
|||||||
- CineDialog: Translations for the toll tips inside the Cine Player Dialog
|
- CineDialog: Translations for the toll tips inside the Cine Player Dialog
|
||||||
- Common: all common jargons that can be reused like `t('$t(common:image)')`
|
- Common: all common jargons that can be reused like `t('$t(common:image)')`
|
||||||
- Header: translations related to OHIF's Header Top Bar
|
- Header: translations related to OHIF's Header Top Bar
|
||||||
- MeasurementTable - Translations for the `@ohif/ui` Measurement Table
|
- MeasurementTable - Translations for the react-viewerbase Measurement Table
|
||||||
- UserPreferencesModal - Translations for the `@ohif/ui` Preferences Modal
|
- UserPreferencesModal - Translations for the react-viewerbase Preferences Modal
|
||||||
|
|
||||||
### How to use another NameSpace inside the current NameSpace?
|
### How to use another NameSpace inside the current NameSpace?
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ NameSpace, like this following example getting data from `Common` NameSpace:
|
|||||||
$t(Common:Reset)
|
$t(Common:Reset)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Extending Languages in @ohif/i18n
|
## - Extending Languages in @ohif/i18n
|
||||||
|
|
||||||
Sometimes, even using the same language, some nouns or jargons can change
|
Sometimes, even using the same language, some nouns or jargons can change
|
||||||
according to the country, states or even from Hospital to Hospital.
|
according to the country, states or even from Hospital to Hospital.
|
||||||
@@ -212,7 +212,7 @@ object like this:
|
|||||||
Please check the `index.js` files inside locales folder for an example of this
|
Please check the `index.js` files inside locales folder for an example of this
|
||||||
exporting structure.
|
exporting structure.
|
||||||
|
|
||||||
### Extending languages dynamically
|
### - Extending languages dynamically
|
||||||
|
|
||||||
You have access to the i18next instance, so you can use the
|
You have access to the i18next instance, so you can use the
|
||||||
[addResourceBundle](https://www.i18next.com/how-to/add-or-load-translations#add-after-init)
|
[addResourceBundle](https://www.i18next.com/how-to/add-or-load-translations#add-after-init)
|
||||||
@@ -314,4 +314,4 @@ REACT_APP_I18N_DEBUG=true yarn run dev
|
|||||||
### Contributing with new languages
|
### Contributing with new languages
|
||||||
|
|
||||||
Contributions of any kind are welcome! Please check the
|
Contributions of any kind are welcome! Please check the
|
||||||
[instructions](../development/contributing.md).
|
[instructions](https://docs.ohif.org/contributing.html).
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
Common GitHub issues that are not easily remedied with cleaner code or
|
||||||
|
documentation will be recorded here. Please feel free to make PRs to update this
|
||||||
|
page.
|
||||||
|
|
||||||
|
## Common Problems
|
||||||
|
|
||||||
|
| Problem | Most Common Reasons |
|
||||||
|
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| ** Can't retrieve Study List over DICOMWeb** | 1. QIDO root URL is incorrect<br> 2. DICOM Web is not enabled on PACS |
|
||||||
|
| ** Can't retrieve images** | 1. WADO Root URL is incorrect<br> 2. DICOM Web is not enabled on PACS<br> 3. HTTP Basic Authentication username and password are incorrect or not provided. |
|
||||||
|
|
||||||
|
## Debugging Steps
|
||||||
|
|
||||||
|
### Can't retrieve Study List over DICOMWeb
|
||||||
|
|
||||||
|
1. Check that you can query your PACS using an alternative DICOM Web client
|
||||||
|
(e.g. cURL, or a Web Browser). If you cannot, then your PACS is configured
|
||||||
|
incorrectly. Refer to the documentation of the image archive.
|
||||||
@@ -1,239 +0,0 @@
|
|||||||
# Extensions
|
|
||||||
|
|
||||||
- [Overview](#overview)
|
|
||||||
- [Concepts](#concepts)
|
|
||||||
- [Extension Skeleton](#extension-skeleton)
|
|
||||||
- [Registering an Extension](#registering-an-extension)
|
|
||||||
- [Lifecylce Hooks](#lifecycle-hooks)
|
|
||||||
- [Modules](#modules)
|
|
||||||
- [Contexts](#contexts)
|
|
||||||
- [Consuming Extensions](#consuming-extensions)
|
|
||||||
- [Extension Manager](#extensionmanager)
|
|
||||||
- [Maintained Extensions](#maintained-extensions)
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
We use extensions to help us isolate and package groups of related features.
|
|
||||||
Extensions provide functionality, ui components, and new behaviors. Ideally,
|
|
||||||
they're built in a way that allows them to extend entirely different
|
|
||||||
implementations of the `@ohif/viewer` project.
|
|
||||||
|
|
||||||
<div style="text-align: center;">
|
|
||||||
<a href="/assets/img/extensions-diagram.png">
|
|
||||||
<img src="/assets/img/extensions-diagram.png" alt="Extensions Diagram" style="margin: 0 auto; max-width: 500px;" />
|
|
||||||
</a>
|
|
||||||
<div><i>Diagram showing how extensions are configured and accessed.</i></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
The `@ohif/viewer`'s application level configuration gives us the ability to add
|
|
||||||
and configure extensions. When the application starts, extensions are registered
|
|
||||||
with the `ExtensionManager`. Different portions of the `@ohif/viewer` project
|
|
||||||
will use registered extensions to influence application behavior.
|
|
||||||
|
|
||||||
Extensions allow us to:
|
|
||||||
|
|
||||||
- Wrap and integrate functionality of 3rd party dependencies in a reusable way
|
|
||||||
- Change how application data is mapped and transformed
|
|
||||||
- Display a consistent/cohesive UI
|
|
||||||
- Inject custom components to override built-in components
|
|
||||||
|
|
||||||
Practical examples of extensions include:
|
|
||||||
|
|
||||||
- A set of segmentation tools that build on top of the `cornerstone` viewport
|
|
||||||
- Showing ML/AI report summaries for the selected study/series/image
|
|
||||||
- Support for parsing DICOM structured reports and displaying them in a user
|
|
||||||
friendly way
|
|
||||||
- [See our maintained extensions for more examples of what's possible](#maintained-extensions)
|
|
||||||
|
|
||||||
## Concepts
|
|
||||||
|
|
||||||
### Extension Skeleton
|
|
||||||
|
|
||||||
An extension is a plain JavaScript object that has an `id` property, and one or
|
|
||||||
more [modules](#modules) and/or [lifecycle hooks](#lifecycle-hooks).
|
|
||||||
|
|
||||||
```js
|
|
||||||
// prettier-ignore
|
|
||||||
export default {
|
|
||||||
/**
|
|
||||||
* Only required property. Should be a unique value across all extensions.
|
|
||||||
*/
|
|
||||||
id: 'example-extension',
|
|
||||||
|
|
||||||
// Lifecyle
|
|
||||||
preRegistration() { /* */ },
|
|
||||||
// Modules
|
|
||||||
getCommandsModule() { /* */ },
|
|
||||||
getToolbarModule() { /* */ },
|
|
||||||
getPanelModule() { /* */ },
|
|
||||||
getSopClassHandler() { /* */ },
|
|
||||||
getViewportModule() { /* */ },
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Registering an Extension
|
|
||||||
|
|
||||||
There are two different ways to register and configure extensions: At
|
|
||||||
[runtime](#registering-at-runtime) and at
|
|
||||||
[build time](#registering-at-build-time).
|
|
||||||
|
|
||||||
You can leverage one or both strategies. Which one(s) you choose depend on your
|
|
||||||
application's requirements. Each [module](#modules) defined by the extension
|
|
||||||
becomes available to the core application via the `ExtensionManager`.
|
|
||||||
|
|
||||||
#### Registering at Runtime
|
|
||||||
|
|
||||||
The `@ohif/viewer` uses a [configuration file](../viewer/configuration.md) at
|
|
||||||
startup. The schema for that file includes an `Extensions` key that supports an
|
|
||||||
array of extensions to register.
|
|
||||||
|
|
||||||
```js
|
|
||||||
// prettier-ignore
|
|
||||||
const config = {
|
|
||||||
extensions: [
|
|
||||||
MyFirstExtension,
|
|
||||||
[
|
|
||||||
MySecondExtension,
|
|
||||||
{ /* MySecondExtensions Configuration */ },
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Registering at Build Time
|
|
||||||
|
|
||||||
The `@ohif/viewer` works best when built as a "Progressive Web Application"
|
|
||||||
(PWA). If you know the extensions your application will need, you can specify
|
|
||||||
them at "build time" to leverage advantages afforded to us by modern tooling:
|
|
||||||
|
|
||||||
- Code Splitting (dynamic imports)
|
|
||||||
- Tree Shaking
|
|
||||||
- Dependency deduplication
|
|
||||||
|
|
||||||
You can update the list of bundled extensions by:
|
|
||||||
|
|
||||||
1. Having your `@ohif/viewer` project depend on the extension
|
|
||||||
2. Importing and adding it to the list of extensions in the
|
|
||||||
`<repo-root>/platform/src/index.js` entrypoint.
|
|
||||||
|
|
||||||
### Lifecycle Hooks
|
|
||||||
|
|
||||||
Currently, there is only a single lifecycle hook for extensions:
|
|
||||||
[`preRegistration`](./lifecycle/pre-registration.md)
|
|
||||||
|
|
||||||
If an extension defines the [`preRegistration`](./lifecycle/pre-registration.md)
|
|
||||||
lifecycle hook, it is called before any modules are registered in the
|
|
||||||
`ExtensionManager`. It's most commonly used to wire up extensions to
|
|
||||||
[services](./../services/index.md) and [commands](./modules/commands.md), and to
|
|
||||||
bootstrap 3rd party libraries.
|
|
||||||
|
|
||||||
### Modules
|
|
||||||
|
|
||||||
Modules are the meat of extensions. They provide "definitions", components, and
|
|
||||||
filtering/mapping logic that are then made available by various managers and
|
|
||||||
services.
|
|
||||||
|
|
||||||
Each module type has a special purpose, and is consumed by our viewer
|
|
||||||
differently.
|
|
||||||
|
|
||||||
| Type | Description | Examples |
|
|
||||||
| ------------------------------------------------- | ---------------------------------------------------------------- | ------------------------------------------------- |
|
|
||||||
| [Commands](./modules/commands.md) | Adds named commands, scoped to a context, to the CommandsManager | `setToolActive()`, `nextSeries()` |
|
|
||||||
| [Panel](./modules/panel.md) | Adds left or right hand side panels | `<ThumbnailList />`, `<MeasurementsTable />` |
|
|
||||||
| [SOPClassHandler](./modules/sop-class-handler.md) | Determines how retrieved study data is split into "DisplaySets" | `getDisplaySetFromSeries()` |
|
|
||||||
| [Toolbar](./modules/toolbar.md) | Adds buttons or custom components to the toolbar | Toolbar button, nested buttons, custom |
|
|
||||||
| [Viewport](./modules/viewport.md) | Adds a component responsible for rendering a "DisplaySet" | `<CornerstoneViewport />`, `<DicomPdfViewport />` |
|
|
||||||
|
|
||||||
<figure style="text-align: center; font-style: italic;">Tbl. Module types with abridged descriptions and examples. Each module links to a dedicated documentation page.</figure>
|
|
||||||
|
|
||||||
### Contexts
|
|
||||||
|
|
||||||
The `@ohif/viewer` tracks "active contexts" that extensions can use to scope
|
|
||||||
their functionality. Some example contexts being:
|
|
||||||
|
|
||||||
- Route: `ROUTE:VIEWER`, `ROUTE:STUDY_LIST`
|
|
||||||
- Active Viewport: `ACTIVE_VIEWPORT:CORNERSTONE`, `ACTIVE_VIEWPORT:VTK`
|
|
||||||
|
|
||||||
An extension module can use these to say "Only show this Toolbar Button if the
|
|
||||||
active viewport is a Cornerstone viewport." This helps us use the appropriate UI
|
|
||||||
and behaviors depending on the current contexts.
|
|
||||||
|
|
||||||
For example, if we have hotkey that "rotates the active viewport", each Viewport
|
|
||||||
module that supports this behavior can add a command with the same name, scoped
|
|
||||||
to the appropriate context. When the `command` is fired, the "active contexts"
|
|
||||||
are used to determine the appropriate implementation of the rotate behavior.
|
|
||||||
|
|
||||||
## Consuming Extensions
|
|
||||||
|
|
||||||
We consume extensions, via the `ExtensionManager`, in our `@ohif/viewer`
|
|
||||||
project.
|
|
||||||
|
|
||||||
```js
|
|
||||||
const extensionManager = new ExtensionManager({
|
|
||||||
commandsManager,
|
|
||||||
servicesManager,
|
|
||||||
});
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
extensionManager.registerExtensions([ /** **/ ]);
|
|
||||||
```
|
|
||||||
|
|
||||||
The `@ohif/viewer` project handles data fetching, basic routing, wires up UI
|
|
||||||
services, and is the home to the more bespoke application logic that doesn't
|
|
||||||
make as much sense to make reusable.
|
|
||||||
|
|
||||||
Long-term, replacing the `@ohif/viewer` application and consuming extensions
|
|
||||||
(and the `ExtensionManager`) in your own project is the ideal path for
|
|
||||||
applications requiring a high degree of customization that can't be achieved
|
|
||||||
with current theming, configuration, extension, and services support.
|
|
||||||
|
|
||||||
If you're not sure how to achieve your goals with the extensibility available
|
|
||||||
today, create a GitHub issue!
|
|
||||||
|
|
||||||
### `ExtensionManager`
|
|
||||||
|
|
||||||
The `ExtensionManager` is a class made available to us via the `@ohif/core`
|
|
||||||
project (platform/core). Our application instantiates a single instance of it,
|
|
||||||
and provides a `ServicesManager` and `CommandsManager` along with the
|
|
||||||
application's configuration through the appConfig key (optional).
|
|
||||||
|
|
||||||
```js
|
|
||||||
const commandsManager = new CommandsManager();
|
|
||||||
const servicesManager = new ServicesManager();
|
|
||||||
const extensionManager = new ExtensionManager({
|
|
||||||
commandsManager,
|
|
||||||
servicesManager,
|
|
||||||
appConfig,
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
The `ExtensionManager` only has a few public members:
|
|
||||||
|
|
||||||
- `registerExtension` - Registers a single extension
|
|
||||||
- `registerExtensions` - Registers an array of extensions
|
|
||||||
- `modules` - An object containing registered extensions by `MODULE_TYPE`
|
|
||||||
|
|
||||||
During registration, lifecycle hooks and modules have access to the extension's
|
|
||||||
config, the application's config and `ExtensionManager`'s `ServicesManager` and
|
|
||||||
`CommandsManager` instances.
|
|
||||||
|
|
||||||
Our `@ohif/viewer` uses the `modules` member to access registered extensions at
|
|
||||||
appropriate places in our application.
|
|
||||||
|
|
||||||
## Maintained Extensions
|
|
||||||
|
|
||||||
A small number of powerful extensions for popular use cases are maintained by
|
|
||||||
OHIF. They're co-located in the [`OHIF/Viewers`][viewers-repo] repository, in
|
|
||||||
the top level [`extensions/`][ext-source] directory.
|
|
||||||
|
|
||||||
{% include "./_maintained-extensions-table.md" %}
|
|
||||||
|
|
||||||
<!--
|
|
||||||
LINKS
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
[viewers-repo]: https://github.com/OHIF/Viewers
|
|
||||||
[ext-source]: https://github.com/OHIF/Viewers/tree/master/extensions
|
|
||||||
[module-types]: https://github.com/OHIF/Viewers/blob/master/platform/core/src/extensions/MODULE_TYPES.js
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# Lifecylce Hook: preRegistration
|
|
||||||
|
|
||||||
If an extension defines the `preRegistration` lifecycle hook, it is called
|
|
||||||
before any modules are registered in the `ExtensionManager`. This hook can be
|
|
||||||
used to:
|
|
||||||
|
|
||||||
- initialize 3rd party libraries
|
|
||||||
- register event listeners
|
|
||||||
- add or call services
|
|
||||||
- add or call commands
|
|
||||||
|
|
||||||
The `preRegistration` hook receives an object containing the
|
|
||||||
`ExtensionManager`'s associated `ServicesManager`, `CommandsManager`, and any
|
|
||||||
`configuration` that was provided with the extension at time of registration.
|
|
||||||
|
|
||||||
_Example `preRegistration` hook implementation_
|
|
||||||
|
|
||||||
```js
|
|
||||||
export default {
|
|
||||||
id: 'MyExampleExtension',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {object} params
|
|
||||||
* @param {object} params.configuration
|
|
||||||
* @param {ServicesManager} params.servicesManager
|
|
||||||
* @param {CommandsManager} params.commandsManager
|
|
||||||
* @returns void
|
|
||||||
*/
|
|
||||||
preRegistration({ servicesManager, commandsManager, configuration }) {
|
|
||||||
console.log('Wiring up important stuff.');
|
|
||||||
|
|
||||||
window.importantStuff = () => {
|
|
||||||
console.log(configuration);
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('Important stuff has been wired.');
|
|
||||||
window.importantStuff();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
# Module: Commands
|
|
||||||
|
|
||||||
- [Overview](#overview)
|
|
||||||
- [Command Definitions](#command-definitions)
|
|
||||||
- [Commands Manager](#commands-manager)
|
|
||||||
- [Instantiating](#instatiating)
|
|
||||||
- [Public API](#public-api)
|
|
||||||
- [Contexts](#contexts)
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
An extension can register a Commands Module by defining a `getCommandsModule`
|
|
||||||
method. The Commands Module allows us to register one or more commands scoped to
|
|
||||||
specific [contexts](./../index.md#contexts). Commands have several unique
|
|
||||||
characteristics that make them tremendously powerful:
|
|
||||||
|
|
||||||
- Multiple implementations for the same command can be defined
|
|
||||||
- Only the correct command's implementation will be run, dependent on the
|
|
||||||
application's "context"
|
|
||||||
- Commands can be called from extensions, modules, and the consuming application
|
|
||||||
|
|
||||||
Here is a simple example commands module:
|
|
||||||
|
|
||||||
```js
|
|
||||||
export default {
|
|
||||||
id: 'example-commands-module',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {object} params
|
|
||||||
* @param {ServicesManager} params.servicesManager
|
|
||||||
* @param {CommandsManager} params.commandsManager
|
|
||||||
*/
|
|
||||||
getCommandsModule({ servicesManager, commandsManager }) {
|
|
||||||
return {
|
|
||||||
definitions: {
|
|
||||||
sayHello: {
|
|
||||||
commandFn: ({ words }) => {
|
|
||||||
console.log(words);
|
|
||||||
},
|
|
||||||
options: { words: 'Hello!' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultContext: 'VIEWER',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Each definition returned by the Commands Module is registered to the
|
|
||||||
`ExtensionManager`'s `CommandsManager`.
|
|
||||||
|
|
||||||
## Command Definitions
|
|
||||||
|
|
||||||
The command definition consists of a named command (`myCommandName` below) and a
|
|
||||||
`commandFn`. The command name is used to call the command, and the `commandFn`
|
|
||||||
is the "command" that is actioned.
|
|
||||||
|
|
||||||
```js
|
|
||||||
myCommandName: {
|
|
||||||
commandFn: ({ viewports, other, options }) => { },
|
|
||||||
storeContexts: ['viewports'],
|
|
||||||
options: { words: 'Just kidding! Goodbye!' },
|
|
||||||
context: 'ACTIVE_VIEWPORT::CORNERSTONE',
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
| --------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| `commandFn` | func | The function to call when command is run. Receives `options` and `storeContexts`. |
|
|
||||||
| `storeContexts` | string[] | (optional) Expected state objects to be passed in as props. Located using `getAppState` fn defined at `CommandsManager`'s instatiation. |
|
|
||||||
| `options` | object | (optional) Arguments to pass at the time of calling to the `commandFn` |
|
|
||||||
| `context` | string[] or string | (optional) Overrides the `defaultContext`. Let's us know if command is currently "available" to be run. |
|
|
||||||
|
|
||||||
## Command Behavior
|
|
||||||
|
|
||||||
**I have many similar commands. How can I share their `commandFn` and make it
|
|
||||||
reusable?**
|
|
||||||
|
|
||||||
This is where `storeContexts` and `options` come in. We use these in our
|
|
||||||
`setToolActive` command. `storeContexts` helps us identify our `activeViewport`,
|
|
||||||
and `options` allow us to pass in the name of a tool we would like to set as
|
|
||||||
active.
|
|
||||||
|
|
||||||
**If there are multiple valid commands for the application's active contexts**
|
|
||||||
|
|
||||||
- What happens: all commands are run
|
|
||||||
- When to use: A `clearData` command that cleans up state for multiple
|
|
||||||
extensions
|
|
||||||
|
|
||||||
**If no commands are valid for the application's active contexts**
|
|
||||||
|
|
||||||
- What happens: a warning is printed to the console
|
|
||||||
- When to use: a `hotkey` (like "invert") that doesn't make sense for the
|
|
||||||
current viewport (PDF or HTML)
|
|
||||||
|
|
||||||
## `CommandsManager`
|
|
||||||
|
|
||||||
The `CommandsManager` is a class defined in the `@ohif/core` project. A single
|
|
||||||
instance of it should be defined in the consuming application, and it should be
|
|
||||||
used when constructing the `ExtensionManager`.
|
|
||||||
|
|
||||||
### Instantiating
|
|
||||||
|
|
||||||
When we instantiate the `CommandsManager`, we need to pass it two methods:
|
|
||||||
|
|
||||||
- `getAppState` - Should return the application's state when called
|
|
||||||
- `getActiveContexts` - Should return the application's active contexts when
|
|
||||||
called
|
|
||||||
|
|
||||||
These methods are used internally to help determine which commands are currently
|
|
||||||
valid, and how to provide them with any state they may need at the time they are
|
|
||||||
called.
|
|
||||||
|
|
||||||
```js
|
|
||||||
const commandsManager = new CommandsManager({
|
|
||||||
getAppState,
|
|
||||||
getActiveContexts,
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Public API
|
|
||||||
|
|
||||||
If you would like to run a command in the consuming app or an extension, you can
|
|
||||||
use one of the following methods:
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Returns all commands for a given context
|
|
||||||
commandsManager.getContext('string');
|
|
||||||
|
|
||||||
// Attempts to run a command
|
|
||||||
commandsManager.runCommand('speak', { command: 'hello' });
|
|
||||||
|
|
||||||
// Run command, but override the active contexts
|
|
||||||
commandsManager.runCommand('speak', { command: 'hello' }, ['VIEWER']);
|
|
||||||
```
|
|
||||||
|
|
||||||
The `ExtensionManager` handles registering commands and creating contexts, so
|
|
||||||
most consumer's won't need these methods. If you find yourself using these, ask
|
|
||||||
yourself "why can't I register these commands via an extension?"
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Used by the `ExtensionManager` to register new commands
|
|
||||||
commandsManager.registerCommand('context', 'name', commandDefinition);
|
|
||||||
|
|
||||||
// Creates a new context; clears the context if it already exists
|
|
||||||
commandsManager.createContext('string');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Contexts
|
|
||||||
|
|
||||||
It is up to the consuming application to define what contexts are possible, and
|
|
||||||
which ones are currently active. As extensions depend heavily on these, we will
|
|
||||||
likely publish guidance around creating contexts, and ways to override extension
|
|
||||||
defined contexts in the near future. If you would like to discuss potential
|
|
||||||
changes to how contexts work, please don't hesistate to createa new GitHub
|
|
||||||
issue.
|
|
||||||
|
|
||||||
[Some additional information on Contexts can be found here.](./../index.md#contexts)
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
# Module: Panel
|
|
||||||
|
|
||||||
An extension can register a Panel Module by defining a `getPanelModule` method.
|
|
||||||
The panel module provides the ability to define `menuOptions` and `components`
|
|
||||||
that can be used by the consuming application. `components` are React Components
|
|
||||||
that can be displayed in the consuming application's "Panel" Component.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<center><i>A panel extension example</i></center>
|
|
||||||
|
|
||||||
The `menuOptions`'s `target` key points to a registered `components`'s `id`. A
|
|
||||||
`defaultContext` is applied to all `menuOption`s; however, each `menuOption` can
|
|
||||||
optional provide it's own `context` value.
|
|
||||||
|
|
||||||
The `getPanelModule` receives an object containing the `ExtensionManager`'s
|
|
||||||
associated `ServicesManager` and `CommandsManager`.
|
|
||||||
|
|
||||||
```js
|
|
||||||
import MyComponent from './MyComponent.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
id: 'example-panel-module',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {object} params
|
|
||||||
* @param {ServicesManager} params.servicesManager
|
|
||||||
* @param {CommandsManager} params.commandsManager
|
|
||||||
*/
|
|
||||||
getPanelModule({ servicesManager, commandsManager }) {
|
|
||||||
return {
|
|
||||||
menuOptions: [
|
|
||||||
{
|
|
||||||
// A suggested icon
|
|
||||||
// Available icons determined by consuming app
|
|
||||||
icon: 'list',
|
|
||||||
// A suggested label
|
|
||||||
label: 'Magic',
|
|
||||||
// 'right' or 'left'
|
|
||||||
from: 'right',
|
|
||||||
// The target component to toggle open/close
|
|
||||||
target: 'target-component-id',
|
|
||||||
// UI Hint; If the target panel is in a "disabled" state
|
|
||||||
isDisabled: studies => {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
// Overrides `defaultContext`, if specified
|
|
||||||
context: ['ACTIVE_VIEWPORT:MAGIC'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
id: 'target-component-id',
|
|
||||||
component: MyComponent,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
defaultContext: ['ROUTE:VIEWER'],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
# Module: SOP Class Handler
|
|
||||||
|
|
||||||
An extension can register a [SOP Class][sop-class-link] Handler Module by
|
|
||||||
defining a `getSopClassHandlerModule` method. The [SOP Class][sop-class-link]
|
|
||||||
Handler is a bit different from the other modules, as it doesn't provide a `1:1`
|
|
||||||
schema for UI or provide it's own components. It instead defines:
|
|
||||||
|
|
||||||
- `sopClassUIDs`: an array of string SOP Class UIDs that the
|
|
||||||
`getDisplaySetFromSeries` method should be applied to.
|
|
||||||
- `getDisplaySetFromSeries`: a method that maps series and study metadata to a
|
|
||||||
display set
|
|
||||||
|
|
||||||
A `displaySet` has the following shape:
|
|
||||||
|
|
||||||
```js
|
|
||||||
return {
|
|
||||||
plugin: 'html',
|
|
||||||
Modality: 'SR',
|
|
||||||
displaySetInstanceUID: 0,
|
|
||||||
wadoRoot: study.getData().wadoRoot,
|
|
||||||
wadoUri: instance.getData().wadouri,
|
|
||||||
SOPInstanceUID: instance.getSOPInstanceUID(),
|
|
||||||
SeriesInstanceUID: series.getSeriesInstanceUID(),
|
|
||||||
StudyInstanceUID: study.getStudyInstanceUID(),
|
|
||||||
authorizationHeaders,
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Where the `plugin` key is used to influence the default `ViewportComponent` for
|
|
||||||
rendering the `displaySet`. Additional properties are passed to the
|
|
||||||
`ViewportComponent` and used by the default `StudyBrowser` to render
|
|
||||||
"thumbnails" for each `displaySet`
|
|
||||||
|
|
||||||
## Example SOP Class Handler Module
|
|
||||||
|
|
||||||
```js
|
|
||||||
const SOP_CLASS_UIDS = {
|
|
||||||
BASIC_TEXT_SR: '1.2.840.10008.5.1.4.1.1.88.11',
|
|
||||||
ENHANCED_SR: '1.2.840.10008.5.1.4.1.1.88.22',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
id: 'example-sop-class-handler-module',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {object} params
|
|
||||||
* @param {ServicesManager} params.servicesManager
|
|
||||||
* @param {CommandsManager} params.commandsManager
|
|
||||||
*/
|
|
||||||
getSopClassHandlerModule({ servicesManager, commandsManager }) {
|
|
||||||
return {
|
|
||||||
id: 'OHIFDicomHtmlSopClassHandler',
|
|
||||||
sopClassUIDs: Object.values(SOP_CLASS_UIDS),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {object} series -
|
|
||||||
* @param {object} study -
|
|
||||||
* @param {object} dicomWebClient -
|
|
||||||
* @param {object} authorizationHeaders -
|
|
||||||
*/
|
|
||||||
getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) {
|
|
||||||
const instance = series.getFirstInstance();
|
|
||||||
|
|
||||||
return {
|
|
||||||
plugin: 'html',
|
|
||||||
displaySetInstanceUID: 0,
|
|
||||||
wadoRoot: study.getData().wadoRoot,
|
|
||||||
wadoUri: instance.getData().wadouri,
|
|
||||||
SOPInstanceUID: instance.getSOPInstanceUID(),
|
|
||||||
SeriesInstanceUID: series.getSeriesInstanceUID(),
|
|
||||||
StudyInstanceUID: study.getStudyInstanceUID(),
|
|
||||||
authorizationHeaders,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### More examples :
|
|
||||||
|
|
||||||
- [Dicom-HTML SOP][dicom-html-sop]
|
|
||||||
- [Dicom-PDF SOP][dicom-pdf-sop]
|
|
||||||
- [Dicom-Microscopy SOP][dicom-micro-sop]
|
|
||||||
- [Dicom-Segmentation SOP][dicom-seg-sop]
|
|
||||||
|
|
||||||
## `@ohif/viewer` usage
|
|
||||||
|
|
||||||
We use the `sopClassHandlerModule`s in three different places:
|
|
||||||
|
|
||||||
- `ViewerLocalFileData.js`
|
|
||||||
- `ViewerRetrieveStudyData.js`
|
|
||||||
- `StandaloneRouting.js`
|
|
||||||
|
|
||||||
Each time, it is used to map study and series data to `displaySets`. It does
|
|
||||||
this by working alongside the `StudyMetadataManager` in `@ohif/core`. That
|
|
||||||
manager has the method `createDisplaySets` that takes an array of
|
|
||||||
`sopClassHandlerModules`.
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
[sop-class-link]: http://dicom.nema.org/dicom/2013/output/chtml/part04/sect_B.5.html
|
|
||||||
[dicom-html-sop]: https://github.com/OHIF/Viewers/blob/master/extensions/dicom-html/src/OHIFDicomHtmlSopClassHandler.js#L4-L12
|
|
||||||
[dicom-pdf-sop]: https://github.com/OHIF/Viewers/blob/master/extensions/dicom-pdf/src/OHIFDicomPDFSopClassHandler.js#L4-L6
|
|
||||||
[dicom-micro-sop]: https://github.com/OHIF/Viewers/blob/master/extensions/dicom-microscopy/src/DicomMicroscopySopClassHandler.js#L5-L7
|
|
||||||
[dicom-seg-sop]: https://github.com/OHIF/Viewers/blob/master/extensions/dicom-segmentation/src/OHIFDicomSegSopClassHandler.js#L5-L7
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
# Module: Toolbar
|
|
||||||
|
|
||||||
An extension can register a Toolbar Module by defining a `getToolbarModule`
|
|
||||||
method. This module is commonly used to define:
|
|
||||||
|
|
||||||
- [Toolbar buttons](#button-definitions)
|
|
||||||
- [Nested toolbar menus](#nested-toolbar-menus)
|
|
||||||
- [Custom components](#custom-components)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<center><i>Example toolbar button using the Dialog Service to show CINE controls.</i></center>
|
|
||||||
|
|
||||||
## Example Toolbar Module
|
|
||||||
|
|
||||||
The Toolbar Module should return an array of `definitions` and a
|
|
||||||
`defaultContext`. There are currently a few different variations of definitions,
|
|
||||||
each one is detailed further down.
|
|
||||||
|
|
||||||
```js
|
|
||||||
export default {
|
|
||||||
id: 'example-toolbar-module',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {object} params
|
|
||||||
* @param {ServicesManager} params.servicesManager
|
|
||||||
* @param {CommandsManager} params.commandsManager
|
|
||||||
*/
|
|
||||||
getToolbarModule({ servicesManager, commandsManager }) {
|
|
||||||
return {
|
|
||||||
definitions: [
|
|
||||||
/* Array of definitions */
|
|
||||||
],
|
|
||||||
defaultContext: ['ROUTE:VIEWER'],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Button Definitions
|
|
||||||
|
|
||||||
The simplest definition has the following properties:
|
|
||||||
|
|
||||||
```js
|
|
||||||
{
|
|
||||||
id: 'StackScroll',
|
|
||||||
label: 'Stack Scroll',
|
|
||||||
icon: 'bars',
|
|
||||||
type: 'setToolActive',
|
|
||||||
commandName: 'setToolActive',
|
|
||||||
commandOptions: { toolName: 'StackScroll' },
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
| property | description | values |
|
|
||||||
| ---------------- | ----------------------------------------------------------------- | ----------------------------------------- |
|
|
||||||
| `id` | Unique string identifier for the definition | \* |
|
|
||||||
| `label` | User/display friendly to show in UI | \* |
|
|
||||||
| `icon` | A string name for an icon supported by the consuming application. | \* |
|
|
||||||
| `type` | Used to determine the button's component and behavior | `"setToolActive"`, `"command"` |
|
|
||||||
| `commandName` | (optional) The command to run when the button is used. | Any command registed by a `CommandModule` |
|
|
||||||
| `commandOptions` | (optional) Options to pass the target `commandName` | \* |
|
|
||||||
| `context` | (optional) Overrides module's `defaultContext` | Array of string context names |
|
|
||||||
|
|
||||||
Where a button with a `type` of `setToolActive` has an "active" styling applied
|
|
||||||
when clicked; removing the active styling from all other buttons.
|
|
||||||
|
|
||||||
## Nested Toolbar Menus
|
|
||||||
|
|
||||||
You can indicate that buttons should be grouped and nested in a submenu by
|
|
||||||
including `buttons` property in a definition:
|
|
||||||
|
|
||||||
```js
|
|
||||||
{
|
|
||||||
id: 'More',
|
|
||||||
label: 'More',
|
|
||||||
icon: 'ellipse-circle',
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
id: 'cstInvert',
|
|
||||||
label: 'Invert',
|
|
||||||
icon: 'circle',
|
|
||||||
type: 'command',
|
|
||||||
commandName: 'invertViewport',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<center><i>Example toolbar button demonstrating nested buttons.</i></center>
|
|
||||||
|
|
||||||
## Custom Components
|
|
||||||
|
|
||||||
The Toolbar Modules supports rendering custom components in place of the
|
|
||||||
application's default. In place of the `type`, `commandName`, and
|
|
||||||
`commandOptions` properties, we instead specify a `CustomComponent`.
|
|
||||||
|
|
||||||
```js
|
|
||||||
{
|
|
||||||
id: 'Custom',
|
|
||||||
label: 'Custom',
|
|
||||||
icon: 'custom-icon',
|
|
||||||
CustomComponent: CustomToolbarComponent,
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
The `CustomComponent` components will receive the following props:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<CustomComponent
|
|
||||||
parentContext="{parentContext}"
|
|
||||||
toolbarClickCallback="{_handleToolbarButtonClick.bind(this)}"
|
|
||||||
button="{button}"
|
|
||||||
key="{button.id}"
|
|
||||||
activeButtons="{activeButtonsIds}"
|
|
||||||
isActive="{isActive}"
|
|
||||||
/>
|
|
||||||
```
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
| ---------------------- | -------- | ------------------------------- |
|
|
||||||
| `activeButtons` | string[] | list of active buttons |
|
|
||||||
| `button` | object | its own definition object |
|
|
||||||
| `key` | string | React key prop |
|
|
||||||
| `isActive` | boolean | If current button is active |
|
|
||||||
| `parentContext` | ? | The parent component's context? |
|
|
||||||
| `toolbarClickCallback` | func | Callback method for clicks |
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
# Module: Viewport
|
|
||||||
|
|
||||||
An extension can register a Viewport Module by defining a `getViewportModule`
|
|
||||||
method that returns a React component. Currently, we use viewport components to
|
|
||||||
add support for:
|
|
||||||
|
|
||||||
- 2D Medical Image Viewing (cornerstone ext.)
|
|
||||||
- Structured Reports as HTML (dicom html ext.)
|
|
||||||
- Encapsulated PDFs as PDFs (dicom pdf ext.)
|
|
||||||
- Whole Slide Microscopy Viewing (whole slide ext.)
|
|
||||||
- etc.
|
|
||||||
|
|
||||||
The general pattern is, the [`sopClassHandlerModule`](#) helps us determine
|
|
||||||
which Viewport Component a set of `sopClassUIDs` should default to. The Viewport
|
|
||||||
Component receives props containing a display set it should know how to render.
|
|
||||||
|
|
||||||
## Viewport Component Props
|
|
||||||
|
|
||||||
Each `ViewportComponent` will receive the following props:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<viewportComponent
|
|
||||||
viewportData="{viewportData}"
|
|
||||||
viewportIndex="{viewportIndex}"
|
|
||||||
children="{[children]}"
|
|
||||||
/>
|
|
||||||
```
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
| --------------- | --------------- | --------------------------------- |
|
|
||||||
| `children` | React.element[] | |
|
|
||||||
| `viewportData` | object | `viewportSpecificData` (probably) |
|
|
||||||
| `viewportIndex` | number | |
|
|
||||||
|
|
||||||
### `@ohif/viewer`
|
|
||||||
|
|
||||||
Viewport components are managed by the `ViewportGrid` Component. Which Viewport
|
|
||||||
component is used depends on:
|
|
||||||
|
|
||||||
- The Layout Configuration
|
|
||||||
- Registered SopClassHandlers
|
|
||||||
- The SopClassUID for visible/selected datasets
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<center><i>An example of three cornerstone Viewports</i></center>
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
# Browser Support
|
|
||||||
|
|
||||||
The browsers that we support are specified in the `.browserlistrc` file located
|
|
||||||
in the `platform/viewer` project. While we leverage the latest language features
|
|
||||||
when writing code, we rely on `babel` to _transpile_ our code so that it can run
|
|
||||||
in the browsers that we support.
|
|
||||||
|
|
||||||
## In Practice
|
|
||||||
|
|
||||||
The OHIF Viewer is capable of _running_ on:
|
|
||||||
|
|
||||||
- IE 11
|
|
||||||
- FireFox
|
|
||||||
- Chrome
|
|
||||||
- Safari
|
|
||||||
- Edge
|
|
||||||
|
|
||||||
However, we do not have the resources to adequately test and maintain bug free
|
|
||||||
functionality across all of these. In order to push web based medical imaging
|
|
||||||
forward, we focus our development efforts on recent version of modern evergreen
|
|
||||||
browsers.
|
|
||||||
|
|
||||||
Our support of older browsers equates to our willingness to review PRs for bug
|
|
||||||
fixes, and target their minimum JS support whenever possible.
|
|
||||||
|
|
||||||
### Polyfills
|
|
||||||
|
|
||||||
> A polyfill, or polyfiller, is a piece of code (or plugin) that provides the
|
|
||||||
> technology that you, the developer, expect the browser to provide natively.
|
|
||||||
|
|
||||||
An example of a polyfill is that you expect `Array.prototype.filter` to exist,
|
|
||||||
but for some reason, the browser that's being used has not implemented that
|
|
||||||
language feature yet. Our earlier transpilation will rectify _syntax_
|
|
||||||
discrepencies, but unimplemented features require a "temporary" implementation.
|
|
||||||
That's where polyfills step in.
|
|
||||||
|
|
||||||
You can utilize a service like [polyfill.io](https://polyfill.io/v3/) to
|
|
||||||
auto-detect and apply polyfills as needed, or you can update the PWA build to
|
|
||||||
include polyfill's in your bundle by incorporating [core-js][core-js]
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Links
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
[core-js]: https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
# Frequently Asked Questions
|
|
||||||
|
|
||||||
## Index
|
|
||||||
|
|
||||||
- [Report a bug][report-bug]
|
|
||||||
- [Request a feature][new-feature]
|
|
||||||
- [Commercial Support & Consulting][commercial-support]
|
|
||||||
- [Academic collaborations][academic]
|
|
||||||
- [FDA Clearance or CE Marking][fda-clearance]
|
|
||||||
- [HIPAA Compliance][hipaa]
|
|
||||||
|
|
||||||
### How do I report a bug?
|
|
||||||
|
|
||||||
Navigate to our [GitHub Repository][new-issue], and submit a new bug report.
|
|
||||||
Follow the steps outlined in the [Bug Report Template][bug-report-template].
|
|
||||||
|
|
||||||
### How can I request a new feature?
|
|
||||||
|
|
||||||
At the moment we are in the process of defining our roadmap and will do our best
|
|
||||||
to communicate this to the community. If your requested feature is on the
|
|
||||||
roadmap, then it will most likely be built at some point. If it is not, you are
|
|
||||||
welcome to build it yourself and [contribute it](development/contributing.md).
|
|
||||||
If you have resources and would like to fund the development of a feature,
|
|
||||||
please [contact us](http://www.ohif.org) or work with community members that
|
|
||||||
offer [consulting services][commercial-support].
|
|
||||||
|
|
||||||
### Who should I contact about Academic Collaborations?
|
|
||||||
|
|
||||||
[Gordon J. Harris](http://www.dfhcc.harvard.edu/insider/member-detail/member/gordon-j-harris-phd/)
|
|
||||||
at Massachusetts General Hospital is the primary contact for any academic
|
|
||||||
collaborators. We are always happy to hear about new groups interested in using
|
|
||||||
the OHIF framework, and may be able to provide development support if the
|
|
||||||
proposed collaboration has an impact on cancer research.
|
|
||||||
|
|
||||||
### Does OHIF offer commercial support?
|
|
||||||
|
|
||||||
The Open Health Imaging Foundation does not offer commercial support, however,
|
|
||||||
some community members do offer consulting services. The following contacts may
|
|
||||||
be useful:
|
|
||||||
|
|
||||||
- Rob Lewis ([Radical Imaging](http://radicalimaging.com/))
|
|
||||||
|
|
||||||
**Please file a Pull Request if you wish to add your name or organization to
|
|
||||||
this list.**
|
|
||||||
|
|
||||||
### Does The OHIF Viewer have [510(k) Clearance][501k-clearance] from the U.S. F.D.A or [CE Marking][ce-marking] from the European Commission?
|
|
||||||
|
|
||||||
**NO.** The OHIF Viewer is **NOT** F.D.A. cleared or CE Marked. It is the users
|
|
||||||
responsibility to ensure compliance with applicable rules and regulations. The
|
|
||||||
[License](https://github.com/OHIF/Viewers/blob/master/LICENSE) for the OHIF
|
|
||||||
Platform does not prevent your company or group from seeking F.D.A. clearance
|
|
||||||
for a product built using the platform.
|
|
||||||
|
|
||||||
If you have gone this route (or are going there), please let us know because we
|
|
||||||
would be interested to hear about your experience.
|
|
||||||
|
|
||||||
### Is The OHIF Viewer [HIPAA][hipaa-def] Compliant?
|
|
||||||
|
|
||||||
**NO.** The OHIF Viewer **DOES NOT** fulfill all of the criteria to become HIPAA
|
|
||||||
Compliant. It is the users responsibility to ensure compliance with applicable
|
|
||||||
rules and regulations.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Links
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
<!-- INDEX -->
|
|
||||||
[report-bug]: #how-do-i-report-a-bug
|
|
||||||
[new-feature]: #how-can-i-request-a-new-feature
|
|
||||||
[commercial-support]: #does-ohif-offer-commercial-support
|
|
||||||
[academic]: #who-should-i-contact-about-academic-collaborations
|
|
||||||
[fda-clearance]: #does-the-ohif-viewer-have-510k-clearance-from-the-us-fda-or-ce-marking-from-the-european-commission
|
|
||||||
[hipaa]: #is-the-ohif-viewer-hipaa-compliant
|
|
||||||
<!-- OTHER -->
|
|
||||||
[501k-clearance]: https://www.fda.gov/MedicalDevices/DeviceRegulationandGuidance/HowtoMarketYourDevice/PremarketSubmissions/PremarketNotification510k/
|
|
||||||
[ce-marking]: https://ec.europa.eu/growth/single-market/ce-marking_en
|
|
||||||
[hipaa-def]: https://en.wikipedia.org/wiki/Health_Insurance_Portability_and_Accountability_Act
|
|
||||||
[new-issue]: https://github.com/OHIF/Viewers/issues/new/choose
|
|
||||||
[bug-report-template]: https://github.com/OHIF/Viewers/issues/new?assignees=&labels=Bug+Report+%3Abug%3A&template=---bug-report.md&title=
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
# PWA vs Packaged
|
|
||||||
|
|
||||||
It's important to know that the OHIF Viewer project provides two different build
|
|
||||||
processes:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Static Asset output: For deploying PWAs
|
|
||||||
yarn run build
|
|
||||||
|
|
||||||
# Single `.js` script, for embedding viewer into existing apps
|
|
||||||
yarn run build:package
|
|
||||||
```
|
|
||||||
|
|
||||||
## Progressive Web Application (PWA)
|
|
||||||
|
|
||||||
> [Progressive Web Apps][pwa] are a new breed of web applications that meet the
|
|
||||||
> [following requirements][pwa-checklist]. Notably, targeting a PWA allows us
|
|
||||||
> provide a reliable, fast, and engaging experience across different devices and
|
|
||||||
> network conditions.
|
|
||||||
|
|
||||||
The OHIF Viewer is maintained as a [monorepo][monorepo]. We use WebPack to build
|
|
||||||
the many small static assets that comprise our application. Also generated is an
|
|
||||||
`index.html` that will serve as an entry point for loading configuration and the
|
|
||||||
application, as well as a `service-worker` that can intelligently cache files so
|
|
||||||
that subsequent requests are from the local file system instead of over the
|
|
||||||
network.
|
|
||||||
|
|
||||||
You can read more about this particular strategy in our
|
|
||||||
[Build for Production Deployment Guide](./../deployment/recipes/build-for-production.md)
|
|
||||||
|
|
||||||
## Commonjs Bundle (Packaged Script)
|
|
||||||
|
|
||||||
The [@ohif/viewer][viewer-npm] package is built with WebPack to provide a React
|
|
||||||
component that can be dropped into a larger application. The `OHIFViewer`
|
|
||||||
component is the entire viewer, configurable via React `props`. This is useful
|
|
||||||
for including the OHIF Viewer in a larger web application, as the entire
|
|
||||||
application can be provided via a `<script>` tag with no build process required.
|
|
||||||
|
|
||||||
The bundle is not as performant or as optimized as the PWA build. It includes
|
|
||||||
fonts, styles, and the core extensions. If you find yourself facing performance
|
|
||||||
issues, you may wish to tweak what's included in this bundle or switch to the
|
|
||||||
PWA build.
|
|
||||||
|
|
||||||
You can read more about this particular strategy in our
|
|
||||||
[Embedded Viewer Deployment Guide](./../deployment/recipes/embedded-viewer.md)
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Links
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
[pwa]: https://developers.google.com/web/progressive-web-apps/
|
|
||||||
[pwa-checklist]: https://developers.google.com/web/progressive-web-apps/checklist
|
|
||||||
[monorepo]: https://github.com/OHIF/Viewers/issues/768
|
|
||||||
[viewer-npm]: https://www.npmjs.com/package/@ohif/viewer
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||