import { Meta } from "@storybook/addon-docs/blocks";

<Meta title="Contributing Guide/Guide/Code Guidelines" />

<style>{`
  a {
    color: #5ECE7B !important;
  }

  .alert {
    background: #efeded;
    padding: 1rem;
    margin: 0.5rem 0 1rem;
  }

  .tip {
    border-left: 5px solid #5ECE7B;
  }

	.warning {
		border-left: 5px solid #ffc107;
		background-color: #fff4d5;
	}

  .danger {
    border-left: 5px solid #c00;
		background-color: #ffe6e6;
  }

  .danger * {
    color: #4d0000;
  }
`}</style>

# Coding guidelines

Below you can find rules that needs to be applied into every component from Storefront UI to ensure that they are highly customizable, follow common format and coding standards.

## Coding conventions

The good news is that for basic coding, we have `prettier` run automatically on each commit made. Besides we do have `eslint` check enabled, so you can know exactly where you code doesn't fit our conventions.

In general, this is an open source project, hence:

- Readability is extremely important. Please consider the next random developer who will read your code, and keep it simple for them.
- No complex logic in `template` view. If there is, please use `computed` properties.

## Design patterns

We use [**atomic design**](http://bradfrost.com/blog/post/atomic-web-design/) concept for grouping components (**without** templates and pages).

![Atomic design](https://res.cloudinary.com/mayashavin/image/upload/h_150/v1567776459/StorefrontUI/atomic-design.png)

In general, there are three groups of components:

- **Atoms** - Atoms are **the most basic and abstract** components for building other components, such as color palletes, icons, button, input, etc.
- **Molecules** - Molecules are components built from **groups of atoms bonded together**, take on their own properties and serve as the backbone of the design systems. Some good examples are a banner, gallery, menu item, etc.
- **Organisms** - Organisms are complex and distinct components built from **groups of molecules** joined together, such as header, grid, accordions, etc.

<div class="alert tip">
  <h5>IDENTIFY COMPONENT GROUP</h5>
  <p>
    Identifying component group is not easy, hence we save you the trouble. In
    the component request issue, the group directory of the component will be
    mentioned as <b>part of the requirements</b>.
  </p>
</div>

## Component rules

### Naming convention

We follow **Pascal Case** for component naming and with a **prefix** `Sf` - hence the naming formula will be:

```
Sf<ComponentName>
```

### Component's file structure

1. Each component should have the same files structures. Each file should be named in the format `Sf<ComponentName>.<extension>`:

```
./<ComponentName>/
|__Sf<ComponentName>.vue
|__Sf<ComponentName>.md
|__Sf<ComponentName>.spec.js
|__Sf<ComponentName>.stories.js
```

- `vue` - template markup and component instance logic, in JavaScript.
- `md` - docs for component,
- `spec.js` - unit test for component, in Java Script.
- `stories.js` - [Storybook stories](https://storybook.js.org/) for the component.

<div class="alert warning">

For the purpose of sharing our component design between projects, all the **`.scss` files** of the components will not reside within each component's directory, but **will be in `shared/styles/components/`**. Naming convention is the **same** as above.

</div>

### CSS Rules

#### CSS Naming convention

We **strictly** follow [BEMs](http://getbem.com/introduction/) for naming SCSS modifiers and CSS classes. And the CSS class names should **match** `slot`'s names.

```css
<component>__<block>--modifier
```

For example: `.gallery__item--selected`

<div class="alert warning">

Keep up to 2 BEM levels (elements) at **most**.

</div>

### CSS Custom Properties

#### Rules and tips to create and use CSS Custom Properties / CSS Variables.

1. **DO NOT** use SASS VARIABLES.

2. **Create variable** with default value like this:

```css
.sf-button {
  padding: var(--button-padding, var(--spacer));
}
```

or empty variable without default

```css
.sf-button {
  border-radius: var(--button-border-radius);
}
```

<div class="alert tip">

Create variables for all customizable properties like
`padding`, `margin`, `order`, `z-index`, `color`, `border`, `border-radius`, `background-color`.

Think of properties you would like to change it as a user and create for it a variable.

Create size variables `var(--component-size, 25px)` in case when you have the same width and height value

```css
.sf-arrow {
  width: var(--component-width, var(--component-size, 266px));
  height: var(--component-height, var(--component-size, 266px));
}
```

</div>

3. Use **global variables** for spacers, font-sizes, colors. You can find all variables here `shared/styles/variables`

<div class="alert tip">

SCSS functions and mixins you can find here `shared/styles/_helpers.scss`

All global variables are here `shared/styles/_variables.scss`

</div>

4. **CSS Variables Naming convention**

- variable name for BLOCK **var(--input-width);**
- variable name for ELEMENTS **var(--input-label-padding);**

```css
.sf-input {
  width: var(--input-width, 350px) &__label {
    padding: var(--input-label-padding, var(--spacer-small) var(--spacer));
  }
}
```

5. If you want to customize some component e.g. SfButton which is nested in molecule or organism you should use defined variable from SfButton to customize it.

```css
.sf-add-to-card {
  &__add-button {
    --button-padding: var(--spacer-medium);
  }
}
```

6.  Write variables always on the top of block before other properties.

```css
.sf-arrow {
  --button-width: 20px;
  --button-height: 20px;
  outline: 0;
}
```

7. To create **font variable** use scss function
   **font(--component-name, font-weight, font-size, line-height, font-family);**

- Use it like this:

```css
font: var(
  --banner-title-font,
  font(
    --banner-title,
    300,
    var(--font-size-small),
    1.6,
    var(--font-family-secondary)
  )
);
```

- If you want to customize:

```css
.sf-banner {
  &__title {
    --banner-title-font-size: var(--font-size-regular);
  }
}
```

8. You shouldn't use media queries inside block elements.
   Instead, use mixin for this **for-desktop** and write all properties at the bottom in the block scope.

```css
.sf-banner{
  &__label{
    padding: var(--banner-label-padding, 10px 20px)
  }
  @mixin for-desktop{
    --banner-label-padding, 20px 40px
  }
}
```

9. Most recommended **NOT TO use &this**

```css
.sf-product-card {
  &:hover {
    #{$this}__add-button {
      opacity: 1;
    }
  }
```

**When it is possible try to use variable**

<div class="alert tip">

```css
.sf-product-card {
  &:hover {
    --product-card-add-button-opacity: 1;
  }
  &__add-button {
      opacity: var(--product-card-add-button-opacity, 0);
  }
```

</div>

### CSS Structure

1. Use flexbox
2. Always import a global helpers at the **top** `@import "../../helpers";`.
3. Start with mobile view and write **mobile-first** CSS. It means that media queries should be **only** for a desktop view.
4. **DO NOT** use **scoped** styles.
5. Use CSS Custom Properties.
6. Use global css variables **whenever it's possible**.
7. Try to order css properties according to **“Grouped by type”**
   example: [Poll results how do you order your css properties](https://css-tricks.com/poll-results-how-do-you-order-your-css-properties/)

<div class="alert warning">

Use global CSS variables for common properties like colors. All the global variables are found in `shared/styles/global/_variables/`.

</div>

7. Properties that **may broke the design** in future changes **shouldn't be customizable**.

<div class="alert tip">

A **safe** set of properties to customize are: `font-size`, `text-align`, `color`, `background-color`, `background-image`, `padding` (_not always_), `margin` (_not always_), `text-transform`, `font-weight`, `font-family`, `background-size`, `align-content` (_not always_), `align-items`(_not always_)

</div>

8. **Provide** CSS modifiers for most common modifications. (`component__black--modifier`)
9. **DO NOT** use any outer positioning for components (like outer margins). The way they're positioned in layout should be determined in outer environment.
10. Try to use rems for 10px up.

<div class="alert tip">
  <h5>SAMPLE CSS FILE</h5>
  <p>
    <a href="https://github.com/DivanteLtd/storefront-ui/blob/develop/packages/shared/styles/components/molecules/SfBanner.scss">
      Here
    </a>{" "}
    you can find an example of properly styled component with all rules applied.
  </p>
</div>

### Template rules

1. HTML should be semantic and not tied to CSS implementation. Total repalcement of CSS should be possible without using semantic meaning of markup.
2. We use **slots** and **props** for content composition.

- Props should be used to fulfil most common use case
- Instead of providing multiple props and configurations for different use cases try to add slots with general usage
- Slots are also meant to be used for markup **replacement**. In other words every default markup **should be replaceable** with slots.
- Always provide proper slot-scope. For example when we have pagination component with slot `next` allowing to replace arrow to next page we would like to provide things like `next()` function or `canGoFurther` variable. Then if someone will choose to replace slot content with his/her own it's much easier to know how to keep same behavior.

3. One slot is usually a **single** BEM element.
4. **DO NOT** use props for setting properties that can be set by css (except for background images).
5. **Provide a default slot** (usually empty) for a **full** customization support.

<div class="alert warning">

Try to make components as customizable as possible **without complicating** them. Think about the parts that are usually customized and allow simple way to change their look.

</div>

## Deprecation guidelines

StorefrontUI cares about providing new features and keeping up-to-date with new best practices. Sometimes it can become necessary to make changes to APIs or to prepare new features in order to stay current. To make this process easier for everyone, we deprecate APIs and features for a period of time before the actual removal by adding proper information about deprecations in the next major version.

**In summary:**

- we don't remove implemented features before the next major version
- we add deprecation infos, so users can prepare for replacements and have time to update their dependencies to the latest version
- on our side: before a release, we can spot deprecated features more easily, remove them and prepare migration documentation

**To standardize this process, we stick with a few rules:**

- add this note next to the deprecated feature/APIs -> `@deprecated will be removed in 1.0.0 use ___ prop instead`
- add this note next to deprecated code that should be removed `// TODO remove in 1.0.0 / BEGIN` -> `// END`
- import helper from utitilies `import { deprecationWarning } from "@/utilities/helpers/deprecation-warning.js";`


- add info about deprecation by using the helper method `deprecationWarning(this.$options.name, "your deprecation message")`

<div class="alert warning">
  <h5>deprecationWarning(componentName, message)</h5>
  <p>
    <code>componentName</code> is a string argument that you have to pass for
    the component's name to appear in front of the deprecation warning – in
    general,
    <code>this.$options.name</code> returns the name of the current Vue component.
  </p>
</div>

As a result the console will show a warning like the following:
`[StorefrontUI][SfComponent] ____ prop has been deprecated and will be removed in 1.0.0. Use ____ instead.`

<div class="alert tip">
  <h5>DEPRECATION INFO</h5>
  <p>Here you can see an example how it looks like in SfAccordion</p>
</div>

```js
props: {
  /**
   * Opens the first accordion item if set to "true"
   * @deprecated will be removed in 1.0.0 use open prop instead
   */
  firstOpen: {
    type: Boolean,
    default: false
  },
}
```

```js
// TODO remove in 1.0.0 / BEGIN
if (this.firstOpen) {
  this.$children[0].isOpen = this.firstOpen;
  console.warn(
    "[StorefrontUI][SfAccordion] Prop 'firstOpen' has been deprecated and will be removed in v1.0.0. Use 'open' instead."
  );
  return;
}
// <- END
```

## Unit tests

The minimum set of tests should contain component:

- **External API:** `props`, `slots`, `$events`
- **Internal API:** `methods`

## Stories for Storybook

Component stories should contain at least these following stories:

1. Default called `Common` for all props, with props and CSS modifiers fillable as knobs.
2. One for every customisable `slot` in the component
3. Use \*.stories.scss to add styles for stories. Use `.sb-` prefix for stories classes. A good example is [SfMegaMenu stories](https://github.com/DivanteLtd/storefront-ui/tree/develop/packages/vue/src/components/organisms/SfMegaMenu)

<div class="alert tip">

A good example is [SfBanner stories](https://github.com/DivanteLtd/storefront-ui/blob/master/packages/vue/src/components/molecules/SfBanner/SfBanner.stories.js).

</div>

You can learn more about Storybook syntax and how to use [here](https://storybook.js.org).

Check [our storybook](https://storybook.storefrontui.io/) for examples.
