Skip to content
/ aiw-ui Public

Artstor Image Workplace: The Artstor Library frontend.

License

Notifications You must be signed in to change notification settings

ithaka/aiw-ui

Repository files navigation

Artstor Image Workspace

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.

Ithaka Open Source Initiative

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.

Table of Contents

Project References: Avatar Jira Project


Project Stack

(Originally based on the Angular 2 webpack starter)

Core Application

Dev Tools and Testing


Quick Start

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.


File Structure

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


Getting Started

Dependencies and Installing

See Quick Start

Running the app

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.

developing the static app

# development
yarn run dev
yarn run dev:ssl
# production
yarn run build:prod

build files

# development
yarn run build
# production
yarn run build:prod

build Node.js/SSR app

# development
yarn run build:ssr
# production
yarn run build:ssr:prod

run Node.js/SSR app

# development
yarn run serve:ssr
# production
yarn run serve:ssr:start

run unit tests

npm run test

watch and run our tests

npm run watch:test

Configuration

NPM and Webpack combine to provide all of our task running needs.

Configuration files live in config/ for webpack, karma, and protractor.


Environment Variables

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.


Server-Side Rendering

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


White Label Verticals

Developing WLVs locally.

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


Local development on Https

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


Styles

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
  • Component specific styles
    • Go in appropriate component.scss file inside component folder
  • Generalized styles
    • Go in an appropriately named Sass file inside /sass/modules

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.

TypeScript

Quality Rules

1. Variable and function should be scoped as private or public

export class ExampleComponent {
  // Ugly
  term;
  
  // Scoped!
  private term;
  
  // Typed!
  private term: string;
  
  ...
}

2. Variable should be typed and declared at the top of its relevant scope

specialAlgorithm(): number {
  // Declare here
  let a: number = 2;
  
  a = a * 2;
  
  // Don't declare here!
  let b: number = 5;
  
  return a + b;
}

3. Function should have a return type! And ‘void’ if it returns nothing!

// Vague!
specialAlgorithm() {
  let a: number = 10;
  return a;
}

// Great!
specialAlgorithm(): number {
  let a: number = 10;
  return a;
}

4. Function should, at minimum, have a description of its purpose in an inline block before it.

/**
 * Special Algorithm's purpose is to give us an even number
 */
specialAlgorithm(): number {
  ...
}

5. Function’s parameters should be typed

/**
 * 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.

Use latest TypeScript compiler

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:


Accessibility

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.

Add labels to buttons

<button class="close" aria-label="Close">
  <span aria-hidden="true">&times;</span>
</button>
  • Note the aria-hidden attribute, telling screenreaders to not read the contained text

Add alt text to images and icons

<img src="/logo.png" alt="Artstor" />
<i class="icon icon-loading" alt="Loading"></i>

Enable tabbing flow

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!

Focus and state styles

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.

Heading tags are for... Headings?

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.

Human centered design

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 🎉


Types

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';

Custom Type Definitions

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;

Frequently asked questions

  • 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?
  • How do I start the app when I get EACCES and EADDRINUSE errors?
    • The EADDRINUSE error means the port 3000 is currently being used and EACCES is lack of permission for webpack to build files to ./dist/
  • node-pre-gyp ERR in npm install (Windows)