npm package: @revive-real-estate/aether
Description: Revive's Component Library
Latest version: 0.27.0

# Aether

Revive's Component Library

- [Getting started](#getting-started)
- [Development](#development)
- [Usage](#usage)
- [Writing Components](#writing-components)
- [Testing](#testing)
- [Publishing](#publishing)

<a name="getting-started"></a>

## Getting started

### Pre-requisites

To work with this project, you'll need to have [Node](https://nodejs.org/en/download/) and [Yarn](https://classic.yarnpkg.com/lang/en/docs/install) installed on your system.

### Installation

1. `git clone https://github.com/Revive-Home/Aether.git && cd aether`
2. `yarn install`

Before you start contributing to Aether, be sure to read the contribution guide (TBA)

<a name="development"></a>

## Development

Aether uses following technologies:

- React
- TypeScript
- Tailwind
- Vite
- Storybook

Run `yarn dev` to start Storybook for development.

### Tailwind

To style the components, Aether uses [TailwindCSS](https://tailwindcss.com/).

#### **Overriding the styles**

The styling of Aether components can be overridden in the consumer apps by passing the Tailwind classes to the className prop.

#### **Prefix**

To avoid the className conflict in consumer apps, Aether's Tailwind is set to require the prefix. All class names should start with the prefix, `aether-`.

Example:

```html
<div
  className="aether-p-10 aether-text-red-300 aether-border aether-border-red-300"
>
   Example
</div>
```

**_TODO: Add tailwind guideline/practice_**

**Theme**
Aether utilizes the custom theming feature of Tailwind. The custom theming can be configured in [`tailwind.config.js`](tailwind.config.cjs).

- [Tailwind, Customizing Colors](https://tailwindcss.com/docs/customizing-colors)

### Headless UI

Aether uses [`headlessui`](https://headlessui.com/) to abstract away some of the component behavior logic.

<a name="usage"></a>

## Usage

Within a separate application that uses Aether, import the `style.css` to apply styling in the root file of the application.

Next.js example:

```ts
// in `_app.tsx`
import "@revive-real-estate/aether/dist/style.css"
// ...other imports
```

And then, import the desired component as follows:

```ts
import { Button } from "@revive-real-estate/aether";
```

Within Aether itself, import the desired component by pointing to the component file. The alias (`@`) should be used for the import path:

```ts
import { Button } from "@/components/Button";
```

### Using Aether package locally

Aether can be installed and tested locally without publishing to NPM by using [yalc](https://github.com/wclr/yalc).

In Aether, change the version in package.json to indicate this is a test package.

Example: `0.0.0-yalc-test`

Then, run

```
yarn build && yalc publish
```

`yarn build` will build the package and create `/dist`, then `yalc publish` command will copy all the files that should be published in remote NPM registry.

Then in consuming app, run

```
yalc add <package_name@version_number> && yarn
```

The `<@version_number>` should match the version in package.json of Aether.

To import Aether components, refer to the [Usage](#usage) section.

<a name="writing-components"></a>

## Writing components

All components should be written in TypeScript. Export the interface or type so they can be consumable in the app.

Each prop defined in the interfaces or types should have a description. Storybook Docs will translate these descriptions and generates the Props Table.

Example:

```ts
export interface ButtonProps
  extends React.HTMLAttributes<HTMLButtonElement | HTMLAnchorElement> {
  /** Text within the button */
  children?: React.ReactNode | React.ReactNode[];
  /** Provide a custom className that is applied directly to the button */
  className?: string;
  /** Specify whether the control is disabled */
  disabled?: boolean;
  /** Specify the theme of the button */
  variant?: "primary" | "secondary" | "gray" | "turquoise";
}
```

The components need to be exported in the `src/index.ts` file to be consumable. The type definition will be automatically generated by the Vite and included to the bundle.

### Class names

To make it easier and quicker for developers to debug, it is recommended that the `className` of a component begins with a description of the component itself, such as `className="aether-button"`.

If there are multiple classNames that get added conditionally, `clsx` should be used to consolidate all `className`s into a string. This helps in avoiding any potential conflicts or errors that may occur due to multiple classNames being added separately.

```ts
const classes = clsx(
  "aether-button",
  ButtonClasses.baseStyle,
  ButtonClasses[size],
  variant && ButtonClasses[variant],
  disabled && ButtonClasses.disabled,
  fullWidth && ButtonClasses.fullWidth,
  loading && ButtonClasses.loading,
  className // className prop should be placed at the end
);
```

### Extending Components with {...rest} Props

Many of Aether components support {...rest} props, allowing you to pass additional attributes directly to the HTML elements rendered by the component. This feature provides flexibility for extending our built-in components in various ways.

#### Benefits

- **Custom Styling**: Add custom classes or styles directly to the component.
- **Event Handling**: Attach event listeners like onClick, onBlur, etc.
- **Accessibility**: Add attributes like aria-label, aria-labelledby, etc., to improve accessibility.
- **Forward Compatibility**: Components will automatically support new HTML and React attributes as they are introduced.

```ts
// Example: Adding custom styles to a Button component using `style` attribute
import { Button } from "@revive-real-estate/aether";

<Button style={{ backgroundColor: 'tomato' }} onClick={handleClick}>
  Click Me
</Button>
```

### Component Template

There's a set of template files for a component. Copy the `src/example/ComponentTemplate` folder and paste it inside the `src/components,` then rename the folder and files to an appropriate component name.

<a name="adding-icons"></a>

## Adding SVG icons

Aether supports the import/export of SVG files. The custom icons should be created by the design team and exported as an SVG file.

Steps to add SVG icon to Aether:
1. Export the SVG files for the desired icon from Figma
2. Add SVG files to [`src/components/Icon/aether-icon-files`](src/components/Icon/aether-icon-files)
3. In [`src/components/Icon/aether-icon-files/index.tsx`](src/components/Icon/aether-icon-files/index.tsx), add an export of the newly added icon:

```ts
//...more icons
import { ReactComponent as NewIconGreen } from "./green/new-icon.svg"

export {
  // ...more icons
  NewIconGreen
}
```

4. In [`src/components/Icon/data.ts`](src/components/Icon/data.ts), add the icon name.

Example:
```ts
export const AetherIconNamesArray = [
  // ...more icons
  "new-icon",
] as const
```

5. In [`src/components/Icon/aether-icons.ts`](src/components/Icon/aether-icons.ts), import the newly added icon from [`src/components/Icon//aether-icon-files`](src/components/Icon//aether-icon-files):

```ts
import {
  // ...more icons
  NewIconGreen
} from "./aether-icon-files"
```

6. Add new entry to iconMap object in [`src/components/Icon/aether-icons.ts`](src/components/Icon/aether-icons.ts)

```ts
export const getAetherIcon = (name: ListIconNames): SvgIcon | undefined => {
  const iconMap: IconMap = {
    // ...more icons
    "new-icon-green": NewIconGreen
  }

  // ...more code
}
```

<a name="testing"></a>

## Testing

For unit testing, Aether uses [Vitest](https://vitest.dev/guide/) and [React Testing Library](https://testing-library.com/docs/react-testing-library/api).

### Chromatic Visual Regression Testing

We use Chromatic, integrated with GitHub Actions, to automatically detect visual regressions in our UI components. This tool compares new changes against established baselines, helping us maintain visual consistency. Our main branch Chromatic link serves as a reference for visual comparisons. This link below gets updated to reflect the latest `main` branch.

Chromatic link: https://main--63cad365fab53dc41982eaae.chromatic.com/

---

<a name="publishing"></a>

## Publishing the package

Our package publishing process is fully automated using GitHub Actions and `semantic-release`. This setup ensures a consistent and efficient release process without manual intervention.

Whenever a push is made to the `main` branch, the GitHub Actions workflow is triggered. It sets up the required environment, builds the package, and utilizes `semantic-release` to handle versioning and changelog generation. The new version is then automatically published to NPM, and a corresponding GitHub release is created.

This streamlined approach allows us to focus on development, knowing that our releases are handled with precision and transparency. Configuration details can be found in the .releaserc file and the corresponding GitHub Actions workflow file in our repository.