Compare commits

..
Author SHA1 Message Date
dannyrb e1542f50f8 chore(release): publish %s [skip ci]
- @ohif/extension-cornerstone@0.0.39-alpha.1
 - @ohif/extension-dicom-html@0.0.4-alpha.1
 - @ohif/extension-dicom-microscopy@0.0.9-alpha.1
 - @ohif/extension-dicom-pdf@0.0.8-alpha.1
 - @ohif/extension-vtk@0.1.4-alpha.1
 - @ohif/core@0.11.1-alpha.1
 - @ohif/i18n@0.2.3-alpha.1
 - @ohif/ui@0.2.18-alpha.1
 - @ohif/viewer@0.0.22-alpha.1
2019-08-07 00:32:04 -04:00
dannyrb abdbe9a608 Safer publish command 2019-08-07 00:24:27 -04:00
dannyrb 755da173ea ohif-core --> @ohif/core 2019-08-06 23:56:44 -04:00
dannyrb 8c5bd55f88 Remove unused/duplicate config files for projects/packages 2019-08-06 23:48:43 -04:00
dannyrb e98611fc24 Redux testkit dep 2019-08-06 23:21:19 -04:00
dannyrb fa0aa7f68e bump @ohif/core versions 2019-08-06 15:48:33 -04:00
dannyrb 30902bc048 Fix @ohif/ui versions 2019-08-06 15:37:44 -04:00
dannyrb 4b532183af Reduce duplicate code in @ohif/core 2019-08-06 15:03:43 -04:00
dannyrb 58b3727a2e Clean up ui and i18n config 2019-08-06 14:41:13 -04:00
dannyrb d48e29d1bb Update UI project's docs 2019-08-06 14:31:01 -04:00
dannyrb e47dcf27fc Clean duplicate code in UI project 2019-08-06 14:22:44 -04:00
dannyrb 60f48fbdc0 Tidy up project links 2019-08-06 13:58:43 -04:00
dannyrb 9a5372766c Clean up UI to set webpack scripts 2019-08-05 23:34:02 -04:00
dannyrb e0cf337222 Remove old scripts 2019-08-05 23:28:12 -04:00
dannyrb 7db3bde95a Shift build command; satisfy default PWA build 2019-08-05 23:26:28 -04:00
dannyrb b081e73297 Support for dev and dev:* commands 2019-08-05 23:15:07 -04:00
dannyrb ce72d8bb15 Clean up primary readme 2019-08-05 22:32:36 -04:00
dannyrb 7ba2916cf5 chore(release): publish %s [skip ci]
- @ohif/extension-cornerstone@0.0.39-alpha.0
 - @ohif/extension-dicom-html@0.0.4-alpha.0
 - @ohif/extension-dicom-microscopy@0.0.9-alpha.0
 - @ohif/extension-dicom-pdf@0.0.8-alpha.0
 - @ohif/extension-vtk@0.1.4-alpha.0
 - @ohif/core@0.11.1-alpha.0
 - @ohif/i18n@0.2.3-alpha.0
 - @ohif/ui@0.2.18-alpha.0
 - @ohif/viewer@0.0.22-alpha.0
