10.49.1 • Published 1 year ago

shopstyle-collective v10.49.1

Weekly downloads
-
License
-
Repository
github
Last release
1 year ago

Setup

Get the code

Fork the repo https://help.github.com/articles/fork-a-repo

Clone the repo and set up your remote fork

$ git clone git@github.rakops.com:ShopStyle/shopstyle-collective.git
$ cd shopstyle-collective
$ git remote add mine git@github.rakops.com:<GIT_USERNAME>/shopstyle-collective.git

If you get an error when you clone the repo, be sure that you have a SSH key added to your GitHub account or create a new one (https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/).

Install global dependencies

ShopStyle has various projects relying on various versions of node. The easiest way to deal with switching node versions is to use nvm. Follow the directions on the nvm gihub page for installation. ShopStyle Collective relies on node 16.17.0, so make sure to install and use that particular version to run this project.

Then, run these commands:

$ gem install sass -v 3.3.1

Install project dependencies

# If using nvm, run this to use the correct version of node
$ nvm use
$ npm install

Configuring your IDE

Prettier is used to format the code on pre-commit. If you would like to have the same configuration set on your editor, please follow these instructions. Then, you will need to update the settings on your editor to match the prettier settings in package.json and enable "Format On Save".

Configuring your /etc/hosts

Add the following entries to your /etc/hosts

127.0.0.1 app.collectivevoicelocal.com
127.0.0.1 connect.collectivevoicelocal.com
127.0.0.1 localwidgettest.shopstyleqa.com

Setup https and private keys

To run https locally, you will need to set up a self signed ssl cert. From your shopstyle-collective-frontend directory, create a host.key and host.cert

$ openssl genrsa -out host.key 1024
$ openssl req -new -key host.key -out host.csr
$ openssl x509 -req -in host.csr -signkey host.key -out host.cert
$ rm host.csr

Development Flow

Build the web app

Development mode (https & http)

To watch for any changes you make to the files, run the following command:

$ npm run watch

This will automatically open the browser on https://app.collectivevoice.com:5443/landing (collective app). If you are working on the connect app, go to https://connect.collectivevoice.com:5443.

If you need to run the website on http, please use:

$ npm run watch:http

Production mode

You do not need to use this command to develop but if you want to build a version the same way it is done for production:

$ npm run build

Testing

To run the unit tests locally, use the command:

$ npm run test

The unit tests will run when creating a PR (part of the Jenkinsfile checks) with the command npm run test:jenkins.

Creating a Pull Request

Find the full guide here (Collective wiki)

Deployment

We have three branches - master, qa, and development. Master is the code that is currently deployed to the production environment, or code that could be deployed to prod at any time. QA is code we are testing - the release canidate. Development is where most changes should be merged. It is the most active branch.

The typical release cycle is to merge development into QA, deploy to test.shopstylecollective.com, and then test. If there are any bugs for that build, send them to QA. Once we are satisfied with the build, then we merge QA into master, and deploy master to prod. After that deployment we merge master back into development to make sure any changes we made during testing are merged in upstream. A hotfix change should get merged to master directly, then master can be deployed straight to prod.

More details about deployment and build captain duties here: FE Collective Build Captain Duties

Using the BRASS interface

The Jenkins job brass-quickactions-ssc is a convenient interface to perform the regular actions we perform throughout the weekly build and release lifecycle with single-click actions.

Weekly cutoff

To perform the weekly cutoff, build with these parameters:

  • ACTION: Weekly cutoff (dev -> qa)
  • FIX_VERSION: Determine the next using the release train

Please note that as part of the cutoff automation, the latest realtime microdata scraper (RTS) is also cut off. Any new commits on the scraper's master branch will be published and reflected on SSC QA.

QA builds

To perform regular QA builds after merging a PR to QA, build with these parameters:

  • ACTION: QA build & deploy
  • FIX_VERSION: Determine the next using the release train

Deploy the build on QA to prod

