Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(components): Document composability best practices [107012] #2136

Merged
merged 4 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,15 @@ import { getGIF, loadingGIF } from "./getGIF";

<Meta title="Guides/Getting started with React" />

# Getting Started with React

## Things to know.
# Getting started with React

## What is React

At its core React is simply a JavaScript view library layer. It provides us with
a way to define and render view components. It also aids with tracking and
re-rendering changes to data that the components are representing.

## What is a Component
## What is a component

Much as a function in programing allows you to encapsulate, test and reuse logic
a React Component allows you to encapsulate, test and reuse pieces of your UI.
Expand Down Expand Up @@ -64,7 +62,7 @@ There are a few things going on in the example above. Lets break them down:
| `{ text, yell }: MessageProps` | There are two parts to this. Starting on the right `MessageProps` indicates that this funciton takes a parameter of type `MessageProps`. On the left the `{ text, yell }` unpacks the parameter into local variables. |
| `return <p>{text}</p>` | Every React component will return an [element](https://reactjs.org/docs/rendering-elements.html) or [fragment](https://reactjs.org/docs/fragments.html). (Note the [fragment short hand syntax](https://reactjs.org/docs/fragments.html#short-syntax).) |

## It's Components All the Way Down
## It's components all the way down

In React everything is a component. A typical page will consist of one top level
component representing the main view. Then within that page you will break down
Expand All @@ -79,7 +77,7 @@ smaller components. Brad Frost covers this break down very well with
When you are writing a front end using React each of the levels he talks about
are represented by components functions.

## Composing Components
## Composing components

As mentioned above a lot of the power of React comes from being able to build
components out of other components. Lets take a look at an example on how to do
Expand All @@ -89,7 +87,7 @@ For this example we are going to build a card that allows you to enter a topic
and then search for a related gif.

<Canvas>
<Story name="Composing Components">
<Story name="Gif Gift">
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I played around with this for ~20mins to try to prevent the split of a Docs page and a page with this story but still having it render nicely in this docs page and couldn't get it. At least now it has a better name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha I don't get why this is its own section. that can't be by design. something must have changed in a storybook upgrade or something.

{() => {
const [topic, setTopic] = useState();
const [url, setURL] = useState();
Expand Down Expand Up @@ -117,7 +115,7 @@ and then search for a related gif.
> `function getGIF(topic: string): Promise<string>;` that will return a promise
> resolving to a url.

### Breaking it Down
### Breaking it down

```tsx
const [topic, setTopic] = useState();
Expand Down Expand Up @@ -183,3 +181,43 @@ The final part of our example defines the custom actions needed to power our
component. In this case the action when the user clicks on the **Search**
button. Here we call the `setURL` setter to show the `loadingGIF`. Then, we call
`setURL` awaiting the async response of `getGIF`.

## Types of components in Atlantis and their ability for customization

#### 1. Primitive components:

Examples: [Icon](..?path=/docs/components-images-and-icons-icon--docs),
[Avatar](..?path=/docs/components-images-and-icons-avatar--docs)

These components do not accept children and have minimal customization.

#### 2. Simple components:

Examples: [Button](..?path=/docs/components-actions-button--docs),
[Link](..?path=/docs/components-text-and-typography-link--docs)

These allow basic customization through props or limited children, like
`ReactNode` or specific child types.

#### 3. Compound components:

Examples: [Chip](..?path=/docs/components-selections-chip--docs),
[DataList](..?path=/docs/components-lists-and-tables-datalist--docs)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DataList is almost in its own category. it's bordering on an entire feature/view.

I suppose it is technically a compound component though. it's just the largest compound component we have.


These are part of larger compositions and may validate their children types to
maintain internal consistency.

#### 4. Strict complex components:

Example:
[Autocomplete](..?path=/docs/components-forms-and-inputs-autocomplete--docs),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we'll have to make a note to update these if we ever make Autocomplete less strict


These have multiple UI pieces they are built out of but strict APIs and limited
customization.

#### 5. Customizable complex components:

Example: [Combobox](..?path=/docs/components-selections-combobox--docs)

These also have multiple UI pieces they are built out of but offer options for
customization.
77 changes: 77 additions & 0 deletions docs/guides/customizing-components.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Meta } from "@storybook/addon-docs";

<Meta title="Guides/Customizing components" />

# Customizing components

### Philosophy: easy to use, flexible to customize

Atlantis is designed to make building consistent, accessible, and visually
appealing user interfaces as effortless as possible. Our goal is to create
components that work out of the box, requiring minimal setup or configuration
from consumers.

While we strive to cover the most common design scenarios with thoughtful
defaults, we understand that unique use cases may require customization. For
these scenarios, we provide mechanisms to extend or modify our components in a
way that maintains flexibility without compromising the integrity of the
components.

We recommend using the default implementations wherever possible to benefit from
consistency, maintainability, and built in accessibility. However, if you choose
to customize, please note that the responsibility for ensuring proper
functionality and cohesion will rest with you.

Atlantis aims to strike a balance between simplicity, flexibility, and
consistency.

### Primary customization approach: named render props

In Atlantis, our primary approach to enabling customization is through named
render props, using the `customRender____` pattern. This method allows consumers
to inject their own UI logic while retaining the default behaviors and
functionality of the component.

#### Example: customRenderItem

Here's an example of how to use a named render prop to customize the rendering
of items in a [List](..?path=/docs/components-lists-and-tables-list--docs)
component:

```tsx
const renderProductItem = item => (
<Flex template={["shrink", "grow"]} align="start">
<Text>{item.price}</Text>
<Heading level={4}>{item.name}</Heading>
</Flex>
);

export const CustomRenderExample = () => (
<List items={items} customRenderItem={renderProductItem} />
);
```

In this example, the default List behavior (such as the main list structure and
handling item interactions) is preserved. Only the presentation of individual
items is customized.

### Other customization approaches

While custom render functions are our primary approach it is not used in all
places. For simple cases where customization does not depend on internal state,
we have chosen to extend prop types to allow for a `ReactNode` to be passed in.

#### Example: Tab component (see [Tabs](..?path=/docs/components-navigation-tabs--docs))

Instead of introducing `customRenderLabel`, the `Tab` component was updated to
allow the `label` prop to accept a `ReactNode`.

```tsx
<Tab label={<MyCustomLabel />} />
```

This approach was chosen to provide a simpler experience for those implementing
Atlantis components. Customizing the `label` didn’t require exposing or
interacting with the `Tab`’s internal state (e.g., active state styling). Once
the design becomes more complex a custom render function would become a better
fit.