2019-08-05 14:45:09 -04:00
dannyrb 4736ad5620 Catch more updates 2019-08-05 14:44:45 -04:00
dannyrb 44f562e24c Adding note 2019-08-05 14:44:20 -04:00
dannyrb 8683e1e8ff Changing to scoped package names 2019-08-05 14:29:14 -04:00
dannyrb f86eb5e8f2 Fix typo 2019-08-05 14:00:20 -04:00
dannyrb 64fd724455 Set default threshold 2019-08-05 13:52:02 -04:00
dannyrb b2dfb6e0f7 Fix path; split PR and Merge unit tests into separate jobs 2019-08-05 13:04:27 -04:00
dannyrb 7c043b46b4 Try running with aliased folder 2019-08-05 12:54:23 -04:00
dannyrb 62932b3e80 Also upload core 2019-08-05 12:49:35 -04:00
dannyrb 7a7b2774e6 Try to see the contents of our cat'd file 2019-08-05 12:41:42 -04:00
dannyrb 1556b3bbf8 Fix filename 2019-08-05 12:37:01 -04:00
dannyrb 199db68674 Use home alias 2019-08-05 12:35:13 -04:00
dannyrb d74b6fb826 long paths 2019-08-05 12:30:27 -04:00
dannyrb aeac900496 Combine lines to reduce path 2019-08-05 12:26:29 -04:00
dannyrb 835fec7f32 Escape string literal 2019-08-05 12:23:10 -04:00
dannyrb 4c202ed064 tryfix syntax 2019-08-05 12:21:39 -04:00
dannyrb e751e8fab8 Escape anchors 2019-08-05 12:12:27 -04:00
dannyrb ae795ea97d Combine files before upload 2019-08-05 12:08:18 -04:00
dannyrb d90c70cab5 Fix dir 2019-08-05 11:52:35 -04:00
dannyrb 1a8fd13896 Remove individual codecov calls 2019-08-05 11:52:09 -04:00
dannyrb 0026801b62 Use full string paths 2019-08-05 11:43:03 -04:00
dannyrb 395315ea4c Upload core and viewer 2019-08-05 11:39:10 -04:00
dannyrb 4664f673d6 Bump circleci version 2019-08-05 11:32:38 -04:00
dannyrb a8c4534b33 Try using codecov orb 2019-08-05 11:07:14 -04:00
dannyrb e4a3510c86 Simplify 2019-08-02 13:33:21 -04:00
dannyrb d28f332312 Try fixing paths 2019-08-02 13:04:03 -04:00
dannyrb 37f60ba0af Generate example for codecov issue 2019-07-19 14:54:34 -04:00
dannyrb d64cdc429e Shift back to calling codecov from root 2019-07-18 15:05:52 -04:00
dannyrb e06e8304d3 Remove clear flag 2019-07-18 14:26:47 -04:00
dannyrb 0e862c69db Fix typo 2019-07-18 13:57:14 -04:00
dannyrb fbdb61bcb2 Use recommended flags from issue comments for codecov 2019-07-18 13:52:27 -04:00
dannyrb 3acea218fb Trigger codecov after everything has finished running; these may not support flags 2019-07-18 13:40:31 -04:00
dannyrb a521ce31ce Generate separate reports 2019-07-18 13:07:29 -04:00
dannyrb 67e2fd39a3 Add projects to split by flags 2019-07-18 12:53:24 -04:00
dannyrb 253620650b Try once relying on codecov yaml to split w/ flags 2019-07-18 12:43:03 -04:00
dannyrb 31357ea9b5 Run and report individually and in parallel 2019-07-18 10:26:33 -04:00
dannyrb b8baae7c25 Get all platform unit tests to run 2019-07-17 14:57:50 -04:00
dannyrb 5544c57c37 Use cpx so our copying finishes? 2019-07-16 14:58:40 -04:00
dannyrb 6b523cb50d Add codecov flags 2019-07-16 14:51:04 -04:00
dannyrb 579c2b41d1 Set path and enable workspaces 2019-07-16 14:27:22 -04:00
dannyrb 9449ae4314 Try alternative jest transform 2019-07-16 14:23:41 -04:00
dannyrb 4386b5c960 Run version command instead of calling node directly 2019-07-16 14:14:34 -04:00
dannyrb fe3ee3010f Try alternative jest-canvas-mock location and version file syntax 2019-07-16 14:06:02 -04:00
dannyrb ee5530183e Lower version to match circleci image 2019-07-16 13:43:55 -04:00
dannyrb 413214786d Change copy syntax; try running tests on viewers from root for circleci 2019-07-16 13:31:52 -04:00
dannyrb 0b62b720c5 Attempt to fix ticks/escapes 2019-07-16 13:24:46 -04:00
dannyrb c3fead1ca9 We need to pull cornerstone-wado-image-loader files from hoisted node_modules 2019-07-16 13:11:16 -04:00
dannyrb bc8d505a0a We may have figured it out johnny, boy 2019-07-16 12:57:37 -04:00
dannyrb ab4067144f and again 2019-07-16 12:50:33 -04:00
dannyrb 575d11ac5b Annd let's try again 2019-07-16 12:42:40 -04:00
dannyrb aa53d3258c Try bash -l instead of exec bash 2019-07-16 12:19:51 -04:00
dannyrb dc672bf38d Try swapping bash with a new shell 2019-07-16 12:11:21 -04:00
dannyrb 65a855d99b try again to set bin path 2019-07-16 11:12:19 -04:00
dannyrb 99c1d0220d Follow deploy log output advice 2019-07-16 10:43:01 -04:00
dannyrb dab0b5e0e3 More agressive with modifying PATH 2019-07-16 10:35:06 -04:00
dannyrb ea3f7d5657 Export node_modules path 2019-07-16 10:01:33 -04:00
dannyrb 3c812792bb Try to use global gitbook-cli 2019-07-16 09:55:34 -04:00
dannyrb ff21408440 push workspace enabled to initial command; remove second yarn install; use npx to call gitbook cli commands 2019-07-16 09:42:43 -04:00
dannyrb fd720bd4d7 Attempt to fix deploy preview build 2019-07-16 09:32:14 -04:00
dannyrb 2279e555a3 First attempt at a netlify deploy preview 2019-07-16 09:24:24 -04:00
dannyrb 91ee615aee Shift links to bottom of doc 2019-07-15 15:56:07 -04:00
dannyrb f8d9bbef8a Rename example extension folder 2019-07-15 15:51:36 -04:00
dannyrb 4fcf544abe Begin providing basic readme info 2019-07-15 15:51:24 -04:00
dannyrb 2ba0e95fe8 Specify additional lerna config props 2019-07-15 15:51:10 -04:00
dannyrb 91564bfa06 Shift docs to root 2019-07-15 15:50:47 -04:00
dannyrb 83e627e5c9 Fix ESM symlink build for Viewers 2019-07-15 13:03:29 -04:00
dannyrb 2fbbb4a8e2 Push changes up to switch PCs 2019-07-11 13:12:37 -04:00
dannyrb c05058a0f5 Better entrypoint for extensions 2019-07-09 12:56:08 -04:00
dannyrb 2429294dda Provide WebPack build options for microscopy, vtk, and ui 2019-07-09 12:39:15 -04:00
dannyrb 2234869b11 Add Webpack Stylus loader 2019-07-09 12:38:12 -04:00
dannyrb 96686850cb Target for UMD bundle 2019-07-09 12:37:58 -04:00
dannyrb 35dc82e120 Resolve viewer's modules 2019-07-09 12:37:02 -04:00
dannyrb f5d6674b62 Make it possible to pass in extensions as App props 2019-07-08 10:18:03 -04:00
dannyrb f3009c7a45 Consolidate how/where we specify file/module type entrypoints 2019-07-08 09:38:16 -04:00
dannyrb e8969fba11 Split packages into platform and extensions 2019-07-08 00:41:25 -04:00
dannyrb f175d4b96b Commit changes before a long weekend 2019-07-05 22:58:25 -04:00
dannyrb e250ac0c50 Begin updating dependent libraries to use sync'd webpack builds w/ watches 2019-07-05 13:28:49 -04:00
dannyrb 3e1e456cc2 Move @babel dependencies up to workspace root 2019-07-04 20:35:30 -04:00
dannyrb e58ba39a60 more shifting 2019-07-04 16:19:28 -04:00
dannyrb b73ea99971 init 2019-07-04 16:12:43 -04:00
968 changed files with 41787 additions and 50377 deletions