Once the build on the QA branch has been tested and approved for release by the QA team, we can use the BRASS interface to deploy to prod using these steps:

  1. Run the brass-quickactions-ssc job once with these parameters to merge and wait for it to complete:
    1. ACTION: Weekly cutoff (dev -> qa)
  2. Run the brass-quickactions-ssc job once with these parameters to deploy to prod:
    1. ACTION: Prod deploy (minor) or Prod deploy (major)
      1. Note: minor is typical
    2. FIX_VERSION: Determine the next using the release train

This will automatically create a new entry in the release train. Visually inspect it and manually populate any missing information, if needed.

Deploy a hotfix to prod

The procedure for deploying a hotfix to prod is the same as a regular release for QA, except since fixes are presumed to be merged directly to master, we do not run a merge prior to the release.

Prior to a hotfix, determine the next hotfix fix version using the release train and ensure that has been manually added to the HF ticket. Next, test the hotfix by staging the master branch on QA with brass-quickactions-ssc and the following parameters:

  1. ACTION: Stage master on QA
  2. FIX_VERSION: (whatever was added to the ticket)

Once the fix has been validated on QA, we're ready to deploy to prod. Run the brass-quickactions-ssc job once with these parameters to deploy to prod:

  1. ACTION: Prod deploy (patch)
  2. FIX_VERSION: (whatever was added to the ticket)

Rolling back to a stable version

In an emergency, we have the ability to manually roll back to a previously-released version. For now, this should be done using the SS_Collective-Web_Deploy-New-alacarte job. The ENVIRONMENT should be prod, and the RELEASE_TAG should be whatever the last stable release's tag was per the release train. FIX_VERSION and ACTION do not matter.

Deployment: RTS

When the Real Time Scraper needs to be deployed, the Collective app needs to explicitly pick up the latest version of the RTS. Here are all the steps necessary to deploy the RTS:

  • Publish a new version of the RTS.
  • Take note of the version number you have just published. If you can't find it, you can also take a look at the release page of the RTS where you can find all the versions that have been published.
  • Update the version that is picked up by Collective by running the commands below.
nvm use
npm install @ssjs/shopstyle-microdata-scraper@1.49.0 --save --save-exact # where 1.49.0 is the latest version
npm shrinkwrap

Notes: Make sure the last command you run does update the version number on npm-shrinkwrap.json. By having this shrinkwrap file, package.json is ignored during the Jenkins build and therefore, the new version would not be picked up.

Coding Guidelines

Deprecated

We are hoping to upgrade this web app to Angular (from AngularJS) soon. In order to get ready for this migration, some important changes had been made that deprecated some of the code you will find in this repo.

Ng-include

ng-include has been removed from Angular since it had some security and performance issues. If you need to create a new template for your view or modal, please create a new component.

Components

Components have been introduced in AngularJS 1.5 and substitute the binding of ctrl/view directly in the router config or directives. This is the way any ctrl/view should be added.

Types of components and examples

In our project, we have two types of components:

  • Components that are considered a full page view, such as the landing page, and are bound to a specific URL in the router config (example). There is no reason to re-use them since they have the full structure of a page. They are located in /modules, grouped by categories (account, analytics, etc...). Full page "unsubscribe" module: /modules/unsubscribe
  • Component with parameters (previously called "directives"), that are made to be re-used. They do not usually contains a full page structure but contains an HTML snippet (for a button, form, etc...) and they are located in /components Re-usable tooltip component: /components/click-tooltip

Migration of old controllers/directives to components

The first step consist in converting a controller (or link for directives) to a class and use the property controllerAs. This property can be added first to the directive declaration/router config (below the property controller) before converting fully the element to a component.

The value of controllerAs, usually ctrl, needs to be added to the view in front of each variable: ctrl.myVariable.

If the directive is added to a view with a <div> or other common HTML tags, this should be updated to the name of the component <my-new-component>.

