Skip to content

Commit

Permalink
Big Rewrite - Add reusable wrappers, add more components (#43)
Browse files Browse the repository at this point in the history
* docs: add code guidelines

* fix: ESLint downgrade

* refactor: update default components for code styling

* refactor(default): update and fix component styles

* refactor: export code styling rule

* refactor: remove showRadio prop

* refactor: update searchfield to use a group

* feat: add field, modify searchfield to use

* feat: add datefield and timefield

* feat: add form helper components

* refactor: move props

* feat: add datepicker demos using input

* refactor: remove date picker button, move to examples

* refactor: tabs component

* feat: add listbox, modify popover, add dialog

* update select and popover, add listbox

* update select, combobox and listbox

* update menu component

* dialog and popover

* add tag group

* update calendar

* add numberfield, add grid list

* Add missing components to new-york

* redo button demos

* Add gridlist demos

* update docs and demos

* more demos and components

* Datepicker demos

* add range calendar

* more components done

* add form demos

* more demos and fixes

* combobox

* finish demos kinda

* update docs mdx

* update new york demos

* fix toolbar

* update dmoes

* add new-york back

* fix build

* run linter

* run prettier

* remove storybook

* update lockfile

* add reusable wrappers

* update icons

* add reusable calendar

* add reusable wrapper demos

* fix export replacement

* add missing reusable wrappers

* registry commit

* update docs

* remove disabled sort

* update reusable wrapper docs

* remove label for field

* rename input

* remove textarea

* update registry

* update registry

* update registry 2

* remove input

* change links for release

* update announcement

* update docs

* update docs
  • Loading branch information
jolbol1 authored Jul 15, 2024
1 parent 6b69bc1 commit 8a8e14c
Show file tree
Hide file tree
Showing 831 changed files with 26,808 additions and 19,208 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ yarn-error.log*
# Misc
.DS_Store
*.pem
storybook-static/


# turbo
.turbo
Expand Down
1 change: 0 additions & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
auto-install-peers = true
enable-pre-post-scripts=true
public-hoist-pattern[]=*storybook*
public-hoist-pattern[]=*eslint-plugin-*
public-hoist-pattern[]=@typescript-eslint/eslint-plugin
public-hoist-pattern[]=@typescript-eslint/parser
249 changes: 249 additions & 0 deletions CODE-STYLE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
# JollyUI Code Styling Guidelines

## Naming Conventions

### Aliasing Aria Components

To avoid naming collisions between JollyUI components and React Aria Components, use the alias Aria(Component) when importing React Aria Components.

#### Example

##### Good:

```tsx
import { Button as AriaButton } from "@react-aria/button"

const Button = ({ className, variant, size, ...props }: ButtonProps) => {
return <AriaButton {...props} />
}
```

##### Bad:

```tsx
import { Button as _Button } from "@react-aria/button"

const Button = ({ className, variant, size, ...props }: ButtonProps) => {
return <_Button {...props} />
}
```

## Styling

### Using Data Attributes for Stateful Styles

Components often support multiple UI states (e.g., pressed, hovered, selected, etc.). React Aria Components expose these states using data attributes, which you can target in CSS selectors. They function similarly to custom CSS pseudo-classes.

In order to ensure high-quality interactions across browsers and devices, React Aria Components include states such as data-hovered and data-pressed. These are similar to CSS pseudo-classes such as :hover and :active, but they work consistently between mouse, touch, and keyboard modalities. You can read more about this in our blog post series and our Interactions overview.

All states supported by each component are listed in the Styling section of their documentation.

#### Example

##### Good:

```tsx
className = "data-[hovered]:bg-red-600"
```

##### Bad:

```tsx
className = "hover:bg-red-600"
```

#### Reasoning for Using Data Attributes

The decision to use data attributes over features such as render props was influenced by several factors:

- Migration and Compatibility: The library is designed with the migration and compatibility of shadcn in mind. Using data attributes makes migration easier for users and is more understandable.
- Reduced Complexity: The renderProps method would require a lot more cva styles for each component, adding unnecessary complexity.
- Minimal Tailwind Configuration: Although React Aria provides a [Tailwind plugin](https://react-spectrum.adobe.com/react-aria/styling.html#plugin) that converts data-[selected]:bg-red-600 to selected:bg-red-600, I chose not to use this to keep styles easily copy-pasteable with minimal Tailwind configuration.
- Debugging: Being able to see the data attribute and its selector in the rendered className helps with debugging. If I used render props, it would only show the computed className.

### Supporting className renderProps functions

To ensure all className props that support renderProps can be styled with either a string or a function, we can use the `composeRenderProps` helper. Use the following pattern:

#### Example

##### Good:

```tsx
const Popover = ({ className, offset = 4, ...props }: PopoverProps) => (
<AriaPopover
offset={offset}
className={composeRenderProps(className, (className) =>
cn("z-50 w-72", className)
)}
{...props}
/>
)
```

##### Bad:

```tsx
const Popover = ({ className, offset = 4, ...props }: PopoverProps) => (
<AriaPopover
offset={offset}
className={(values) => cn("z-50 w-72", className)}
{...props}
/>
)
```

By using `typeof className === "function" ? className(values) : className`, you can handle both string and function types for the className prop, making the component more flexible and easier to use with renderProps. The only issue in this approach is that users using renderProps should be aware that as I use data attributes for styling, they will have to override these as well.

### Organizing Data Attributes in the cn Function

To maintain readability and organization in your components, ensure data attributes are clearly laid out in their own sections within the cn function, with appropriate comments. This helps in understanding the styling logic and makes the code more maintainable.

#### Example

##### Good:

```tsx
const Popover = ({ className, offset = 4, ...props }: PopoverProps) => (
<AriaPopover
offset={offset}
className={composeRenderProps(className, (className) =>
cn(
"z-50 w-72 overflow-y-auto rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none",
/* Entering */
"data-[entering]:animate-in data-[exiting]:animate-out data-[entering]:fade-in-0",
/* Exiting */
"data-[exiting]:fade-out-0 data-[exiting]:zoom-out-95",
/* Placement */
"data-[placement=bottom]:slide-in-from-top-2 data-[placement=left]:slide-in-from-right-2 data-[placement=right]:slide-in-from-left-2 data-[placement=top]:slide-in-from-bottom-2",
className
)
)}
{...props}
/>
)
```

##### Bad:

```tsx
const Popover = ({ className, offset = 4, ...props }: PopoverProps) => (
<AriaPopover
offset={offset}
className={composeRenderProps(className, (className) =>
cn(
"z-50 w-72 overflow-y-auto rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[entering]:animate-in data-[exiting]:animate-out data-[entering]:fade-in-0 data-[exiting]:fade-out-0 data-[exiting]:zoom-out-95 data-[placement=bottom]:slide-in-from-top-2 data-[placement=left]:slide-in-from-right-2 data-[placement=right]:slide-in-from-left-2 data-[placement=top]:slide-in-from-bottom-2",
className
)
)}
{...props}
/>
)
```

By organizing data attributes in their own sections with comments, the code becomes easier to read and maintain, and the purpose of each styling rule is more apparent.

## Component Composition

### Supporting Children as RenderProp Functions

Where possible, ensure children can be renderProp functions as well, using the `composeRenderProps` helper. This allows greater flexibility and control over component rendering, enabling users to dynamically style or modify components based on their states.

#### Example

##### Good:

```tsx
const Checkbox = ({ className, children, ...props }: CheckboxProps) => (
<AriaCheckbox {...props}>
{composeRenderProps(children, (children, renderProps) => (
<>
<div>
{renderProps.isIndeterminate ? (
<Minus />
) : renderProps.isSelected ? (
<Check />
) : null}
</div>
{children}
</>
))}
</Checkbox>
)
```

##### Bad

```tsx
const Checkbox = ({ className, children, ...props }: CheckboxProps) => (
<AriaCheckbox {...props}>
{(renderProps) => (
<>
<div>
{renderProps.isIndeterminate ? (
<Minus />
) : renderProps.isSelected ? (
<Check />
) : null}
</div>
{children}
</>
)}
</Checkbox>
)
```

### Do Not Use forwardRef

Forward ref is being deprecated, and in React 19 you can pass ref as a prop. Previously, all components were wrapped in forwardRef like shadcn, but with this upcoming change, forwardRef is no longer necessary. Going forward, you will be able to pass ref directly as a prop. Refer to the [React 19 blog post](https://react.dev/blog/2024/04/25/react-19#ref-as-a-prop) for more details.

If you have an advances use-case for ref, then you can always add it since the code is in your codebase.

#### Example

##### Good:

```tsx
const Button = ({ className, ...props }) => (
<AriaButton className={className} {...props} />
)
```

##### Bad:

```tsx
const Button = forwardRef(({ className, ...props }, ref) => (
<AriaButton ref={ref} className={className} {...props} />
))
```

By adopting these practices, JollyUI components will be more flexible, easier to maintain, and ready for future updates in React.

### Handle All Exports at the End of the File

To maintain a clear and organized structure, all export statements should be placed at the end of the file. This avoids inline exports and keeps the codebase consistent.

#### Example

##### Good:

```tsx

const Button = ({ className, ...props }: ButtonProps) => (
<AriaButton className={className} {...props} />
)

// Export at the end
export { Button }
export type { ButtonProps }
```

##### Bad:

```tsx
export const Button = ({ className, ...props }: ButtonProps) => (
<AriaButton className={className} {...props} />
)
```

By placing all export statements at the end of the file, the code is easier to read and maintain, and it ensures a consistent style throughout the codebase.
11 changes: 4 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Thanks for your interest in contributing to Jolly-UI

## Code Style Guidelines

Please ensure you follow the [code style guidelines](./CODE-STYLE.md) when contributing.

## About this repository

This repository is a monorepo.
Expand Down Expand Up @@ -36,7 +40,6 @@ packages
| `apps/docs/components` | The React components for the website. |
| `apps/docs/content` | The content for the website. |
| `apps/docs/registry` | The registry for the components. |
| `apps/storybook` | Storbook for components |

## Development

Expand Down Expand Up @@ -76,12 +79,6 @@ pnpm install
pnpm --filter=docs dev
```

2. To run the storybook:

```bash
pnpm --filter=storybook dev
```

## Documentation

The documentation for this project is located in the `docs` workspace. You can run the documentation locally by running the following command:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ Licensed under the [MIT license](https://github.com/shadcn/ui/blob/main/LICENSE.
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
Loading

0 comments on commit 8a8e14c

Please sign in to comment.