Skip to content

Commit 73cb94d

Browse files
authored
Merge pull request #182 from rtCamp/feat/component-label
Feat: Component Label
2 parents e2b3bb1 + 43b1d1d commit 73cb94d

10 files changed

Lines changed: 157 additions & 2 deletions

File tree

packages/frappe-ui-react/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,21 @@
4242
"@headlessui/react": "^2.2.9",
4343
"@popperjs/core": "^2.11.8",
4444
"@tailwindcss/typography": "^0.5.19",
45+
"@tiptap/extension-blockquote": "^3.17.1",
4546
"@tiptap/extension-code-block-lowlight": "^3.17.1",
4647
"@tiptap/extension-highlight": "^3.17.1",
47-
"@tiptap/extension-blockquote": "^3.17.1",
4848
"@tiptap/extension-horizontal-rule": "^3.17.1",
49-
"@tiptap/extension-strike": "^3.17.1",
5049
"@tiptap/extension-list": "^3.17.1",
5150
"@tiptap/extension-placeholder": "^3.17.1",
51+
"@tiptap/extension-strike": "^3.17.1",
5252
"@tiptap/extension-table": "^3.17.1",
5353
"@tiptap/extension-task-list": "^3.17.1",
5454
"@tiptap/extension-text-align": "^3.17.1",
5555
"@tiptap/extension-text-style": "^3.17.1",
5656
"@tiptap/pm": "^3.17.1",
5757
"@tiptap/react": "^3.17.1",
5858
"@tiptap/starter-kit": "^3.17.1",
59+
"class-variance-authority": "^0.7.1",
5960
"clsx": "^2.1.1",
6061
"dayjs": "^1.11.19",
6162
"dompurify": "^3.3.1",
@@ -68,6 +69,7 @@
6869
"react-grid-layout": "^1.5.3",
6970
"react-resizable": "^3.1.3",
7071
"styled-components": "^6.3.8",
72+
"tailwind-merge": "^3.4.0",
7173
"tailwindcss": "^4.1.18"
7274
},
7375
"devDependencies": {

packages/frappe-ui-react/src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export * from "./fileUploader";
1919
export * from "./formControl";
2020
export * from "./gridLayout";
2121
export * from "./hooks";
22+
export * from "./label";
2223
export * from "./listview";
2324
export * from "./password";
2425
export * from "./progress";
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { Label } from "./label";
2+
export * from "./types";
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { Meta, StoryObj } from "@storybook/react-vite";
2+
import { Label } from "./label";
3+
import { Field } from "@base-ui/react/field";
4+
5+
const meta: Meta<typeof Label> = {
6+
title: "Components/Label",
7+
component: Label,
8+
parameters: {
9+
layout: "centered",
10+
},
11+
tags: ["autodocs"],
12+
argTypes: {
13+
children: {
14+
description: "The label text component",
15+
},
16+
},
17+
};
18+
19+
export default meta;
20+
type Story = StoryObj<typeof meta>;
21+
22+
export const Default: Story = {
23+
args: {
24+
children: "Label Text",
25+
},
26+
render: ({ children }) => {
27+
return (
28+
<Field.Root>
29+
<Label>{children}</Label>
30+
</Field.Root>
31+
);
32+
},
33+
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* External dependencies.
3+
*/
4+
import { Field } from "@base-ui/react/field";
5+
import { cva } from "class-variance-authority";
6+
7+
/**
8+
* Internal dependencies.
9+
*/
10+
import type { LabelProps } from "./types";
11+
import { cn } from "../../utils";
12+
13+
const labelVariants = cva(
14+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
15+
);
16+
17+
export const Label: React.FC<LabelProps> = ({
18+
children,
19+
className,
20+
...props
21+
}) => {
22+
return (
23+
<Field.Label className={cn(labelVariants(), className)} {...props}>
24+
{children}
25+
</Field.Label>
26+
);
27+
};
28+
29+
Label.displayName = "Label";
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
2+
children: React.ReactNode;
3+
className?: string;
4+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* External dependencies.
3+
*/
4+
import { type ClassValue, clsx } from "clsx";
5+
import { twMerge } from "tailwind-merge";
6+
7+
export function cn(...args: ClassValue[]) {
8+
return twMerge(clsx(args));
9+
}

packages/frappe-ui-react/src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from "./cn";
12
export { default as noop } from "./noop";
23
export * from "./debounce";
34
export * from "./fileUploadHandler";
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import "@testing-library/jest-dom";
2+
3+
import { cn } from "../cn";
4+
5+
describe("cn", () => {
6+
it("should return empty string when no arguments provided", () => {
7+
expect(cn()).toBe("");
8+
});
9+
10+
it("should return single class name", () => {
11+
expect(cn("px-4")).toBe("px-4");
12+
});
13+
14+
it("should concatenate multiple class names", () => {
15+
expect(cn("px-4", "py-2", "rounded")).toBe("px-4 py-2 rounded");
16+
});
17+
18+
it("should handle conditional class names with boolean values", () => {
19+
// eslint-disable-next-line no-constant-binary-expression
20+
expect(cn("px-4", true && "py-2", false && "rounded")).toBe("px-4 py-2");
21+
});
22+
23+
it("should handle undefined and null values", () => {
24+
expect(cn("px-4", undefined, null, "py-2")).toBe("px-4 py-2");
25+
});
26+
27+
it("should handle objects with conditional classes", () => {
28+
expect(cn({ "px-4": true, "py-2": false, rounded: true })).toBe(
29+
"px-4 rounded"
30+
);
31+
});
32+
33+
it("should handle arrays of class names", () => {
34+
expect(cn(["px-4", "py-2", "rounded"])).toBe("px-4 py-2 rounded");
35+
});
36+
37+
it("should handle mixed arguments", () => {
38+
expect(
39+
cn(
40+
"base",
41+
{ "px-4": true, "py-2": false },
42+
["rounded", "shadow"],
43+
undefined,
44+
"text-sm"
45+
)
46+
).toBe("base px-4 rounded shadow text-sm");
47+
});
48+
49+
it("should handle empty strings", () => {
50+
expect(cn("px-4", "", "py-2")).toBe("px-4 py-2");
51+
});
52+
53+
it("should deduplicate class names", () => {
54+
expect(cn("px-4", "py-2", "px-4")).toBe("py-2 px-4");
55+
});
56+
});

pnpm-lock.yaml

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)