Here is an example of the changes required to convert a directive to a component: click-tooltip PR. Please note that you will also have to add a 'ngInject'; in between the constructor and injections for minification purposes as added in the final code here

Modals

Our modal service offers now the possibility of opening any component as a modal, with two data binding types supported: one-way (<) and callbacks (&).

// my-custom-modal component config
export default {
  template: require('./my-custom-modal.tpl.html'),
  controller: MyCustomModalCtrl,
  controllerAs: 'ctrl',
  bindings: {
    myProperty: '<',
    myCallback: '&'
  }
};

// controller opening my-custom-modal component as a modal
this.ModalService.openComponent(
  'my-custom-modal',
  {
    myProperty: 'first property',
    callbacks: {
      myCallback: () => this.onMyCustomModalCallback()
    }
  },
  this.onMyCustomModalClose()
);

When possible, it is strongly recommended to use one of our predefined modal template, such as ModalService.openInfo(), in order to keep consistency with the user experience across the web app. More information on how to use each of these methods in modal.service.js.

Avoiding binding this

With the conversion to components and the way our callback were configured beforehand, bound with '=', we can easily run into context issues that requires: this.myFunction = this.myFunction.bind(this);. To avoid using .bind(this), and to keep the original context of this, the callback parameter needs to have the binding type '&', and the function passed as an ng-click, callback-param="myFunction()".

Here is an example of a component using a callback implementation with '&': multi-image-select.js

Scoped CSS

If the CSS of a component is not scoped, there is a risk of overriding CSS rules that apply to other components. Most of the new front-end frameworks have a built-in feature to scope CSS but this is not the case for AngularJS. Fortunately, the CSS can be scoped easily: a component is added to a page through a HTML tag so we can use this tag as a selector. Please, always scope the CSS when creating a new component.

// multi-image-select.scss (for a component named "multi-image-select")
multi-image-select {
  // all the component's css goes here
}

Svg Icons

The icons used on this project come from the harmony-core library.

Using an icon

You can use these icons directly from a SCSS or HTML file if needed but we recommend adding them with the component svg-icon coming from harmony-angularjs:

<svg-icon src="arrow-drop-down"></svg-icon>

Styling an icon

Implementing an icon with the component svg-icon adds some predefined style to the icon, and a class name in order to select it. If you implement the arrow-drop-down icon, you can select the icon with the class name svg-arrow-drop-down and change its properties as follows:

.svg-arrow-drop-down {
  /* added by default
    display: inline-block;
    fill: $black;
    transition: fill $transition-fast;
  */
  @include size-svg(20px); // update size (moving away from this and using the density param instead)

  svg {
    stroke: $black; // update icon stroke color
    margin-top: $u1; // update position

    path {
      fill: $black; //to fill within the stoke of the icon, not just the borders
    }
  }
}

Note that to modify the size of the icon you can set a density param as follows:

<svg-icon density="sm" src="arrow-drop-down"></svg-icon>

You can see all the different density sizes in the harmony-angularjs folder svg-icon.scss

Harmony Styles

In order to have a consistent design on our website, we use Harmony:

The most important elements are listed below but there are plenty of other useful class names and mixins that can be found on these files such as h-btn--primary, truncate-text and arrow(up) to mention a few.

Colors

Any color present on the website is one of the colors coming from harmony-core colors.scss file.

  • If the design you are implementing has an hexadecimal code that does not match one of these colors but is very close, please use one of the variables above.
  • If you need to use a color from an external brand, please add it to the brand colors section.

Notes: do not use the filter colors as they are not part of our branding. They are only used on the search page for filtering by colors.

Spacing unit

We have a base unit of 8px defined in variables.scss, that helps us having consistent margin. Unless the positioning is critical (such as centering an element), please try to always use this unit when defining a margin, padding, position. If the final design you are implementing has only a couple pixels difference from this unit, stick to the unit. Using this unit for width and height is also nice to have but not mandatory. You can also use the unit for font-size, line-height, etc... but this is not required.

Breakpoints

