Skip to content

Commit

Permalink
docs(atomic-elements): Moved documentation outside of storybook and i…
Browse files Browse the repository at this point in the history
…nto docs dir & updated / cleaned up the documentation
  • Loading branch information
seanrcollings committed Nov 20, 2024
1 parent 272566b commit 0886cc7
Show file tree
Hide file tree
Showing 28 changed files with 397 additions and 1,187 deletions.
1 change: 0 additions & 1 deletion .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { StorybookConfig } from "@storybook/react-vite";

const config: StorybookConfig = {
stories: [
"../packages/atomic-elements/src/**/*.mdx",
"../packages/atomic-elements/**/*.stories.@(js|jsx|ts|tsx)",
],

Expand Down
6 changes: 2 additions & 4 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,12 @@ const preview: Preview = {
options: {
storySort: {
order: [
"Introduction",
"Installation",
"Usage",
"Layouts",
"Inputs",
"Fields",
"Buttons",
"Overlays",
"Dropdowns",
"Fields",
],
},
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Accessibility


## Aria Announcements

Atomic Elements implementation of live announcements is mostly implemented to be compatible with: https://github.com/AlmeroSteyn/react-aria-live which we used previously.
Atomic Elements implementation of live announcements is mostly implemented to be compatible with: [react-aria-live](https://github.com/AlmeroSteyn/react-aria-live) which we used previously.

### `LiveAnnouncer`

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { SensibleDefaults } from "@atomicjolt/atomic-elements";
import { CustomSelect, Item, Section, CssVariables, LoadFonts } from "@atomicjolt/atomic-elements"
import { Example } from "../components/Example"

# Collection Components

<CssVariables />
<LoadFonts />

## Introduction

[From React Aria Docs](https://react-spectrum.adobe.com/react-stately/collections.html):
Expand All @@ -14,55 +18,48 @@ import { SensibleDefaults } from "@atomicjolt/atomic-elements";

It is a unified approach for building component that display a collection of items.
It is designed to be flexible and powerful, and to support a wide variety of use cases.
It is used by components that render a collection of items, so:

- `Menu`
- `CustomSelect`
- `ListBox`
- `Combobox`
- `IconMenu`
- `Table`
- `Tabs`
It is used by components that render a collection of items, such as `Menu`, `ListBox`, `Combobox`, `Table`, and `Tabs`

## Static Collections

When the component's items are known statically, you can define them using the JSX API:
When the component's items are known statically, you can define them using the JSX children of the component:

```jsx
<CustomSelect>
<Item key="david">David</Item>
<Item key="sam">Sam</Item>
<Item key="jane">Jane</Item>
<Item id="david">David</Item>
<Item id="sam">Sam</Item>
<Item id="jane">Jane</Item>
</CustomSelect>
```

### Keys
### Collection IDs

Each element in a collection should have a unique key prop. This is used to identify the item in the collection. It is what the event handler will receive when an item is selected.
Each element in a collection should have a unique id prop. This is used to identify the item in the collection. It is what the event handler will receive when an item is selected.

```jsx
const handleSelectionChange = (key) => {
console.log(`Selected item with key: ${key}`);
};

<CustomSelect onSelectionChange={handleSelectionChange}>
<Item key="david">David</Item>
<Item key="sam">Sam</Item>
<Item key="jane">Jane</Item>
</CustomSelect>;
<CustomSelect onSelectionChange={alert}>
<Item id="david">David</Item>
<Item id="sam">Sam</Item>
<Item id="jane">Jane</Item>
</CustomSelect>
```

Clicking on an item will log the key of the item to the console.
Clicking on an item will log the id of the item to the console.

**NOTE**: Currently in Storybook, the key prop is not displayed in examples, just keep in mind that any time that one of the components mentione above is used, the collection items should each
have a unique key prop
<Example style={{ "--btn-font-size": "16px", "--listbox-font-size": "16px" }}>
<CustomSelect onSelectionChange={(key) => alert(key)} >
<Item id="david">David</Item>
<Item id="sam">Sam</Item>
<Item id="jane">Jane</Item>
</CustomSelect>
</Example>

### Sections

Sections or groups of items can be constructed by wrapping the items as needed.

```jsx
<CustomSelect label="Custom Select">
<CustomSelect>
<Section title="People">
<Item>David</Item>
<Item>Sam</Item>
Expand All @@ -76,6 +73,21 @@ Sections or groups of items can be constructed by wrapping the items as needed.
</CustomSelect>
```

<Example style={{ "--btn-font-size": "16px", "--listbox-font-size": "16px" }}>
<CustomSelect>
<Section title="People">
<Item>David</Item>
<Item>Sam</Item>
<Item>Jane</Item>
</Section>
<Section title="Animals">
<Item>Aardvark</Item>
<Item>Kangaroo</Item>
<Item>Snake</Item>
</Section>
</CustomSelect>
</Example>

The `<Item>` and `<Section>` components are used across multiple collection components to ensure a consistent interface.
They define the data for the items and sections, while the rendering, visual appearance, and behavior are implemented by each individual collection component (e.g., Menu or ListBox).

Expand All @@ -86,39 +98,54 @@ Then the child of the component must be a function that takes an item and return

```jsx
let [animals, setAnimals] = useState([
{ name: "Aardvark" },
{ name: "Kangaroo" },
{ name: "Snake" },
{ id: "aardvark", name: "Aardvark" },
{ id: "kangaroo", name: "Kangaroo" },
{ id: "snake", name: "Snake" },
]);

<CustomSelect items={animals}>
{(item) => <Item key={item.name}>{item.name}</Item>}
</CustomSelect>;
{/* You can provide the id prop manually, but if each item
has an id property, you can omit it */}
{(item) => <Item>{item.name}</Item>}
</CustomSelect>
```

:::warning

The `items` prop must receive a list of **objects**. If you try to do something like this:

```jsx
<CustomSelect items={["David", "Sam", "Jane"]}>
{(item) => <Item id={item}>{item}</Item>}
</CustomSelect>
```

You will get an error. This is because internally the elements in `items` are used as the keys to a `WeakMap` which only accepts objects as keys.
:::

### Why not array map?

You may be wondering why we didn't use `animals.map` in this example. In fact, you can do this if you want and it will work, but it will not be as performant.

```jsx
let [animals, setAnimals] = useState([
{ name: "Aardvark" },
{ name: "Kangaroo" },
{ name: "Snake" },
{ id: "aardvark", name: "Aardvark" },
{ id: "kangaroo", name: "Kangaroo" },
{ id: "snake", name: "Snake" },
]);

<ListBox>
<CustomSelect>
{animals.map((item) => (
<Item key={item.name}>{item.name}</Item>
<Item id={item.id}>{item.name}</Item>
))}
</ListBox>;
</CustomSelect>
```

By using the `items` prop and a render function, we can optimize performance by caching the rendered results of each item. This avoids unnecessary re-rendering of the entire collection when only a single item changes. This optimization is particularly beneficial for large collections.

### Sections

Sections can also be used with dynamic collections.
Sections can also be used within dynamic collections.

```jsx
let [sections, setSections] = useState([
Expand All @@ -134,8 +161,8 @@ let [sections, setSections] = useState([

<CustomSelect items={sections}>
{(section) => (
<Section key={section.name} title={section.name} items={section.items}>
{(item) => <Item key={item.name}>{item.name}</Item>}
<Section id={section.name} title={section.name} items={section.items}>
{(item) => <Item id={item.name}>{item.name}</Item>}
</Section>
)}
</CustomSelect>;
Expand All @@ -147,57 +174,17 @@ When rendering non plain-text content in `Item`, it is important to add a `textV

```jsx
<CustomSelect>
<Item key="1" textValue="Add">
<Item id="1" textValue="Add">
<MaterialIcon icon="add" />
Add
</Item>
<Item key="3" textValue="Edit">
<Item id="3" textValue="Edit">
<MaterialIcon icon="edit" />
Edit
</Item>
<Item key="2" textValue="Delete">
<Item id="2" textValue="Delete">
<MaterialIcon icon="delete" />
Delete
</Item>
</CustomSelect>
```

## Common Mistakes when using Collection Components

### Rendering Items in Subcomponents

If you try and do something like this:

```jsx
function Items() {
return (
<>
<Item key="1">David</Item>
<Item key="2">Sam</Item>
<Item key="3">Jane</Item>
</>
);
}

<CustomSelect>
<Items />
</CustomSelect>;
```

You will see an error similar to this

```
Uncaught TypeError: type.getCollectionNode is not a function
```

This is because components that make use of the Collection API need to be able to access the `Item` and `Section` components directly.
**This is not possible when they are rendered in a subcomponent.** When using a collection component, you should always render the `Item` and `Section`
components directly as children of the collection component.

```jsx
<CustomSelect>
<Item key="1">David</Item>
<Item key="2">Sam</Item>
<Item key="3">Jane</Item>
</CustomSelect>
```
26 changes: 26 additions & 0 deletions packages/atomic-elements/docs/Concepts/Localization.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Meta } from "@storybook/blocks";
import { useState } from "react";

# Localization

Atomic Elements components are designed to work with multiple languages and locales. The components are built to be easily localized, and we provide a number of tools to help you do so.

Several components handle the details of localization for you. For instance:
- The `DatePicker` component will automatically localize the month and day names
- The `NumberInput` component will automatically localize the decimal and thousands separators.

:::note

Localization support for components is currently in-progress. If you need better support for localization in a component please [open an issue](https://github.com/atomicjolt/atomic-libs/issues/new)

:::

## Customizing the Locale

By default, the locale is determined by the user's browser settings. You can override this by wrapping your component in a `LocaleProvider` and setting the `locale` prop.

```jsx
import { LocaleProvider } from "@atomicjolt/atomic-elements";

<LocaleProvider locale="en">{/* Your app here */}</LocaleProvider>;
```
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Meta } from "@storybook/blocks";
import { Button, CssVariables } from "@atomicjolt/atomic-elements";
import { Example } from "../components/Example";

# Routing

Expand All @@ -9,29 +9,29 @@ Several components in Atomic Elements support being rendered as an anchor tag.
This allows you to use the component as a link. When rendered as a link, each component supports the same props as an anchor tag such
as `href`, `target`, and `rel`.

By deafult, links perform native browser navigation. However, if your application supports client-side routing, you can
use the `RouterProvider` component to integrate with your routing library of choice.
Note that links to external sites or links with any other value for target except `target="_self"` attribute will always perform native browser navigation.

## Example

For example, the `Button` component can be rendered as an anchor tag by setting the `href` props.
The `Button` component can be rendered as an anchor tag by setting the `href` props.

```jsx
import { Button } from "@atomicjolt/atomic-elements";

<Button href="https://atomicjolt.com">Atomic Jolt</Button>;
<Button href="https://atomicjolt.com" target="_blank">Atomic Jolt</Button>;
```

The `Button` component will render as an anchor tag with the `href` attribute set to `https://atomicjolt.com`.

<Button href="https://atomicjolt.com">Atomic Jolt</Button>
<Example>
<Button href="https://atomicjolt.com" target="_blank">Atomic Jolt</Button>
</Example>

<br />
## Client Side Routing

## `RouterProvider`
By deafult, links perform native browser navigation. However, if your application supports client-side routing, you can
use the `RouterProvider` component to integrate with your routing library of choice.

The `RouterProvider` component allows you to integrate Atomic Elements with your client-side routing library of choice.
Note that links to external sites or links with any other value for target except `target="_self"` attribute will always perform native browser navigation.

```jsx
import { RouterProvider } from "@atomicjolt/atomic-elements";
Expand All @@ -48,12 +48,3 @@ function App() {
}
```

## Supported Components

The following components support being rendered as an anchor tag:

- `Button`
- `Link`
- `IconButton`
- `Menu.Item`
- `IconMenu.Item`
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ This is useful for adding custom styles or overriding the default styles. The pr
[classnames](https://github.com/JedWatson/classnames#readme) package to generate the final class name,
so you can take advantage of that to make your class names cleaner.

## Examples

Below are some common examples of using class names. Please reference the above package for more.

### Concatenate class names
## Concatenate class names

It will combine a list of classNames into a single string.

Expand All @@ -26,7 +24,7 @@ function App({ className }) {
}
```

### Conditional class names
## Conditional class names

The `is-hidden` class name will only be applied to the class when `hidden === true`

Expand Down
Loading

0 comments on commit 0886cc7

Please sign in to comment.