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,83 +9,18 @@ version: 2.1
|
||||
# create pull request previews and to update `https://docs.ohif.org`
|
||||
###
|
||||
|
||||
## https://github.com/cypress-io/circleci-orb
|
||||
##
|
||||
orbs:
|
||||
codecov: codecov/codecov@1.0.5
|
||||
cypress: cypress-io/cypress@1.13.0
|
||||
|
||||
defaults: &defaults
|
||||
docker:
|
||||
- image: circleci/node:12.9.1
|
||||
environment:
|
||||
TERM: xterm # Enable colors in term
|
||||
QUICK_BUILD: true
|
||||
- image: circleci/node:10.16.0
|
||||
working_directory: ~/repo
|
||||
|
||||
jobs:
|
||||
###
|
||||
# Workflow: PR_CHECKS
|
||||
###
|
||||
UNIT_TESTS:
|
||||
PR_UNIT_TESTS:
|
||||
<<: *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:
|
||||
# Enable yarn workspaces
|
||||
- run: yarn config set workspaces-experimental true
|
||||
@@ -94,49 +29,13 @@ jobs:
|
||||
- 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
|
||||
|
||||
- setup_remote_docker:
|
||||
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-
|
||||
- yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
- yarn-packages-v1-{{ .Branch }}-
|
||||
- yarn-packages-v1-
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: yarn install --frozen-lockfile
|
||||
@@ -144,154 +43,188 @@ jobs:
|
||||
name: Save Yarn Package Cache
|
||||
paths:
|
||||
- ~/.cache ## Cache yarn and Cypress
|
||||
key: yarn-packages-{{ 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
|
||||
key: yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
|
||||
DEPLOY_TO_DEV:
|
||||
docker:
|
||||
- image: circleci/node:12.9.1
|
||||
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 TESTS
|
||||
- run:
|
||||
cp .netlify/deploy-workflow/_redirects platform/viewer/dist/_redirects
|
||||
- run: cd .netlify && npm run deploy
|
||||
name: "JavaScript Test Suite"
|
||||
command: yarn run test:unit:ci
|
||||
|
||||
DEPLOY_TO_STAGING:
|
||||
docker:
|
||||
- image: circleci/node:12.9.1
|
||||
environment:
|
||||
TERM: xterm
|
||||
NETLIFY_SITE_ID: c7502ae3-b150-493c-8422-05701e44a969
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
- run: cd .netlify && npm install
|
||||
# PLATFORM/VIEWER
|
||||
- run:
|
||||
cp .netlify/deploy-workflow/_redirects platform/viewer/dist/_redirects
|
||||
- run: cd .netlify && npm run deploy
|
||||
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"
|
||||
|
||||
DEPLOY_TO_PRODUCTION:
|
||||
docker:
|
||||
- image: circleci/node:12.9.1
|
||||
environment:
|
||||
TERM: xterm
|
||||
NETLIFY_SITE_ID: 79c4a5da-5c95-4dc9-84f7-45fd9dfe21b0
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
- run: cd .netlify && npm install
|
||||
# PLATFORM/CORE
|
||||
- run:
|
||||
cp .netlify/deploy-workflow/_redirects platform/viewer/dist/_redirects
|
||||
- run: cd .netlify && npm run deploy
|
||||
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: RELEASE
|
||||
###
|
||||
NPM_PUBLISH:
|
||||
MERGE_UNIT_TESTS:
|
||||
<<: *defaults
|
||||
|
||||
steps:
|
||||
- run: yarn -v
|
||||
# Enable yarn workspaces
|
||||
- run: yarn config set workspaces-experimental true
|
||||
|
||||
# Checkout code and ALL Git Tags
|
||||
- checkout:
|
||||
post:
|
||||
- git fetch --all
|
||||
# Use increasingly general patterns to restore cache
|
||||
- restore_cache:
|
||||
name: Restore Yarn and Cypress Package Cache
|
||||
keys:
|
||||
- yarn-packages-{{ checksum "yarn.lock" }}
|
||||
- yarn-packages-
|
||||
# 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-{{ checksum "yarn.lock" }}
|
||||
- ~/.cache ## Cache yarn and Cypress
|
||||
key: yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
|
||||
# RUN TESTS
|
||||
- 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: |
|
||||
rm -rf ~/.ssh
|
||||
mkdir ~/.ssh/
|
||||
echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config
|
||||
git config --global user.email "danny.ri.brown+ohif-bot@gmail.com"
|
||||
git config --global user.name "ohif-bot"
|
||||
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: Authenticate with NPM registry
|
||||
command:
|
||||
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc
|
||||
- run: npx lerna version
|
||||
- run: npx lerna publish from-package
|
||||
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"
|
||||
|
||||
# Persist :+1:
|
||||
- persist_to_workspace:
|
||||
root: ~/repo
|
||||
paths: .
|
||||
|
||||
DOCS_PUBLISH:
|
||||
e2e_test:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: cypress/base:8
|
||||
environment:
|
||||
## this enables colors in the output
|
||||
TERM: xterm
|
||||
steps:
|
||||
- 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:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
- run:
|
||||
name: Avoid hosts unknown for github
|
||||
command: |
|
||||
rm -rf ~/.ssh
|
||||
mkdir ~/.ssh/
|
||||
echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config
|
||||
git config --global user.email "danny.ri.brown+ohif-bot@gmail.com"
|
||||
git config --global user.name "ohif-bot"
|
||||
- run: yarn global add gitbook-cli gh-pages
|
||||
- run: chmod +x ~/repo/.circleci/build-and-publish-docs.sh
|
||||
- run: ~/repo/.circleci/build-and-publish-docs.sh
|
||||
command:
|
||||
mkdir ~/.ssh/ && echo -e "Host github.com\n\tStrictHostKeyChecking
|
||||
no\n" > ~/.ssh/config
|
||||
# --no-ci argument is not ideal; however, semantic-rlease thinks we're
|
||||
# attempting to run it from a `pr`, which is not the case
|
||||
- run:
|
||||
name: Publish using Semantic Release
|
||||
command: npx semantic-release --debug
|
||||
# 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
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: false
|
||||
docker_layer_caching: true
|
||||
- run:
|
||||
name: Build and push Docker image
|
||||
command: |
|
||||
# This file will exist if a new version was published by
|
||||
# our command in the previous job. Created in npm postpublish hook
|
||||
# in the `platform/viewer` project.
|
||||
if [[ ! -e platform/viewer/success_version.txt ]]; then
|
||||
# our `semantic-release` command in the previous job
|
||||
if [[ ! -e tmp/updated-version.txt ]]; then
|
||||
exit 0
|
||||
else
|
||||
# Remove npm config
|
||||
rm -f ./.npmrc
|
||||
# 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}
|
||||
echo $IMAGE_VERSION
|
||||
echo $IMAGE_VERSION_FULL
|
||||
@@ -302,176 +235,109 @@ jobs:
|
||||
docker push ohif/$IMAGE_NAME:latest
|
||||
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:
|
||||
version: 2
|
||||
|
||||
PR_CHECKS:
|
||||
# PULL REQUESTS
|
||||
pull_requests:
|
||||
jobs:
|
||||
- UNIT_TESTS:
|
||||
- PR_UNIT_TESTS:
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
- master
|
||||
- feature/*
|
||||
- hotfix/*
|
||||
# E2E: PWA
|
||||
- cypress/run:
|
||||
name: 'E2E: PWA'
|
||||
executor: cypress/browsers-chrome76
|
||||
browser: chrome
|
||||
pre-steps:
|
||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
||||
add wait-on' # Use yarn latest
|
||||
yarn: true
|
||||
record: false
|
||||
store_artifacts: false
|
||||
working_directory: platform/viewer
|
||||
build: npx cross-env QUICK_BUILD=true 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
|
||||
requires:
|
||||
- UNIT_TESTS
|
||||
# E2E: script-tag
|
||||
- cypress/run:
|
||||
name: 'E2E: Script Tag'
|
||||
executor: cypress/browsers-chrome76
|
||||
browser: chrome
|
||||
pre-steps:
|
||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
||||
add wait-on' # Use yarn latest
|
||||
yarn: true
|
||||
record: false
|
||||
store_artifacts: false
|
||||
working_directory: platform/viewer
|
||||
build: npx cross-env QUICK_BUILD=true 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
|
||||
requires:
|
||||
- UNIT_TESTS
|
||||
|
||||
PR_OPTIONAL_VISUAL_TESTS:
|
||||
# MERGE TO MASTER
|
||||
cut_release:
|
||||
jobs:
|
||||
- AWAIT_APPROVAL:
|
||||
type: approval
|
||||
- MERGE_UNIT_TESTS:
|
||||
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
|
||||
- cypress/run:
|
||||
name: 'Generate Percy Snapshots'
|
||||
executor: cypress/browsers-chrome76
|
||||
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: npx cross-env QUICK_BUILD=true 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
|
||||
- docker_publish:
|
||||
requires:
|
||||
- AWAIT_APPROVAL
|
||||
|
||||
PR_OPTIONAL_DOCKER_PUBLISH:
|
||||
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
|
||||
- npm_publish
|
||||
# Update viewer.ohif.org
|
||||
- build_demo_site:
|
||||
requires:
|
||||
- AWAIT_APPROVAL
|
||||
|
||||
###
|
||||
# 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:
|
||||
- e2e_test
|
||||
- demo_site_publish:
|
||||
requires:
|
||||
- BUILD
|
||||
- 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: cypress/browsers-chrome76
|
||||
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: npx cross-env QUICK_BUILD=true 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
|
||||
- build_demo_site
|
||||
@@ -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:
|
||||
project:
|
||||
default:
|
||||
threshold: 0.5%
|
||||
threshold: 0.10%
|
||||
core:
|
||||
flags: core
|
||||
threshold: 0.5%
|
||||
viewer:
|
||||
flags: viewer
|
||||
threshold: 0.5%
|
||||
patch: off
|
||||
flags:
|
||||
core:
|
||||
|
||||
@@ -1,33 +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
|
||||
|
||||
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": {
|
||||
"cy": true,
|
||||
"before": true,
|
||||
"context": true,
|
||||
"Cypress": true,
|
||||
"assert": true
|
||||
@@ -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,18 +0,0 @@
|
||||
---
|
||||
name: "\U0001F917 Support Question"
|
||||
about: "I have a question \U0001F4AC"
|
||||
title: ''
|
||||
labels: 'Community: Question :question:, Triage :white_flag:'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
> :hand: We are a small team with limited resources. Your question is much more
|
||||
> likely to be answered if it is
|
||||
> [a good question](https://stackoverflow.com/help/how-to-ask)
|
||||
|
||||
**Description**
|
||||
|
||||
Questions can often be answered by our documentation. Unable to find an answer
|
||||
in our docs? We'll try to help. In the meantime, if you answer your own
|
||||
question, please respond with the answer here so that others may benefit as
|
||||
well. Better yet, open a PR to expand our docs ^\_^
|
||||
@@ -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: 21
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# 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,7 +8,6 @@ docs/_book
|
||||
src/version.js
|
||||
junit.xml
|
||||
coverage/
|
||||
.docz/
|
||||
|
||||
# YALC (for Erik)
|
||||
.yalc
|
||||
@@ -21,7 +20,6 @@ npm-debug.log
|
||||
package-lock.json
|
||||
yarn-error.log
|
||||
.DS_Store
|
||||
.env
|
||||
|
||||
# Common Example Data Directories
|
||||
sampledata/
|
||||
@@ -29,8 +27,4 @@ example/deps/
|
||||
docker/dcm4che/dcm4che-arc
|
||||
|
||||
# Cypress test results
|
||||
videos/
|
||||
screenshots/
|
||||
|
||||
# Locize settings
|
||||
.locize
|
||||
cypress/videos/
|
||||
@@ -3,39 +3,79 @@
|
||||
# Set directory to location of this script
|
||||
# https://stackoverflow.com/a/3355423/1867984
|
||||
cd "$(dirname "$0")"
|
||||
cd .. # Up to project root
|
||||
|
||||
# Helpful to verify which versions we're using
|
||||
yarn -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
|
||||
# should also symlink all projects appropriately
|
||||
yarn run lerna:restore
|
||||
yarn install --no-ignore-optional --pure-lockfile
|
||||
|
||||
# Build && Move PWA Output
|
||||
yarn run build:ci
|
||||
mkdir -p ./.netlify/www/pwa
|
||||
mv platform/viewer/dist/* .netlify/www/pwa -v
|
||||
|
||||
# Build && Move script output
|
||||
# yarn run build:package
|
||||
|
||||
# Build && Move Docz Output
|
||||
# Using local yarn install to prevent Gatsby from needing to access
|
||||
# node_modules above the platform/ui folder
|
||||
cd platform/ui
|
||||
yarn install
|
||||
yarn run build
|
||||
cd ../..
|
||||
mkdir -p ./.netlify/www/ui
|
||||
mv platform/ui/.docz/dist/* .netlify/www/ui -v
|
||||
|
||||
# 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.'
|
||||
# Navigate to our Viewer project
|
||||
cd ./platform/viewer/
|
||||
|
||||
# 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
|
||||
# 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,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,8 +1,10 @@
|
||||
{
|
||||
"editor.rulers": [80, 120],
|
||||
|
||||
// ===
|
||||
// Spacing
|
||||
// ===
|
||||
|
||||
"editor.insertSpaces": true,
|
||||
"editor.tabSize": 2,
|
||||
"editor.trimAutoWhitespace": true,
|
||||
@@ -10,26 +12,19 @@
|
||||
"files.eol": "\n",
|
||||
"files.insertFinalNewline": true,
|
||||
"files.trimFinalNewlines": true,
|
||||
|
||||
// ===
|
||||
// Event Triggers
|
||||
// ===
|
||||
|
||||
"editor.formatOnSave": true,
|
||||
"eslint.autoFixOnSave": true,
|
||||
"eslint.run": "onSave",
|
||||
"eslint.validate": [
|
||||
{
|
||||
"language": "javascript",
|
||||
"autoFix": true
|
||||
},
|
||||
{
|
||||
"language": "javascriptreact",
|
||||
"autoFix": true
|
||||
}
|
||||
{ "language": "javascript", "autoFix": true },
|
||||
{ "language": "javascriptreact", "autoFix": true }
|
||||
],
|
||||
"prettier.disableLanguages": ["html"],
|
||||
"prettier.disableLanguages": [],
|
||||
"prettier.endOfLine": "lf",
|
||||
"workbench.colorCustomizations": {},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
}
|
||||
"workbench.colorCustomizations": {}
|
||||
}
|
||||
@@ -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,113 +0,0 @@
|
||||
// ~~ ENV
|
||||
const dotenv = require('dotenv');
|
||||
//
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const PACKAGE = require('../platform/viewer/package.json');
|
||||
// ~~ 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' : 'cheap-module-eval-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': JSON.stringify(PACKAGE.version || ''),
|
||||
'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,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,186 +1,38 @@
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- 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)
|
||||
# OHIF Medical Imaging Platform
|
||||
|
||||
[![lerna][lerna-image]][lerna-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
|
||||
|
||||
### Community & Experience
|
||||
...
|
||||
|
||||
The OHIF Viewer is a collaborative effort that has served as the basis for many
|
||||
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.
|
||||
## Quick Start
|
||||
|
||||
### 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
|
||||
|
||||
These commands are available from the root directory. Each project directory
|
||||
also supports a number of commands that can be found in their respective
|
||||
`README.md` and `project.json` files.
|
||||
### Global
|
||||
|
||||
| Yarn Commands | Description |
|
||||
| ---------------------------- | ------------------------------------------------------------- |
|
||||
| **Develop** | |
|
||||
| `dev` or `start` | Default development experience for Viewer |
|
||||
| `dev:project <package-name>` | Replace with `core`, `ui`, `i18n`, `cornerstone`, `vtk`, etc. |
|
||||
| `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 |
|
||||
| Commands | | |
|
||||
| --------------- | --- | ----------------------------------------------------- |
|
||||
| `build:package` | | Builds commonjs bundles for all projects |
|
||||
| `test:unit` | | Jest multi-project test runner; overall coverage |
|
||||
| `test:unit:ci` | | Runs tests in parallel. Reports coverage per project. |
|
||||
|
||||
\* - For more information on our different builds, check out our [Deploy
|
||||
Docs][deployment-docs]
|
||||
### Local
|
||||
|
||||
## 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
|
||||
[`monorepo`][monorepo]. This means that this repository, instead of containing a
|
||||
@@ -210,58 +62,72 @@ you'll see the following:
|
||||
```
|
||||
|
||||
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 |
|
||||
| ------------------------------- | ---------------------------------------------------------------------------------------------------- | ----------------- |
|
||||
| [@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] |
|
||||
### Getting Started
|
||||
|
||||
### 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
|
||||
customize and configure these extensions, and you can even create your own. You
|
||||
can [read more about extensions here][ohif-extensions].
|
||||
#### To Develop
|
||||
|
||||
| Name | Description | Links |
|
||||
| -------------------------------------------------------------- | ------------------------------------------------------- | ---------------------- |
|
||||
| [@ohif/extension-cornestone][extension-cornerstone] | 2D image viewing, annotation, and segementation tools | [NPM][cornerstone-npm] |
|
||||
| [@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] |
|
||||
```bash
|
||||
# Force install
|
||||
yarn install --force
|
||||
|
||||
## 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
|
||||
> Imaging Research and Clinical Trials_
|
||||
>
|
||||
> 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)
|
||||
```bash
|
||||
# Add shared dev dependency for workspace
|
||||
yarn add --dev -W package-name
|
||||
```
|
||||
|
||||
**Note:** If you use or find this repository helpful, please take the time to
|
||||
star this repository on Github. This is an easy way for us to assess adoption
|
||||
and it can help us obtain future funding for the project.
|
||||
// module vs main vs jsnext:main vs browser
|
||||
https://babeljs.io/blog/2018/06/26/on-consuming-and-publishing-es2015+-packages
|
||||
|
||||
This work is supported primarily by the National Institutes of Health, National
|
||||
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).
|
||||
Webpack: https://webpack.js.org/configuration/resolve/
|
||||
|
||||
## License
|
||||
UMD builds go through
|
||||
|
||||
MIT © [OHIF](https://github.com/OHIF)
|
||||
- index.umd.js
|
||||
- Extensions passed in via window config
|
||||
|
||||
PWA builds go through
|
||||
|
||||
- index.js
|
||||
- Extensions specified in file or by window config
|
||||
|
||||
> The module property should point to a script that utilizes ES2015 module
|
||||
> 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.
|
||||
|
||||
## Notes
|
||||
|
||||
Lerna requires `GH_TOKEN` for GitHub authentication token
|
||||
`git remote update` 1st call, need to verify host key
|
||||
|
||||
<!--
|
||||
Links
|
||||
@@ -271,59 +137,12 @@ MIT © [OHIF](https://github.com/OHIF)
|
||||
<!-- Badges -->
|
||||
[lerna-image]: https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg
|
||||
[lerna-url]: https://lerna.js.org/
|
||||
[netlify-image]: https://api.netlify.com/api/v1/badges/32708787-c9b0-4634-b50f-7ca41952da77/deploy-status
|
||||
[netlify-url]: https://app.netlify.com/sites/ohif-dev/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
|
||||
[netlify-image]: https://api.netlify.com/api/v1/badges/a5d369ab-18a6-41c3-bcde-83805205ac7f/deploy-status
|
||||
[netlify-url]: https://app.netlify.com/sites/ohif/deploys
|
||||
<!-- Links -->
|
||||
[monorepo]: https://en.wikipedia.org/wiki/Monorepo
|
||||
[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
|
||||
[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 path = require('path');
|
||||
const aliases = require("./aliases.config");
|
||||
const path = require("path");
|
||||
|
||||
// https://babeljs.io/docs/en/options#babelrcroots
|
||||
module.exports = {
|
||||
babelrcRoots: ['./platform/*', './extensions/*'],
|
||||
plugins: ['inline-react-svg', '@babel/plugin-proposal-class-properties'],
|
||||
// https://babeljs.io/docs/en/options#babelrcroots
|
||||
presets: [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
targets: {
|
||||
ie: "11"
|
||||
}
|
||||
}
|
||||
],
|
||||
"@babel/preset-react"
|
||||
],
|
||||
babelrcRoots: ["./platform/*", "./extensions/*"],
|
||||
plugins: [
|
||||
"inline-react-svg",
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
"@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: {
|
||||
test: {
|
||||
presets: [
|
||||
[
|
||||
// TODO: https://babeljs.io/blog/2019/03/19/7.4.0#migration-from-core-js-2
|
||||
'@babel/preset-env',
|
||||
{
|
||||
modules: 'commonjs',
|
||||
debug: false,
|
||||
},
|
||||
],
|
||||
'@babel/preset-react',
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'@babel/plugin-transform-regenerator',
|
||||
'@babel/plugin-transform-runtime',
|
||||
],
|
||||
debug: {
|
||||
sourceMaps: "inline",
|
||||
retainLines: true
|
||||
},
|
||||
production: {
|
||||
presets: [
|
||||
// WebPack handles ES6 --> Target Syntax
|
||||
['@babel/preset-env', { modules: false }],
|
||||
'@babel/preset-react',
|
||||
],
|
||||
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__'],
|
||||
},
|
||||
},
|
||||
build: {
|
||||
ignore: ["**/*.test.jsx", "**/*.test.js", "__snapshots__", "__tests__"]
|
||||
}
|
||||
}
|
||||
// ignore: ["node_modules"]
|
||||
};
|
||||
|
||||
// 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='column' style='text-align: right; padding: 0 20px'>
|
||||
<strong>Looking for a Live Demo?</strong>
|
||||
<a href="http://viewer.ohif.org/">Preview The OHIF Viewer</a>
|
||||
<strong>Looking for a Deploy Preview?</strong>
|
||||
<a onclick="function redirect() { window.location.href='/demo/'; } redirect();">Deploy Preview for Viewer</a>
|
||||
</div>
|
||||
<div class='column' style='text-align: left; padding: 0 20px'>
|
||||
<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
|
||||
our integration and extension points.
|
||||
|
||||
## Where to next?
|
||||
## Where to Next?
|
||||
|
||||
Check out these helpful links:
|
||||
|
||||
- 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.
|
||||
[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).
|
||||
|
||||
<!--
|
||||
@@ -48,7 +48,7 @@ Check out these helpful links:
|
||||
-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[ohif-org]: http://www.ohif.org
|
||||
[ohif-org]: https://www.ohif.org
|
||||
[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb
|
||||
[gh-issues]: https://github.com/OHIF/Viewers/issues
|
||||
<!-- prettier-ignore-end -->
|
||||
@@ -1,40 +1,31 @@
|
||||
# OHIF Viewers
|
||||
|
||||
- [Our Process](our-process.md)
|
||||
- Development
|
||||
- [Getting Started](development/getting-started.md)
|
||||
- [Contributing](development/contributing.md)
|
||||
- [Continuous Integration](development/continous-integration.md)
|
||||
- [Testing](development/testing.md)
|
||||
- [Configuring](configuring/index.md)
|
||||
- [Data Source](configuring/data-source.md)
|
||||
- Essentials
|
||||
- [Getting Started](essentials/getting-started.md)
|
||||
- [Installation](essentials/installation.md)
|
||||
- [Data Source](essentials/data-source.md)
|
||||
- [Configuration](essentials/configuration.md)
|
||||
- [Themeing](essentials/themeing.md)
|
||||
- [Translating](essentials/translating.md)
|
||||
- [Troubleshooting](essentials/troubleshooting.md)
|
||||
- [Scope of Project](essentials/scope-of-project.md)
|
||||
|
||||
---
|
||||
|
||||
- [Architecture](architecture/index.md)
|
||||
- [Viewer](viewer/index.md)
|
||||
- [Configuration](viewer/configuration.md)
|
||||
- [Themeing](viewer/themeing.md)
|
||||
- [Internationalization](viewer/internationalization.md)
|
||||
- [Extensions](extensions/index.md)
|
||||
- [Registering](extensions/index.md#registering-an-extension)
|
||||
- [Lifecycle Hooks](extensions/index.md#lifecycle-hooks)
|
||||
- [preRegistration](extensions/lifecycle/pre-registration.md)
|
||||
- [Modules](extensions/index.md#modules)
|
||||
- [Commands](extensions/modules/commands.md)
|
||||
- [Panel](extensions/modules/panel.md)
|
||||
- [SOP Class Handler](extensions/modules/sop-class-handler.md)
|
||||
- [Toolbar](extensions/modules/toolbar.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)
|
||||
- [Advanced](advanced/index.md)
|
||||
- [Architecture](advanced/architecture.md)
|
||||
- [Overview](advanced/architecture.md#overview)
|
||||
- [Business Logic](advanced/architecture.md#business-logic)
|
||||
- [Component Library](advanced/architecture.md#react-component-library)
|
||||
- [Extensions](advanced/architecture.md#misc-extensions)
|
||||
- [Diagram](advanced/architecture.md#diagram)
|
||||
- [Common Questions](advanced/architecture.md#common-questions)
|
||||
- [Extensions](advanced/extensions.md)
|
||||
- [Overview](advanced/extensions.md#overview)
|
||||
- [Modules](advanced/extensions.md#modules)
|
||||
- [Registering](advanced/extensions.md#registering-extensions)
|
||||
- [OHIF Maintained](advanced/extensions.md#ohif-maintained-extensions)
|
||||
- [Custom Tools](advanced/custom-tools.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -54,8 +45,7 @@
|
||||
|
||||
---
|
||||
|
||||
- [FAQ](faq/index.md)
|
||||
- [Scope of Project](faq/scope-of-project.md)
|
||||
- [Browser Support](faq/browser-support.md)
|
||||
- [PWA vs Packaged](faq/pwa-vs-packaged.md)
|
||||
- [Contributing](contributing.md)
|
||||
- [FAQ](frequently-asked-questions.md)
|
||||
- [Roadmap](roadmap.md)
|
||||
- [Help](help.md)
|
||||
@@ -10,7 +10,7 @@
|
||||
<!-- CORNERSTONE.js -->
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.npmjs.com/package/@ohif/extension-cornerstone">
|
||||
<a href="https://www.npmjs.com/package/ohif-cornerstone-extension">
|
||||
Cornerstone
|
||||
</a>
|
||||
</td>
|
||||
@@ -22,7 +22,7 @@
|
||||
<!-- VTK.js -->
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.npmjs.com/package/@ohif/extension-vtk">
|
||||
<a href="https://www.npmjs.com/package/ohif-vtk-extension">
|
||||
VTK.js
|
||||
</a>
|
||||
</td>
|
||||
@@ -31,45 +31,32 @@
|
||||
</td>
|
||||
<td>Viewport, Toolbar</td>
|
||||
</tr>
|
||||
<!-- dicom-html -->
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.npmjs.com/package/@ohif/extension-dicom-html">DICOM HTML</a>
|
||||
<a href="">HTML</a>
|
||||
</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>Viewport, SopClassHandler</td>
|
||||
</tr>
|
||||
<!-- dicom-pdf -->
|
||||
<tr>
|
||||
<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>
|
||||
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>Viewport, SopClassHandler</td>
|
||||
</tr>
|
||||
<!-- dicom-microscopy -->
|
||||
<tr>
|
||||
<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>
|
||||
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>Viewport, SopClassHandler</td>
|
||||
</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>
|
||||
</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 |
@@ -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
|
||||
|
||||
> 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
|
||||
> 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)
|
||||
which requires tokens generated via the
|
||||
[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
|
||||
|
||||
@@ -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)
|
||||
- Add your domain (e.g. `http://localhost:3000`) to Authorized JavaScript
|
||||
origins.
|
||||
- Add your domain, plus `callback` (e.g. `http://localhost:3000/callback`) to
|
||||
Authorized Redirect URIs.
|
||||
- Add your domain, plus `_oauth/google` (e.g.
|
||||
`http://localhost:3000/_oauth/google`) to Authorized Redirect URIs.
|
||||
- Save your Client ID for later.
|
||||
|
||||
- (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
|
||||
|
||||
1. Open the `config/google.js` file and change `YOURCLIENTID` to your Client ID
|
||||
value.
|
||||
1. Run the OHIF Viewer using the config/google.js configuration file
|
||||
1. Open the `config/oidc-googleCloud.json` file and change `YOURCLIENTID` to
|
||||
your Client ID value.
|
||||
1. Run the OHIF Viewer using the oidc-googleCloud.json configuration file
|
||||
|
||||
```bash
|
||||
cd OHIFViewer
|
||||
yarn install
|
||||
APP_CONFIG=config/google.js yarn run dev
|
||||
METEOR_PACKAGE_DIRS="../Packages" meteor npm install
|
||||
METEOR_PACKAGE_DIRS="../Packages" meteor --settings ../config/oidc-googleCloud.json
|
||||
```
|
||||
|
||||
## Running via Docker
|
||||
|
||||
The OHIF Viewer Docker container can be connected to Google Cloud Healthcare by
|
||||
providing a Client ID at runtime. This is a very simple method to get up and
|
||||
running.
|
||||
OHIF is also providing a Docker container which can connect to Google Cloud
|
||||
Healthcare with a Client ID which is provided at runtime. This is a very simple
|
||||
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. Run the Docker container, providing a Client ID as an environment variable.
|
||||
Client IDs look like `xyz.apps.googleusercontent.com`.
|
||||
|
||||
```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
|
||||
|
||||
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
|
||||
application][pwa-url]) by building and hosting a collection of static assets. In
|
||||
either case, you will need to configure your instance of the Viewer so that it
|
||||
can connect to your data source (the database or PACS that provides the data
|
||||
your Viewer will display).
|
||||
script source][ohif-viewer-npm], or served up as a stand-alone PWA ([progressive
|
||||
web application][pwa-url]) by building and hosting a collection of static
|
||||
assets. In either case, you will need to configure your instance of the Viewer
|
||||
so that it can connect to your data source (the database or PACS that provides
|
||||
the data your Viewer will display).
|
||||
|
||||
## Overview
|
||||
|
||||
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
|
||||
applications. If you find yourself a little lost, please don't hesitate to
|
||||
[reach out for help](/help.md)
|
||||
there is an inherent amount of complexity in customizing, optimizing, and
|
||||
deploying web applications. If you find yourself a little lost, please don't
|
||||
hesitate to [reach out for help](/help.md)
|
||||
|
||||
## Deployment Scenarios
|
||||
|
||||
@@ -35,7 +35,8 @@ benefits, but comes at the cost of time and complexity. Some benefits include:
|
||||
|
||||
_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
|
||||
- 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
|
||||
domain name as your archive, then connecting the two is as simple as following
|
||||
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?
|
||||
|
||||
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
|
||||
[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".
|
||||
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
|
||||
[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?
|
||||
|
||||
It's possible to supply all Study data via JSON format, in the event you do not have a DicomWeb endpoint.
|
||||
You can host all of the relevant files on any web accessible server (Amazon S3, Azure Blob Storage, Local file server etc.)
|
||||
|
||||
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.
|
||||
> This is possible to do with the OHIF Viewer, but not as straightforward. Look
|
||||
> out for documentation on this subject in the near future.
|
||||
|
||||
...
|
||||
|
||||
### 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
|
||||
it's OpenID-Connect settings on the `oidc` configuration key. You can set these
|
||||
values following the instructions laid out in the
|
||||
[Configuration Essentials Guide](./../configuring/index.md).
|
||||
[Configuration Essentials Guide](./../essentials/configuration.md).
|
||||
|
||||
_Example OpenID-Connect Settings:_
|
||||
|
||||
@@ -263,7 +167,7 @@ window.config = {
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
@@ -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.
|
||||
|
||||
- Script Include
|
||||
- [Embedding the Viewer](./recipes/embedded-viewer.md)
|
||||
- [Embedding the Viewer](deployment/recipes/embedded-viewer.md)
|
||||
- Stand-Alone
|
||||
- [Build for Production](./recipes/build-for-production.md)
|
||||
- [Static](./recipes/static-assets.md)
|
||||
- [Nginx + Image Archive](./recipes/nginx--image-archive.md)
|
||||
- [User Account Control](./recipes/user-account-control.md)
|
||||
- [Build for Production](deployment/recipes/build-for-production.md)
|
||||
- [Static](deployment/recipes/static-assets.md)
|
||||
- [Nginx + Image Archive](deployment/recipes/nginx--image-archive.md)
|
||||
- [User Account Control](deployment/recipes/user-account-control.md)
|
||||
|
||||
<!--
|
||||
Links
|
||||
-->
|
||||
|
||||
<!-- 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/
|
||||
[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
|
||||
@@ -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/
|
||||
[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-sandbox]: https://codesandbox.io/s/viewer-script-tag-tprch
|
||||
[code-sandbox]: https://codesandbox.io/s/ohif-viewer-script-tag-usage-b3st9
|
||||
<!-- prettier-ignore-end -->
|
||||
@@ -1,7 +1,7 @@
|
||||
# Build for Production
|
||||
|
||||
> 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)
|
||||
|
||||
## Overview
|
||||
@@ -19,6 +19,9 @@ _With Git:_
|
||||
```bash
|
||||
# Clone the remote repository to your local machine
|
||||
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),
|
||||
@@ -26,7 +29,7 @@ More on: _[`git clone`](https://git-scm.com/docs/git-clone),
|
||||
|
||||
_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
|
||||
|
||||
@@ -34,24 +37,20 @@ Open your terminal, and navigate to the directory containing the source files.
|
||||
Next run these commands:
|
||||
|
||||
```js
|
||||
// If you haven't already, enable yarn workspaces
|
||||
yarn config set workspaces-experimental true
|
||||
|
||||
// Restore dependencies
|
||||
yarn install
|
||||
|
||||
// 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
|
||||
project's folder. It should roughly resemble the following:
|
||||
If everything worked as expected, you should have a new `build/` directory in
|
||||
the project's folder. It should roughly resemble the following:
|
||||
|
||||
```bash
|
||||
<root>platform/viewer/dist/
|
||||
├── app-config.js
|
||||
├── app.bundle.js
|
||||
├── app.css
|
||||
build
|
||||
├── config/
|
||||
├── static/
|
||||
├── index.html
|
||||
├── manifest.json
|
||||
├── service-worker.js
|
||||
@@ -65,15 +64,61 @@ how to configure the project for your own imaging archive below.
|
||||
|
||||
### Configuration
|
||||
|
||||
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.
|
||||
> This step assumes you have an imaging archive. If you need assistance setting
|
||||
> one up, check out the [`Data Source` Guide](./../../essentials/data-source.md)
|
||||
> or a deployment recipe that contains an open source Image Archive
|
||||
|
||||
The easiest way to apply your own configuration is to modify the `default.js`
|
||||
file. For more advanced cofiguration options, check out our
|
||||
[configuration essentials guide](/configuring/index.md).
|
||||
#### 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.
|
||||
|
||||
## Next Steps
|
||||
|
||||
@@ -97,7 +142,7 @@ _Advanced_
|
||||
### Testing Build Output Locally
|
||||
|
||||
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:
|
||||
|
||||
```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]
|
||||
- [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
|
||||
|
||||
@@ -128,8 +174,10 @@ web application. For a starting point, check out this repository's own use of:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[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.toml]: https://github.com/OHIF/Viewers/blob/master/netlify.toml
|
||||
[build-deploy-preview.sh]: https://github.com/OHIF/Viewers/blob/master/.netlify/build-deploy-preview.sh
|
||||
[netlify.toml]: https://github.com/OHIF/Viewers/blob/react/netlify.toml
|
||||
[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 -->
|
||||
@@ -12,27 +12,46 @@ include tags. Here's how it works:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700&display=swap">
|
||||
<code>Google Font: Roboto</code>
|
||||
<a href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
|
||||
<code>bootstrap@3.3.7</code>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://unpkg.com/@ohif/viewer">
|
||||
<code>@ohif/viewer@latest</code>
|
||||
<a href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700|Sanchez&display=swap">
|
||||
<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>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<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>
|
||||
</ol>
|
||||
|
||||
```js
|
||||
// Set before importing `ohif-viewer` (JS Object)
|
||||
// Set before importing `ohif-viewer`
|
||||
window.config = {
|
||||
// default: '/'
|
||||
routerBasename: '/',
|
||||
// default: ''
|
||||
relativeWebWorkerScriptsPath: '',
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
{
|
||||
@@ -43,67 +62,28 @@ window.config = {
|
||||
qidoSupportsIncludeField: true,
|
||||
imageRendering: 'wadors',
|
||||
thumbnailRendering: 'wadors',
|
||||
requestOptions: {
|
||||
requestFromBrowser: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
To learn more about how you can configure the OHIF Viewer, check out our
|
||||
[Configuration Guide](../../configuring/index.md).
|
||||
|
||||
<ol start="3"><li>
|
||||
<ol start="5"><li>
|
||||
Render the viewer in the web page's target <code>div</code>
|
||||
</li></ol>
|
||||
|
||||
```js
|
||||
// Made available by the `@ohif/viewer` script included in step 1
|
||||
var containerId = 'id-of-div-to-render-component-to';
|
||||
var componentRenderedOrUpdatedCallback = function() {
|
||||
console.log('OHIF Viewer rendered/updated');
|
||||
};
|
||||
window.OHIFViewer.installViewer(
|
||||
window.config,
|
||||
containerId,
|
||||
componentRenderedOrUpdatedCallback
|
||||
);
|
||||
// Made available by the `ohif-viewer` script included in step 1
|
||||
var Viewer = window.OHIFStandaloneViewer.App;
|
||||
var app = React.createElement(Viewer, window.config, null);
|
||||
|
||||
ReactDOM.render(app, document.getElementById('ohif-viewer-target'));
|
||||
```
|
||||
|
||||
You can see a live example of this recipe in [this CodeSandbox][code-sandbox].
|
||||
|
||||
## 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
|
||||
#### Tips & Tricks
|
||||
|
||||
> 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
|
||||
anything related your problem? Create a new one.
|
||||
|
||||
> My application's styles are impacting the OHIF Viewer's look and feel. What
|
||||
> can I do?
|
||||
> When I include bootstrap, other styles on my page no longer work correctly.
|
||||
> What can I do?
|
||||
|
||||
When you include stylesheets and scripts, they are added globally. This has the
|
||||
potential of causing conflicts with other scripts and styles on the page. To
|
||||
prevent this, `embed` the viewer in a new/empty web page. Have that working?
|
||||
Good. Now `embed` that new page using an
|
||||
When we include `bootsrap` (and the other dependencies), they are added
|
||||
globally. This has the potential of causing conflicts with other scripts and
|
||||
styles on the page. To prevent this, `embed` the viewer in a new/empty web page.
|
||||
Have that working? Good. Now `embed` that new page using an
|
||||
[`<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
|
||||
globally defined styles/scripts.
|
||||
|
||||
> We're trying to embed the OHIF Viewer into an existing React App, but seeing
|
||||
> 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 -->
|
||||
[code-sandbox]: https://codesandbox.io/s/ohif-viewer-script-tag-usage-b3st9
|
||||
@@ -11,7 +11,7 @@ control.
|
||||
Do not use this recipe to host sensitive medical data on the open web. Depending
|
||||
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
|
||||
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.
|
||||
|
||||
## Overview
|
||||
@@ -49,7 +49,9 @@ We can solve this one of two ways:
|
||||
1. Have our Image Archive located at the same domain as our Web App
|
||||
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]
|
||||
@@ -119,12 +121,13 @@ likely want to update:
|
||||
|
||||
#### OHIF Viewer
|
||||
|
||||
The OHIF Viewer's configuration is imported from a static `.js` file. The
|
||||
configuration we use is set to a specific file when we build the viewer, and
|
||||
determined by the env variable: `APP_CONFIG`. You can see where we set its value
|
||||
in the `dockerfile` for this solution:
|
||||
The OHIF Viewer's configuration is imported from a static `.js` file and made
|
||||
available globally at `window.config`. The configuration we use is set to a
|
||||
specific file when we build the viewer, and determined by the env variable:
|
||||
`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`
|
||||
@@ -139,11 +142,11 @@ Viewer's configuration, you can run:
|
||||
|
||||
All other files are found in: `/docker/OpenResty-Orthanc/`
|
||||
|
||||
| Service | Configuration | Docs |
|
||||
| ----------------- | --------------------------------- | ------------------------------------------- |
|
||||
| OHIF Viewer | [dockerfile][dockerfile] | You're reading them now! |
|
||||
| OpenResty (Nginx) | [`/nginx.conf`][config-nginx] | [lua-resty-openidc][lua-resty-openidc-docs] |
|
||||
| Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] |
|
||||
| Service | Configuration | Docs |
|
||||
| ----------------- | ---------------------------------------------- | ------------------------------------------- |
|
||||
| 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] |
|
||||
| Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] |
|
||||
|
||||
## Next Steps
|
||||
|
||||
@@ -219,11 +222,10 @@ following resources helpful:
|
||||
- [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/)
|
||||
|
||||
For a different take on this setup, check out the repositories our community
|
||||
members put together:
|
||||
For a different take on this setup, check out the repository one of our
|
||||
community members put together:
|
||||
|
||||
- [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
|
||||
@@ -236,7 +238,8 @@ members put together:
|
||||
[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration
|
||||
[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc
|
||||
<!-- SRC -->
|
||||
[dockerfile]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc/dockerfile
|
||||
[config-nginx]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc/config/nginx.conf
|
||||
[config-orthanc]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc/config/orthanc.json
|
||||
[config]: #
|
||||
[dockerfile]: #
|
||||
[config-nginx]: #
|
||||
[config-orthanc]: #
|
||||
<!-- prettier-ignore-end -->
|
||||
@@ -122,12 +122,13 @@ likely want to update:
|
||||
|
||||
#### OHIF Viewer
|
||||
|
||||
The OHIF Viewer's configuration is imported from a static `.js` file. The
|
||||
configuration we use is set to a specific file when we build the viewer, and
|
||||
determined by the env variable: `APP_CONFIG`. You can see where we set its value
|
||||
in the `dockerfile` for this solution:
|
||||
The OHIF Viewer's configuration is imported from a static `.js` file and made
|
||||
available globally at `window.config`. The configuration we use is set to a
|
||||
specific file when we build the viewer, and determined by the env variable:
|
||||
`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:
|
||||
`/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] |
|
||||
| Keycloak | [`/ohif-keycloak-realm.json`][config-keycloak]\* | |
|
||||
|
||||
\* These are the seed values for Keycloak. They can be manually updated at
|
||||
`http://127.0.0.1/auth/admin`
|
||||
- \* These are the seed values for Keycloak. They can be manually updated at
|
||||
`http://127.0.0.1/auth/admin`
|
||||
|
||||
#### 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)
|
||||
- [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
|
||||
members put together:
|
||||
For a different take on this setup, check out the repository one of our
|
||||
community members put together:
|
||||
|
||||
- [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
|
||||
@@ -280,9 +281,9 @@ members put together:
|
||||
[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration
|
||||
[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc
|
||||
<!-- SRC -->
|
||||
[config]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/src/config.js
|
||||
[dockerfile]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/dockerfile
|
||||
[config-nginx]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/nginx.conf
|
||||
[config-orthanc]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/orthanc.json
|
||||
[config-keycloak]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/ohif-keycloak-realm.json
|
||||
[config]: #
|
||||
[dockerfile]: #
|
||||
[config-nginx]: #
|
||||
[config-orthanc]: #
|
||||
[config-keycloak]: #
|
||||
<!-- 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
|
||||
|
||||
After following the steps outlined in
|
||||
[Getting Started](./../development/getting-started.md), you'll notice that the
|
||||
OHIF Viewer has data for several studies and their images. You didn't add this
|
||||
data, so where is it coming from?
|
||||
After following the steps outlined in [Getting Started](./getting-started.md),
|
||||
you'll notice that the OHIF Viewer has data for several studies and their
|
||||
images. You didn't add this data, so where is it coming from?
|
||||
|
||||
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,
|
||||
@@ -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`
|
||||
in command prompt or terminal_
|
||||
|
||||
> If you are using `Docker Toolbox` you need to change the _PROXY_DOMAIN_
|
||||
> parameter in _platform/viewer/package.json_ to http://192.168.99.100:8042 or
|
||||
> the ip docker-machine ip throws. This is the value [`WebPack`][webpack-proxy]
|
||||
> uses to proxy requests
|
||||
> If you are using `Docker Toolbox` you need to change the _proxy_ parameter in
|
||||
> _package.json_ to http://192.168.99.100:8042 or the ip docker-machine ip
|
||||
> throws. This is the value [`react-scripts`][react-proxy] uses to proxy
|
||||
> requests
|
||||
|
||||
### Running Orthanc
|
||||
|
||||
@@ -52,9 +51,8 @@ yarn run orthanc:up
|
||||
|
||||
_Upload your first Study:_
|
||||
|
||||
1. Navigate to
|
||||
[Orthanc's web interface](http://localhost:8042/app/explorer.html) at
|
||||
`http://localhost:8042/app/explorer.html` in a web browser.
|
||||
1. Navigate to [Orthanc's web interface](http://localhost:8899) at
|
||||
`http://localhost:8899` in a web browser.
|
||||
2. In the top right corner, click "Upload"
|
||||
3. Click "Select files to upload..." and select one or more DICOM files
|
||||
4. Click "Start the upload"
|
||||
@@ -62,20 +60,17 @@ _Upload your first Study:_
|
||||
#### Orthanc: Learn More
|
||||
|
||||
You can see the `docker-compose.yml` file this command runs at
|
||||
[`<project-root>/.docker/Nginx-Orthanc/`][orthanc-docker-compose], and more on
|
||||
Orthanc for Docker in [Orthanc's documentation][orthanc-docker].
|
||||
[`<project-root>/docker/Nginx-Docker/`](#), and more on Orthanc for Docker in
|
||||
[Orthanc's documentation][orthanc-docker].
|
||||
|
||||
### Connecting to Orthanc
|
||||
|
||||
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
|
||||
this repository's root directory, and run:
|
||||
this project's root directory, and run:
|
||||
|
||||
```bash
|
||||
# If you haven't already, enable yarn workspaces
|
||||
yarn config set workspaces-experimental true
|
||||
|
||||
# Restore dependencies
|
||||
# If you haven't already, restore dependencies
|
||||
yarn install
|
||||
|
||||
# Run our dev command, but with the local orthanc config
|
||||
@@ -85,35 +80,31 @@ yarn run dev:orthanc
|
||||
#### Configuration: Learn More
|
||||
|
||||
> 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`
|
||||
is running the `dev:orthanc` script in our project's `package.json`. That script
|
||||
is:
|
||||
|
||||
```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
|
||||
- PROXY_TARGET: `/dicom-web`
|
||||
- PROXY_DOMAIN: `http://localhost:8042`
|
||||
- APP_CONFIG: `config/docker_nginx-orthanc.js`
|
||||
- `webpack-dev-server` runs using the `.webpack/webpack.pwa.js` configuration
|
||||
file. It will watch for changes and update as we develop.
|
||||
- `cross-env` sets two environment variables
|
||||
- PORT: 5000
|
||||
- REACT_APP_CONFIG: `config/docker_nginx-orthanc.js`
|
||||
- `react-scripts` runs it's `start` script. This is [the de-facto
|
||||
way][cra-start] to run a "Create React App" in development mode.
|
||||
|
||||
`PROXY_TARGET` and `PROXY_DOMAIN` tell our development server to proxy requests
|
||||
to `Orthanc`. This allows us to bypass CORS issues that normally occur when
|
||||
requesting resources that live at a different domain.
|
||||
|
||||
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:
|
||||
The `REACT_APP_CONFIG` value tells our app which file to load on to
|
||||
`window.config`. By default, our app uses the file at
|
||||
`<project-root>/public/config/default.js`. Here is what that configuration looks
|
||||
like:
|
||||
|
||||
```js
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
relativeWebWorkerScriptsPath: '',
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
{
|
||||
@@ -131,7 +122,7 @@ window.config = {
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
@@ -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
|
||||
[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb
|
||||
[storescu]: http://support.dcmtk.org/docs/storescu.html
|
||||
[webpack-proxy]: https://webpack.js.org/configuration/dev-server/#devserverproxy
|
||||
[orthanc-docker-compose]: https://github.com/OHIF/Viewers/tree/master/.docker/Nginx-Orthanc
|
||||
[cra-start]: https://github.com/facebook/create-react-app#npm-start-or-yarn-start
|
||||
[react-proxy]: https://facebook.github.io/create-react-app/docs/proxying-api-requests-in-development#configuring-the-proxy-manually
|
||||
<!-- Archives -->
|
||||
[dcm4chee]: https://github.com/dcm4che/dcm4chee-arc-light
|
||||
[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
|
||||
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`
|
||||
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/)
|
||||
- [Yarn](https://yarnpkg.com/en/)
|
||||
- Yarn workspaces should be enabled:
|
||||
- `yarn config set workspaces-experimental true`
|
||||
|
||||
### Kick the tires
|
||||
|
||||
@@ -51,18 +50,21 @@ following commands:
|
||||
yarn install
|
||||
|
||||
# Start local development server
|
||||
yarn run dev
|
||||
yarn start
|
||||
```
|
||||
|
||||
You should see the following output:
|
||||
|
||||
```bash
|
||||
@ohif/viewer: i 「wds」: Project is running at http://localhost:3000/
|
||||
@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
|
||||
Compiled successfully!
|
||||
|
||||
# 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 🎉
|
||||
@@ -79,18 +81,24 @@ You should see the following output:
|
||||
|
||||
```bash
|
||||
# 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
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
...
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- If you receive a _"No Studies Found"_ message and do not see your studies, try
|
||||
changing the Study Date filters to a wider range.
|
||||
- If you see a 'Loading' message which never resolves, check your browser
|
||||
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
|
||||
@@ -103,5 +111,5 @@ yarn run build:package
|
||||
[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
|
||||
[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 -->
|
||||
@@ -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.
|
||||
|
||||
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
|
||||
most recipes, they should be augmented to best fit you and your organization's
|
||||
taste, preferences, etc.
|
||||
@@ -1,10 +1,10 @@
|
||||
# Viewer: Themeing
|
||||
# Themeing
|
||||
|
||||
Themeing is currently accomplished with color variables that are defined within
|
||||
the [`:root`](https://css-tricks.com/almanac/selectors/r/root/) selector
|
||||
(allowing them to cascade across all elements). This repository's components,
|
||||
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
|
||||
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
|
||||
"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).
|
||||
|
||||
```js
|
||||
function RadicalImagingLogo(React) {
|
||||
function RadicalImagingLogo() {
|
||||
return React.createElement(
|
||||
'a',
|
||||
{
|
||||
@@ -80,12 +80,12 @@ function RadicalImagingLogo(React) {
|
||||
href: 'http://radicalimaging.com',
|
||||
},
|
||||
React.createElement('h5', {}, 'RADICAL IMAGING')
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
props.whiteLabeling = {
|
||||
createLogoComponentFn: RadicalImagingLogo,
|
||||
};
|
||||
props.whiteLabelling = {
|
||||
logoComponent: RadicalImagingLogo(),
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
@@ -1,4 +1,4 @@
|
||||
# Viewer: Internationalization
|
||||
# Translating
|
||||
|
||||
OHIF supports internationalization using [i18next](https://www.i18next.com/)
|
||||
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>
|
||||
|
||||
## Installing
|
||||
### Installing
|
||||
|
||||
```bash
|
||||
yarn add @ohif/i18n
|
||||
@@ -25,7 +25,7 @@ yarn add @ohif/i18n
|
||||
npm install --save @ohif/i18n
|
||||
```
|
||||
|
||||
## How it works
|
||||
### How it works
|
||||
|
||||
After installing `@ohif/i18n` npm package, the translation function
|
||||
[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/)
|
||||
basics and show how to implement the [t](https://www.i18next.com/overview/api#t)
|
||||
function easily.
|
||||
|
||||
#### Using HOCs
|
||||
##### Using HOCs
|
||||
|
||||
In most cases we used
|
||||
[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`
|
||||
> HOC doesnt works without a I18nextProvider
|
||||
|
||||
#### Using Hooks
|
||||
##### Using Hooks
|
||||
|
||||
Also, it's possible to get the `t` tool using
|
||||
[React Hooks](https://react.i18next.com/latest/usetranslation-hook), but it
|
||||
requires at least React > 16.8 😉
|
||||
|
||||
### Using outside of OHIF viewer
|
||||
#### Using outside of OHIF viewer
|
||||
|
||||
OHIF Viewer already sets a main
|
||||
[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_.
|
||||
|
||||
@@ -135,7 +135,7 @@ console.log(T('$t(Common:Play) my translated text'));
|
||||
|
||||
# Main Concepts While Translating
|
||||
|
||||
## Namespaces
|
||||
## - Namespaces
|
||||
|
||||
Namespaces are being used to organize translations in smaller portions, combined
|
||||
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
|
||||
- Common: all common jargons that can be reused like `t('$t(common:image)')`
|
||||
- Header: translations related to OHIF's Header Top Bar
|
||||
- MeasurementTable - Translations for the `@ohif/ui` Measurement Table
|
||||
- UserPreferencesModal - Translations for the `@ohif/ui` Preferences Modal
|
||||
- MeasurementTable - Translations for the react-viewerbase Measurement Table
|
||||
- UserPreferencesModal - Translations for the react-viewerbase Preferences Modal
|
||||
|
||||
### 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)
|
||||
```
|
||||
|
||||
## Extending Languages in @ohif/i18n
|
||||
## - Extending Languages in @ohif/i18n
|
||||
|
||||
Sometimes, even using the same language, some nouns or jargons can change
|
||||
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
|
||||
exporting structure.
|
||||
|
||||
### Extending languages dynamically
|
||||
### - Extending languages dynamically
|
||||
|
||||
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)
|
||||
@@ -314,4 +314,4 @@ REACT_APP_I18N_DEBUG=true yarn run dev
|
||||
### Contributing with new languages
|
||||
|
||||
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 -->
|
||||
@@ -0,0 +1,95 @@
|
||||
# Frequently Asked Questions
|
||||
|
||||
## How do I file a bug?
|
||||
|
||||
We accept and triage bug reports through Github primarily.
|
||||
|
||||
- [Create a Github account](https://github.com/join)
|
||||
- Search the current [Issue List](https://github.com/OHIF/Viewers/issues) to
|
||||
ensure you are not creating a duplicate issue.
|
||||
- If your issue already exists, post a comment to show us that this issue also
|
||||
affects you.
|
||||
- If no prior issue exists,
|
||||
[Create a New Issue](https://github.com/OHIF/Viewers/issues/new) on the
|
||||
repository.
|
||||
|
||||
Some tips for filing a new issue:
|
||||
|
||||
- **Make sure your issue is reproducible**: If we try to reproduce your issue
|
||||
given your provided steps and we cannot reproduce it, we will not be able to
|
||||
fix it. _Nobody wants to spend time guessing how to reproduce your issue!_
|
||||
Before filing, please reproduce your issue more than once and clearly describe
|
||||
the steps taken.
|
||||
- **If you are reporting a user interface issue, provide screenshots**: A
|
||||
picture is worth a thousand words. If your issue concerns the UI, screenshots
|
||||
will help us identify the issue dramatically faster since it can be extremely
|
||||
challenging to describe UI bugs with text. _You should still clearly describe
|
||||
the steps that you took to produce the issue_.
|
||||
- **Include platform & environment**: Your operating system, web browser, and
|
||||
web browser version are highly relevant for many bugs. Please provide these
|
||||
with all bug reports.
|
||||
- **Include expected and actual result**: Tell us what you expected to happen,
|
||||
and what actually happened. If you don't do this, we might not consider it a
|
||||
bug.
|
||||
|
||||
### 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](../contributing.md). If you
|
||||
have resources and would like to fund the development of a feature, please
|
||||
[contact us](http://www.ohif.org).
|
||||
|
||||
### 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.
|
||||
|
||||
### Do you 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.**
|
||||
|
||||
### I emailed my question to you directly and you did not respond. Why not?
|
||||
|
||||
Emailing developers directly is not a shortcut to faster support. Please file
|
||||
your issues and questions on Github so that everyone can benefit from the
|
||||
discussion and solutions.
|
||||
|
||||
### Do your Viewers 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, Lesion Tracker, and Standalone Viewer, **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
|
||||
Framework does not prevent your company or group from seeking F.D.A. clearance
|
||||
for a product built using the framework.
|
||||
|
||||
If you have gone this route (or are going there), please let us know because we
|
||||
would be interested to hear about your experience.
|
||||
|
||||
### Are your Viewers [HIPAA][hipaa] Compliant?
|
||||
|
||||
**NO.** The OHIF Viewer, Lesion Tracker, and Standalone Viewer **DO 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 -->
|
||||
[501k-clearance]: https://www.fda.gov/MedicalDevices/DeviceRegulationandGuidance/HowtoMarketYourDevice/PremarketSubmissions/PremarketNotification510k/
|
||||
[ce-marking]: https://ec.europa.eu/growth/single-market/ce-marking_en
|
||||
[hipaa]: https://en.wikipedia.org/wiki/Health_Insurance_Portability_and_Accountability_Act
|
||||
<!-- prettier-ignore-end -->
|
||||
@@ -3,7 +3,7 @@
|
||||
We all need a little help sometimes. Don't let a few roadblocks stand in the way
|
||||
of you building something awesome.
|
||||
|
||||
## Community Support
|
||||
## Free
|
||||
|
||||
If you're a developer looking to contribute code, documentation, or discussion;
|
||||
we are more than happy to help provide clarification and answer questions via
|
||||
@@ -21,7 +21,7 @@ resources and must be judicious with how we allocate them. If you find yourself
|
||||
in this situation and in need of assistance, it may be in your best interest to
|
||||
persue paid support.
|
||||
|
||||
## Commercial Support
|
||||
## Paid / Commercial
|
||||
|
||||
The Open Health Imaging Foundation does not offer commercial support, however,
|
||||
some community members do offer consulting services:
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
# Our Process
|
||||
|
||||
Our process is a living, breathing thing. We strive to have regular
|
||||
[retrospectives][retrospective] that help us shape and adapt our process to our
|
||||
team's current needs. This document attempts to capture the broad strokes of
|
||||
that process in an effort to:
|
||||
|
||||
- Strengthen community member involvement and understanding
|
||||
- Welcome feedback and helpful suggestions
|
||||
|
||||
## Overview
|
||||
|
||||
- [Issue Triage](#issue-triage)
|
||||
- [Issue Curation ("backlog grooming")](#issue-curation-backlog-grooming)
|
||||
- [Contributions (Pull Requests)](#contributions-pull-requests)
|
||||
- [Releases](#releases)
|
||||
|
||||
_Include issue lifecycle diagram_
|
||||
|
||||
## Issue Triage
|
||||
|
||||
[GitHub issues][gh-issues] are the best way to provide feedback, ask questions,
|
||||
and suggest changes to the OHIF Viewer's core team. Community issues generally
|
||||
fall into one of three categories, and are marked with a `triage` label when
|
||||
created.
|
||||
|
||||
| Issue Template Name | Description |
|
||||
| ---------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| Community: Report 🐛 | Describe a new issue; Provide steps to reproduce; Expected versus actual result? |
|
||||
| Community: Request ✋ | Describe a proposed new feature. Why should it be implemented? What is the impact/value? |
|
||||
| Community: Question ❓ | Seek clarification or assistance relevant to the repository. |
|
||||
|
||||
_table 1. issue template names and descriptions_
|
||||
|
||||
Issues that require `triage` are akin to support tickets. As this is often our
|
||||
first contact with would-be adopters and contributors, it's important that we
|
||||
strive for timely responses and satisfactory resolutions. We attempt to
|
||||
accomplish this by:
|
||||
|
||||
1. Responding to issues requiring `triage` at least once a week
|
||||
2. Create new "official issues" from "community issues"
|
||||
3. Provide clear guidance and next steps (when applicable)
|
||||
4. Regularly clean up old (stale) issues
|
||||
|
||||
> :pencil: Less obviously, patterns in the issues being reported can highlight
|
||||
> areas that need improvement. For example, users often have difficulty
|
||||
> navigating CORS issues when deploying the OHIF Viewer -- how do we best reduce
|
||||
> our ticket volume for this issue?
|
||||
|
||||
### Backlogged Issues
|
||||
|
||||
Community issues serve as vehicles of discussion that lead us to "backlogged
|
||||
issues". Backlogged issues are the distilled and actionable information
|
||||
extracted from community issues. They contain the scope and requirements
|
||||
necessary for hand-off to a core-team (or community) contributor ^\_^
|
||||
|
||||
| Category | Description | Labels |
|
||||
| -------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Bugs | An issue with steps that produce a bug (an unexpected result). | [Bug: Verified 🐛][label-bug] |
|
||||
| Stories | A feature/enhancement with a clear benefit, boundaries, and requirements. | [Story 🙌][label-story] |
|
||||
| Tasks | Changes that improve [UX], [DX], or test coverage; but don't impact application behavior | [Task: CI/Tooling 🤖][label-tooling], [Task: Docs 📖][label-docs], [Task: Refactor 🛠][label-refactor], [Task: Tests 🔬][label-tests] |
|
||||
|
||||
_table 2. backlogged issue types ([full list of labels][gh-labels])_
|
||||
|
||||
## Issue Curation (["backlog grooming"][groom-backlog])
|
||||
|
||||
If a [GitHub issue][gh-issues] has a `bug`, `story`, or `task` label; it's on
|
||||
our backlog. If an issue is on our backlog, it means we are, at the very least,
|
||||
committed to reviewing any community drafted Pull Requests to complete the
|
||||
issue. If you're interested in seeing an issue completed but don't know where to
|
||||
start, please don't hesitate to leave a comment!
|
||||
|
||||
While we don't yet have a long-term or quarterly road map, we do regularly add
|
||||
items to our ["Active Development" GitHub Project Board][gh-board]. Items on
|
||||
this project board are either in active development by Core Team members, or
|
||||
queued up for development as in-progress items are completed.
|
||||
|
||||
> 🖋 Want to contribute but not sure where to start? Check out [Up for
|
||||
> grabs][label-grabs] issues and our [Contributing
|
||||
> documentation][contributing-docs]
|
||||
|
||||
## Contributions (Pull Requests)
|
||||
|
||||
Incoming Pull Requests (PRs) are triaged using the following labels. Code review
|
||||
is performed on all PRs where the bug fix or added functionality is deemed
|
||||
appropriate:
|
||||
|
||||
| Labels | Description |
|
||||
| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
||||
| **Classification** | |
|
||||
| [PR: Bug Fix][label-bug] | Filed to address a Bug. |
|
||||
| [PR: Draft][draft] | Filed to gather early feedback from the core team, but which is not intended for merging in the short term. |
|
||||
| **Review Workflow** | |
|
||||
| [PR: Awaiting Response 💬][awaiting-response] | The core team is waiting for additional information from the author. |
|
||||
| [PR: Awaiting Review 👀][awaiting-review] | The core team has not yet performed a code review. |
|
||||
| [PR: Awaiting Revisions 🖊][awaiting-revisions] | Following code review, this label is applied until the author has made sufficient changes. |
|
||||
| **QA** | |
|
||||
| [PR: Awaiting User Cases 💃][awaiting-stories] | The PR code changes need common language descriptions of impact to end users before the review can start |
|
||||
| [PR: No UX Impact 🙃][no-ux-impact] | The PR code changes do not impact the user's experience |
|
||||
|
||||
We rely on GitHub Checks and integrations with third party services to evaluate
|
||||
changes in code quality and test coverage. Tests must pass and User cases must
|
||||
be present (when applicable) before a PR can be merged to master, and code
|
||||
quality and test coverage must not changed by a significant margin. For some
|
||||
repositories, visual screenshot-based tests are also included, and video
|
||||
recordings of end-to-end tests are stored for later review.
|
||||
|
||||
[You can read more about our continous integration efforts here](/development/continous-integration.md)
|
||||
|
||||
## Releases
|
||||
|
||||
Releases are made automatically based on the type of commits which have been
|
||||
merged (major.minor.patch). Releases are automatically pushed to NPM. Release
|
||||
notes are automatically generated. Users can subscribe to GitHub and NPM
|
||||
releases.
|
||||
|
||||
We host development, staging, and production environments for the Progressive
|
||||
Web Application version of the OHIF Viewer. [Development][ohif-dev] always
|
||||
reflects the latest changes on our master branch. [Staging][ohif-stage] is used
|
||||
to regression test a release before a bi-weekly deploy to our [Production
|
||||
environment][ohif-prod].
|
||||
|
||||
Important announcements are made on GitHub, tagged as Announcement, and pinned
|
||||
so that they remain at the top of the Issue page.
|
||||
|
||||
The Core team occasionally performs full manual testing to begin the process of
|
||||
releasing a Stable version. Once testing is complete, the known issues are
|
||||
addressed and a Stable version is released.
|
||||
|
||||
<!--
|
||||
LINKS
|
||||
-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[groom-backlog]: https://www.agilealliance.org/glossary/backlog-grooming
|
||||
[retrospective]: https://www.atlassian.com/team-playbook/plays/retrospective
|
||||
[gh-issues]: https://github.com/OHIF/Viewers/issues/new/choose
|
||||
[gh-labels]: https://github.com/OHIF/Viewers/labels
|
||||
<!-- Issue Labels -->
|
||||
[label-story]: https://github.com/OHIF/Viewers/labels/Story%20%3Araised_hands%3A
|
||||
[label-tooling]: https://github.com/OHIF/Viewers/labels/Task%3A%20CI%2FTooling%20%3Arobot%3A
|
||||
[label-docs]: https://github.com/OHIF/Viewers/labels/Task%3A%20Docs%20%3Abook%3A
|
||||
[label-refactor]: https://github.com/OHIF/Viewers/labels/Task%3A%20Refactor%20%3Ahammer_and_wrench%3A
|
||||
[label-tests]: https://github.com/OHIF/Viewers/labels/Task%3A%20Tests%20%3Amicroscope%3A
|
||||
[label-bug]: https://github.com/OHIF/Viewers/labels/Bug%3A%20Verified%20%3Abug%3A
|
||||
<!-- PR Labels -->
|
||||
[draft]: https://github.com/OHIF/Viewers/labels/PR%3A%20Draft
|
||||
[awaiting-response]: https://github.com/OHIF/Viewers/labels/PR%3A%20Awaiting%20Response%20%3Aspeech_balloon%3A
|
||||
[awaiting-review]: https://github.com/OHIF/Viewers/labels/PR%3A%20Awaiting%20Review%20%3Aeyes%3A
|
||||
[awaiting-stories]: https://github.com/OHIF/Viewers/labels/PR%3A%20Awaiting%20UX%20Stories%20%3Adancer%3A
|
||||
[awaiting-revisions]: https://github.com/OHIF/Viewers/labels/PR%3A%20Awaiting%20Revisions%20%3Apen%3A
|
||||
[no-ux-impact]: https://github.com/OHIF/Viewers/labels/PR%3A%20No%20UX%20Impact%20%3Aupside_down_face%3A
|
||||
<!-- -->
|
||||
[ohif-dev]: https://viewer-dev.ohif.org
|
||||
[ohif-stage]: https://viewer-stage.ohif.org
|
||||
[ohif-prod]: https://viewer.ohif.org
|
||||
[gh-board]: https://github.com/OHIF/Viewers/projects/4
|
||||
[label-grabs]: https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3A%22Up+For+Grabs+%3Araising_hand_woman%3A%22
|
||||
[contributing-docs]: ./development/contributing.md
|
||||
<!-- prettier-ignore-end -->
|
||||
@@ -1,6 +1,6 @@
|
||||
# Service: Measurements
|
||||
# Measurements Package (ohif-measurements)
|
||||
|
||||
...
|
||||
## Package design
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
# Quick Start
|
||||
|
||||
This page details how to get an instance of the OHIF Viewer up and running as
|
||||
fast as possible. It shows how to grab a pre-built version of the application,
|
||||
point it at your data source (PACS), and plop it on a web server.
|
||||
|
||||
## Options
|
||||
|
||||
### 1. Pre-built PWA
|
||||
|
||||
...
|
||||
|
||||
### 2. Script-Tag
|
||||
|
||||
...
|
||||
|
||||
### 3. Docker
|
||||
|
||||
...
|
||||
|
||||
## Security Concerns
|
||||
|
||||
- Secure your data
|
||||
|
||||
## Common Issues
|
||||
|
||||
- Missing server rewrite rules
|
||||
- CORS issues when requesting data from PACS
|
||||
@@ -0,0 +1,25 @@
|
||||
# Roadmap
|
||||
|
||||
If you want to know what's planned for the very near future,
|
||||
[check out our roadmap](https://ohif.canny.io/). The best way to influence when
|
||||
and what is worked on is to contribute to the conversation by creating GitHub
|
||||
issues, and contributing code through pull requests. OHIF's high level
|
||||
priorities for the near future are:
|
||||
|
||||
- Feature parity with version 1
|
||||
- Extension and configuration improvements with key integration partners
|
||||
- Continued Developer Experience Improvements
|
||||
- Segmentation Tools, and improved VTK.js support
|
||||
|
||||
More granular information will make it's way to the backlog as these items
|
||||
become scoped for development by core maintainers.
|
||||
|
||||
> Don't hesitate to ask questions, propose features, or create pull requests.
|
||||
> We're here, we're listening, and we're ready to build the best open source
|
||||
> medical imaging viewer on the web.
|
||||
|
||||
### Roadmap Generously Powered by Canny.io
|
||||
|
||||
<a href="https://ohif.canny.io/">
|
||||
<img height="30" src="assets/img/canny-full.png" />
|
||||
</a>
|
||||