The latest version of the Artstor Image Workspace interface, aiming to make browsing the Artstor collections a modern and refreshing experience.
If you are a new developer to the project, please read through the entire README. You won't regret it.
This repository is open sourced by Ithaka as part of our initiative to increase transparency and contribute to the community. This particular repository is a working repository not intended as software available for consumption.
Learn more about our open source initiative
Copyright 2018 Ithaka Harbors, Inc.
- Project Stack
- Quick Start
- File Structure
- Getting Started
- Configuration
- Environment Variables
- Server-Side Rendering
- WLVs Local Setup
- Styles
- TypeScript
- Accessibility
- @Types
- Frequently asked questions
Project References: Avatar Jira Project
(Originally based on the Angular 2 webpack starter)
Core Application
- Angular 2 (Router, Forms, Http, Services, Tests
- TypeScript
- @types (type manager)
- Bootstrap 4 and ngBootstrap
- Sass (plus Bourbon)
- Webpack 2
- Karma
- Protractor (for end-to-end tests)
- Jasmine (unit tests)
- TsLint
- Codelyzer
- Nucleus (style guide generator)
Requires latest version of Node and Yarn
On Mac, we recommended installing Node, NPM, and Yarn with Homebrew
Instructions for Mac:
# clone the project
git clone https://github.com/ithaka/aiw-ui.git
# change directory to our repo
cd aiw-ui
# install node, npm, and yarn with Homebrew
brew install node yarn
# Install global build packages
yarn global add typescript rimraf
# install the repo with npm
yarn install
# start the server
yarn start
Access at http://localhost:3000 in your browser.
Component approach! The goal is flat and modular. This is the new standard for developing Angular apps and a great way to ensure maintainable code by encapsulation of our behavior logic. A component is basically a self contained app usually in a single file or a folder with each concern as a file: style, template, specs, e2e, and component class. Here's how it looks:
aiw-ui/
├──config/ * our configuration
| ├──helpers.js * helper functions for our configuration files
| ├──spec-bundle.js * ignore this magic that sets up our angular 2+ testing environment
| ├──karma.conf.js * karma config for our unit tests
│
├──src/ * our application source files
| ├──main.ts * our entry file for our browser environment
│ │
│ ├──environments/ * runtime environment variables (integrated via angular.json replacements)
│ │
| ├──index.html * Index.html: where we generate our index page
│ │
| ├──polyfills.ts * our polyfills file
│ │
| ├──vendor.ts * our vendor file
│ │
│ ├──app/ * WebApp: folder (holds components)
│ │ ├──app.spec.ts * a simple test of components in app.ts
│ │ ├──app.e2e.ts * a simple end-to-end test for /
│ │ └──app.ts * App.ts: a simple version of our App component
| | └──white-label-config * Options for WLVs, white label sites
│ │
│ ├──sass/ * Sass style folder
│ │ ├──core/ * Core sass utilities, variables, and reset
│ │ ├──libraries/ * Sass files from other resources
│ │ ├──modules/ * Elements for project-wide use and inheritance
│ │ └──app.scss * App.ts: a simple version of our App component
│ │
│ └──assets/ * static assets are served here
│ ├──icon/ * our list of icons from www.favicon-generator.org
│ ├──service-worker.js * ignore this. Web App service worker that's not complete yet
│ └──humans.txt * for humans to know who the developers are
│
├──src/ * our application source files
| ├──server.ts * Node.js/SSR server app entry point
│ └──package.json * used to list packages that should be installed on SSR Docker image
│
├──angular.json * Angular configuration file
├──Dockerfile * Node.js server Dockerfile configuration
├──deploy-ssr.sh * Script for triggering deployment on Sagoku (used in deploy commands)
├──docker-build-image.sh * Script for building docker image and uploading for use
├──tslint.json * typescript lint config
├──typedoc.json * typescript documentation generator
├──tsconfig.json * config that webpack uses for typescript
├──package.json * what npm uses to manage it's dependencies
├──webpack.server.config.js * config for Node.js app bundling
└──webpack.config.js * webpack main configuration file
See Quick Start
After you have installed all dependencies you can now run the app. Run yarn dev
or yarn dev:ssl
to start a local server using Angular CLI which will watch, build (in-memory), and reload for you. The port will be displayed to you as http://localhost:3000
.
# development
yarn run dev
yarn run dev:ssl
# production
yarn run build:prod
# development
yarn run build
# production
yarn run build:prod
# development
yarn run build:ssr
# production
yarn run build:ssr:prod
# development
yarn run serve:ssr
# production
yarn run serve:ssr:start
npm run test
npm run watch:test
NPM and Webpack combine to provide all of our task running needs.
Configuration files live in config/
for webpack, karma, and protractor.
We manager our environment variables at runtime using Angular CLI and simple files in /src/environments
. Build/deployment variables can be handled in a .env
file that is not committed to the repository.
To deploy our server-side rendering docker web app, first commit all changeds. Then, run:
yarn docker:build
yarn deploy:ssr:test
For testing the docker image locally:
yarn docker:build
docker run -p 49161:80 -d artifactory.acorn.cirrostratus.org/artstor-air-node:BUILD-HASH
And access via localhost:49161
WLVs, such as SAHARA, use the hostname to determine different configuration options.
For local development on Mac, you'll need to edit your /etc/hosts file:
sudo nano /etc/hosts
And add:
127.0.0.1 local.artstor.org
127.0.0.1 local.stage.artstor.org
127.0.0.1 local.sahara.artstor.org
127.0.0.1 local.sahara.test.artstor.org
Then clear your dns cache with:
sudo dscacheutil -flushcache;sudo killall -HUP mDNSResponder
Now you're all setup! Just run:
yarn run sahara
You should now be able to open the site locally as: local.sahara.test.artstor.org:3000
From the /ssl
directory under project root, execute the following steps:
Generate private key:
openssl genrsa -out private.key 4096
Generate a Certificate Signing Request:
openssl req -new -sha256 -out private.csr -key private.key -config ssl.conf
Generate the certificate:
openssl x509 -req -days 3650 -in private.csr -signkey private.key -out private.crt -extensions req_ext -extfile ssl.conf
Add the certificate to keychain and always trust it:
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain private.crt
Create a pem file:
openssl x509 -in private.crt -out private.pem -outform PEM
Finally, from the project root run yarn dev:ssl
to start local development on https at https://localhost:3000
All of our styles are written in Sass, and should be placed in one of three places based on our file structure:
├──src/
│ ├──sass/ * Sass folder
│ │ ├──core/ * Core sass utilities, variables, and reset
│ │ │ ├──_helpers.scss * Core sass utilities, variables, and reset
│ │ │ ├──_layout.scss * Organizational classes for page layout
│ │ │ ├──_reset.scss * Base style resets
│ │ │ ├──_typography.scss * Font imports and all type styles used
│ │ │ └──_variables.scss * Project-wide values such as colors and sizes
│ │ ├──libraries/ * Sass files from other resources
│ │ │ └──bourbon/ * Library of mixins
│ │ ├──modules/ * Elements for project-wide use and inheritance
│ │ │ ├──_base.scss * Base html element classes
│ │ │ ├──_buttons.scss * Button element classes
│ │ │ └──_forms.scss * Input element classes
│ │ └──app.scss * Imports Sass partials, and has style Inbox
│ ├──app/ * App root folder that holds components
│ │ ├──component/ * Core sass utilities, variables, and reset
│ │ │ └──component.scss * Styles scoped/unique to individual component
Those three flavors of styles are:
- Pending/New styles
- Go in "Inbox" section of
/sass/app.scss
- Go in "Inbox" section of
- Component specific styles
- Go in appropriate
component.scss
file inside component folder
- Go in appropriate
- Generalized styles
- Go in an appropriately named Sass file inside
/sass/modules
- Go in an appropriately named Sass file inside
When writing styles:
- Reference the Style Guide Check existing styles in the style guide, which can be generated by running
npm run styleguide
and opened from the/styleguide
folder. - Use Bootstrap Elements We don't need to write everything from scratch! When possible start off with a Bootstrap component or style (Bootstrap 4 and ngBootstrap) and simply tweak the styles to your liking.
- Use BEM Naming BEM makes our style names logical, consistent, and easy to interpret by other developers. The ✨magic formula✨ is
.block__element--modifier
which might look like.button__icon--large
. Don't fear the double dashes and double underscores! - Check out Bourbon And find other ways to make writing Sass easier! Bourbon is a super useful library of mixins that makes things such as font includes and animations much easier and cleaner.
- Add in-line documentation for the style guide If it is a new style, or new modifier to a style, please add documentation so the style will be added to the style guide.
export class ExampleComponent {
// Ugly
term;
// Scoped!
private term;
// Typed!
private term: string;
...
}
specialAlgorithm(): number {
// Declare here
let a: number = 2;
a = a * 2;
// Don't declare here!
let b: number = 5;
return a + b;
}
// Vague!
specialAlgorithm() {
let a: number = 10;
return a;
}
// Great!
specialAlgorithm(): number {
let a: number = 10;
return a;
}
/**
* Special Algorithm's purpose is to give us an even number
*/
specialAlgorithm(): number {
...
}
/**
* Special Algorithm's purpose is to give us an even number
*/
specialAlgorithm(input: number): number {
return 2 * input;
}
To take full advantage of TypeScript with autocomplete you would have to install it globally and use an editor with the correct TypeScript plugins.
TypeScript 1.7.x includes everything you need. Make sure to upgrade, even if you installed TypeScript previously.
npm install --global typescript
TypeScript-aware editors:
- Visual Studio Code (Recommended)
- Webstorm 10
- Atom with TypeScript plugin
- Sublime Text with Typescript-Sublime-Plugin
Improving accessibilty makes our site accessible to those who are disabled and to those who are temporarily unable to see well, hear well, or use both of their hands. These are some simple things we can do to make a big difference.
<button class="close" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
- Note the
aria-hidden
attribute, telling screenreaders to not read the contained text
<img src="/logo.png" alt="Artstor" />
<i class="icon icon-loading" alt="Loading"></i>
To assist in keyboard navigation, add tabindex
attributes in an appropriate order.
For example, the login form might be tabbed first:
<label for="inputEmail">Email</label>
<input tabindex="1" type="text" class="form-control" id="inputEmail" placeholder="Password">
<label for="inputPassword">Password</label>
<input tabindex="1" type="password" class="form-control" id="inputPassword" placeholder="Password">
So each input element is given a tabindex
of 1
, effectively grouping them together. The main navigation might be next in the tabbing sequence:
<nav>
<a tabindex="2" class="nav-link" href="#">Link</a>
<a tabindex="2" class="nav-link" href="#">Link</a>
<a tabindex="2" class="nav-link" href="#">Link</a>
</nav>
Note that tabindex='0'
is effectively the default value, and adding a tabindex
of 0
will put that element after any elements with a tabindex
greater than 0
. And lastly, assigning a negative tabindex
will remove the element from the tabbing sequence entirely!
Elements should not rely solely on color to indicate they are focused or have changed state, leaving a color blind user clueless. Providing an additional indicator also makes interactions more obvious to other users.
Bonus If you want to remove the outline of a :focus
style, replace it with something equally clear to the user! :focus
is how a user can see where they are when they use keyboard navigation.
And not for styles! H1
, H2
, H3
... are interpretted by screenreaders as indicating sections within a document. Now it's okay to style those tags, but in this project we use .h1
, .h2
, .h3
... when we want the style but element is not a true heading or subheading.
We should always be considering visual clarity, hierarchy, and flow of information in this context. With a little bit of extra thought each time we implement a new feature or add content, the quality of our alt text, keyboard navigation, and ultimately the user experience, will improve 🎉
When you include a module that doesn't include Type Definitions inside of the module you can include external Type Definitions with @types
i.e, to have youtube api support, run this command in terminal:
npm i @types/youtube @types/gapi @types/gapi.youtube
In some cases where your code editor doesn't support Typescript 2 yet or these types weren't listed in tsconfig.json
, add these to "src/custom-typings.d.ts" to make peace with the compile check:
import '@types/gapi.youtube';
import '@types/gapi';
import '@types/youtube';
When including 3rd party modules you also need to include the type definition for the module if they don't provide one within the module. You can try to install it with @types
npm install @types/node
npm install @types/lodash
If you can't find the type definition in the registry we can make an ambient definition in this file for now. For example
declare module "my-module" {
export function doesSomething(value: string): string;
}
If you're prototyping and you will fix the types later you can also declare it as type any
declare var assert: any;
declare var _: any;
declare var $: any;
- Webpack says it can't find a module or loader?
- We use the cutting-edge version of Webpack. The first thing to verify is that you are running the latest version of Node.
- Where do I write my tests?
- You can write your tests next to your component files. See
/src/app/home/home.spec.ts
- You can write your tests next to your component files. See
- How do I start the app when I get
EACCES
andEADDRINUSE
errors?- The
EADDRINUSE
error means the port3000
is currently being used andEACCES
is lack of permission for webpack to build files to./dist/
- The
- node-pre-gyp ERR in npm install (Windows)