## Pie Project - React

This package implements the PIE design system for React. You can [see it in action here](https://react.pie.design/).

## Tech Stack

- [React](https://reactjs.org/)
- [Storybook](https://storybook.js.org/)
- [styled-components](https://styled-components.com/)
- [polished](https://github.com/styled-components/polished)
- [react-popper](https://popper.js.org/react-popper/)
- [@popperjs/core](https://popper.js.org/react-popper/)

## Install

Via package managers, install `PIE React` and its dependencies:

```jsx
yarn add @jet-pie/react @jet-pie/theme styled-components polished @popperjs/core react-popper

//If is a TypeScript application
yarn add -D @types/styled-components
```

### Theme Provider

Our components need specific colors, fonts and styles, which are provided via `styled-components` ’s `ThemeProvider` API. The `PIEThemeProvider` component takes care of providing the _theme_ and loading the fonts.

To use it, add this component to the top of you component tree, wrapping the rest of the application as `children` components. It also allows you to consume the Theme in your application using `styled-components`'s template functions. All tokens values will be accessible via the theme property:

**NOTE:** Make sure every PIE React component and your components that consume the theme to be wrapped by the `PIEThemeProvider`.

**TYPING YOUR THEME PROVIDER**

To have full TypeScript support on your theme, follow the next steps:

- Create type definition file called `styled.d.ts` and make sure it is included by your `tsconfig.json`
- In that file include the following code:

```tsx
import 'styled-components';
//ESM import
import { ThemeWithMode } from '@jet-pie/react/esm';
//Or CJS import
import { ThemeWithMode } from '@jet-pie/react/dist/utils/types';

declare module 'styled-components' {
  interface DefaultTheme extends ThemeWithMode {}
}
```

You now have full the whole theme typed inside when it is consumed on `styled-components`'s template functions.

```tsx
export const MyLocalComponent = styled.div`
  background-color: ${(props) => props.theme.global.colors.orange};
`;
```

**NOTE:** Make sure every `@pie/react` component and your components that consume the theme to be wrapped by the `PIEThemeProvider`.

#### Importing CSS variables

By default, our PIE `ThemeProvider` tokens points to CSS variables instead of hardcoded values. To add CSS variables to your project, add the following line to your root component:

```tsx
import '@jet-pie/theme/variations/skip/variables.css';
```

If you don't want to use CSS variables, you can override the tokens using the object with hardcoded values:

```tsx
import { skipTokens } from '@jet-pie/theme/variations/skip';

function ParentComponent() {
  return (
    <PIEThemeProvider customTheme={skipTokens} mode={'light'}>
      {children}
    </PIEThemeProvider>
  );
}
```

### Customization

We don't treat customization as a first-class citizen. Our colors, spacing, and fonts were handpicked by designers to provide the best UI/UX and have consistancy across different products. Having fully customizable components would go against those principles.

Be that as it may, the library allows customizations in two ways:

- **Component Based:** Some of our components have props that allow customizing the colors and behaviour of the component. This is a
  more restrict approach as not all components allow to do this type of customization.

- **Theme Based:** Its possible to customize the PIE theme. The `PIEThemeProvider` component has a `customTheme` prop that allows you to override
  values from the original theme. **CAUTION: Changes to the theme could affect all components, cause unexpected behaviour and create UI bugs**

### Extending the Theme

If your app needs token values that the PIE Theme does not have, you're welcome to add them! You will be able to consume same way as the rest of the theme.

To do that, pass your custom properties to the `PIEThemeProvider`:

```tsx
const const customTheme = {
  magentaColor: 'magenta',
};

 <PIEThemeProvider extraProperties={customTheme}></PIEThemeProvider>
```

Now you can get those values using `styled-components` template functions. They will all be inside the `custom` property.

```tsx
export const MyLocalComponent = styled.p`
  color: ${(props) => props.theme.custom.magentaColor};
`;
```

NOTE: To have custom properties typed by the theme provider, you need to add it to the type definition file mentioned before

```tsx
import 'styled-components';
import { Theme } from '@jet-pie/theme/variations/skip';
import { CustomProperties } from '../types';

type CustomTheme = Theme & CustomProperties;

declare module 'styled-components' {
  interface DefaultTheme extends CustomTheme {}
}
```

## Guidelines

On this section we will go over all the requirements to make updates to our component library.

### Initial setup

To make changes to a component there are two things you need to ensure:

- All dependencies are installed: run `yarn install` from the project's root folder to do it so.
- All supporting packages (PIE Theme and PIE Icons) are ready: Run `yarn build:packages` from the root folder to run a build for each of them.
- The Storybook is running locally: Run `yarn storybook:react` from the root folder to open the PIE React Storybook, this way you can see all the changes you made.

### Component File Structure

Components on PIE are found following this structure:

- **Component.story.tsx**: Story file to display the component and interact with it on Storybook
- **Component.tsx**: Actual implementation of the component
- **styles.tsx**: Export all styling need for the component (and Story)
- **types.tsx**: Export types definitions for the component

You don't have to strictly follow this structure per se. You can have a different structure based on the
requirements of your component, as long as it has a similar separation of concerns.

### Project helpers

We have a couple function that will help you extract values from our Theme:

#### getColorAlias

Strongly typed helper to get the color alias values from the theme. It allows us to handle different themes (**light/dark**) without
having to worry about it in a component level.

The alias needed for each color of your component should be specified in the tech ticket.

**NOTE: Dark theme has not been implemented**

```tsx
export const PrimaryButtonSmall = styled(ButtonSmall)`
  background-color: ${getColorAlias('interactivePrimary')};
  color: ${getColorAlias('contentnteractive-primary')};
`;
```

#### getFontSize

We have 8 font sizes available on PIE: `size-a` throught `size-h`.
As a design decision, not only we set the `font-size`, but also the `line-height`.

Using this helper will set both properties based on the font alias used:

```tsx
export const RangeWrapper = styled.div`
  ${getFontSize('size16')};
  font-family: ${(props) => props.theme.font.familyPrimary};
  width: 100%;
`;
```

#### getSpacing

We have 7 spacing variants, that goes from 0px to 80px.
Use the getSpacing API to retrieve theme spacing values, which are represented by the following parameters.
's00' => 0px, 's04' => 4px, 's08' => 8px, 's12' => 12px, 's16' => 16px, 's24' => 24px, 's32' => 32px, 's40' => 40px, 's56' => 56px, 's64' => 64px, 's80' => 80px

```tsx
export const RangeWrapper = styled.div`
  ${getFontSize('size16')};
  font-family: ${(props) => props.theme.font.familyPrimary};
  padding: ${getSpacing('s24')};
  width: 100%;
`;
```

You can also concat up to 4 values for better customization of your spacing values:

```tsx
export const RangeWrapper = styled.div`
  padding: ${getSpacing('s16', 's08', 's08', 's24')};
  margin: ${getSpacing('s24', 's00')};
  width: 100%;
`;
```

### Styled Components for styling

When styling your components, focus on only using Styled Component concepts.
There is no need to create CSS files, CSS variables or even CSS classes!

**DONT DO THIS**

```tsx
export const GhostButtonMedium = styled(ButtonMedium)`
  background-color: ${({ isLoading }) =>
    isLoading
      ? opacifyAliasColor('active02', 'backgroundDefault')
      : 'transparent'};
  color: ${getColorAlias('contentLink')};

  .spinner svg {
    stroke: ${getColorAlias('contentLink')};
  }
`;
```

**DO THIS**

```tsx
export const SpinnerWrapper = styled.div`
  // DEFAULT STYLING
`;

export const GhostButtonMedium = styled(ButtonMedium)`
  background-color: ${({ isLoading }) =>
    isLoading
      ? opacifyAliasColor('active02', 'backgroundDefault')
      : 'transparent'};
  color: ${getColorAlias('contentLink')};

  // Styling for GhostButtonMedium
  ${SpinnerWrapper} svg {
    stroke: ${getColorAlias('contentLink')};
  }
`;
```

## Adding a new component

There are a few steps to follow to maintain the project quality and component tracking.
When creating a new component, to make sure it will exported:

- add a `story` to make sure that component is visible on our Component catalog.
- export component from root index file `src/index.ts`

For version tracking, we use [Semantic Versioning](https://semver.org/) to bump versions on our package.
Until the writing of this document, this process in entirely manual. So when creating a new version of the PIE React Package:

- Bump the version on `package.json` based on Sematic Versioning
- Add line on `changelog.md` with the new version a quick description of the changes

Testing in **develop**:

We have a `develop` environment for our [PIE React Storybook](https://pie-react.dev.internal.skipthedishes.com/). To trigger a new build,
all you have to do is merge your branch to `develop` and push it. CircleCI will take care of creating the new release.

## Testing your component

Our Design System is already setup with Jest and React Testing Library. To add test to your components, create a folder called `__tests__` inside of the component main folder.
From there you can create `.test.tsx` file and write tests for that component.

We have githooks setup, and all tests will run when the code is pushed to remote, to make sure no broken features are being send to remote.

When implementing the component, we recommend using the `getId` helper. This is a composable data-testid/trackid/id values, making it easier to use

```tsx
const componentTestId = getId('component-test');

const wrapper = componentTestId(); //component-test
const innerInput = componentTestId('input'); //component-test-input
const innerLabel = componentTestId('label'); //component-test-label
```

### Error handling

When creating tests/suites that test the error states of a component, please include either the `silenceSuiteConsoleLogs` or `silenceTestConsoleLogs` within the appropriate `describe` or `it` block (respectively). This will hide the expected error console logs from appearing when running tests.

Any test/suite that doesn't use one of these utils and does produce a console error, should be treated as a bug in the code and addressed accordingly.

## Reviewing process

To have a component added and merged into the production build we need to follow some PR guidelines:

- It should have least 3 approvals, ideally from the different teams that adopt `PIE React` and
- If the PR includes changes on a existing component, it should be review my members of each team that consumes that component.
- If a component behaviour has changed, it should be informed on our #team-pie-canada Slack channel and the component's documentation should be updated.

This way we can ensure the transparency of our design system and a clear communication with the teams that consume it.

## Testing changes

There are two ways to consume PIE React before a production release:

#### Method 1

Use Storybook's playground by running in your terminal

```
yarn storybook
```

or

```
yarn storybook:react
```

If you are creating a brand new component you have to create a playground page following Storybook's patterns.

#### Method 2

We can release an `alpha` version of our library when that's necessary. If you require to do it so, reach out to one of our maintainers.

## Maintainers

- David Nascimento [Principal Engineer]
- Jose de Freitas Jr [Staff Engineer]
