Skip to content

Commit

Permalink
Added badge component
Browse files Browse the repository at this point in the history
  • Loading branch information
Shubhdeep12 committed Oct 19, 2024
1 parent 7631960 commit cacc3f3
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 1 deletion.
7 changes: 7 additions & 0 deletions apps/docs/src/examples/badge.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.badge {
display: inline-block;
padding: 0.2em 0.4em;
background-color: hsl(201 96% 32%);
color: white; border-radius: 12px;
font-size: 0.875rem;
}
11 changes: 11 additions & 0 deletions apps/docs/src/examples/badge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Badge } from "@kobalte/core/badge";

import style from "./badge.module.css";

export function BasicExample() {
return (
<Badge class={style.badge} textValue="Unread messages: 5">
5 messages
</Badge>
);
}
5 changes: 5 additions & 0 deletions apps/docs/src/routes/docs/core.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ const CORE_NAV_SECTIONS: NavSection[] = [
title: "Alert Dialog",
href: "/docs/core/components/alert-dialog",
},
{
title: "Badge",
href: "/docs/core/components/badge",
status: "new",
},
{
title: "Breadcrumbs",
href: "/docs/core/components/breadcrumbs",
Expand Down
94 changes: 94 additions & 0 deletions apps/docs/src/routes/docs/core/components/badge.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Preview, TabsSnippets, Callout } from "../../../../components";
import { BasicExample } from "../../../../examples/badge";

# Badge

A `Badge` component is used to display small pieces of information or status indicators.

## Import

```ts
import { Badge } from "@kobalte/core/badge";
// or
import { Root } from "@kobalte/core/badge";
// or (deprecated)
import { Badge } from "@kobalte/core";
```

## Features

- Auto-populated ARIA labeling via the textValue prop for enhanced accessibility.
- Built-in ARIA support with role="status" to communicate dynamic updates.

## Anatomy

The badge consists of:

- **Badge:** The root container for the badge that supports accessibility and content customization.

```tsx
<Badge textValue="3 online users">3</Badge>
```

## Example

<Preview>
<BasicExample />
</Preview>

<TabsSnippets>
<TabsSnippets.List>
<TabsSnippets.Trigger value="index.tsx">index.tsx</TabsSnippets.Trigger>
<TabsSnippets.Trigger value="style.css">style.css</TabsSnippets.Trigger>
</TabsSnippets.List>
{/* <!-- prettier-ignore-start -->*/}
<TabsSnippets.Content value="index.tsx">
```tsx
import { Badge } from "@kobalte/core/badge";
import "./style.css";

function App() {
return (
<Badge class="badge" textValue="Unread messages: 5">
5 messages
</Badge>
);
}
```

</TabsSnippets.Content>
<TabsSnippets.Content value="style.css">
```css
.badge {
display: inline-block;
padding: 0.2em 0.4em;
background-color: hsl(201 96% 32%);
color: white; border-radius: 12px;
font-size: 0.875rem;
}
```

</TabsSnippets.Content>
{/* <!-- prettier-ignore-end -->*/}
</TabsSnippets>


## API Reference

### Badge

`Badge` is equivalent to the `Root` import from `@kobalte/core/badge` (and deprecated `Badge.Root`).

| Prop | Description |
| :----------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| textValue | `string \| undefined` <br/> The textValue prop is essential for accessibility. It auto-populates the aria-label attribute, allowing screen readers to interpret the badges content. |

| Data attribute | Description |
| :------------- | :------------------------------------------------------------ |
| data-label | Present when textValue is passed or aria-label is passed as a prop. |

## Rendered elements

| Component | Default rendered element |
| :---------------- | :------------------------ |
| `Badge` | `span` |
7 changes: 6 additions & 1 deletion packages/core/dev/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Badge } from "../src/badge";

export default function App() {
return (
<></>
<>
<Badge as="div" class="aaall" aria-label="qqqq" aria-checked textValue="aaaa">aaa1123</Badge>
<Badge textValue="10">11</Badge>
</>
);
}
46 changes: 46 additions & 0 deletions packages/core/src/badge/badge-root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Portions of this file are based on code from react-spectrum.
* Apache License Version 2.0, Copyright 2020 Adobe.
*
* Credits to the React Spectrum team:
* https://github.com/adobe/react-spectrum/blob/main/packages/%40react-spectrum/badge/src/Badge.tsx
*/

import { type ValidComponent, splitProps } from "solid-js";
import { Polymorphic, type PolymorphicProps } from "../polymorphic";

export interface BadgeRootOptions {
/**
* Optional textValue for badge.
*/
textValue?: string;
}

export interface BadgeRootCommonProps<T extends HTMLElement = HTMLElement> {
"aria-label"?: string;
}

export interface BadgeRootRenderProps extends BadgeRootCommonProps {}

export type BadgeRootProps<
T extends ValidComponent | HTMLElement = HTMLElement,
> = BadgeRootOptions & Partial<BadgeRootCommonProps>;

export function BadgeRoot<T extends ValidComponent = "span">(
props: PolymorphicProps<T, BadgeRootProps<T>>,
) {
const [local, others] = splitProps(props, ["textValue", "aria-label"]);

const ariaLabel = () => local["aria-label"] || local.textValue;

return (
<Polymorphic<BadgeRootRenderProps>
as="span"
role="status"
aria-label={ariaLabel()}
{...others}
>
{others.children}
</Polymorphic>
);
}
33 changes: 33 additions & 0 deletions packages/core/src/badge/badge.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { render } from "@solidjs/testing-library";
import * as Badge from ".";

describe("Badge", () => {
it("badge with textValue", () => {
const { getByRole } = render(() => (
<Badge.Root textValue="Online">Online</Badge.Root>
));

const badge = getByRole("status");
expect(badge).toHaveTextContent("Online");
});

it("badge with defined aria-label", () => {
const { getByText } = render(() => (
<Badge.Root textValue="Online" aria-label="User is online">
Online
</Badge.Root>
));

const badge = getByText("Online");
expect(badge).toHaveAttribute("aria-label", "User is online");
});

it("badge with default aria-labe as textValue", () => {
const { getByRole } = render(() => (
<Badge.Root textValue="Online">Online</Badge.Root>
));

const badge = getByRole("status");
expect(badge).toHaveAttribute("aria-label", "Online");
});
});
17 changes: 17 additions & 0 deletions packages/core/src/badge/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {
type BadgeRootCommonProps,
type BadgeRootOptions,
type BadgeRootProps,
type BadgeRootRenderProps,
BadgeRoot as Root,
} from "./badge-root";

export type {
BadgeRootOptions,
BadgeRootCommonProps,
BadgeRootRenderProps,
BadgeRootProps,
};
export { Root };

export const Badge = Root;
1 change: 1 addition & 0 deletions packages/core/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * from "./toast/toaster";
export * as Accordion from "./accordion";
export * as Alert from "./alert";
export * as AlertDialog from "./alert-dialog";
export * as Badge from "./badge";
export * as Breadcrumbs from "./breadcrumbs";
export * as Button from "./button";
//export * as Calendar from "./calendar";
Expand Down

0 comments on commit cacc3f3

Please sign in to comment.