From b2fa6ead3b95b742fb6d9fad8b9613075a5ac700 Mon Sep 17 00:00:00 2001 From: Erik Ziegler Date: Mon, 25 Jan 2016 17:14:42 +0100 Subject: [PATCH] Add Clinical Meteor packages to Lesion Tracker --- LesionTracker/.meteor/packages | 12 +- LesionTracker/.meteor/versions | 20 +- LesionTracker/activeEntry.js | 36 ++++ LesionTracker/client/routes.js | 9 +- Packages/active-entry/.travis.yml | 13 ++ Packages/active-entry/Contributing.md | 29 +++ Packages/active-entry/README.md | 127 +++++++++++ Packages/active-entry/circle.yml | 61 ++++++ .../active-entry/components/entryPages.js | 52 +++++ .../active-entry/components/entryPages.less | 168 +++++++++++++++ .../.tests/actions/meteorLogout.js | 10 + .../entrySignIn/.tests/actions/signIn.js | 23 ++ .../entrySignIn/.tests/actions/signOut.js | 22 ++ .../entrySignIn/.tests/reviewSignIn.js | 35 ++++ .../components/entrySignIn/entrySignIn.html | 46 ++++ .../components/entrySignIn/entrySignIn.js | 131 ++++++++++++ .../components/entrySignIn/entrySignIn.less | 17 ++ .../.tests/actions/dropEntryUsers.js | 22 ++ .../entrySignUp/.tests/actions/resetEntry.js | 11 + .../entrySignUp/.tests/actions/signUp.js | 37 ++++ .../entrySignUp/.tests/reviewSignUp.js | 31 +++ .../components/entrySignUp/entrySignUp.html | 63 ++++++ .../components/entrySignUp/entrySignUp.js | 139 ++++++++++++ .../components/entrySignUp/entrySignUp.less | 7 + .../.tests/reviewForgotPassword.js | 4 + .../forgotPassword/forgotPassword.html | 24 +++ .../forgotPassword/forgotPassword.js | 22 ++ .../forgotPassword/forgotPassword.less | 5 + Packages/active-entry/lib/Accounts.js | 48 +++++ Packages/active-entry/lib/ActiveEntry.js | 150 +++++++++++++ Packages/active-entry/package.js | 73 +++++++ Packages/active-entry/server/methods.js | 12 ++ .../tests/gagarin/activeEntryTests.js | 197 ++++++++++++++++++ .../nightwatch/commands/dropCollaborations.js | 22 ++ .../walkthroughs/activeEntryWalkthrough.js | 184 ++++++++++++++++ Packages/clinical-themes/README.md | 15 ++ .../clinical-themes/components/themeCard.html | 0 .../clinical-themes/components/themeCard.js | 0 .../clinical-themes/components/themeCard.less | 0 Packages/clinical-themes/lib/ActiveThemes.js | 0 Packages/clinical-themes/package.js | 31 +++ .../clinical-themes/tests/clinical-themes.js | 10 + Packages/clinical-ui-vocabulary/.gitignore | 1 + Packages/clinical-ui-vocabulary/README.md | 84 ++++++++ .../clinical-ui-vocabulary/anchoring.less | 56 +++++ Packages/clinical-ui-vocabulary/borders.less | 6 + Packages/clinical-ui-vocabulary/colors.less | 66 ++++++ Packages/clinical-ui-vocabulary/fonts.less | 3 + Packages/clinical-ui-vocabulary/haptics.less | 13 ++ Packages/clinical-ui-vocabulary/package.js | 27 +++ Packages/clinical-ui-vocabulary/padding.less | 111 ++++++++++ Packages/clinical-ui-vocabulary/pages.less | 4 + Packages/clinical-ui-vocabulary/sizing.less | 10 + Packages/clinical-ui-vocabulary/text.less | 36 ++++ Packages/clinical-ui-vocabulary/versions.json | 27 +++ Packages/lesiontracker/package.js | 2 +- 56 files changed, 2355 insertions(+), 9 deletions(-) create mode 100644 LesionTracker/activeEntry.js create mode 100755 Packages/active-entry/.travis.yml create mode 100644 Packages/active-entry/Contributing.md create mode 100755 Packages/active-entry/README.md create mode 100644 Packages/active-entry/circle.yml create mode 100755 Packages/active-entry/components/entryPages.js create mode 100755 Packages/active-entry/components/entryPages.less create mode 100755 Packages/active-entry/components/entrySignIn/.tests/actions/meteorLogout.js create mode 100755 Packages/active-entry/components/entrySignIn/.tests/actions/signIn.js create mode 100755 Packages/active-entry/components/entrySignIn/.tests/actions/signOut.js create mode 100755 Packages/active-entry/components/entrySignIn/.tests/reviewSignIn.js create mode 100755 Packages/active-entry/components/entrySignIn/entrySignIn.html create mode 100755 Packages/active-entry/components/entrySignIn/entrySignIn.js create mode 100755 Packages/active-entry/components/entrySignIn/entrySignIn.less create mode 100644 Packages/active-entry/components/entrySignUp/.tests/actions/dropEntryUsers.js create mode 100644 Packages/active-entry/components/entrySignUp/.tests/actions/resetEntry.js create mode 100755 Packages/active-entry/components/entrySignUp/.tests/actions/signUp.js create mode 100755 Packages/active-entry/components/entrySignUp/.tests/reviewSignUp.js create mode 100755 Packages/active-entry/components/entrySignUp/entrySignUp.html create mode 100755 Packages/active-entry/components/entrySignUp/entrySignUp.js create mode 100755 Packages/active-entry/components/entrySignUp/entrySignUp.less create mode 100755 Packages/active-entry/components/forgotPassword/.tests/reviewForgotPassword.js create mode 100755 Packages/active-entry/components/forgotPassword/forgotPassword.html create mode 100755 Packages/active-entry/components/forgotPassword/forgotPassword.js create mode 100755 Packages/active-entry/components/forgotPassword/forgotPassword.less create mode 100755 Packages/active-entry/lib/Accounts.js create mode 100755 Packages/active-entry/lib/ActiveEntry.js create mode 100755 Packages/active-entry/package.js create mode 100644 Packages/active-entry/server/methods.js create mode 100644 Packages/active-entry/tests/gagarin/activeEntryTests.js create mode 100755 Packages/active-entry/tests/nightwatch/commands/dropCollaborations.js create mode 100755 Packages/active-entry/tests/nightwatch/walkthroughs/activeEntryWalkthrough.js create mode 100644 Packages/clinical-themes/README.md create mode 100644 Packages/clinical-themes/components/themeCard.html create mode 100644 Packages/clinical-themes/components/themeCard.js create mode 100644 Packages/clinical-themes/components/themeCard.less create mode 100644 Packages/clinical-themes/lib/ActiveThemes.js create mode 100644 Packages/clinical-themes/package.js create mode 100644 Packages/clinical-themes/tests/clinical-themes.js create mode 100644 Packages/clinical-ui-vocabulary/.gitignore create mode 100644 Packages/clinical-ui-vocabulary/README.md create mode 100644 Packages/clinical-ui-vocabulary/anchoring.less create mode 100644 Packages/clinical-ui-vocabulary/borders.less create mode 100644 Packages/clinical-ui-vocabulary/colors.less create mode 100644 Packages/clinical-ui-vocabulary/fonts.less create mode 100644 Packages/clinical-ui-vocabulary/haptics.less create mode 100644 Packages/clinical-ui-vocabulary/package.js create mode 100644 Packages/clinical-ui-vocabulary/padding.less create mode 100644 Packages/clinical-ui-vocabulary/pages.less create mode 100644 Packages/clinical-ui-vocabulary/sizing.less create mode 100644 Packages/clinical-ui-vocabulary/text.less create mode 100644 Packages/clinical-ui-vocabulary/versions.json diff --git a/LesionTracker/.meteor/packages b/LesionTracker/.meteor/packages index fe177b6c7..142f41d60 100644 --- a/LesionTracker/.meteor/packages +++ b/LesionTracker/.meteor/packages @@ -24,7 +24,6 @@ dicomweb accounts-password ian:accounts-ui-bootstrap-3 -iron:router twbs:bootstrap http arsnebula:reactive-promise @@ -37,3 +36,14 @@ reactive-dict lesiontracker practicalmeteor:loglevel momentjs:moment + +clinical:router +clinical:extended-api +clinical:active-entry +clinical:static-pages +clinical:error-pages +clinical:theming +clinical:theming-ui +clinical:fonts +clinical:glass-ui +clinical:hipaa-logger diff --git a/LesionTracker/.meteor/versions b/LesionTracker/.meteor/versions index 51c778862..a8cf32829 100644 --- a/LesionTracker/.meteor/versions +++ b/LesionTracker/.meteor/versions @@ -3,6 +3,7 @@ accounts-password@1.1.4 anti:i18n@0.4.3 arsnebula:reactive-promise@0.9.1 autoupdate@1.2.4 +awatson1978:fonts-helveticas@1.0.4 babel-compiler@5.8.24_1 babel-runtime@0.1.4 base64@1.0.4 @@ -15,6 +16,20 @@ caching-compiler@1.0.0 caching-html-compiler@1.0.2 callback-hook@1.0.4 check@1.1.0 +clinical:active-entry@1.5.14 +clinical:barcode@3.0.1 +clinical:error-pages@0.1.1 +clinical:extended-api@2.2.1 +clinical:fonts@1.0.0 +clinical:glass-ui@1.3.7 +clinical:hipaa-logger@1.0.1 +clinical:router@2.0.17 +clinical:router-location@2.0.14 +clinical:router-middleware-stack@2.0.13 +clinical:router-url@2.0.15 +clinical:static-pages@1.0.7 +clinical:theming@0.3.1 +clinical:theming-ui@0.2.3 coffeescript@1.0.11 cornerstone@0.0.1 ddp@1.2.2 @@ -33,6 +48,7 @@ es5-shim@4.1.14 fastclick@1.0.7 fortawesome:fontawesome@4.4.0 geojson-utils@1.0.4 +grove:less@0.1.1 hangingprotocols@0.0.1 hot-code-push@1.0.0 html-tools@1.0.5 @@ -45,10 +61,6 @@ iron:controller@1.0.12 iron:core@1.0.11 iron:dynamic-template@1.0.12 iron:layout@1.0.12 -iron:location@1.0.11 -iron:middleware-stack@1.0.11 -iron:router@1.0.12 -iron:url@1.0.11 jquery@1.11.4 launch-screen@1.0.4 lesiontracker@0.0.1 diff --git a/LesionTracker/activeEntry.js b/LesionTracker/activeEntry.js new file mode 100644 index 000000000..a1413b8c8 --- /dev/null +++ b/LesionTracker/activeEntry.js @@ -0,0 +1,36 @@ +if (Meteor.isClient){ + ActiveEntry.configure({ + logo: { + url: '/mini-circles.png', + displayed: true + }, + signIn: { + displayFullName: true, + destination: '/worklist' + }, + signUp: { + destination: '/worklist' + }, + themeColors: { + primary: 'black' + } + }); +} + +if (Meteor.isServer){ + Accounts.emailTemplates.siteName = 'AwesomeSite'; + Accounts.emailTemplates.from = 'AwesomeSite Admin '; + Accounts.emailTemplates.enrollAccount.subject = function(user) { + return 'Welcome to Awesome Town, ' + user.profile.name; + }; + + Accounts.emailTemplates.enrollAccount.text = function(user, url) { + return 'You have been selected to participate in building a better future!' + + ' To activate your account, simply click the link below:\n\n' + + url; + }; + + Meteor.startup(function() { + //process.env.MAIL_URL = 'smtp://sandboxid.mailgun.org:mypassword@smtp.mailgun.org:587'; + }); +} diff --git a/LesionTracker/client/routes.js b/LesionTracker/client/routes.js index 845a9eedf..85846aea1 100644 --- a/LesionTracker/client/routes.js +++ b/LesionTracker/client/routes.js @@ -5,15 +5,14 @@ Object.keys(ViewerData).forEach(function(contentId) { var tabData = ViewerData[contentId]; var data = { title: tabData.title, - contentid: tabData.contentid, + contentid: tabData.contentid }; WorklistTabs.insert(data); }); Router.configure({ layoutTemplate: 'layoutLesionTracker', - loadingTemplate: 'layoutLesionTracker', - notFoundTemplate: 'notFound' + loadingTemplate: 'layoutLesionTracker' }); Router.onBeforeAction('loading'); @@ -32,6 +31,10 @@ Router.route('/', function() { this.render('worklist', routerOptions); }); +Router.route('/worklist', function() { + this.render('worklist', routerOptions); +}); + Router.route('/viewer/:_id', { layoutTemplate: 'layoutLesionTracker', name: 'viewer', diff --git a/Packages/active-entry/.travis.yml b/Packages/active-entry/.travis.yml new file mode 100755 index 000000000..27cbbc9d8 --- /dev/null +++ b/Packages/active-entry/.travis.yml @@ -0,0 +1,13 @@ +language: node_js +node_js: + - "0.10" +#sudo: required + +sudo: required + +env: + global: + - METEOR_ENV=development + +before_install: + - "curl -L http://git.io/ejPSng | /bin/sh" diff --git a/Packages/active-entry/Contributing.md b/Packages/active-entry/Contributing.md new file mode 100644 index 000000000..bcfbe714a --- /dev/null +++ b/Packages/active-entry/Contributing.md @@ -0,0 +1,29 @@ +## Contributing + +**Quality Assurance** +- Pull Requests will be generally accepted as long as the QA tests pass on [Circle CI](https://circleci.com/gh/clinical-meteor/clinical-active-entry). +- Begin a Pull Request by logging an Issue for discussion. +- Next, clone the package into a project for local development. +- Use ``git checkout -b newfeature`` to create a new branch. +- Run the package verification tests to ensure that the test runner works. +- Add an it() clause at the bottom of the [activeEntryTests.js](https://github.com/clinical-meteor/clinical-active-entry/blob/master/tests/gagarin/activeEntryTests.js) file for each new feature you wish to implement. +- Sketch out a test script for a feature. +- Run the verification tests, and confirm that the new test fails. +- Update the package with the new feature until the new test passes. +- Push the package to the repo on GitHub. +- Submit a pull-request. +- If the PR passes tests on Circle CI, we'll merge it in! + +**Reference Implementation** +- Please see the [ChecklistManifest](http://checklist-manifesto.meteor.com/) and it's [source code](https://github.com/clinical-meteor/checklist-manifesto) for a reference implementation of the ActiveEntry package. It's being built as a workqueue, and is being designed to be FDA, HIPAA, and HL7 FHIR compliant. + +**Feature Toggling** +- In general, it's a best practice to implement features that can be enabled/disabled. Some people will want a feature; others will not. The best way to toggle features is adding them to a config file, which is what the [ActiveEntry](https://github.com/clinical-meteor/clinical-active-entry/blob/master/lib/ActiveEntry.js) object is for. +- It's recommended that fields be added to the ActiveEntryConfig object that can be used to enabled/disable the feature you are implementing. +- The ActiveEntryConfig object can be roughly considered as analogous to the ``props`` field that is passed into React templates. + +**Theming and Styling** +- Different apps require different presentation/style layers, so Clinical Meteor has implemented a [Theme](https://github.com/clinical-meteor/clinical-theming/blob/master/objects/Theme.js) object which manages theming. Generally speaking, we're trying to avoid the inclusion of third-party component libraries, UI widgets, and CSS frameworks, and keeping the Clinical Meteor packages as close to the default HTML that Blaze produces as possible (while using line styles for animation effects). +- Generally speaking, Bootstrap HTML structure in acceptable in designing pages, but avoid including global CSS files, and keep any CSS locally scoped using LESS nested classes. This approach allows people to add the Bootstrap library if they want it, prevents cascading leaks, and allows more fine-grained control over CSS to implement animation effects. +- Pull requests that include visual integration with other projects are acceptable, but please submit an issue for discussion before submitting PRs with wholesale changes that include Jade, Material UI, React, etc. +- The ActiveEntry pages are designed to be used in an IronRouter Layout template. As such, no height/width constraints are provided. It's assumed that the layout template will take care of such things. But basic padding/margin is provided. diff --git a/Packages/active-entry/README.md b/Packages/active-entry/README.md new file mode 100755 index 000000000..e55460600 --- /dev/null +++ b/Packages/active-entry/README.md @@ -0,0 +1,127 @@ +## clinical:active-entry + +This package provides the SignIn, SignUp, and ForgotPassword pages. + +[![Circle CI](https://circleci.com/gh/clinical-meteor/clinical-active-entry/tree/master.svg?style=svg)](https://circleci.com/gh/clinical-meteor/clinical-active-entry/tree/master) + +=============================== +#### Installation + +```` +meteor add clinical:active-entry +```` + +=============================== +#### Entry Flowchart + +The following diagram represents the entry workflow that is being implemented in this package. This package is under active development, and is about half completed. Pull requests which help implement the following workflow will be fast-tracked and accepted into the package. + +![entry-workflow](https://raw.githubusercontent.com/clinical-meteor/clinical-active-entry/master/docs/Entry%20Workflow.png) + + + +=============================== +#### Routing API + +```` +/entrySignIn +/entrySignUp +/forgotPassword +```` + +=============================== +#### Component API + +```` +{{> entrySignIn }} +{{> entrySignUp }} +{{> forgotPassword }} +```` + + +=============================== +#### ActiveEntry Configuration + +````js + +if(Meteor.isClient){ + ActiveEntry.configure({ + logo: { + url: "/mini-circles.png", + displayed: true + }, + signIn: { + displayFullName: true, + destination: "/table/users" + }, + signUp: { + destination: "/table/users" + }, + themeColors: { + primary: "" + } + }); +} + +if(Meteor.isServer){ + Accounts.emailTemplates.siteName = "AwesomeSite"; + Accounts.emailTemplates.from = "AwesomeSite Admin "; + Accounts.emailTemplates.enrollAccount.subject = function (user) { + return "Welcome to Awesome Town, " + user.profile.name; + }; + Accounts.emailTemplates.enrollAccount.text = function (user, url) { + return "You have been selected to participate in building a better future!" + + " To activate your account, simply click the link below:\n\n" + + url; + }; + + Meteor.startup(function(){ + process.env.MAIL_URL = 'smtp://sandboxid.mailgun.org:mypassword@smtp.mailgun.org:587'; + }) +} +```` +Alternatively, you may want to set the ``MAIL_URL`` via an external environment variable, particularly if you're using a SaaS hosting provider. + +````sh +MAIL_URL = 'smtp://sandboxid.mailgun.org:mypassword@smtp.mailgun.org:587' meteor +```` + +=============================== +#### Local Development + +Simply clone the repository into your ``/packages`` directory. You can also specify the packages you want to develop locally in your ``.git-packages.json`` file, and use starrynight to fetch them. + +````bash +# clone a single package into your application +git clone http://github.com/clinical-meteor/clinical-active-entry packages/active-entry + +# fetch all the packages listed in git-packages.json +starrynight fetch +```` + +=============================== +#### Quality Assurance Testing + +There are two types of quality assurance tests you can run: verification and validation tests. Verification tests are similar to unit or integration tests; and can run either at the application or package level. Validation tests are application-wide, but often require commands exposed in packages. So you'll need to run the ``autoconfig`` command to scan the filesystem for validation commands. See [http://starrynight.meteor.com/](http://starrynight.meteor.com/) for more details. + +````bash +# install the testing utility +npm install -g starrynight + +# verification testing (a.k.a. package-level unit/integration testing) +starrynight run-tests --type package-verification + +#validation testing (a.k.a. application acceptance/end-to-end testing) +starrynight autoscan +starrynight run-tests --type validation +```` + +=============================== +#### Contributing + +See our [notes on contributing](https://github.com/clinical-meteor/clinical-active-entry/blob/master/Contributing.md). + +=============================== +#### Licensing + +![MIT License](https://img.shields.io/badge/license-MIT-blue.svg) diff --git a/Packages/active-entry/circle.yml b/Packages/active-entry/circle.yml new file mode 100644 index 000000000..16a78dee3 --- /dev/null +++ b/Packages/active-entry/circle.yml @@ -0,0 +1,61 @@ +## Customize the test machine +machine: + + # Timezone + timezone: + America/Los_Angeles # Set the timezone + + # Add some environment variables + environment: + CIRCLE_ENV: test + CXX: g++-4.8 + DISPLAY: :99.0 + NPM_PREFIX: /home/ubuntu/nvm/v0.10.33 + + +## Customize checkout +checkout: + post: + #- git submodule sync + #- git submodule update --init --recursive # use submodules + +#general: +# build_dir: helloworld + +## Customize dependencies +dependencies: + cache_directories: + - "~/.meteor" # relative to the user's home directory + + override: + - meteor || curl https://install.meteor.com | /bin/sh + - npm install starrynight -g + - meteor create --release METEOR@1.1.0.3 helloworld + - cd helloworld + - cd helloworld && ls -la + - cd helloworld && rm helloworld.* + - cd helloworld && meteor add anti:gagarin@0.4.11 accounts-base accounts-password session meteor-platform clinical:active-entry clinical:user-model + - cd helloworld && git clone http://github.com/clinical-meteor/clinical-active-entry packages/active-entry + - cd helloworld && starrynight autoconfig + +## Customize test commands +test: + pre: + - cd helloworld && meteor: + background: true + - sleep 80 + override: + - cd helloworld && starrynight run-tests --type package-verification + +## Customize deployment commands +#deployment: +# production: +# branch: master +# commands: +# - printf "\n\n" | meteor deploy circlecivelocity.meteor.com + +## Custom notifications +#notify: + #webhooks: + # A list of hashes representing hooks. Only the url field is supported. + #- url: https://someurl.com/hooks/circle diff --git a/Packages/active-entry/components/entryPages.js b/Packages/active-entry/components/entryPages.js new file mode 100755 index 000000000..144f04f18 --- /dev/null +++ b/Packages/active-entry/components/entryPages.js @@ -0,0 +1,52 @@ + +/** + * @summary Determines if a block of code should be displayed based on whether the logo is set to be displayed. + * @locus Client + * @memberOf Entry + * @name {{logoIsDisplayed}} + * @version 1.2.3 + * @returns {Boolean} + * @example + * ```html + * {{#if logoIsDisplayed}} +* + * {{/if}} + * ``` + */ +Template.registerHelper("logoIsDisplayed", function (argument){ + var config = Session.get('Photonic.ActiveEntry'); + if(config && config.logo && config.logo.displayed){ + return config.logo.displayed; + }else{ + return false; + } +}); + + +/** + * @summary Retruns the Url of the logo asset. + * @locus Client + * @memberOf Entry + * @name {{logoUrl}} + * @version 1.2.3 + * @returns {String} + * @example + * ```html + * {{#if logoIsDisplayed}} +* + * {{/if}} + * ``` + */ +Template.registerHelper("logoUrl", function (argument){ + var config = Session.get('Photonic.ActiveEntry'); + if(config && config.logo && config.logo.url){ + return config.logo.url; + }else{ + return ""; + } +}); + + +Template.registerHelper("getButtonColor", function (argument){ + return "background-color: " + Theme.getPaletteColor("colorB") + "; "; +}); diff --git a/Packages/active-entry/components/entryPages.less b/Packages/active-entry/components/entryPages.less new file mode 100755 index 000000000..693636aaf --- /dev/null +++ b/Packages/active-entry/components/entryPages.less @@ -0,0 +1,168 @@ +// @import 'client/app/stylesheets/util/reset.lessimport'; +// @import 'client/app/stylesheets/util/lesshat.lessimport'; +// @import 'client/app/stylesheets/util/typography.lessimport'; + + +// this probably wants to be moved into clinical:glass-ui +input, button, select{ + font-size: 14px; + line-height: 20px; + font-family: 'Open Sans', "Helvetica Neue", Helvetica, Arial, sans-serif; + font-style: 400; + padding: .75rem 0; + line-height: 1.5rem !important; + border: none; + border-radius: 0; + box-sizing: border-box; + //color: #333333; + outline: none; + padding-left: 3em; +} + + +.entryLogo{ + //background-color: lightgray; + height: 200px; + width: 200px; + background-size: contain; + background-repeat: no-repeat; + margin-left: auto; + margin-right: auto; +} + +.btn-primary{ + background-color: forestgreen; + color: #ffffff; +} +.btn-gray{ + background-color: #aaaaaa; + color: white; +} +.btn-main{ + font-weight: 300; + letter-spacing: 3px; + text-transform: uppercase; +} +.input-symbol { + display: inline-block; + position: relative; +} + +.entryPage { + text-align: center; + + input, button{ + width: 100%; + } + + .fa{ + position: absolute; + left: 0px; + top: 0px; + padding-left: 15px; + padding-top: 14px; + } + + .checkmarkIcon{ + position: absolute; + color: #831d2c; + } + .btn-gray{ + width: 100%; + } + .checkmarkTitle{ + padding-bottom: 100px; + } + .fa{ + position: absolute; + left: 0px; + top: 0px; + padding-left: 15px; + padding-top: 16px; + // color: #555555; + } + + .wrapper-auth { + //padding-top: 4em; + + @media screen and (min-width: 40em) { + margin: 0 auto; + max-width: 480px; + width: 80%; + } + @media screen and (max-width: 480px) { + margin: 0 auto; + padding-left: 20px; + padding-right: 20px; + } + // .input-symbol{ + // border: 1px solid gray; + // } + + .title-auth { + color: black; + margin-bottom: .75rem; + } + + .subtitle-auth { + color: black; + //margin: 0 15% 3rem; + margin-bottom: 2rem; + padding: 15px; + } + + form { + input{ + color: #333333; + height: 48px; + border-left: solid 1px !important; + } + .input-symbol { + margin-bottom: 1px; + width: 100%; + } + + .btn-primary { + //margin: 1em 5% 0; + //width: 90%; + margin-top: 1em; + + @media screen and (min-width: 40em) { + // margin-left: 0; + // margin-right: 0; + width: 100%; + } + } + } + .list-errors { + margin-top: -2rem; + .taskItems { + //.title-caps; + background: white; + color: red; + font-size: .625em; // 10px + margin-bottom: 1px; + padding: .7rem 0; + } + } + } + + .link-auth-alt { + //.font-s1; + //.position(absolute, auto, 0, 1em, 0); + color: black; + display: inline-block; + + @media screen and (min-width: 40em) { + bottom: 0; + margin-top: 1rem; + position: relative; + } + } + + // @media only screen and (max-width: 704px) { + // .fa{ + // left: 5%; + // } + // } +} diff --git a/Packages/active-entry/components/entrySignIn/.tests/actions/meteorLogout.js b/Packages/active-entry/components/entrySignIn/.tests/actions/meteorLogout.js new file mode 100755 index 000000000..636737ab9 --- /dev/null +++ b/Packages/active-entry/components/entrySignIn/.tests/actions/meteorLogout.js @@ -0,0 +1,10 @@ + + +exports.command = function () { + this + .execute(function () { + return Meteor.logout(); + }).pause(1000); + + return this; +}; diff --git a/Packages/active-entry/components/entrySignIn/.tests/actions/signIn.js b/Packages/active-entry/components/entrySignIn/.tests/actions/signIn.js new file mode 100755 index 000000000..2da9ff589 --- /dev/null +++ b/Packages/active-entry/components/entrySignIn/.tests/actions/signIn.js @@ -0,0 +1,23 @@ +exports.command = function (email, password) { + + this.verify.elementPresent("#entrySignIn"); + + if (email) { + this + .verify.elementPresent("#signInPageEmailInput") + .clearValue("#signInPageEmailInput") + .setValue("#signInPageEmailInput", email); + } + + + if (password) { + this + .verify.elementPresent("#signInPagePasswordInput") + .clearValue("#signInPagePasswordInput") + .setValue("#signInPagePasswordInput", password); + } + + this.click("#signInToAppButton").pause(1000); + + return this; +}; diff --git a/Packages/active-entry/components/entrySignIn/.tests/actions/signOut.js b/Packages/active-entry/components/entrySignIn/.tests/actions/signOut.js new file mode 100755 index 000000000..ab6742b39 --- /dev/null +++ b/Packages/active-entry/components/entrySignIn/.tests/actions/signOut.js @@ -0,0 +1,22 @@ +exports.command = function (fullname) { + this + .sectionBreak(".signOut()"); + + if (fullname) { + this + .verify.elementPresent("#usernameLink") + .verify.containsText("#usernameLink", fullname); + } + + this + .verify.elementPresent("#logoutLink") + .click("#logoutLink").pause(1000); + + if (fullname) { + this + .verify.elementPresent("#usernameLink") + .verify.containsText("#usernameLink", "Sign In"); + } + + return this; +}; diff --git a/Packages/active-entry/components/entrySignIn/.tests/reviewSignIn.js b/Packages/active-entry/components/entrySignIn/.tests/reviewSignIn.js new file mode 100755 index 000000000..3c23fe4a0 --- /dev/null +++ b/Packages/active-entry/components/entrySignIn/.tests/reviewSignIn.js @@ -0,0 +1,35 @@ +exports.command = function (email, password, title, message) { + this + // .verify.element("#entrySignIn").to.be.visible + // .verify.element("#signInPageTitle").to.be.visible + // .verify.element("#signInPageMessage").to.be.visible + // .verify.element("#signInPageEmailInput").to.be.visible + // .verify.element("#signInPagePasswordInput").to.be.visible + // .verify.element("#signInToAppButton").to.be.visible + // .verify.element("#needAnAccountButton").to.be.visible + // + + .verify.elementPresent("#entrySignIn") + .verify.elementPresent("#signInPageTitle") + .verify.elementPresent("#signInPageMessage") + .verify.elementPresent("#signInPageEmailInput") + .verify.elementPresent("#signInPagePasswordInput") + .verify.elementPresent("#signInToAppButton") + .verify.elementPresent("#needAnAccountButton"); + + if (email) { + this.verify.containsText("#signInPageEmailInput", email); + } + if (password) { + this.verify.containsText("#signInPageEmailInput", password); + } + + if (title) { + this.verify.containsText("#signInPageTitle", title); + } + if (message) { + this.verify.containsText("#signInPageMessage", message); + } + + return this; +}; diff --git a/Packages/active-entry/components/entrySignIn/entrySignIn.html b/Packages/active-entry/components/entrySignIn/entrySignIn.html new file mode 100755 index 000000000..b3202cae5 --- /dev/null +++ b/Packages/active-entry/components/entrySignIn/entrySignIn.html @@ -0,0 +1,46 @@ + diff --git a/Packages/active-entry/components/entrySignIn/entrySignIn.js b/Packages/active-entry/components/entrySignIn/entrySignIn.js new file mode 100755 index 000000000..523854089 --- /dev/null +++ b/Packages/active-entry/components/entrySignIn/entrySignIn.js @@ -0,0 +1,131 @@ + +// REFACTOR: Move to ActiveRecord object +Session.set("defaultSignInMessage", "Improve your clinical practice with checklists."); + +//================================================================================================== +// ROUTER + +Router.route('/entrySignIn', { + template: 'entrySignIn', + name: 'entrySignIn' +}); +Router.route('/sign-in', { + template: 'entrySignIn', + name: 'signInRoute' +}); + +//================================================================================================== +// COMPONENT OUTPUTS + + + + +Template.entrySignIn.helpers({ + getSignInMessageColor: function (){ + if (ActiveEntry.errorMessages.get('signInError')) { + return "color: #a94442; background-color: #f2dede; border-color: #ebccd1;" + } else { + return "color: black;" + } + }, + getSignInMessage: function (){ + if (ActiveEntry.errorMessages.get('signInError')) { + return ActiveEntry.errorMessages.get('signInError'); + } else { + return Session.get('defaultSignInMessage'); + } + }, + getButtonText: function () { + return "Sign In"; + // if (ActiveEntry.errorMessages.get('signInError')){ + // return ActiveEntry.errorMessages.get('signInError'); + // } else { + // return "Sign In"; + // } + }, + getEmailValidationStyling: function () { + if (ActiveEntry.errorMessages.equals('email', "Email is required")) { + return "border: 1px solid #a94442"; + } else if (ActiveEntry.errorMessages.equals('email', "Email is poorly formatted")) { + return "border: 1px solid #f2dede"; + } else if (ActiveEntry.errorMessages.equals('email', "Email present")) { + return "border: 1px solid green"; + } else { + return "border: 1px solid gray"; + } + }, + getPasswordValidationStyling: function () { + if (ActiveEntry.errorMessages.equals('password', "Password is required")) { + return "border: 1px solid #a94442"; + } else if (ActiveEntry.errorMessages.equals('password', "Password is weak")) { + return "border: 1px solid #f2dede"; + } else if (ActiveEntry.errorMessages.equals('password', "Password present")) { + return "border: 1px solid green"; + } else { + return "border: 1px solid gray"; + } + } +}); + + +//================================================================================================== +// COMPONENT OUTPUTS + +Template.entrySignIn.events({ + 'click #logoutButton': function () { + Meteor.logout(); + }, + 'click #forgotPasswordButton': function (event) { + event.preventDefault(); + Router.go('/forgotPassword'); + }, + "click #needAnAccountButton": function (event) { + event.preventDefault(); + Router.go('/entrySignUp'); + }, + 'keyup input[name="email"]': function (event, template) { + var email = $('input[name="email"]').val(); + + ActiveEntry.verifyEmail(email); + ActiveEntry.errorMessages.set('signInError', null); + }, + 'change input[name="email"]': function (event, template) { + var email = $('input[name="email"]').val(); + + ActiveEntry.verifyEmail(email); + ActiveEntry.errorMessages.set('signInError', null); + }, + 'keyup #signInPagePasswordInput': function (event, template) { + var password = $('input[name="password"]').val(); + + ActiveEntry.verifyPassword(password); + ActiveEntry.errorMessages.set('signInError', null); + }, + 'change #signInPagePasswordInput': function (event, template) { + var password = $('input[name="password"]').val(); + + ActiveEntry.verifyPassword(password); + ActiveEntry.errorMessages.set('signInError', null); + }, + // 'submit': function (event, template) { + // event.preventDefault(); + // var emailValue = template.$('[name=email]').val(); + // var passwordValue = template.$('[name=password]').val(); + // + // ActiveEntry.signIn(emailValue, passwordValue); + // }, + 'click #signInToAppButton': function (event, template){ + console.log('click #signInToAppButton'); + // var emailValue = template.$('[name=email]').val(); + // var passwordValue = template.$('[name=password]').val(); + var emailValue = template.$('#signInPageEmailInput').val(); + var passwordValue = template.$('#signInPagePasswordInput').val(); + + ActiveEntry.signIn(emailValue, passwordValue); + event.preventDefault(); + } +}); + + + +//================================================================================================== diff --git a/Packages/active-entry/components/entrySignIn/entrySignIn.less b/Packages/active-entry/components/entrySignIn/entrySignIn.less new file mode 100755 index 000000000..5b3b93e34 --- /dev/null +++ b/Packages/active-entry/components/entrySignIn/entrySignIn.less @@ -0,0 +1,17 @@ +#entrySignIn{ + padding: 40px; + input{ + padding-left: 40px; + color: #333333 !important; + } + #errorMessages{ + padding-top: .5rem; + padding-bottom: .5rem; + font-size: 14px; + box-sizing: border-box; + width: 100.5%; + margin-bottom: 15px; + color: red; + font-weight: bold; + } +} diff --git a/Packages/active-entry/components/entrySignUp/.tests/actions/dropEntryUsers.js b/Packages/active-entry/components/entrySignUp/.tests/actions/dropEntryUsers.js new file mode 100644 index 000000000..3e19972ec --- /dev/null +++ b/Packages/active-entry/components/entrySignUp/.tests/actions/dropEntryUsers.js @@ -0,0 +1,22 @@ +// async version calls method on the server +exports.command = function () { + var client = this; + + this + .timeoutsAsyncScript(5000) + .executeAsync(function (data, meteorCallback) { + //return HipaaLogger.logEventObject(data); + Meteor.call("dropEntryUsers", data, function (meteorError, meteorResult) { + var response = (meteorError ? { + error: meteorError + } : { + result: meteorResult + }); + meteorCallback(response); + }); + }, [], function (result) { + //console.log("result.value", result.value); + //client.assert.ok(result.value); + }).pause(1000); + return this; +}; diff --git a/Packages/active-entry/components/entrySignUp/.tests/actions/resetEntry.js b/Packages/active-entry/components/entrySignUp/.tests/actions/resetEntry.js new file mode 100644 index 000000000..d9cd8bc0a --- /dev/null +++ b/Packages/active-entry/components/entrySignUp/.tests/actions/resetEntry.js @@ -0,0 +1,11 @@ + +// resetEntry.js +exports.command = function () { + + this + .timeoutsAsyncScript(5000) + .executeAsync(function (data, meteorCallback) { + ActiveEntry.reset(); + }, []).pause(1000); + return this; +}; diff --git a/Packages/active-entry/components/entrySignUp/.tests/actions/signUp.js b/Packages/active-entry/components/entrySignUp/.tests/actions/signUp.js new file mode 100755 index 000000000..fecd06ce3 --- /dev/null +++ b/Packages/active-entry/components/entrySignUp/.tests/actions/signUp.js @@ -0,0 +1,37 @@ +exports.command = function (email, password, fullName) { + + this.verify.elementPresent("#entrySignUp"); + + if (email) { + this + .verify.elementPresent("#signUpPageEmailInput") + .clearValue("#signUpPageEmailInput") + .setValue("#signUpPageEmailInput", email); + } + + + if (password) { + this + .verify.elementPresent("#signUpPagePasswordInput") + .clearValue("#signUpPagePasswordInput") + .setValue("#signUpPagePasswordInput", password) + + .verify.elementPresent("#signUpPagePasswordConfirmInput") + .clearValue("#signUpPagePasswordConfirmInput") + .setValue("#signUpPagePasswordConfirmInput", password); + } + + + if (fullName) { + this + .verify.elementPresent("#signUpPageFullNameInput") + .clearValue("#signUpPageFullNameInput") + .setValue("#signUpPageFullNameInput", fullName); + } + + + this.click("#signUpPageJoinNowButton").pause(300); + + + return this; +}; diff --git a/Packages/active-entry/components/entrySignUp/.tests/reviewSignUp.js b/Packages/active-entry/components/entrySignUp/.tests/reviewSignUp.js new file mode 100755 index 000000000..926f22599 --- /dev/null +++ b/Packages/active-entry/components/entrySignUp/.tests/reviewSignUp.js @@ -0,0 +1,31 @@ +exports.command = function (email, password, fullName, message) { + this + .verify.elementPresent("#entrySignUp") + .verify.elementPresent("#signUpPageTitle") + .verify.elementPresent("#signUpPageMessage") + .verify.elementPresent("#signUpPageFullNameInput") + .verify.elementPresent("#signUpPageEmailInput") + .verify.elementPresent("#signUpPagePasswordInput") + .verify.elementPresent("#signUpPagePasswordConfirmInput") + + .verify.elementPresent("#signUpPageJoinNowButton") + .verify.elementPresent("#signUpPageSignInButton"); + + if (email) { + this.verify.containsText("#signUpPageEmailInput", email); + } + if (password) { + this.verify.containsText("#signUpPagePasswordInput", password) + .verify.containsText("#signUpPagePasswordConfirmInput", password); + } + if (fullName) { + this.verify.containsText("#signUpPageFullNameInput", fullName); + } + if (message) { + this.verify.elementPresent("#errorMessages") + .verify.containsText("#errorMessages", message); + } + + + return this; +}; diff --git a/Packages/active-entry/components/entrySignUp/entrySignUp.html b/Packages/active-entry/components/entrySignUp/entrySignUp.html new file mode 100755 index 000000000..b871967ad --- /dev/null +++ b/Packages/active-entry/components/entrySignUp/entrySignUp.html @@ -0,0 +1,63 @@ + diff --git a/Packages/active-entry/components/entrySignUp/entrySignUp.js b/Packages/active-entry/components/entrySignUp/entrySignUp.js new file mode 100755 index 000000000..463a40825 --- /dev/null +++ b/Packages/active-entry/components/entrySignUp/entrySignUp.js @@ -0,0 +1,139 @@ +//================================================================================================== +// ROUTER + +Router.route('/entrySignUp', { + template: 'entrySignUp', + name: 'entrySignUp' +}); +Router.route('/sign-up', { + template: 'entrySignUp', + name: 'signUpRoute' +}); + +//================================================================================================== + + + +Template.entrySignUp.helpers({ + getSignUpMessageColor: function (){ + if (ActiveEntry.errorMessages.get('signInError')) { + return "color: #a94442; background-color: #f2dede; border-color: #ebccd1;" + } else { + return "color: black;" + } + }, + getSignUpMessage: function (){ + if (ActiveEntry.errorMessages.get('signInError')) { + return ActiveEntry.errorMessages.get('signInError'); + } else { + return Session.get('defaultSignInMessage'); + } + }, + getButtonText: function () { + if (ActiveEntry.errorMessages.get('signInError')) { + return ActiveEntry.errorMessages.get('signInError').message; + } else { + return "Sign In"; + } + }, + getEmailStyling: function () { + if (ActiveEntry.errorMessages.equals('email', "Email is required")) { + return "border: 1px solid #a94442"; + } else if (ActiveEntry.errorMessages.equals('email', "Email is poorly formatted")) { + return "border: 1px solid #f2dede"; + } else if (ActiveEntry.errorMessages.equals('email', "Email present")) { + return "border: 1px solid green"; + } else { + return "border: 1px solid gray"; + } + }, + getPasswordStyling: function () { + if (ActiveEntry.errorMessages.equals('password', "Password is required")) { + return "border: 1px solid #a94442"; + } else if (ActiveEntry.errorMessages.equals('password', "Password is weak")) { + return "border: 1px solid #f2dede"; + } else if (ActiveEntry.errorMessages.equals('password', "Password present")) { + return "border: 1px solid green"; + } else { + return "border: 1px solid gray"; + } + }, + getConfirmPasswordStyling: function () { + if (ActiveEntry.errorMessages.equals('confirm', "Password is required")) { + return "border: 1px solid #a94442"; + } else if (ActiveEntry.errorMessages.equals('confirm', "Password is weak")) { + return "border: 1px solid #f2dede"; + } else if (ActiveEntry.errorMessages.equals('confirm', "Passwords match")) { + return "border: 1px solid green"; + } else { + return "border: 1px solid gray"; + } + }, + getFullNameStyling: function () { + if (ActiveEntry.errorMessages.equals('fullName', "Name is required")) { + return "border: 1px solid #a94442"; + } else if (ActiveEntry.errorMessages.equals('fullName', "Name is probably not complete")) { + return "border: 1px solid #f2dede"; + } else if (ActiveEntry.errorMessages.equals('fullName', "Name present")) { + return "border: 1px solid green"; + } else { + return "border: 1px solid gray"; + } + } +}); + +Template.entrySignUp.events({ + "click #signUpPageSignInButton": function (event) { + event.preventDefault(); + Router.go('/entrySignIn'); + }, + 'change, keyup #signUpPageEmailInput': function (event, template) { + var email = $('[name="email"]').val(); + + ActiveEntry.verifyEmail(email); + ActiveEntry.errorMessages.set('signInError', null); + }, + 'change, keyup #signUpPagePasswordInput': function (event, template) { + var password = $('[name="password"]').val(); + + ActiveEntry.verifyPassword(password); + ActiveEntry.errorMessages.set('signInError', null); + }, + 'change, keyup #signUpPagePasswordConfirmInput': function (event, template) { + + var password = $('[name="password"]').val(); + var confirmPassword = $('[name="confirm"]').val(); + // var password = $('#signUpPagePasswordInput').val(); + // var confirmPassword = $('#signUpPagePasswordConfirmInput').val(); + + ActiveEntry.verifyConfirmPassword(password, confirmPassword); + ActiveEntry.errorMessages.set('signInError', null); + }, + 'change, keyup #signUpPageFullNameInput': function (event, template) { + var fullName = template.$('[name="fullName"]').val(); + + ActiveEntry.verifyFullName(fullName); + ActiveEntry.errorMessages.set('signInError', null); + }, + // TODO: this is outdated, and should be changed to match the signature/pattern in entrySignIn + 'click #signUpPageJoinNowButton': function (event, template) { + event.preventDefault(); + + ActiveEntry.errorMessages.set('signInError', null); + ActiveEntry.verifyPassword(event, template); + + var newUser = { + fullName: template.$('[name="fullName"]').val(), + email: template.$('[name="email"]').val(), + password: template.$('[name="password"]').val(), + confirm: template.$('[name="confirm"]').val() + }; + + ActiveEntry.signUp( + newUser.email, + newUser.password, + newUser.confirm, + newUser.fullName + ); + } +}); diff --git a/Packages/active-entry/components/entrySignUp/entrySignUp.less b/Packages/active-entry/components/entrySignUp/entrySignUp.less new file mode 100755 index 000000000..de57917e4 --- /dev/null +++ b/Packages/active-entry/components/entrySignUp/entrySignUp.less @@ -0,0 +1,7 @@ +#entrySignUp{ + padding: 40px; + input{ + padding-left: 40px !important; + color: black; + } +} diff --git a/Packages/active-entry/components/forgotPassword/.tests/reviewForgotPassword.js b/Packages/active-entry/components/forgotPassword/.tests/reviewForgotPassword.js new file mode 100755 index 000000000..d6f00b72c --- /dev/null +++ b/Packages/active-entry/components/forgotPassword/.tests/reviewForgotPassword.js @@ -0,0 +1,4 @@ +exports.command = function () { + this + .verify.elementPresent("#forgotPassword"); return this; +}; diff --git a/Packages/active-entry/components/forgotPassword/forgotPassword.html b/Packages/active-entry/components/forgotPassword/forgotPassword.html new file mode 100755 index 000000000..8a77f6c92 --- /dev/null +++ b/Packages/active-entry/components/forgotPassword/forgotPassword.html @@ -0,0 +1,24 @@ + diff --git a/Packages/active-entry/components/forgotPassword/forgotPassword.js b/Packages/active-entry/components/forgotPassword/forgotPassword.js new file mode 100755 index 000000000..b39444fa4 --- /dev/null +++ b/Packages/active-entry/components/forgotPassword/forgotPassword.js @@ -0,0 +1,22 @@ +//========================================== + +Router.route('/forgotPassword', { + name: "forgotPassword", + template: "forgotPassword" +}); + + +Template.forgotPassword.helpers({ + getForgotPasswordStyle: function (){ + return "border: 1px solid gray"; + } +}); + +Template.forgotPassword.events({ + "submit": function (event, template) { + event.preventDefault(); + + console.log('send reminder!'); + Accounts.forgotPassword({email: $('#signInPageEmailInput').val()}); + } +}); diff --git a/Packages/active-entry/components/forgotPassword/forgotPassword.less b/Packages/active-entry/components/forgotPassword/forgotPassword.less new file mode 100755 index 000000000..17db0dc7e --- /dev/null +++ b/Packages/active-entry/components/forgotPassword/forgotPassword.less @@ -0,0 +1,5 @@ +#forgotPassword{ + input{ + padding-left: 40px; + } +} diff --git a/Packages/active-entry/lib/Accounts.js b/Packages/active-entry/lib/Accounts.js new file mode 100755 index 000000000..fbc3de2d1 --- /dev/null +++ b/Packages/active-entry/lib/Accounts.js @@ -0,0 +1,48 @@ + + +if (Meteor.isClient) { + + + Accounts.onResetPasswordLink(function (token, done){ + console.log('Accounts.onResearchPasswordLink'); + console.log('Sending reset password email...'); + console.log('NOT IMPLEMENTED YET. PLEASE LOG AN ISSUE'); + console.log('token: ' + token); + done(); + }); + + + Accounts.onEnrollmentLink(function (token, done){ + console.log('Accounts.onResearchPasswordLink'); + console.log('Sending enrollment email...'); + console.log('NOT IMPLEMENTED YET. PLEASE LOG AN ISSUE'); + console.log('token: ' + token); + done(); + }); + + Accounts.onEmailVerificationLink(function (token, done){ + console.log('Accounts.onEmailVerificationLink'); + console.log('Sending verification email...'); + console.log('NOT IMPLEMENTED YET. PLEASE LOG AN ISSUE'); + console.log('token: ' + token); + done(); + }); +} + + +if (Meteor.isServer){ + // Support for playing D&D: Roll 3d6 for dexterity + Accounts.onCreateUser(function(options, user) { + + var d6 = function () { return Math.floor(Random.fraction() * 6) + 1; }; + user.dexterity = d6() + d6() + d6(); + user.role = "user"; + + // We still want the default hook's 'profile' behavior. + if (options.profile){ + user.profile = options.profile; + } + + return user; + }); +} diff --git a/Packages/active-entry/lib/ActiveEntry.js b/Packages/active-entry/lib/ActiveEntry.js new file mode 100755 index 000000000..cc0e897fb --- /dev/null +++ b/Packages/active-entry/lib/ActiveEntry.js @@ -0,0 +1,150 @@ + + +ActiveEntry = {}; +ActiveEntry.isAbc = function () { + return "abc"; +}; + + + +if (Meteor.isClient) { + Session.setDefault('Photonic.ActiveEntry', { + logo: { + url: "https://upload.wikimedia.org/wikipedia/commons/1/1a/Photon-photon_scattering.png", + displayed: true + }, + signIn: { + displayFullName: true, + destination: "/" + }, + signUp: { + destination: "/" + }, + themeColors: { + primary: "" + } + }); +} + + +if (Meteor.isClient) { + ActiveEntry.errorMessages = new ReactiveDict('errorMessages'); + ActiveEntry.errorMessages.set('signInError', false); + +} + + +ActiveEntry.configure = function (configObject) { + if (Meteor.isClient) { + Session.set('Photonic.ActiveEntry', configObject); + } +}; + + + + +ActiveEntry.verifyPassword = function (password) { + if (password.length === 0) { + ActiveEntry.errorMessages.set('password', 'Password is required'); + } else if (password.length < 8) { + ActiveEntry.errorMessages.set('password', 'Password is weak'); + } else if (password.length >= 8) { + ActiveEntry.errorMessages.set('password', 'Password present'); + } +}; +ActiveEntry.verifyConfirmPassword = function (password, confirmPassword) { + if (confirmPassword.length === 0) { + ActiveEntry.errorMessages.set('confirm', 'Password is required'); + } else if (confirmPassword.length < 8) { + ActiveEntry.errorMessages.set('confirm', 'Password is weak'); + } + + if (confirmPassword === password) { + ActiveEntry.errorMessages.set('confirm', 'Passwords match'); + } +}; +ActiveEntry.verifyEmail = function (email) { + if (email.length === 0) { + ActiveEntry.errorMessages.set('email', 'Email is required'); + } else if (email.indexOf("@") === -1){ + ActiveEntry.errorMessages.set('email', 'Email is poorly formatted'); + } else if (email.indexOf("@") >= 0){ + ActiveEntry.errorMessages.set('email', 'Email present'); + } +}; +ActiveEntry.verifyFullName = function (fullName) { + if (fullName.length === 0) { + ActiveEntry.errorMessages.set('fullName', 'Name is required'); + } else if (fullName.indexOf(" ") === -1){ + ActiveEntry.errorMessages.set('fullName', 'Name is probably not complete'); + } else if (fullName.indexOf(" ") >= 0){ + ActiveEntry.errorMessages.set('fullName', 'Name present'); + } +}; + +ActiveEntry.signIn = function (emailValue, passwordValue){ + ActiveEntry.verifyPassword(passwordValue); + ActiveEntry.verifyEmail(emailValue); + + Meteor.loginWithPassword({email: emailValue}, passwordValue, function (error, result) { + if (error) { + ActiveEntry.errorMessages.set('signInError', error.message); + } else { + console.log('result', result); + var ActiveEntryConfig = Session.get('Photonic.ActiveEntry'); + console.log('ActiveEntryConfig', JSON.stringify(ActiveEntryConfig)); + Router.go(ActiveEntryConfig.signIn.destination); + } + }); +}; + +ActiveEntry.signUp = function (emailValue, passwordValue, confirmPassword, fullName){ + ActiveEntry.verifyEmail(emailValue); + ActiveEntry.verifyPassword(passwordValue); + ActiveEntry.verifyConfirmPassword(passwordValue, confirmPassword); + ActiveEntry.verifyFullName(fullName); + + Accounts.createUser({ + email: emailValue, + password: passwordValue, + profile: { + fullName: fullName + } + }, function (error, result) { + if (error) { + console.log(error); + ActiveEntry.errorMessages.set('signInError', error.message); + } else { + var ActiveEntryConfig = Session.get('Photonic.ActiveEntry'); + Router.go(ActiveEntryConfig.signUp.destination); + } + }); + + // Meteor.loginWithPassword({email: emailValue}, passwordValue, function (error, result) { + // if (error) { + // console.log(error); + // Session.set('errorMessage', error); + // } + // + // if (result) { + // console.log('result', result); + // } + // var ActiveEntryConfig = Session.get('Photonic.ActiveEntry'); + // console.log('ActiveEntryConfig', JSON.stringify(ActiveEntryConfig)); + // Router.go(ActiveEntryConfig.signIn.destination); + // }); +}; +ActiveEntry.signOut = function (){ + Meteor.logout(); +}; + +ActiveEntry.reset = function (){ + ActiveEntry.errorMessages.set('fullName', false); + ActiveEntry.errorMessages.set('email', false); + ActiveEntry.errorMessages.set('confirm', false); + ActiveEntry.errorMessages.set('password', false); +}; +ActiveEntry.logoIsDisplayed = function (){ + var ActiveEntryConfig = Session.get('Photonic.ActiveEntry'); + return ActiveEntryConfig.logo.displayed; +} diff --git a/Packages/active-entry/package.js b/Packages/active-entry/package.js new file mode 100755 index 000000000..df73cd9ad --- /dev/null +++ b/Packages/active-entry/package.js @@ -0,0 +1,73 @@ +Package.describe({ + name: 'clinical:active-entry', + version: '1.5.14', + summary: 'SignIn, SignUp, and ForgotPassword pages for Clinical Framework.', + git: 'https://github.com/clinical-meteor/clinical-active-entry', + documentation: 'README.md' +}); + +Package.onUse(function (api) { + api.versionsFrom('1.1.0.3'); + + api.use([ + 'meteor-platform', + 'templating', + 'clinical:router@2.0.17', + 'grove:less@0.1.1', + 'session', + 'reactive-dict', + 'accounts-base', + 'accounts-password' + ], ['client']); + + api.use([ + 'accounts-base', + 'accounts-password' + ], ['server']); + + api.addFiles([ + 'lib/ActiveEntry.js', + 'lib/Accounts.js' + ]); + + api.imply('accounts-base'); + api.imply('accounts-password'); + + api.addFiles([ + 'components/entryPages.js', + 'components/entryPages.less', + + 'components/entrySignIn/entrySignIn.html', + 'components/entrySignIn/entrySignIn.js', + 'components/entrySignIn/entrySignIn.less', + + 'components/entrySignUp/entrySignUp.html', + 'components/entrySignUp/entrySignUp.js', + 'components/entrySignUp/entrySignUp.less', + + 'components/forgotPassword/forgotPassword.html', + 'components/forgotPassword/forgotPassword.js', + 'components/forgotPassword/forgotPassword.less', + + ], ['client']); + + + api.addFiles('server/methods.js', "server", {testOnly: true}); + + api.export("ActiveEntry"); +}); + + +Package.onTest(function (api) { + api.use([ + 'templating', + 'clinical:router@2.0.13', + 'grove:less@0.1.1', + 'standard-app-packages' + ], ['client']); + + api.use('tinytest'); + api.use('clinical:active-entry'); + api.use('clinical:verification'); + api.addFiles('tests/gagarin/activeEntryTests.js'); +}); diff --git a/Packages/active-entry/server/methods.js b/Packages/active-entry/server/methods.js new file mode 100644 index 000000000..834dfde21 --- /dev/null +++ b/Packages/active-entry/server/methods.js @@ -0,0 +1,12 @@ +Meteor.methods({ + initializeEntryUsers: function (){ + console.log('Initializing Users', Meteor.users.find().fetch()); + + }, + dropEntryUsers: function (){ + console.log('Drop Users', Meteor.users.find().fetch()); + Meteor.users.find().forEach(function(user){ + Meteor.users.remove({_id: user._id}); + }); + } +}); diff --git a/Packages/active-entry/tests/gagarin/activeEntryTests.js b/Packages/active-entry/tests/gagarin/activeEntryTests.js new file mode 100644 index 000000000..f93225997 --- /dev/null +++ b/Packages/active-entry/tests/gagarin/activeEntryTests.js @@ -0,0 +1,197 @@ +// var nightwatch = require('nightwatch'); + +describe('clinical:active-entry', function () { + var server = meteor(); + var client = browser(server); + + // before(function () { + // return server.promise(function (resolve){ + // Meteor.users.find().forEach(function(user){ + // Meteor.users.remove({_id: user._id}); + // }, function(){ + // resolve(); + // }); + // }); + // }); + + // afterEach(function (){ + // return client.promise(function (resolve){ + // Meteor.logout(function(error, result){ + // resolve(); + // }); + // }); + // }); + + + it("ActiveEntry object should be loaded on client and server", function () { + return server.execute(function () { + expect(ActiveEntry.isAbc()).to.equal('abc'); + }).then(function (data){ + return client.execute(function (a) { + expect(ActiveEntry.isAbc()).to.equal('abc'); + }); + }); + }); + + it("Error messages should be empty by default", function () { + return client.execute(function () { + expect(ActiveEntry.errorMessages.get('signInError')).to.equal(false); + }); + }); + + // ActiveEntry.verifyEmail + it('Email validation confirms it is a properly formatted email.', function () { + return client.execute(function (a) { + ActiveEntry.verifyEmail('janedoe@somewhere.com'); + expect(ActiveEntry.errorMessages.get('email')).to.equal("Email present"); + + ActiveEntry.verifyEmail(''); + expect(ActiveEntry.errorMessages.get('email')).to.equal("Email is required"); + + ActiveEntry.verifyEmail('janedoe.somewhere.com'); + expect(ActiveEntry.errorMessages.get('email')).to.equal("Email is poorly formatted"); + }); + }); + + + // ActiveEntry.verifyPassword + it('Password validation confirms it is a properly formatted password.', function () { + return client.execute(function (a) { + ActiveEntry.verifyPassword(''); + expect(ActiveEntry.errorMessages.get('password')).to.equal("Password is required"); + + ActiveEntry.verifyPassword('kittens'); + expect(ActiveEntry.errorMessages.get('password')).to.equal("Password is weak"); + + ActiveEntry.verifyPassword('kittens123'); + expect(ActiveEntry.errorMessages.get('password')).to.equal("Password present"); + }); + }); + + // ActiveEntry.verifyConfirmPassword + it('Password match validation confirms that two passwords are the same.', function () { + return client.execute(function (a) { + ActiveEntry.verifyConfirmPassword('kittens123', ''); + expect(ActiveEntry.errorMessages.get('confirm')).to.equal("Password is required"); + + ActiveEntry.verifyConfirmPassword('kittens123', 'kittens'); + expect(ActiveEntry.errorMessages.get('confirm')).to.equal("Password is weak"); + + ActiveEntry.verifyConfirmPassword('kittens123', 'kittens123'); + expect(ActiveEntry.errorMessages.get('confirm')).to.equal("Passwords match"); + }); + }); + + // ActiveEntry.verifyFullName + it('Fullname validation confirms that at least a first and last name are entered.', function () { + return client.execute(function (a) { + ActiveEntry.verifyFullName(''); + expect(ActiveEntry.errorMessages.get('fullName')).to.equal("Name is required"); + + ActiveEntry.verifyFullName('Jane'); + expect(ActiveEntry.errorMessages.get('fullName')).to.equal("Name is probably not complete"); + + ActiveEntry.verifyFullName('Jane Doe'); + expect(ActiveEntry.errorMessages.get('fullName')).to.equal("Name present"); + }); + }); + + + // ActiveEntry.signIn + it('Newly created user record should have role, profile, and name set.', function () { + return client.execute(function () { + ActiveEntry.signUp('janedoe@test.org', 'janedoe123', 'janedoe123', 'Jane Doe'); + expect(ActiveEntry.errorMessages.get('fullName')).to.equal("Name present"); + }).then(function (){ + return server.wait(300, 'until account is created on the server', function () { + return Meteor.users.findOne({'emails.address': 'janedoe@test.org'}); + }).then(function (user){ + expect(user.role).to.equal('user'); + expect(user.profile.fullName).to.equal('Jane Doe'); + }); + }); + }); + + + + it("Newly created user should have fullName(), preferredName(), and familyName() methods.", function () { + return server.execute(function () { + var user = Meteor.users.findOne({'emails.address': 'janedoe@test.org'}); + expect(user).to.be.ok; + expect(user.fullName()).to.equal('Jane Doe'); + expect(user.givenName()).to.equal('Jane'); + expect(user.familyName()).to.equal('Doe'); + }).then(function(){ + // client.wait(500, "until user is logged out", function(){ + // Meteor.logout(); + // }); + return client.promise(function (resolve){ + Meteor.logout(function(error, result){ + resolve(); + }); + }); + + }); + }); + it("Newly created user can sign in to the application.", function () { + return client.execute(function () { + expect(Meteor.userId()).to.not.exist; + ActiveEntry.signIn('janedoe@test.org', 'janedoe123'); + }).then(function (){ + client.wait(3000, "for user to sign in", function (){ + expect(Meteor.userId()).to.exist; + }); + }); + }); + it("Newly created user can sign out of the application.", function () { + return client.execute(function () { + expect(Meteor.userId()).to.not.exist; + ActiveEntry.signIn('janedoe@test.org', 'janedoe123'); + }).then(function (){ + client.wait(3000, "for user to sign in", function (){ + expect(Meteor.userId()).to.exist; + ActiveEntry.signOut('janedoe@test.org'); + }).then(function (){ + expect(Meteor.userId()).to.not.exist; + }); + }); + }); + + + + // it("config should be able to change company logo", function () { + // + // }); + // it("config should be able to change entry message text", function () { + // + // }); + + // it("new user should be able to register on desktop", function () { + // client.location = "/sign-in"; + // + // client.execute(function () { + // expect($('#entrySignIn')).to.exist(); + // expect($('#signInPageEmailInput')).to.exist(); + // expect($('#signInPagePasswordInput')).to.exist(); + // expect($('#signInToAppButton')).to.exist(); + // }).setValue('#signInPageEmailInput', 'house@test.org') + // .setValue('#signInPagePasswordInput', 'house@test.org') + // .click('#signInToAppButton').execute(function(){ + // expect($('#entrySignIn')).to.exist(); + // expect($('#signInPageEmailInput')).to.exist(); + // expect($('#signInPagePasswordInput')).to.exist(); + // expect($('#signInToAppButton')).to.exist(); + // // expect($('#entrySignIn')).to.not.exist(); + // // expect($('#signInPageEmailInput')).to.not.exist(); + // // expect($('#signInPagePasswordInput')).to.not.exist(); + // // expect($('#signInToAppButton')).to.not.exist(); + // }); + // // .then(function(data){ + // // return server.execute(3000, 'until the account is created', function () { + // // expect(Meteor.users.find().count()).to.be.above(20); + // // }); + // // }); + // }); + + +}); diff --git a/Packages/active-entry/tests/nightwatch/commands/dropCollaborations.js b/Packages/active-entry/tests/nightwatch/commands/dropCollaborations.js new file mode 100755 index 000000000..c6c1652f4 --- /dev/null +++ b/Packages/active-entry/tests/nightwatch/commands/dropCollaborations.js @@ -0,0 +1,22 @@ +// async version calls method on the server +exports.command = function () { + var client = this; + + this + .timeoutsAsyncScript(5000) + .executeAsync(function (data, meteorCallback) { + //return HipaaLogger.logEventObject(data); + Meteor.call("dropCollaborations", data, function (meteorError, meteorResult) { + var response = (meteorError ? { + error: meteorError + } : { + result: meteorResult + }); + meteorCallback(response); + }); + }, [], function (result) { + console.log("result.value", result.value); + client.assert.ok(result.value); + }).pause(1000); + return this; +}; diff --git a/Packages/active-entry/tests/nightwatch/walkthroughs/activeEntryWalkthrough.js b/Packages/active-entry/tests/nightwatch/walkthroughs/activeEntryWalkthrough.js new file mode 100755 index 000000000..b3b6ff046 --- /dev/null +++ b/Packages/active-entry/tests/nightwatch/walkthroughs/activeEntryWalkthrough.js @@ -0,0 +1,184 @@ +// when new user fills out form and registers, new user should get created +// when user signs in with username and password, should redirect to home page +// newly created user record should have role +// newly created user record should have profile +// newly created user record should have full, preferred, and family name +// user object should return first name +// user object should return last name +// user should be able to request reset password email +// user should be able to request be able to create new account +// guest should be notified if username already exists +// guest should be notified if passwords do not match +// guest should be notified if email is not correctly formatted +// new user should be able to register on desktop +// new user should be able to register on tablet +// existing user should be able to sign in on desktop +// existing user should be able to sign in on tablet +// existing user should be able to sign in on phone +// company logo should display on sign//in page + + + +module.exports = { + tags: ['users', 'entry'], + before: function(client){ + client + .url("http://localhost:3000/entrySignUp") + .initializeUsers() + .resizeWindow(1024, 768) + }, + "new user should be able to register on desktop" : function (client) { + client + .verify.elementPresent("#entrySignUp") + .verify.elementPresent("#signUpPageTitle") + .verify.elementPresent("#signUpPageMessage") + .verify.elementPresent("#signUpPageEmailInput") + .verify.elementPresent("#signUpPagePasswordInput") + .verify.elementPresent("#signUpPageJoinNowButton") + .verify.elementPresent("#signUpPageSignInButton") + }, + "company logo should display on sign-in page" : function (client) { + client + .verify.elementPresent("#entrySignUp") + .verify.elementPresent("#entryAppLogo") + }, + "user should be able to request be able to create new account" : function (client) { + client.verify.elementPresent("#signUpPageEmailInput") + .verify.elementPresent("#signUpPagePasswordInput") + .verify.elementPresent("#signUpPagePasswordInput") + .verify.elementPresent("#signUpPageJoinNowButton") + }, + + "guest should be notified if password is insecure" : function (client) { + client + .clearValue("input") + .verify.elementPresent("#signUpPagePasswordInput") + .verify.cssProperty('#signUpPagePasswordInput', 'border', '1px solid gray') + .setValue("#signUpPagePasswordInput", "jan") + .verify.cssProperty('#signUpPagePasswordInput', 'border', '1px solid rgb(242, 222, 222)') + .setValue("#signUpPagePasswordInput", "icedoe123") + .verify.cssProperty('#signUpPagePasswordInput', 'border', '1px solid green') + + .verify.cssProperty('#signUpPagePasswordConfirmInput', 'border', '1px solid gray') + .setValue("#signUpPagePasswordConfirmInput", "ja") + .verify.cssProperty('#signUpPagePasswordConfirmInput', 'border', '1px solid rgb(242, 222, 222)') + .clearValue("#signUpPagePasswordConfirmInput") + .setValue("#signUpPagePasswordConfirmInput", "janicedoe123") + .verify.cssProperty('#signUpPagePasswordConfirmInput', 'border', '1px solid green') + }, + "guest should be notified if passwords do not match" : function (client) { + client + .clearValue("#signUpPagePasswordConfirmInput") + .clearValue("#signUpPagePasswordInput") + .resetEntry() + .pause(500) + .verify.cssProperty('#signUpPagePasswordInput', 'border', '1px solid gray') + .verify.cssProperty('#signUpPagePasswordConfirmInput', 'border', '1px solid gray') + .setValue("#signUpPagePasswordInput", "janicedoe123") + .verify.cssProperty('#signUpPagePasswordInput', 'border', '1px solid green') + .verify.cssProperty('#signUpPagePasswordConfirmInput', 'border', '1px solid gray') + .setValue("#signUpPagePasswordConfirmInput", "janicedoe123") + .verify.cssProperty('#signUpPagePasswordInput', 'border', '1px solid green') + .verify.cssProperty('#signUpPagePasswordConfirmInput', 'border', '1px solid green') + }, + "guest should be notified if email is not correctly formatted" : function (client) { + client + .clearValue("#signUpPageEmailInput") + .resetEntry() + .verify.elementPresent("#signUpPageEmailInput") + .verify.cssProperty('#signUpPageEmailInput', 'border', '1px solid gray') + .setValue("#signUpPageEmailInput", "janicedoe") + .verify.cssProperty('#signUpPageEmailInput', 'border', '1px solid rgb(242, 222, 222)') + .setValue("#signUpPageEmailInput", "@symptomatic.io") + .verify.cssProperty('#signUpPageEmailInput', 'border', '1px solid green') + }, + "when new user fills out form and registers, new user should get created" : function (client) { + client + .verify.elementPresent("#entrySignUp") + + .clearValue("#signUpPagePasswordConfirmInput") + .clearValue("#signUpPagePasswordInput") + .clearValue("#signUpPageFullNameInput") + .clearValue("#signUpPageEmailInput") + .resetEntry() + + .setValue("#signUpPageFullNameInput", "Janice Doe") + .setValue("#signUpPageEmailInput", "janicedoe@symptomatic.io") + .setValue("#signUpPagePasswordInput", "janicedoe123") + .setValue("#signUpPagePasswordConfirmInput", "janicedoe123") + + .click("#signUpPageJoinNowButton").pause(1000) + + .verify.containsText("#usernameLink", "janicedoe@symptomatic.io") + }, + "user should be able to signout" : function (client) { + client + .verify.elementPresent("#logoutButton") + .click("#logoutButton").pause(300) + .verify.containsText("#usernameLink", "Sign In") + }, + "user should be able to request reset password email" : function (client) { + client + .url("http://localhost:3000/entrySignIn") + .verify.elementPresent("#forgotPasswordButton") + .click("#forgotPasswordButton") + .verify.elementPresent("#forgotPassword") + .verify.elementPresent("#signInPageEmailInput") + .verify.elementPresent("#sendReminderButton") + }, + "existing user should be able to sign in on desktop" : function (client) { + client + .url("http://localhost:3000/entrySignIn") + .resizeWindow(1600, 1200) + .verify.containsText("#usernameLink", "Sign In") + .signIn("janicedoe@symptomatic.io", "janicedoe123").pause(500) + .verify.containsText("#usernameLink", "janicedoe@symptomatic.io") + .click("#logoutButton").pause(200) + .verify.containsText("#usernameLink", "Sign In") + }, + "existing user should be able to sign in on tablet" : function (client) { + client + .url("http://localhost:3000/entrySignIn") + .resizeWindow(1024, 768) + .verify.containsText("#usernameLink", "Sign In") + .signIn("janicedoe@symptomatic.io", "janicedoe123").pause(500) + .verify.containsText("#usernameLink", "janicedoe@symptomatic.io") + .click("#logoutButton").pause(200) + .verify.containsText("#usernameLink", "Sign In") + }, + "existing user should be able to sign in on phone" : function (client) { + client + .url("http://localhost:3000/entrySignIn") + .resizeWindow(320, 960) + // .verify.containsText("#usernameLink", "Sign In") + .signIn("janicedoe@symptomatic.io", "janicedoe123").pause(500) + .click("#sidebarToggle").pause(300) + .verify.containsText("#usernameLink", "janicedoe@symptomatic.io") + .click("#logoutButton").pause(200) + .verify.containsText("#usernameLink", "Sign In") + }, + "if anonymous user tries to log in with non-existing account, a message is shown" : function (client) { + client + .url("http://localhost:3000/entrySignIn") + .resizeWindow(1024, 768) + .signIn("alice@symptomatic.io", "alice123").pause(500) + .verify.containsText("#signInPageMessage", "User not found [403]") + .verify.cssProperty("#signInPageMessage", "color", "rgba(169, 68, 66, 1)") + .verify.cssProperty("#signInPageMessage", "background-color", "rgba(242, 222, 222, 1)") + .verify.cssProperty("#signInPageMessage", "border-color", "rgb(235, 204, 209)") + }, + "anonymous guest should be notified if email already exists" : function (client) { + client + .url("http://localhost:3000/entrySignUp") + .resizeWindow(1024, 768) + .signUp("janicedoe@symptomatic.io", "janicedoe123").pause(500) + .click("#signUpPageEmailInput").pause(500) + .verify.elementPresent("#signUpPageMessage") + .verify.containsText("#signUpPageMessage", "Email already exists. [403]") + }, + after: function(client){ + client + .dropEntryUsers() + .end(); + } +}; diff --git a/Packages/clinical-themes/README.md b/Packages/clinical-themes/README.md new file mode 100644 index 000000000..40d7f8c92 --- /dev/null +++ b/Packages/clinical-themes/README.md @@ -0,0 +1,15 @@ +## clinical:themes +Theme selection component for ClinicalFramework apps. + +------------------------ +### Installation + +```` +meteor add clinical:themes +```` + + +------------------------ +### License + +MIT License. Use as you wish, including for commercial purposes. diff --git a/Packages/clinical-themes/components/themeCard.html b/Packages/clinical-themes/components/themeCard.html new file mode 100644 index 000000000..e69de29bb diff --git a/Packages/clinical-themes/components/themeCard.js b/Packages/clinical-themes/components/themeCard.js new file mode 100644 index 000000000..e69de29bb diff --git a/Packages/clinical-themes/components/themeCard.less b/Packages/clinical-themes/components/themeCard.less new file mode 100644 index 000000000..e69de29bb diff --git a/Packages/clinical-themes/lib/ActiveThemes.js b/Packages/clinical-themes/lib/ActiveThemes.js new file mode 100644 index 000000000..e69de29bb diff --git a/Packages/clinical-themes/package.js b/Packages/clinical-themes/package.js new file mode 100644 index 000000000..d52a220e6 --- /dev/null +++ b/Packages/clinical-themes/package.js @@ -0,0 +1,31 @@ +Package.describe({ + name: 'clinical:themes', + version: '0.0.1', + // Brief, one-line summary of the package. + summary: 'Theme selection component for ClinicalFramework apps.', + // URL to the Git repository containing the source code for this package. + git: 'https://github.com/awatson1978/clinical-themes', + // By default, Meteor will default to using README.md for documentation. + // To avoid submitting documentation, set this field to null. + documentation: 'README.md' +}); + +Package.onUse(function (api) { + api.versionsFrom('1.2.1'); + + api.use('meteor-platform'); + api.use('less'); + + api.addFiles('lib/ActiveThemes.js'); + + api.addFiles('components/themeCard.html'); + api.addFiles('components/themeCard.js'); + api.addFiles('components/themeCard.less'); + +}); + +Package.onTest(function (api) { + api.use('tinytest'); + api.use('clinical:themes'); + api.addFiles('tests/clinical-themes.js'); +}); diff --git a/Packages/clinical-themes/tests/clinical-themes.js b/Packages/clinical-themes/tests/clinical-themes.js new file mode 100644 index 000000000..6433a646d --- /dev/null +++ b/Packages/clinical-themes/tests/clinical-themes.js @@ -0,0 +1,10 @@ +// Write your tests here! +// Here is an example. +describe('clinical:collaborations-ui', function () { + it.client('runs only in client', function () { + expect(Meteor.isClient).to.be.true; + }); + it.server('runs only in server', function () { + expect(Meteor.isServer).to.be.true; + }); +}); diff --git a/Packages/clinical-ui-vocabulary/.gitignore b/Packages/clinical-ui-vocabulary/.gitignore new file mode 100644 index 000000000..677a6fc26 --- /dev/null +++ b/Packages/clinical-ui-vocabulary/.gitignore @@ -0,0 +1 @@ +.build* diff --git a/Packages/clinical-ui-vocabulary/README.md b/Packages/clinical-ui-vocabulary/README.md new file mode 100644 index 000000000..e4cb354e5 --- /dev/null +++ b/Packages/clinical-ui-vocabulary/README.md @@ -0,0 +1,84 @@ +## clinical:ui-vocabulary + + +Package to provide semantic CSS classes to your app. Based on Bootstrap3. + + +========================= +#### Installation + +Simply nstall the clinical-ui-vocabulary package from the command line, like so: + +````js +meteor add clinical:ui-vocabulary +```` + +Then feel free to use the following CSS classes in your application. + +========================= +#### Vocabulary - Anchoring + +- fixed-layout +- left-anchored +- right-anchored +- bottom-anchored +- top-anchored +- right-aligned +- left-aligned +- absolute-layout +- fixed-layout + +========================= +#### Vocabulary - Borders + +- gray-border +- with-rounded-corners + +========================= +#### Vocabulary - Fonts + +- monospace + +========================= +#### Vocabulary - Haptics + +- clickable +- unselectable + +========================= +#### Vocabulary - Padding + +- well-behaved +- padded +- padded-horizontal, with-horizontal-padding, horizontally-padded +- padded-vertical, with-vertical-padding, vertically-padded +- with-right-padding +- with-left-padding + +- without-padding, nopadding, no-padding +- without-right-padding +- without-left-padding +- without-top-padding +- without-bottom-padding +- without-vertical-padding +- without-horizontal-padding + +========================= +#### Vocabulary - Colors + +- dark +- light +- white +- black +- gray +- lightgray +- darkgray +- transparent +- glass +- fog +- mostly-opaque +- red +- blue +- green +- maroon +- etc diff --git a/Packages/clinical-ui-vocabulary/anchoring.less b/Packages/clinical-ui-vocabulary/anchoring.less new file mode 100644 index 000000000..b4ff2ea78 --- /dev/null +++ b/Packages/clinical-ui-vocabulary/anchoring.less @@ -0,0 +1,56 @@ +//--------------------------------- +// layouts + +.absolute-layout{ + position:absolute; +} +.fixed-layout{ + position: fixed; +} + + +//--------------------------------- +// anchoring + +.left-anchored{ + position: fixed; + left: 0px; +} +.right-anchored{ + position: fixed; + right: 0px; +} +.float-right{ + position: absolute; + float: right; + right: 0px; +} + + +.bottom-anchored{ + position: fixed; + bottom: 0px; +} +.top-anchored{ + position: fixed; + top: 0px; +} + + +// alignment + +.right-aligned{ + text-align: right; +} +.left-aligned{ + text-align: left; +} + + +// layouts +.absolute-layout{ + position:absolute; +} +.fixed-layout{ + position: fixed; +} diff --git a/Packages/clinical-ui-vocabulary/borders.less b/Packages/clinical-ui-vocabulary/borders.less new file mode 100644 index 000000000..29aa957db --- /dev/null +++ b/Packages/clinical-ui-vocabulary/borders.less @@ -0,0 +1,6 @@ + +.with-rounded-corners { + -moz-border-radius: .4em; + -webkit-border-radius: .4em; + border-radius: .4em; +} diff --git a/Packages/clinical-ui-vocabulary/colors.less b/Packages/clinical-ui-vocabulary/colors.less new file mode 100644 index 000000000..2889cb5b9 --- /dev/null +++ b/Packages/clinical-ui-vocabulary/colors.less @@ -0,0 +1,66 @@ +.dark{ + color: white; + background-color: black; +} +.light{ + color: black; + background-color: white; +} + +.maroon{ + color: #6B1A2C; +} +.green{ + color: green; +} +.red{ + color: red; +} +.blue{ + color: blue; +} +.yellow{ + color: yellow; +} +.purple{ + color: purple; +} +.orange{ + color: orange; +} +.black{ + color: black; +} +.white{ + color: white; +} +.gray{ + color: gray; +} +.lightgray{ + color: lightgray; +} +.darkgray{ + color: darkgray; +} +.notepad{ + background-color: lightyellow; +} + +.transparent{ + opacity: 0.3; +} +.glass{ + opacity: 0.8; + background: lightgray; +} +.fog{ + opacity: 0.8; + background: gray; +} +.mostly-opaque{ + opacity: 0.2; +} +.mostly-transparent{ + +} diff --git a/Packages/clinical-ui-vocabulary/fonts.less b/Packages/clinical-ui-vocabulary/fonts.less new file mode 100644 index 000000000..54676b7f4 --- /dev/null +++ b/Packages/clinical-ui-vocabulary/fonts.less @@ -0,0 +1,3 @@ +.monospace{ + font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace; +} diff --git a/Packages/clinical-ui-vocabulary/haptics.less b/Packages/clinical-ui-vocabulary/haptics.less new file mode 100644 index 000000000..8eaaa9985 --- /dev/null +++ b/Packages/clinical-ui-vocabulary/haptics.less @@ -0,0 +1,13 @@ +//----------------------------------------- +// Clicking + +.clickable{ + cursor: pointer; +} +.unselectable{ + -moz-user-select: -moz-none; + -khtml-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} diff --git a/Packages/clinical-ui-vocabulary/package.js b/Packages/clinical-ui-vocabulary/package.js new file mode 100644 index 000000000..155b092a4 --- /dev/null +++ b/Packages/clinical-ui-vocabulary/package.js @@ -0,0 +1,27 @@ +Package.describe({ + summary: "UI vocabulary for ClinicalFramework.", + version: "1.0.5", + git: "http://github.com/awatson1978/clinical-ui-vocabulary.git", + name: "clinical:ui-vocabulary" +}); + +Package.onUse(function(api) { + api.versionsFrom('1.2.1'); + + api.use('ian:bootstrap-3@3.3.1'); + + api.addFiles('anchoring.less', 'client'); + api.addFiles('borders.less', 'client'); + api.addFiles('colors.less', 'client'); + api.addFiles('fonts.less', 'client'); + api.addFiles('haptics.less', 'client'); + api.addFiles('padding.less', 'client'); + api.addFiles('pages.less', 'client'); + api.addFiles('sizing.less', 'client'); + api.addFiles('text.less', 'client'); + +}); + +Package.onTest(function(api) { + api.use('tinytest'); +}); diff --git a/Packages/clinical-ui-vocabulary/padding.less b/Packages/clinical-ui-vocabulary/padding.less new file mode 100644 index 000000000..6fe5c6e96 --- /dev/null +++ b/Packages/clinical-ui-vocabulary/padding.less @@ -0,0 +1,111 @@ + +.well-behaved{ + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +//----------------------------------------- +// Padding + +.padded{ + box-sizing: border-box; + padding: 20px; +} +.padded-horizontal, .with-horizontal-padding, .horizontally-padded{ + box-sizing: border-box; + padding-left: 20px; + padding-right: 20px; +} +.padded-vertical, .with-vertical-padding, .vertically-padded{ + box-sizing: border-box; + padding-top: 20px; + padding-bottom: 20px; +} + +.with-right-padding{ + padding-right: 20px !important; +} +.with-left-padding{ + padding-left: 20px !important; +} + + +//----------------------------------------- +// Without Padding + +.without-padding, .nopadding, .no-padding{ + padding: 0px; +} +.without-right-padding{ + padding-right: 0px !important; +} +.without-left-padding{ + padding-left: 0px !important; +} +.without-top-padding{ + padding-top: 0px !important; +} +.without-bottom-padding{ + padding-bottom: 0px !important; +} +.without-vertical-padding{ + padding-top: 0px !important; + padding-bottom: 0px !important; +} +.without-vertical-padding{ + padding-left: 0px !important; + padding-right: 0px !important; +} + + + + +//----------------------------------------- +// Spacers + +.with-bottom-spacer, .with-bottom-padding{ + padding-bottom: 20px !important; +} +.with-top-spacer, .with-top-padding{ + padding-top:20px !important; +} +.with-right-spacer{ + margin-right: 5px !important; +} +.with-right-spacer{ + margin-left: 5px !important; +} + + + + +//----------------------------------------- +// Margins + +.margined, .with-margins{ + margin-left: 20px; + margin-right: 20px; + margin-top: 20px; + margin-bottom: 20px; +} +.with-bottom-margin{ + margin-bottom: 20px !important; +} +.with-top-margin{ + margin-top:20px !important; +} +.with-right-margin{ + margin-right: 20px !important; +} +.with-left-margin{ + margin-left:20px !important; +} + + +//----------------------------------------- +// Margins + +.spacer{ + height: 20px; +} diff --git a/Packages/clinical-ui-vocabulary/pages.less b/Packages/clinical-ui-vocabulary/pages.less new file mode 100644 index 000000000..d6acb2a9e --- /dev/null +++ b/Packages/clinical-ui-vocabulary/pages.less @@ -0,0 +1,4 @@ +.page{ + padding-top: 50px; + padding-bottom: 50px; +} diff --git a/Packages/clinical-ui-vocabulary/sizing.less b/Packages/clinical-ui-vocabulary/sizing.less new file mode 100644 index 000000000..7e78b0bae --- /dev/null +++ b/Packages/clinical-ui-vocabulary/sizing.less @@ -0,0 +1,10 @@ + +//----------------------------------------- +// Sizing + +.fullwidth{ + width: 100%; +} +.fullheight{ + height: 100%; +} diff --git a/Packages/clinical-ui-vocabulary/text.less b/Packages/clinical-ui-vocabulary/text.less new file mode 100644 index 000000000..0e373c15b --- /dev/null +++ b/Packages/clinical-ui-vocabulary/text.less @@ -0,0 +1,36 @@ +//----------------------------------------- +// Text and Fonts + +.bold{ + font-weight: bold; +} +.centered{ + text-align: center; +} +.strikeout{ + text-decoration: line-through; + color: lightgray !important; +} +.dotted{ + border-style: dotted; + border-width: 2px; + border-color: lightgray; +} +.strikeout{ + text-decoration: line-through; + color: lightgray !important; +} + + +.hidden{ + visibility: hidden; + display: none; +} +.visible{ + visibility: visible; + display: block; +} + +.largish{ + font-size: 32px; +} diff --git a/Packages/clinical-ui-vocabulary/versions.json b/Packages/clinical-ui-vocabulary/versions.json new file mode 100644 index 000000000..2d91fc10d --- /dev/null +++ b/Packages/clinical-ui-vocabulary/versions.json @@ -0,0 +1,27 @@ +{ + "dependencies": [ + [ + "ian:bootstrap-3", + "3.3.1" + ], + [ + "jquery", + "1.0.1" + ], + [ + "less", + "1.0.11" + ], + [ + "meteor", + "1.1.3" + ], + [ + "underscore", + "1.0.1" + ] + ], + "pluginDependencies": [], + "toolVersion": "meteor-tool@1.0.35", + "format": "1.0" +} \ No newline at end of file diff --git a/Packages/lesiontracker/package.js b/Packages/lesiontracker/package.js index 5bafd81d9..19aadc5e0 100644 --- a/Packages/lesiontracker/package.js +++ b/Packages/lesiontracker/package.js @@ -100,7 +100,7 @@ Package.onUse(function(api) { api.addFiles('lib/deactivateAllToolData.js', 'client'); api.addFiles('lib/clearTools.js', 'client'); - // Export gloabal functions + // Export global functions api.export('activateLesion','client'); api.export('activateMeasurements','client'); api.export('deactivateAllToolData','client');