The breakpoints used for responsive design are defined in variables.scss. The design is usually implemented as a desktop version first and have rules for other screen sizes, from bigger to smaller, as follows:

.my-element {
  // properties here for desktop

  @include screen-md {
    // properties for medium screen
  }

  @include screen-sm {
    // properties for small screen
  }
}

It is usually recommended to add this property to each of the elements that need to be responsive as shown above. If the layout has large modifications in between desktop and mobile, you can also add these mixins at the bottom of the file and style your elements inside:

@include screen-md {
  .my-element {
    // properties for medium screen for my-element
  }
  .other-element {
    // properties for medium screen for other-element
  }
}

Font style

The default font on the website is GT America. Browsers do not always recognize or apply correctly the strength to a font so it is NOT recommended to use font-weight: bold;. Instead, please use the mixin h-body--strong.

The font styles used on a website is usually repetitive so in order to achieve consistency and avoid duplicates, there are some defined styles that can be found in the typography section of mixins.scss . The available styles goes from h-headline--1 to h-headline--4; h-caption--md, h-body--md... The design to implement might have the style name set explicitly. If it is not the case, consult Harmony leaders.

Note that we also have the font size and font strength defined in their own mixins to make the code more readable.

  • Sizes can go from xs to xl (e.g.: h-button-xs, h-body--xl)
  • Strength are h-body--strong and h-text--muted

Elevation

Elevation... what? This is the concept used by Material Design of having a "relative distance between two surfaces along the z-axis" (different layers of elements on the page). If you have never heard of elevations, take a look at material documention, the first animation summarizes it all. We have currently 4 types of elevations, that can be found in mixins.scss.

  • elevation-0 only applies a border to the element. This means that the element is on a layer right on top of the others -- at a distance 0. It is used for buttons, headers, anything that needs some separation and does not need to stand out.
  • elevation-1 to elevation-3 applies a box-shadow to the element. This is used for the different kind of modals present on the website, or other elements that need to be at a certain distance from our base layer such as the mobile menu.

S3 Configs

To make changing config values and other non-code dependent changes we have files in an s3 bucket: https://console.aws.amazon.com/s3/buckets/spimobile/collective/config/production/v4/. These are split up by locale (e.g. http://spimobile.s3.amazonaws.com/collective/config/production/v4/en_US/appConfig.json). The local versions of these are located in the ShopStyle-App repository. If you need to make a change to them, edit the local file, send a PR, once approved and merged a script is run to deploy the changes to S3.

Optimizely

When developing features, we often gate them using opitimizely (https://app.optimizely.com/). In addition to adding the gate in the Optimizely dashboard, you will also need to add code to our config file (prod.config.js).

optimizely: {
  newFeatureToGate: {
    name: 'new_feature_to_gate', // will match feature key in optimizely
    localOverride: null,
    useVisitorId: true
  },
}

The localOverride param is used to bypass calling the Optimizely api and manually enables or disables the feature for testing. If it is set to null, the Optimizely api will be called to determine if the feature is enabled.

The useVisitorId param is set to true if you have a test where you want to have the visitor id used throughout the duration of the test.

** When conducting an A/B test, the same id must be used throughout the duration of the test (cannot change after logging in or Optimizely will not track conversion events correctly). Use this parameter when you are starting a test from a logged out state and continue to track events when the user is logged in.

Translations

The update of the translation files is automated through the deployment process. However, if you need to update the translation files manually, please follow the instructions below.

Extracting translations

Used to extract, sort and add the translation keys from HTML and JS files to en.json. Integrated in the workflow as follows:

  • Creating a PR: command will run to make sure there is no error in the translations (part of the Jenkinsfile checks)
  • Merging dev into QA: command will run then commit the results to QA
make extract-translations

Update translations

Used to copy the source (en.json) file to localized files (e.g. en_US.json), formerly used to pull files from translation platform, then convert american to british spelling. Command will run when building the QA branch.

make update-translations