No files matched your search

-6
View File
@@ -1,6 +0,0 @@
# Browsers that we support
> 1%
IE 11
not dead
not op_mini all
-69
View File
@@ -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
+229 -363
View File
@@ -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
+15
View File
@@ -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>
+1 -3
View File
@@ -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:
-33
View File
@@ -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 "$@"
-30
View File
@@ -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
View File
@@ -1 +0,0 @@
PERCY_TOKEN=<your token here>
-1
View File
@@ -16,7 +16,6 @@
},
"globals": {
"cy": true,
"before": true,
"context": true,
"Cypress": true,
"assert": true
+23 -16
View File
@@ -2,32 +2,39 @@
name: "\U0001F41B Bug report"
about: Create a report to help us improve
title: ''
labels: 'Community: Report :bug:, Awaiting Reproduction, Triage :white_flag:'
labels: 'Bug Report :bug:'
assignees: ''
---
> **Before Creating an issue**
>
> - Are you running the latest version?
> - Are you reporting to the correct repository?
> - Did you search existing issues?
**Before Creating an issue**
## Bug Report
- Are you running the latest version?
- Are you reporting to the correct repository?
- Did you search existing issues?
### Describe the Bug
**Describe the bug**
_A clear and concise description of what the bug is._
*A clear and concise description of what the bug is.*
### What steps can we follow to reproduce the bug?
**Steps To Reproduce**
1. First step
2. Second step
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.
**Expected behavior**
*A clear and concise description of what you expected to happen.*
**Environment**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Additional context**
+7 -14
View File
@@ -2,24 +2,17 @@
name: "\U0001F680 Feature request"
about: Suggest an idea for this project
title: ''
labels: 'Community: Request :hand:, Triage :white_flag:'
labels: enhancement
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).
## Awesome, do you have an idea? 😍
## Request
If you have a **feature request, improvement or idea**, check [our official roadmap](https://github.com/OHIF/react-viewerbase/projects) to see if it is already planned!
**What feature or change would you like to see made?**
### 👉 &nbsp; [Go to Roadmap](https://github.com/OHIF/react-viewerbase/projects)
...
If your feature request isn't there, continue with this issue and we can discuss it 🤟
**Why should we prioritize this feature?**
...
Please include the reasons why you think your change should be made, and any supporting evidence that helps us asses its value and priority.
@@ -2,17 +2,14 @@
name: "\U0001F917 Support Question"
about: "I have a question \U0001F4AC"
title: ''
labels: 'Community: Question :question:, Triage :white_flag:'
labels: question
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)
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 ^\_^
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 ^_^
-16
View File
@@ -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 -->
-32
View File
@@ -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
+1 -7
View File
@@ -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/
+64 -10
View File
@@ -3,25 +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 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
# Navigate to our Viewer project
cd ./platform/viewer/
# Build && Move script output
# yarn run build:package
# 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 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.'
# Build using react-scripts
# npx cross-env PUBLIC_URL=/demo APP_CONFIG=config/netlify.js react-scripts --max_old_space_size=4096 build
-5
View File
@@ -1,5 +0,0 @@
# Specific to our non-deploy-preview deploys
# Confgure redirects using netlify.toml
# PWA Redirect
/* /index.html 200
-15
View File
@@ -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"
}
}
+13
View File
@@ -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
-6
View File
@@ -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
-14
View File
@@ -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>
-8
View File
@@ -1,8 +0,0 @@
{
"trailingComma": "es5",
"printWidth": 80,
"proseWrap": "always",
"tabWidth": 2,
"semi": true,
"singleQuote": true
}
Executable → Regular
View File
File mode changed.
+8 -13
View File
@@ -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;
-17
View File
@@ -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;
-10
View File
@@ -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;
-17
View File
@@ -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;
-10
View File
@@ -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;
-39
View File
@@ -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;
-113
View File
@@ -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;
};
+90
View File
@@ -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
}
};
};
-19
View File
@@ -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],
},
});
};
-76
View File
@@ -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
View File
@@ -1 +0,0 @@
See our contributing guidelines at [`https://docs.ohif.org`](https://docs.ohif.org/development/contributing.html)
+64 -125
View File
@@ -11,7 +11,8 @@
<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://docs.ohif.org/demo">Demo</a> |
<a href="https://ohif.canny.io/">Roadmap</a> |
<a href="https://react.ohif.org/">Component Library</a>
</div>
@@ -22,119 +23,74 @@
[![NPM downloads][npm-downloads-image]][npm-url]
[![Pulls][docker-pulls-img]][docker-image-url]
[![MIT License][license-image]][license-url]
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FOHIF%2FViewers.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2FOHIF%2FViewers?ref=badge_shield)
[![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)
[![All Contributors](https://img.shields.io/badge/all_contributors-10-orange.svg?style=flat-square)](#contributors)
<!-- prettier-ignore-end -->
## About
## What?
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.
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.
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.
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.
### 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/).
> 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:
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)
- [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@0.19.5/dist/index.umd.js)
- Have an element with an ID of `root` on the page
- Configure the OHIF Viewer at `window.config`:
```js
window.config = {
routerBasename: '/',
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',
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',
},
],
},
imageRendering: "wadors",
thumbnailRendering: "wadors"
}
]
}
};
```
- Install the viewer:
`window.OHIFStandaloneViewer.installViewer(window.config);`
- Install the viewer: `window.OHIFStandaloneViewer.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.
This exact setup is demonstrated in this [CodeSandbox](https://codesandbox.io/s/ohif-viewer-script-tag-usage-c4u4t) 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/)
- [Node 8+](https://nodejs.org/en/)
- Yarn Workspaces should be enabled on your machine:
- `yarn config set workspaces-experimental true`
@@ -162,23 +118,20 @@ 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.
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.
| 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 |
| Yarn Commands | Description |
| -------------------- | ------------------------------------------------------------- |
| **Develop** | |
| `dev` or `start` | Default development experience for Viewer |
| `dev:<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 commonjs bundles for all projects |
\* - For more information on our different builds, check out our [Deploy
Docs][deployment-docs]
\* - For more information on our different builds, check out our [Deploy Docs][deployment-docs]
## Projects
@@ -216,26 +169,24 @@ more about it in our [Architecture Documentation][ohif-architecture].
These projects comprise the
| 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] |
| Name | Description | Links |
| ------------------------------- | ----------- | ----- |
| [@ohif/core][platform-core] | | NPM |
| [@ohif/i18n][platform-i18n] | | NPM |
| [@ohif/viewer][platform-viewer] | | NPM |
| [@ohif/ui][platform-ui] | | NPM |
### Extensions
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].
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].
| 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] |
| Name | Description | Links |
| -------------------------------------------------------------- | ----------- | ----- |
| [@ohif/extension-cornestone][extension-cornerstone] | | NPM |
| [@ohif/extension-dicom-html][extension-dicom-html] | | NPM |
| [@ohif/extension-dicom-microscopy][extension-dicom-microscopy] | | NPM |
| [@ohif/extension-dicom-pdf][extension-dicom-pdf] | | NPM |
| [@ohif/extension-vtk][extension-vtk] | | NPM |
## Acknowledgments
@@ -271,59 +222,47 @@ 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
[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
[all-contributors-image]: https://img.shields.io/badge/all_contributors-0-orange.svg?style=flat-square
[contributing-url]: https://github.com/OHIF/Viewers/blob/react/CONTRIBUTING.md
[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
[codecov-image]: https://codecov.io/gh/OHIF/Viewers/branch/react/graph/badge.svg
[codecov-url]: https://codecov.io/gh/OHIF/Viewers/branch/react
[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
[npm-url]: https://npmjs.org/package/ohif-viewer
[npm-downloads-image]: https://img.shields.io/npm/dm/ohif-viewer.svg?style=flat-square
[npm-version-image]: https://img.shields.io/npm/v/ohif-viewer.svg?style=flat-square
[docker-pulls-img]: https://img.shields.io/docker/pulls/ohif/viewer.svg?style=flat-square
[docker-image-url]: https://hub.docker.com/r/ohif/viewer
[license-image]: https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square
[license-url]: LICENSE
[percy-image]: https://percy.io/static/images/percy-badge.svg
[percy-url]: https://percy.io/Open-Health-Imaging-Foundation/OHIF-Viewer
<!-- Links -->
[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
[ohif-architecture]: https://docs.ohif.org/advanced/architecture.html
[ohif-extensions]: https://docs.ohif.org/advanced/architecture.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/
[ohif-viewer-url]: https://www.npmjs.com/package/ohif-viewer
[configuration-url]: https://docs.ohif.org/essentials/configuration.html
[extensions-url]: https://docs.ohif.org/advanced/extensions.html
<!-- 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 -->
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FOHIF%2FViewers.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2FOHIF%2FViewers?ref=badge_large)
+45 -58
View File
@@ -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];
// },
// },
// ],
-64
View File
@@ -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
-3
View File
@@ -1,3 +0,0 @@
# Netlify redirects
# SPA rules for our docs
/* /index.html 200
+6 -6
View File
@@ -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 -->
+26 -36
View File
@@ -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>
+109
View File
@@ -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))
![Architecture Diagram](../assets/img/architecture-diagram.png)
<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 -->
+8
View File
@@ -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).
+230
View File
@@ -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
![Cornerstone Viewport](../assets/img/extensions-viewport.png)
<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.
![Toolbar Extension](../assets/img/extensions-toolbar.gif)
<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 -->
+3
View File
@@ -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 -->
-153
View File
@@ -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.
![Architecture Diagram](../assets/img/architecture-diagram.png)
<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 -->
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

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

+19
View File
@@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

-126
View File
@@ -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
```
+103
View File
@@ -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 -->
+26 -121
View File
@@ -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 -->
+42 -107
View File
@@ -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 -->
-146
View File
@@ -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 -->
-145
View File
@@ -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 -->
+57
View File
@@ -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 -->
+42
View File
@@ -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).
+20
View File
@@ -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.
-239
View File
@@ -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();
},
};
```
-158
View File
@@ -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)
-61
View File
@@ -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.
![Panel Extension](../../assets/img/extensions-panel.gif)
<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 -->
-130
View File
@@ -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)
![Toolbar Extension](../../assets/img/extensions-toolbar.gif)
<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',
},
],
},
```
![Toolbar Extension](../../assets/img/extensions-toolbar-nested.gif)
<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
![Cornerstone Viewport](../../assets/img/extensions-viewport.png)
<center><i>An example of three cornerstone Viewports</i></center>
-47
View File
@@ -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 -->
-81
View File
@@ -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 -->
-56
View File
@@ -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 -->
+95
View File
@@ -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 -->
+2 -2
View File
@@ -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:
-160
View File
@@ -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
-28
View File
@@ -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
+25
View File
@@ -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>
-65
View File
@@ -1,65 +0,0 @@
# Services (default)
- [Overview](#overview)
- [Example](#example)
## Overview
Services are a work in progress. As we are still in the progress of creating a
non-ui maintained service, this usage may change.
<div style="text-align: center;">
<a href="/assets/img/services.png">
<img src="/assets/img/services.png" alt="UI Services Diagram" style="margin: 0 auto; max-width: 500px;" />
</a>
<div><i>Diagram showing relationship between React Context and UI Service</i></div>
</div>
## Example
The simplest service return a new object that has a `name` property, and
methods/properties that give the service its functionality. The "Factory
Function" that creates the service is provided with the implementation (this is
slightly different for UI Services).
```js
const _speak = () => {
console.warn('Speak is not implemented');
};
/**
* Factory function to create `HelloWorldService`
*
* @param {object} implementation
* @param {function} implementation.speak - Speak's implementation
* @returns HelloWorldService
*/
export default function createHelloWorldService({ speak }) {
return {
name: 'HelloWorldService',
speak: speak || _speak,
};
}
```
A service, once created, can be registered with the `ServicesManager` to make it
accessible to extensions. Similarly, the application code can access named
services from the `ServicesManager`.
```js
// In the application
const speak = () => {
window.alert('HELLO WORLD');
};
const HelloWorldService = createHelloWorldService({ speak });
const servicesManager = new ServicesManager();
servicesManager.registerService(HelloWorldService);
// In an extension
const { HelloWorldService } = servicesManager.services;
if (HelloWorldService) {
HelloWorldService.speak();
}
```
Loaded 100 of 968 files, more files were not shown because too many files have changed in this diff. Show more