@fpkit/acss 0.5.5 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/libs/chunk-PWVRDQ3R.js +8 -0
- package/libs/chunk-PWVRDQ3R.js.map +1 -0
- package/libs/chunk-SVS4MX3U.cjs +31 -0
- package/libs/chunk-SVS4MX3U.cjs.map +1 -0
- package/libs/{icons-1f5afc0c.d.ts → icons-31ace3de.d.ts} +86 -49
- package/libs/icons.cjs +2 -2
- package/libs/icons.d.cts +1 -1
- package/libs/icons.d.ts +1 -1
- package/libs/icons.js +1 -1
- package/libs/index.cjs +34 -34
- package/libs/index.cjs.map +1 -1
- package/libs/index.d.cts +46 -15
- package/libs/index.d.ts +46 -15
- package/libs/index.js +7 -7
- package/libs/index.js.map +1 -1
- package/package.json +4 -3
- package/src/components/README.mdx +84 -0
- package/src/components/alert/README.mdx +86 -0
- package/src/components/alert/alert.mdx +74 -0
- package/src/components/alert/alert.scss +80 -0
- package/src/components/alert/alert.stories.tsx +132 -0
- package/src/components/alert/alert.tsx +154 -0
- package/src/components/alert/elements/README.mdx +77 -0
- package/src/components/alert/elements/dismiss-button.stories.tsx +31 -0
- package/src/components/alert/elements/dismiss-button.tsx +28 -0
- package/src/components/badge/badge.mdx +124 -0
- package/src/components/badge/badge.scss +4 -4
- package/src/components/buttons/button.scss +1 -0
- package/src/components/buttons/button.test.tsx +72 -72
- package/src/components/details/details.scss +20 -1
- package/src/components/dialog/README.mdx +187 -0
- package/src/components/dialog/dialog-modal.stories.tsx +113 -0
- package/src/components/dialog/dialog-modal.tsx +111 -0
- package/src/components/dialog/dialog.scss +28 -13
- package/src/components/dialog/dialog.stories.tsx +85 -30
- package/src/components/dialog/dialog.tsx +106 -61
- package/src/components/dialog/hooks/useClickOutside.ts +33 -0
- package/src/components/dialog/views/README.mdx +182 -0
- package/src/components/dialog/views/dialog-footer.tsx +45 -0
- package/src/components/dialog/{view → views}/dialog-header.stories.tsx +3 -4
- package/src/components/dialog/views/dialog-header.tsx +61 -0
- package/src/components/icons/components/add.tsx +14 -14
- package/src/components/icons/components/alert-solid.tsx +36 -0
- package/src/components/icons/components/alert-square-solid.tsx +36 -0
- package/src/components/icons/components/info-solid.tsx +40 -0
- package/src/components/icons/components/info.tsx +36 -0
- package/src/components/icons/components/question-solid.tsx +36 -0
- package/src/components/icons/components/success-solid.tsx +36 -0
- package/src/components/icons/components/warn-solid.tsx +36 -0
- package/src/components/icons/icon.stories.tsx +42 -0
- package/src/components/icons/icon.tsx +57 -41
- package/src/components/icons/index.ts +36 -29
- package/src/components/ui.tsx +28 -25
- package/src/decorators/instructions.tsx +44 -0
- package/src/hooks/useDialogClickHandler.ts +26 -0
- package/src/index.scss +23 -22
- package/src/sass/_globals.scss +7 -1
- package/src/styles/alert/alert.css +68 -0
- package/src/styles/alert/alert.css.map +1 -0
- package/src/styles/badge/badge.css +3 -3
- package/src/styles/details/details.css +14 -1
- package/src/styles/details/details.css.map +1 -1
- package/src/styles/dialog/dialog.css +28 -13
- package/src/styles/dialog/dialog.css.map +1 -1
- package/src/styles/index.css +121 -28
- package/src/styles/index.css.map +1 -1
- package/libs/chunk-QHIABQNQ.js +0 -8
- package/libs/chunk-QHIABQNQ.js.map +0 -1
- package/libs/chunk-ZOHIKF6I.cjs +0 -31
- package/libs/chunk-ZOHIKF6I.cjs.map +0 -1
- package/libs/components/badge/badge.css +0 -1
- package/libs/components/badge/badge.css.map +0 -1
- package/libs/components/badge/badge.min.css +0 -3
- package/libs/components/breadcrumbs/breadcrumb.css +0 -1
- package/libs/components/breadcrumbs/breadcrumb.css.map +0 -1
- package/libs/components/breadcrumbs/breadcrumb.min.css +0 -3
- package/libs/components/buttons/button.css +0 -1
- package/libs/components/buttons/button.css.map +0 -1
- package/libs/components/buttons/button.min.css +0 -3
- package/libs/components/cards/card-style.css +0 -1
- package/libs/components/cards/card-style.css.map +0 -1
- package/libs/components/cards/card-style.min.css +0 -3
- package/libs/components/cards/card.css +0 -1
- package/libs/components/cards/card.css.map +0 -1
- package/libs/components/cards/card.min.css +0 -3
- package/libs/components/details/details.css +0 -1
- package/libs/components/details/details.css.map +0 -1
- package/libs/components/details/details.min.css +0 -3
- package/libs/components/dialog/dialog.css +0 -1
- package/libs/components/dialog/dialog.css.map +0 -1
- package/libs/components/dialog/dialog.min.css +0 -3
- package/libs/components/form/form.css +0 -1
- package/libs/components/form/form.css.map +0 -1
- package/libs/components/form/form.min.css +0 -3
- package/libs/components/icons/icon.css +0 -1
- package/libs/components/icons/icon.css.map +0 -1
- package/libs/components/icons/icon.min.css +0 -3
- package/libs/components/images/img.css +0 -1
- package/libs/components/images/img.css.map +0 -1
- package/libs/components/images/img.min.css +0 -3
- package/libs/components/layout/landmarks.css +0 -1
- package/libs/components/layout/landmarks.css.map +0 -1
- package/libs/components/layout/landmarks.min.css +0 -3
- package/libs/components/link/link.css +0 -1
- package/libs/components/link/link.css.map +0 -1
- package/libs/components/link/link.min.css +0 -3
- package/libs/components/nav/nav.css +0 -1
- package/libs/components/nav/nav.css.map +0 -1
- package/libs/components/nav/nav.min.css +0 -3
- package/libs/components/progress/progress.css +0 -1
- package/libs/components/progress/progress.css.map +0 -1
- package/libs/components/progress/progress.min.css +0 -3
- package/libs/components/styles/index.css +0 -1
- package/libs/components/styles/index.css.map +0 -1
- package/libs/components/styles/index.min.css +0 -3
- package/libs/components/tag/tag.css +0 -1
- package/libs/components/tag/tag.css.map +0 -1
- package/libs/components/tag/tag.min.css +0 -3
- package/libs/components/text-to-speech/text-to-speech.css +0 -1
- package/libs/components/text-to-speech/text-to-speech.css.map +0 -1
- package/libs/components/text-to-speech/text-to-speech.min.css +0 -3
- package/libs/index.css +0 -1
- package/libs/index.css.map +0 -1
- package/src/components/alert-dialog/alert-dialog.stories.tsx +0 -35
- package/src/components/alert-dialog/alert-dialog.tsx +0 -76
- package/src/components/dialog/view/dialog-header.tsx +0 -32
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { StoryObj, Meta } from "@storybook/react";
|
|
2
|
+
import { within, expect } from "@storybook/test";
|
|
3
|
+
import DismissButton from "./dismiss-button";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof DismissButton> = {
|
|
6
|
+
title: "FP.REACT Elements/DismissButton",
|
|
7
|
+
component: DismissButton,
|
|
8
|
+
tags: ["new"],
|
|
9
|
+
parameters: {
|
|
10
|
+
actions: { argTypesRegex: "^on.*" },
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component: "A button component used to dismiss alerts.",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
args: {
|
|
18
|
+
onDismiss: () => alert("Dismiss button clicked"),
|
|
19
|
+
},
|
|
20
|
+
} as Meta;
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof DismissButton>;
|
|
24
|
+
|
|
25
|
+
export const Default: Story = {
|
|
26
|
+
args: {},
|
|
27
|
+
play: async ({ canvasElement }: { canvasElement: HTMLElement }) => {
|
|
28
|
+
const canvas = within(canvasElement);
|
|
29
|
+
expect(canvas.getByLabelText(/close alert/i)).toBeInTheDocument();
|
|
30
|
+
},
|
|
31
|
+
} as Story;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import Button from "#components/buttons/button";
|
|
3
|
+
import Icon from "#components/icons/icon";
|
|
4
|
+
/** Props for the DismissButton component */
|
|
5
|
+
type DismissButtonProps = {
|
|
6
|
+
/** Callback function when dismiss button is clicked */
|
|
7
|
+
onDismiss: () => void;
|
|
8
|
+
/** Size of the close icon in pixels. Defaults to 16 */
|
|
9
|
+
iconSize?: number;
|
|
10
|
+
};
|
|
11
|
+
export const DismissButton = React.memo(
|
|
12
|
+
({ onDismiss, iconSize = 16 }: DismissButtonProps) => (
|
|
13
|
+
<Button
|
|
14
|
+
type="button"
|
|
15
|
+
onClick={onDismiss}
|
|
16
|
+
aria-label="Close alert"
|
|
17
|
+
className="alert-dismiss"
|
|
18
|
+
data-btn="icon sm"
|
|
19
|
+
>
|
|
20
|
+
<Icon>
|
|
21
|
+
<Icon.Close size={iconSize} />
|
|
22
|
+
</Icon>
|
|
23
|
+
</Button>
|
|
24
|
+
)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
export default DismissButton;
|
|
28
|
+
DismissButton.displayName = "DismissButton";
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Meta } from "@storybook/blocks";
|
|
2
|
+
|
|
3
|
+
<Meta title="FP.REACT Components/Badge/Readme" />
|
|
4
|
+
|
|
5
|
+
# Badge Component
|
|
6
|
+
|
|
7
|
+
A lightweight and flexible badge component for displaying small counts, labels,
|
|
8
|
+
or status indicators. The Badge component renders as a superscript element with
|
|
9
|
+
customizable styling.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Renders as a `<sup>` element with nested `<span>`
|
|
14
|
+
- Fully customizable via CSS custom properties
|
|
15
|
+
- Supports rounded and standard variants
|
|
16
|
+
- ARIA-friendly with proper accessibility attributes
|
|
17
|
+
- Integrates with FPKit UI component system
|
|
18
|
+
|
|
19
|
+
## Props
|
|
20
|
+
|
|
21
|
+
The Badge component accepts the following props:
|
|
22
|
+
|
|
23
|
+
- `children: React.ReactNode` - Content to display inside the badge
|
|
24
|
+
- `id?: string` - Optional ID for the badge element
|
|
25
|
+
- `styles?: object` - Custom styles object
|
|
26
|
+
- `classes?: string` - Additional CSS classes
|
|
27
|
+
- `data-badge?: string` - Data attribute for badge variants (e.g. "rounded")
|
|
28
|
+
- All standard HTML attributes supported by `<sup>` element
|
|
29
|
+
|
|
30
|
+
## Technical Details
|
|
31
|
+
|
|
32
|
+
- Built using React functional components
|
|
33
|
+
- Uses TypeScript for type safety
|
|
34
|
+
- Styled with CSS custom properties
|
|
35
|
+
- Leverages base UI component from FPKit
|
|
36
|
+
|
|
37
|
+
## Usage Example
|
|
38
|
+
|
|
39
|
+
### Basic Usage
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import Badge from "./badge";
|
|
43
|
+
|
|
44
|
+
function Example() {
|
|
45
|
+
return (
|
|
46
|
+
<p>
|
|
47
|
+
Messages <Badge>3</Badge>
|
|
48
|
+
</p>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Rounded Badge
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import Badge from "./badge";
|
|
57
|
+
|
|
58
|
+
function Example() {
|
|
59
|
+
return (
|
|
60
|
+
<p>
|
|
61
|
+
Status <Badge data-badge="rounded">21</Badge>
|
|
62
|
+
</p>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Custom Styled Badge
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import Badge from "./badge";
|
|
71
|
+
|
|
72
|
+
function Example() {
|
|
73
|
+
return (
|
|
74
|
+
<p>
|
|
75
|
+
Count{" "}
|
|
76
|
+
<Badge
|
|
77
|
+
styles={{
|
|
78
|
+
"--badge-bg": "blue",
|
|
79
|
+
"--badge-color": "white",
|
|
80
|
+
}}
|
|
81
|
+
aria-label="badge count"
|
|
82
|
+
>
|
|
83
|
+
5
|
|
84
|
+
</Badge>
|
|
85
|
+
</p>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Styling
|
|
91
|
+
|
|
92
|
+
The component uses CSS custom properties for styling:
|
|
93
|
+
|
|
94
|
+
```css
|
|
95
|
+
--badge-bg: lightgray; /* Background color */
|
|
96
|
+
--badge-color: currentColor; /* Text color */
|
|
97
|
+
--badge-radius: 0.5rem; /* Border radius */
|
|
98
|
+
--badge-padding: 0.3rem; /* Inner padding */
|
|
99
|
+
--badge-v-align: 0.5rem; /* Vertical alignment */
|
|
100
|
+
--badge-fs: var(--fs-1); /* Font size */
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
For rounded badges, use the `data-badge="rounded"` attribute which sets:
|
|
104
|
+
|
|
105
|
+
```css
|
|
106
|
+
--badge-radius: 100%;
|
|
107
|
+
--badge-padding: 0.2rem;
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## WCAG 2.1 Guidelines
|
|
111
|
+
|
|
112
|
+
- Ensures sufficient color contrast (1.4.3)
|
|
113
|
+
- Supports text resizing (1.4.4)
|
|
114
|
+
- Maintains spacing on zoom (1.4.12)
|
|
115
|
+
- Uses semantic HTML elements (4.1.1)
|
|
116
|
+
- Provides meaningful content (4.1.2)
|
|
117
|
+
|
|
118
|
+
## Additional Notes
|
|
119
|
+
|
|
120
|
+
- Use semantic values for badge content
|
|
121
|
+
- Ensure adequate color contrast when customizing
|
|
122
|
+
- Keep badge content concise
|
|
123
|
+
- Include ARIA labels when needed
|
|
124
|
+
- Test with screen readers and keyboard navigation
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
sup:has(> span) {
|
|
2
2
|
--badge-bg: lightgray;
|
|
3
3
|
--badge-color: currentColor;
|
|
4
|
-
--badge-radius: 0.
|
|
5
|
-
--badge-padding: 0.
|
|
4
|
+
--badge-radius: 0.5rem;
|
|
5
|
+
--badge-padding: 0.3rem;
|
|
6
6
|
--badge-v-align: 0.5rem;
|
|
7
|
-
--badge-fs:
|
|
7
|
+
--badge-fs: var(--fs-1);
|
|
8
8
|
background-color: var(--badge-bg);
|
|
9
9
|
color: var(--badge-color);
|
|
10
10
|
padding: var(--badge-padding);
|
|
@@ -14,7 +14,7 @@ sup:has(> span) {
|
|
|
14
14
|
span {
|
|
15
15
|
color: inherit;
|
|
16
16
|
}
|
|
17
|
-
&[data-badge~=
|
|
17
|
+
&[data-badge~="rounded"] {
|
|
18
18
|
--badge-radius: 100%;
|
|
19
19
|
--badge-padding: 0.2rem;
|
|
20
20
|
}
|
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { render, screen } from
|
|
3
|
-
import { Button } from
|
|
4
|
-
import user from '@testing-library/user-event'
|
|
5
|
-
import jest from
|
|
6
|
-
import { userEvent } from
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { Button } from "./button";
|
|
4
|
+
// import user from '@testing-library/user-event'
|
|
5
|
+
import jest from "jest-mock";
|
|
6
|
+
import { userEvent } from "@storybook/test";
|
|
7
7
|
|
|
8
|
-
describe(
|
|
9
|
-
it(
|
|
10
|
-
render(<Button type="button">Click me</Button>)
|
|
11
|
-
const button = screen.getByText(
|
|
12
|
-
expect(button.tagName).toBe(
|
|
13
|
-
screen.logTestingPlaygroundURL()
|
|
14
|
-
})
|
|
8
|
+
describe("Button", () => {
|
|
9
|
+
it("renders a button element with the correct label", () => {
|
|
10
|
+
render(<Button type="button">Click me</Button>);
|
|
11
|
+
const button = screen.getByText("Click me");
|
|
12
|
+
expect(button.tagName).toBe("BUTTON");
|
|
13
|
+
screen.logTestingPlaygroundURL();
|
|
14
|
+
});
|
|
15
15
|
|
|
16
16
|
it('has the type attribute set to "button" by default', () => {
|
|
17
|
-
render(<Button type="button">Click me</Button>)
|
|
18
|
-
const button = screen.getByText(
|
|
19
|
-
expect(button).toHaveAttribute(
|
|
20
|
-
})
|
|
17
|
+
render(<Button type="button">Click me</Button>);
|
|
18
|
+
const button = screen.getByText("Click me");
|
|
19
|
+
expect(button).toHaveAttribute("type", "button");
|
|
20
|
+
});
|
|
21
21
|
|
|
22
|
-
it(
|
|
23
|
-
const handleClick = jest.fn()
|
|
24
|
-
const handlePointerEvents = jest.fn()
|
|
22
|
+
it("calls the onClick handler when clicked", async () => {
|
|
23
|
+
const handleClick = jest.fn();
|
|
24
|
+
const handlePointerEvents = jest.fn();
|
|
25
25
|
render(
|
|
26
26
|
<Button type="button" onClick={handleClick}>
|
|
27
27
|
Click me
|
|
28
|
-
</Button
|
|
29
|
-
)
|
|
30
|
-
const button = screen.getByText(
|
|
31
|
-
await userEvent.click(button)
|
|
32
|
-
expect(handleClick).toHaveBeenCalledTimes(1)
|
|
33
|
-
expect(handlePointerEvents).toHaveBeenCalledTimes(0)
|
|
34
|
-
})
|
|
28
|
+
</Button>
|
|
29
|
+
);
|
|
30
|
+
const button = screen.getByText("Click me");
|
|
31
|
+
await userEvent.click(button);
|
|
32
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
33
|
+
expect(handlePointerEvents).toHaveBeenCalledTimes(0);
|
|
34
|
+
});
|
|
35
35
|
|
|
36
|
-
it(
|
|
36
|
+
it("applies custom styles when provided", () => {
|
|
37
37
|
render(
|
|
38
|
-
<Button type="button" styles={{ backgroundColor:
|
|
38
|
+
<Button type="button" styles={{ backgroundColor: "red" }}>
|
|
39
39
|
Click me
|
|
40
|
-
</Button
|
|
41
|
-
)
|
|
42
|
-
const button = screen.getByRole(
|
|
43
|
-
expect(button).toHaveAttribute(
|
|
44
|
-
})
|
|
40
|
+
</Button>
|
|
41
|
+
);
|
|
42
|
+
const button = screen.getByRole("button");
|
|
43
|
+
expect(button).toHaveAttribute("style");
|
|
44
|
+
});
|
|
45
45
|
|
|
46
|
-
it(
|
|
47
|
-
const handlePointerDown = jest.fn()
|
|
46
|
+
it("calls the onPointerDown handler when pointer is down", async () => {
|
|
47
|
+
const handlePointerDown = jest.fn();
|
|
48
48
|
render(
|
|
49
49
|
<Button type="button" onPointerDown={handlePointerDown}>
|
|
50
50
|
Click me
|
|
51
|
-
</Button
|
|
52
|
-
)
|
|
53
|
-
const button = screen.getByText(
|
|
54
|
-
await userEvent.click(button)
|
|
55
|
-
expect(handlePointerDown).toHaveBeenCalledTimes(1)
|
|
56
|
-
})
|
|
51
|
+
</Button>
|
|
52
|
+
);
|
|
53
|
+
const button = screen.getByText("Click me");
|
|
54
|
+
await userEvent.click(button);
|
|
55
|
+
expect(handlePointerDown).toHaveBeenCalledTimes(1);
|
|
56
|
+
});
|
|
57
57
|
|
|
58
|
-
it(
|
|
59
|
-
const handlePointerOver = jest.fn()
|
|
58
|
+
it("calls the onPointerOver handler when pointer is over", async () => {
|
|
59
|
+
const handlePointerOver = jest.fn();
|
|
60
60
|
render(
|
|
61
61
|
<Button type="button" onPointerOver={handlePointerOver}>
|
|
62
62
|
Click me
|
|
63
|
-
</Button
|
|
64
|
-
)
|
|
65
|
-
const button = screen.getByText(
|
|
66
|
-
await userEvent.hover(button)
|
|
67
|
-
expect(handlePointerOver).toHaveBeenCalledTimes(1)
|
|
68
|
-
})
|
|
63
|
+
</Button>
|
|
64
|
+
);
|
|
65
|
+
const button = screen.getByText("Click me");
|
|
66
|
+
await userEvent.hover(button);
|
|
67
|
+
expect(handlePointerOver).toHaveBeenCalledTimes(1);
|
|
68
|
+
});
|
|
69
69
|
|
|
70
|
-
it(
|
|
71
|
-
const handlePointerEvents = jest.fn()
|
|
70
|
+
it("calls the onPointerLeave handler when pointer leaves", async () => {
|
|
71
|
+
const handlePointerEvents = jest.fn();
|
|
72
72
|
render(
|
|
73
73
|
<Button type="button" onPointerLeave={handlePointerEvents}>
|
|
74
74
|
Click me
|
|
75
|
-
</Button
|
|
76
|
-
)
|
|
77
|
-
const button = screen.getByText(
|
|
78
|
-
await userEvent.unhover(button)
|
|
79
|
-
expect(handlePointerEvents).toHaveBeenCalledTimes(1)
|
|
80
|
-
})
|
|
75
|
+
</Button>
|
|
76
|
+
);
|
|
77
|
+
const button = screen.getByText("Click me");
|
|
78
|
+
await userEvent.unhover(button);
|
|
79
|
+
expect(handlePointerEvents).toHaveBeenCalledTimes(1);
|
|
80
|
+
});
|
|
81
81
|
|
|
82
|
-
it(
|
|
83
|
-
const handleClick = jest.fn()
|
|
82
|
+
it("it is disabled when disabled is true", () => {
|
|
83
|
+
const handleClick = jest.fn();
|
|
84
84
|
render(
|
|
85
85
|
<Button
|
|
86
86
|
type="button"
|
|
@@ -90,15 +90,15 @@ describe('Button', () => {
|
|
|
90
90
|
onPointerLeave={handleClick}
|
|
91
91
|
>
|
|
92
92
|
Click me
|
|
93
|
-
</Button
|
|
94
|
-
)
|
|
95
|
-
const button = screen.getByText(
|
|
96
|
-
expect(button).not.toBeDisabled()
|
|
97
|
-
userEvent.click(button)
|
|
98
|
-
expect(handleClick).toHaveBeenCalledTimes(0)
|
|
99
|
-
userEvent.hover(button)
|
|
100
|
-
expect(handleClick).toHaveBeenCalledTimes(0)
|
|
101
|
-
userEvent.unhover(button)
|
|
102
|
-
expect(handleClick).toHaveBeenCalledTimes(0)
|
|
103
|
-
})
|
|
104
|
-
})
|
|
93
|
+
</Button>
|
|
94
|
+
);
|
|
95
|
+
const button = screen.getByText("Click me");
|
|
96
|
+
expect(button).not.toBeDisabled();
|
|
97
|
+
userEvent.click(button);
|
|
98
|
+
expect(handleClick).toHaveBeenCalledTimes(0);
|
|
99
|
+
userEvent.hover(button);
|
|
100
|
+
expect(handleClick).toHaveBeenCalledTimes(0);
|
|
101
|
+
userEvent.unhover(button);
|
|
102
|
+
expect(handleClick).toHaveBeenCalledTimes(0);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -8,7 +8,7 @@ details {
|
|
|
8
8
|
--details-gap: 0rem;
|
|
9
9
|
--details-px: 1.5rem;
|
|
10
10
|
--details-py: 1rem;
|
|
11
|
-
--details-radius:
|
|
11
|
+
--details-radius: var(--border-radius);
|
|
12
12
|
--summary-cursor: pointer;
|
|
13
13
|
--summary-transitions: all 0.75s ease-in-out;
|
|
14
14
|
--summary-display: flex;
|
|
@@ -30,6 +30,25 @@ details {
|
|
|
30
30
|
overflow: clip;
|
|
31
31
|
border-radius: var(--details-radius);
|
|
32
32
|
|
|
33
|
+
// Handle multiple details elements
|
|
34
|
+
& + details {
|
|
35
|
+
border-radius: 0; // remove radius from middle elements
|
|
36
|
+
border-top: none; // optional: remove double borders
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&:first-of-type {
|
|
40
|
+
border-radius: var(--details-radius) var(--details-radius) 0 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
&:last-of-type {
|
|
44
|
+
border-radius: 0 0 var(--details-radius) var(--details-radius);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// If it's the only details element, keep original radius
|
|
48
|
+
&:only-of-type {
|
|
49
|
+
border-radius: var(--details-radius);
|
|
50
|
+
}
|
|
51
|
+
|
|
33
52
|
&::marker {
|
|
34
53
|
content: none;
|
|
35
54
|
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { Meta } from "@storybook/blocks";
|
|
2
|
+
|
|
3
|
+
<Meta title="FP.REACT Components/Dialog/Readme" />
|
|
4
|
+
|
|
5
|
+
# Dialog Components
|
|
6
|
+
|
|
7
|
+
A flexible Dialog component system for building accessible modal dialogs in
|
|
8
|
+
React applications. The component supports:
|
|
9
|
+
|
|
10
|
+
- Controlled modal behavior with built-in state management
|
|
11
|
+
- Full ARIA accessibility support out of the box
|
|
12
|
+
- Customizable headers and content layout
|
|
13
|
+
- Multiple variants including standard dialogs and alert dialogs
|
|
14
|
+
- Event handling for open, close, and cancel actions
|
|
15
|
+
- Responsive design with inline rendering option
|
|
16
|
+
- Modal and non-modal dialog variants via `isAlertDialog`
|
|
17
|
+
- Built-in header with close functionality
|
|
18
|
+
- Configurable footer with confirm/cancel actions
|
|
19
|
+
- Click-outside-to-close behavior
|
|
20
|
+
- Focus management and keyboard navigation
|
|
21
|
+
|
|
22
|
+
Built with TypeScript and React, this component follows modern best practices
|
|
23
|
+
and provides a developer-friendly API for implementing modal dialogs in your web
|
|
24
|
+
applications.
|
|
25
|
+
|
|
26
|
+
## Overview
|
|
27
|
+
|
|
28
|
+
The dialog component system consists of the following key parts:
|
|
29
|
+
|
|
30
|
+
### Dialog Component
|
|
31
|
+
|
|
32
|
+
The dialog system consists of:
|
|
33
|
+
|
|
34
|
+
- `Dialog`: Main modal component
|
|
35
|
+
- `DialogHeader`: Header with title and close button
|
|
36
|
+
- `DialogFooter`: Footer with confirm/cancel actions
|
|
37
|
+
|
|
38
|
+
### Props
|
|
39
|
+
|
|
40
|
+
The Dialog component accepts two types of props - required core props and
|
|
41
|
+
optional configuration props.
|
|
42
|
+
|
|
43
|
+
#### Required Props
|
|
44
|
+
|
|
45
|
+
- `dialogTitle` (`string`): Title text displayed in dialog header
|
|
46
|
+
- `children` (`React.ReactNode`): Content rendered inside dialog body
|
|
47
|
+
- `showDialog` (`boolean`): Controls dialog visibility state
|
|
48
|
+
|
|
49
|
+
#### Optional Props
|
|
50
|
+
|
|
51
|
+
- `isAlertDialog` (`boolean`, default: `false`): Renders as non-modal alert
|
|
52
|
+
dialog
|
|
53
|
+
- `onClose` (`() => void`): Callback when dialog closes
|
|
54
|
+
- `onConfirm` (`() => void | Promise<void>`): Confirmation action callback
|
|
55
|
+
- `confirmLabel` (`string`, default: "Confirm"): Custom confirm button text
|
|
56
|
+
- `cancelLabel` (`string`, default: "Cancel"): Custom cancel button text
|
|
57
|
+
- `className` (`string`, default: ""): Additional CSS classes
|
|
58
|
+
|
|
59
|
+
The component also inherits props from:
|
|
60
|
+
|
|
61
|
+
- `React.ComponentProps<typeof UI>`
|
|
62
|
+
- `React.ComponentProps<dialog>`
|
|
63
|
+
|
|
64
|
+
### Usage Examples
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
// Basic usage
|
|
68
|
+
import { Dialog } from "./dialog";
|
|
69
|
+
|
|
70
|
+
function MyComponent() {
|
|
71
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<>
|
|
75
|
+
<button onClick={() => setIsOpen(true)}>Open Dialog</button>
|
|
76
|
+
<Dialog
|
|
77
|
+
isOpen={isOpen}
|
|
78
|
+
onClose={() => setIsOpen(false)}
|
|
79
|
+
dialogTitle="My Dialog"
|
|
80
|
+
dialogId="example-dialog"
|
|
81
|
+
>
|
|
82
|
+
<div>Dialog content goes here</div>
|
|
83
|
+
</Dialog>
|
|
84
|
+
</>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Alert Dialog Example
|
|
90
|
+
|
|
91
|
+
A basic example of an Alert Dialog component implementation. Alert dialogs are
|
|
92
|
+
used to show important messages that require user acknowledgment or
|
|
93
|
+
confirmation.
|
|
94
|
+
|
|
95
|
+
### Features
|
|
96
|
+
|
|
97
|
+
- Uses the `Dialog` component with `isAlertDialog` prop set to true
|
|
98
|
+
- Displays a warning message with a confirmation button
|
|
99
|
+
- Custom dialog title "Warning"
|
|
100
|
+
- Unique dialog identifier "alert-dialog"
|
|
101
|
+
|
|
102
|
+
### Usage
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
function AlertExample() {
|
|
106
|
+
return (
|
|
107
|
+
<Dialog isAlertDialog dialogTitle="Warning" dialogId="alert-dialog">
|
|
108
|
+
<p>This action cannot be undone. Continue?</p>
|
|
109
|
+
<button onClick={() => {}}>Confirm</button>
|
|
110
|
+
</Dialog>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Custom header dialog example
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
// Custom header dialog
|
|
119
|
+
function CustomDialog() {
|
|
120
|
+
return (
|
|
121
|
+
<Dialog hideDialogHeader dialogId="custom-dialog">
|
|
122
|
+
<h2>Custom Header</h2>
|
|
123
|
+
<div>Content with custom header</div>
|
|
124
|
+
</Dialog>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Styles
|
|
130
|
+
|
|
131
|
+
This `dialog.scss` file defines the styling for a dialog component with
|
|
132
|
+
customizable properties using CSS variables.
|
|
133
|
+
|
|
134
|
+
## CSS Custom Properties
|
|
135
|
+
|
|
136
|
+
### Dialog Dimensions and Spacing
|
|
137
|
+
|
|
138
|
+
- `--dialog-min-w`: Minimum width of the dialog (320px)
|
|
139
|
+
- `--dialog-gap`: Spacing between dialog elements (0.75rem)
|
|
140
|
+
- `--dialog-padding`: General padding inside dialog (0.75rem)
|
|
141
|
+
- `--dialog-padding-inline`: Horizontal padding inside dialog (1rem)
|
|
142
|
+
|
|
143
|
+
### Border Properties
|
|
144
|
+
|
|
145
|
+
- `--dialog-border-color`: Color of dialog border (lightgray)
|
|
146
|
+
- `--dialog-border-width`: Width of dialog border (thin)
|
|
147
|
+
- `--dialog-border-style`: Style of dialog border (solid)
|
|
148
|
+
- `--dialog-border-radius`: Border radius of dialog corners (0.5rem)
|
|
149
|
+
|
|
150
|
+
### Dialog Button Styles
|
|
151
|
+
|
|
152
|
+
- `--dialog-button-bg`: Background color for dialog buttons (transparent)
|
|
153
|
+
- `--dialog-button-border`: Border style for dialog buttons (transparent thin
|
|
154
|
+
solid)
|
|
155
|
+
- `--dialog-button-hover-bg`: Background color for button hover state
|
|
156
|
+
(whitesmoke)
|
|
157
|
+
- `--dialog-close-color`: Color for close button (gray)
|
|
158
|
+
|
|
159
|
+
### Layout Properties
|
|
160
|
+
|
|
161
|
+
- `--dialog-display`: Display property for dialog (flex)
|
|
162
|
+
- `--dialog-flex-direction`: Flex direction for dialog layout (column)
|
|
163
|
+
|
|
164
|
+
## Component Structure
|
|
165
|
+
|
|
166
|
+
### Base Dialog
|
|
167
|
+
|
|
168
|
+
- Sets minimum width and spacing
|
|
169
|
+
- Applies border and padding styles
|
|
170
|
+
- Implements flex layout when dialog is open
|
|
171
|
+
|
|
172
|
+
### Dialog Header
|
|
173
|
+
|
|
174
|
+
- Implements a flex layout with space-between alignment
|
|
175
|
+
- Contains title (h3) and close button
|
|
176
|
+
- Custom button styling with hover states
|
|
177
|
+
|
|
178
|
+
### Alert Dialog Actions
|
|
179
|
+
|
|
180
|
+
- Container for dialog action buttons
|
|
181
|
+
- Uses flex layout with left alignment
|
|
182
|
+
- Includes gap spacing between buttons
|
|
183
|
+
|
|
184
|
+
## Usage
|
|
185
|
+
|
|
186
|
+
To customize the dialog appearance, override the CSS custom properties in your
|
|
187
|
+
stylesheet.
|