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

Enhancement : Badge Component #66555

Merged
merged 24 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
6 changes: 6 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,12 @@
"markdown_source": "../packages/components/src/autocomplete/README.md",
"parent": "components"
},
{
"title": "Badge",
"slug": "badge",
"markdown_source": "../packages/components/src/badge/README.md",
"parent": "components"
},
Vrishabhsk marked this conversation as resolved.
Show resolved Hide resolved
{
"title": "BaseControl",
"slug": "base-control",
Expand Down
38 changes: 38 additions & 0 deletions packages/components/src/badge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Badge
Vrishabhsk marked this conversation as resolved.
Show resolved Hide resolved

`Badge` is a `reusable component` to display important information in (not limited to) `data-views`.

## Usage

```jsx
import { Badge } from '@wordpress/components';

const ExampleBadge = () => {
return (
<Badge className="my-badge" as="span">
Code is Poetry
</Badge>
);
};
```

## Props

### `className`: `string`

Additional classes for the badge component.

- Required: No

### `as`: `ElementType`

Component type that will be used to render the badge component.

- Required: No
- Default: `div`

### `children`: `ReactNode`

The content to be displayed within the `Badge`.

- Required: Yes
27 changes: 27 additions & 0 deletions packages/components/src/badge/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* External dependencies
*/
import clsx from 'clsx';

/**
* Internal dependencies
*/
import type { BadgeProps } from './types';

function Badge( {
className,
as: Component = 'div',
children,
...props
}: BadgeProps ) {
Vrishabhsk marked this conversation as resolved.
Show resolved Hide resolved
return (
<Component
className={ clsx( 'components-badge', className ) }
{ ...props }
>
{ children }
</Component>
);
}

export default Badge;
37 changes: 37 additions & 0 deletions packages/components/src/badge/stories/index.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* External dependencies
*/
import type { Meta, StoryFn } from '@storybook/react';

/**
* Internal dependencies
*/
import Badge from '..';

const meta: Meta< typeof Badge > = {
component: Badge,
title: 'Components/Containers/Badge',
argTypes: {
className: {
control: { type: 'text' },
},
as: {
control: { type: 'select' },
options: [ 'div', 'span' ],
},
children: {
control: { type: null },
},
},
};

export default meta;

const Template: StoryFn< typeof Badge > = ( args ) => {
Vrishabhsk marked this conversation as resolved.
Show resolved Hide resolved
return <Badge { ...args } />;
};

export const Default = Template.bind( {} );
Default.args = {
children: 'Code is Poetry',
};
11 changes: 11 additions & 0 deletions packages/components/src/badge/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.components-badge {
background: $gray-100;
color: $gray-800;
padding: $grid-unit-05;
Vrishabhsk marked this conversation as resolved.
Show resolved Hide resolved
border-radius: $radius-small;
font-size: 12px;
Vrishabhsk marked this conversation as resolved.
Show resolved Hide resolved
font-weight: 400;
flex-shrink: 0;
line-height: $grid-unit-05 * 5;
Vrishabhsk marked this conversation as resolved.
Show resolved Hide resolved
width: fit-content;
}
66 changes: 66 additions & 0 deletions packages/components/src/badge/test/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* External dependencies
*/
import { render, screen } from '@testing-library/react';

/**
* Internal dependencies
*/
import Badge from '..';

describe( 'Badge', () => {
it( 'should render correctly with default props', () => {
render( <Badge>Code is Poetry</Badge> );
const badge = screen.getByText( 'Code is Poetry' );
expect( badge ).toBeInTheDocument();
expect( badge.tagName ).toBe( 'DIV' ); // Default element should be a div
expect( badge ).toHaveClass( 'components-badge' );
} );

it( 'should render as a span when specified', () => {
render( <Badge as="span">Code is Poetry</Badge> );
const badge = screen.getByText( 'Code is Poetry' );
expect( badge.tagName ).toBe( 'SPAN' );
expect( badge ).toHaveClass( 'components-badge' );
} );

it( 'should render as a custom element when specified', () => {
render( <Badge as="article">Code is Poetry</Badge> );
const badge = screen.getByText( 'Code is Poetry' );
expect( badge.tagName ).toBe( 'ARTICLE' );
expect( badge ).toHaveClass( 'components-badge' );
} );

it( 'should combine custom className with default class', () => {
render( <Badge className="custom-class">Code is Poetry</Badge> );
const badge = screen.getByText( 'Code is Poetry' );
expect( badge ).toHaveClass( 'components-badge' );
expect( badge ).toHaveClass( 'custom-class' );
} );

it( 'should render children correctly', () => {
render(
<Badge>
<span>Nested</span> Content
</Badge>
);

const badge = screen.getByText( ( content, element ) => {
return element?.classList?.contains( 'components-badge' ) ?? false;
} );

expect( badge ).toBeInTheDocument();
expect( badge ).toHaveClass( 'components-badge' );
expect( badge ).toHaveTextContent( 'Nested Content' );

const nestedSpan = screen.getByText( 'Nested' );
expect( nestedSpan.tagName ).toBe( 'SPAN' );
} );

it( 'should pass through additional props', () => {
render( <Badge data-testid="custom-badge">Code is Poetry</Badge> );
const badge = screen.getByTestId( 'custom-badge' );
expect( badge ).toHaveTextContent( 'Code is Poetry' );
expect( badge ).toHaveClass( 'components-badge' );
} );
} );
19 changes: 19 additions & 0 deletions packages/components/src/badge/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* External dependencies
*/
import type { ElementType, ReactNode } from 'react';

export type BadgeProps = {
/**
* Element to display inside the badge.
*/
children: ReactNode;
/**
* Additional classes for the badge component.
*/
className?: string;
/**
* Component type that will be used to render the badge component.
*/
as?: ElementType;
};
1 change: 1 addition & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ export {
} from './higher-order/with-focus-return';
export { default as withNotices } from './higher-order/with-notices';
export { default as withSpokenMessages } from './higher-order/with-spoken-messages';
export { default as Badge } from './badge';
Vrishabhsk marked this conversation as resolved.
Show resolved Hide resolved

// Private APIs.
export { privateApis } from './private-apis';
1 change: 1 addition & 0 deletions packages/components/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// Components
@import "./animate/style.scss";
@import "./autocomplete/style.scss";
@import "./badge/styles.scss";
@import "./button-group/style.scss";
@import "./button/style.scss";
@import "./checkbox-control/style.scss";
Expand Down
Loading