Skip to content

Commit 1d956a5

Browse files
committed
feat: Add ElevatedCard component with comprehensive functionality
- Introduce new ElevatedCard component with flexible configuration - Add stories demonstrating various ElevatedCard use cases - Implement comprehensive test suite for ElevatedCard - Export ElevatedCard in index.ts and types.ts - Update package version to 1.0.210
1 parent edfb4ad commit 1d956a5

File tree

20 files changed

+398
-109
lines changed

20 files changed

+398
-109
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@programmer_network/yail",
3-
"version": "1.0.209",
3+
"version": "1.0.210",
44
"description": "Programmer Network's official UI library for React",
55
"author": "Aleksandar Grbic - (https://programmer.network)",
66
"publishConfig": {

src/Components/Button/__snapshots__/Button.test.tsx.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
exports[`Button component > renders correctly - snapshot test 1`] = `
44
<DocumentFragment>
55
<button
6-
class="yl:select-none yl:px-3 yl:py-2 yl:font-semibold yl:tracking-tight yl:rounded-md yl:border-2 yl:border-primary yl:bg-primary yl:hover:text-primary yl:hover:bg-transparent yl:hover:text-primary yl:hover:fill-primary yl:text-background"
6+
class="yl:select-none yl:px-3 yl:py-2 yl:font-semibold yl:tracking-tight yl:rounded-md yl:cursor-pointer yl:border-2 yl:border-primary yl:bg-primary yl:hover:text-primary yl:hover:bg-transparent yl:hover:text-primary yl:hover:fill-primary yl:text-background"
77
type="button"
88
>
99
<div

src/Components/Button/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const Button: React.FC<IButtonProps> = (
2727
}
2828
) => {
2929
let cls =
30-
"yl:select-none yl:px-3 yl:py-2 yl:font-semibold yl:tracking-tight yl:rounded-md ";
30+
"yl:select-none yl:px-3 yl:py-2 yl:font-semibold yl:tracking-tight yl:rounded-md yl:cursor-pointer ";
3131

3232
if (variant === ButtonVariantEnum.PRIMARY) {
3333
cls += "yl:border-2 yl:border-primary ";
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { action } from "@storybook/addon-actions";
2+
3+
import Button from "Components/Button";
4+
import { ButtonVariantEnum } from "Components/Button/types";
5+
6+
import ElevatedCard from ".";
7+
8+
export default {
9+
title: "Components/ElevatedCard",
10+
component: ElevatedCard,
11+
parameters: {
12+
layout: "centered"
13+
},
14+
tags: ["autodocs"]
15+
};
16+
17+
export const Basic = () => (
18+
<ElevatedCard
19+
title='Curated Resources'
20+
description='Explore our collection of tools, libraries, and tutorials to enhance your development workflow.'
21+
/>
22+
);
23+
24+
export const WithIcon = () => (
25+
<ElevatedCard
26+
title='Curated Resources'
27+
description='Explore our collection of tools, libraries, and tutorials to enhance your development workflow.'
28+
icon={{
29+
iconName: "IconBook",
30+
className: "yl:w-6 yl:h-6 yl:text-primary"
31+
}}
32+
/>
33+
);
34+
35+
export const WithChildren = () => (
36+
<ElevatedCard
37+
title='Curated Resources'
38+
description='Explore our collection of tools, libraries, and tutorials to enhance your development workflow.'
39+
icon={{
40+
iconName: "IconAddYoutube",
41+
className: "yl:w-6 yl:h-6 yl:text-primary"
42+
}}
43+
>
44+
<div className='yl:mt-4 yl:flex yl:gap-2'>
45+
<Button>Explore</Button>
46+
<Button variant={ButtonVariantEnum.SECONDARY}>Learn More</Button>
47+
</div>
48+
</ElevatedCard>
49+
);
50+
51+
export const Clickable = () => (
52+
<ElevatedCard
53+
title='Curated Resources'
54+
description='Explore our collection of tools, libraries, and tutorials to enhance your development workflow.'
55+
icon={{
56+
iconName: "IconGroupWork",
57+
className: "yl:w-6 yl:h-6 yl:text-primary"
58+
}}
59+
onClick={() => action("Card clicked")()}
60+
/>
61+
);
62+
63+
export const CustomStyling = () => (
64+
<ElevatedCard
65+
title='Curated Resources'
66+
description='Explore our collection of tools, libraries, and tutorials to enhance your development workflow.'
67+
icon={{
68+
iconName: "IconSkull",
69+
className: "yl:w-6 yl:h-6 yl:text-primary"
70+
}}
71+
className='yl:bg-primary-50 yl:border yl:border-primary-100 yl:max-w-sm'
72+
/>
73+
);
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { fireEvent, render, screen } from "@testing-library/react";
2+
import { vi } from "vitest";
3+
4+
import ElevatedCard from "./";
5+
6+
describe("ElevatedCard component", () => {
7+
const title = "Curated Resources";
8+
const description =
9+
"Explore our collection of tools, libraries, and tutorials to enhance your development workflow.";
10+
11+
test("renders correctly - snapshot test", () => {
12+
const { asFragment } = render(
13+
<ElevatedCard title={title} description={description} />
14+
);
15+
16+
expect(asFragment()).toMatchSnapshot();
17+
});
18+
19+
test("renders title and description", () => {
20+
render(<ElevatedCard title={title} description={description} />);
21+
expect(screen.getByText(title)).toBeInTheDocument();
22+
expect(screen.getByText(description)).toBeInTheDocument();
23+
});
24+
25+
test("conditionally renders icon", () => {
26+
render(
27+
<ElevatedCard
28+
title={title}
29+
description={description}
30+
icon={{
31+
iconName: "IconBook",
32+
dataTestId: "icon-book"
33+
}}
34+
/>
35+
);
36+
expect(screen.getByTestId("icon-book")).toBeInTheDocument();
37+
});
38+
39+
test("renders children when provided", () => {
40+
const childText = "Child Content";
41+
render(
42+
<ElevatedCard title={title} description={description}>
43+
<div data-testid='child-content'>{childText}</div>
44+
</ElevatedCard>
45+
);
46+
expect(screen.getByTestId("child-content")).toBeInTheDocument();
47+
expect(screen.getByText(childText)).toBeInTheDocument();
48+
});
49+
50+
test("handles click events", () => {
51+
const onClick = vi.fn();
52+
render(
53+
<ElevatedCard title={title} description={description} onClick={onClick} />
54+
);
55+
56+
fireEvent.click(screen.getByTestId("elevated-card"));
57+
expect(onClick).toHaveBeenCalledTimes(1);
58+
});
59+
60+
test("applies custom class name", () => {
61+
const customClass = "custom-card-class";
62+
63+
render(
64+
<ElevatedCard
65+
title={title}
66+
description={description}
67+
className={customClass}
68+
/>
69+
);
70+
71+
const cardElement = screen.getByTestId("elevated-card");
72+
expect(cardElement).toHaveClass(customClass);
73+
});
74+
75+
test("uses custom data-testid when provided", () => {
76+
const customTestId = "custom-card-test-id";
77+
render(
78+
<ElevatedCard
79+
title={title}
80+
description={description}
81+
dataTestId={customTestId}
82+
/>
83+
);
84+
85+
expect(screen.getByTestId(customTestId)).toBeInTheDocument();
86+
});
87+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`ElevatedCard component > renders correctly - snapshot test 1`] = `
4+
<DocumentFragment>
5+
<div
6+
class="yl:flex yl:flex-col yl:rounded-lg yl:p-6 yl:border yl:border-border yl:bg-text/1"
7+
data-testid="elevated-card"
8+
>
9+
<div
10+
class="yl:space-y-2"
11+
>
12+
<h5
13+
class="yl:text-md yl:sm:text-lg yl:md:text-xl yl:text-text yl:break-words yl:overflow-hidden yl:font-bold"
14+
>
15+
Curated Resources
16+
</h5>
17+
<p
18+
class="yl:text-text yl:break-words yl:overflow-hidden"
19+
>
20+
Explore our collection of tools, libraries, and tutorials to enhance your development workflow.
21+
</p>
22+
</div>
23+
</div>
24+
</DocumentFragment>
25+
`;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import classNames from "classnames";
2+
import React from "react";
3+
4+
import Icon from "Components/Icon";
5+
import { IIconProps } from "Components/Icon/types";
6+
import { H5, Paragraph } from "Components/Typography";
7+
8+
import { IElevatedCardProps } from "./types";
9+
10+
const ElevatedCard: React.FC<IElevatedCardProps> = ({
11+
title,
12+
description,
13+
icon,
14+
children,
15+
className,
16+
onClick,
17+
dataTestId = "elevated-card"
18+
}) => {
19+
return (
20+
<div
21+
data-testid={dataTestId}
22+
className={classNames(
23+
"yl:flex yl:flex-col yl:rounded-lg yl:p-6 yl:border yl:border-border yl:bg-text/1",
24+
{
25+
"yl:cursor-pointer yl:hover:bg-text/3": onClick
26+
},
27+
className
28+
)}
29+
onClick={onClick}
30+
>
31+
{icon && (
32+
<div className='yl:mb-4'>
33+
<div className='yl:inline-flex yl:items-center yl:justify-center yl:rounded-md yl:p-2 yl:border yl:border-primary'>
34+
<Icon {...(icon as IIconProps)} />
35+
</div>
36+
</div>
37+
)}
38+
<div className='yl:space-y-2'>
39+
<H5>{title}</H5>
40+
{description && <Paragraph>{description}</Paragraph>}
41+
{children && <div className='yl:mt-4'>{children}</div>}
42+
</div>
43+
</div>
44+
);
45+
};
46+
47+
export default ElevatedCard;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { ReactNode } from "react";
2+
3+
import { IIconProps } from "Components/Icon/types";
4+
5+
export interface IElevatedCardProps {
6+
title: string;
7+
description?: string;
8+
icon?: IIconProps;
9+
children?: ReactNode;
10+
className?: string;
11+
onClick?: () => void;
12+
dataTestId?: string;
13+
}

src/Components/Inputs/Tiptap/Components/Actions/index.tsx

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,33 @@ const TiptapActions: FC<{
1111
}> = ({ isEditorEmpty, actions }) => {
1212
const { isConfirming, buttons, onAction } = actions;
1313

14+
if (isEditorEmpty) {
15+
return null;
16+
}
17+
1418
return (
15-
<div className='yl:flex yl:justify-end yl:relative yl:-right-2'>
16-
<div className='yl:rounded-md yl:border-2 yl:border-border yl:p-1'>
17-
<div className='yl:flex yl:items-center yl:gap-1 yl:px-1'>
19+
<div className='yl:flex yl:justify-end yl:relative yl:top-[2px] yl:right-[-2px]'>
20+
<div className='yl:rounded-md yl:rounded-tr-none yl:rounded-bl-none yl:border-2 yl:border-border/40 yl:p-1 yl:border-r-0 yl:border-b-0 yl:bg-text/1'>
21+
<div className='yl:flex yl:items-center'>
22+
{buttons.includes(TiptapActionsEnum.CANCEL) && (
23+
<Icon
24+
onClick={() => onAction(TiptapActionsEnum.CANCEL)}
25+
iconName='IconCloseCircle'
26+
className='yl:w-10 yl:cursor-pointer yl:text-text/30 yl:hover:text-text'
27+
/>
28+
)}
1829
{buttons.includes(TiptapActionsEnum.CONFIRM) && (
1930
<Icon
20-
onClick={
21-
!isEditorEmpty
22-
? () => onAction(TiptapActionsEnum.CONFIRM)
23-
: () => null
24-
}
31+
onClick={() => onAction(TiptapActionsEnum.CONFIRM)}
2532
iconName={!isConfirming ? "IconCheck" : "IconSpinner"}
2633
className={classNames(
27-
"yl:w-8 yl:cursor-pointer yl:fill-primary",
34+
"yl:w-10 yl:cursor-pointer yl:fill-success",
2835
{
29-
"yl:text-text/60": isEditorEmpty,
30-
"yl:text-text": !isEditorEmpty
36+
"yl:text-success": !isEditorEmpty
3137
}
3238
)}
3339
/>
3440
)}
35-
{buttons.includes(TiptapActionsEnum.CANCEL) && (
36-
<Icon
37-
onClick={() => onAction(TiptapActionsEnum.CANCEL)}
38-
iconName='IconCloseCircle'
39-
className='yl:w-8 yl:cursor-pointer yl:text-text/60 yl:hover:text-text'
40-
/>
41-
)}
4241
</div>
4342
</div>
4443
</div>

0 commit comments

Comments
 (0)