@fpkit/acss 0.5.4 → 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-2f29127c.d.ts → icons-31ace3de.d.ts} +87 -81
- 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 +42 -42
- package/libs/index.cjs.map +1 -1
- package/libs/index.d.cts +59 -29
- package/libs/index.d.ts +59 -29
- 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/badge/badge.stories.tsx +21 -23
- package/src/components/breadcrumbs/breadcrumb.scss +2 -2
- package/src/components/breadcrumbs/breadcrumb.stories.tsx +42 -47
- package/src/components/buttons/button.scss +41 -15
- package/src/components/buttons/button.stories.tsx +8 -1
- package/src/components/buttons/button.test.tsx +72 -72
- package/src/components/cards/card.stories.tsx +15 -15
- package/src/components/details/details.scss +26 -6
- package/src/components/details/details.stories.tsx +33 -30
- package/src/components/details/details.tsx +17 -17
- 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 +76 -0
- package/src/components/dialog/dialog.stories.tsx +116 -0
- package/src/components/dialog/dialog.tsx +128 -0
- 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/views/dialog-header.stories.tsx +42 -0
- package/src/components/dialog/views/dialog-header.tsx +61 -0
- package/src/components/form/form.stories.tsx +16 -16
- package/src/components/form/input.stories.tsx +62 -62
- package/src/components/form/select.stories.tsx +22 -15
- package/src/components/heading/heading.stories.tsx +32 -33
- package/src/components/heading/heading.tsx +1 -1
- 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/svg.tsx +0 -1
- package/src/components/icons/components/warn-solid.tsx +36 -0
- package/src/components/icons/icon.scss +1 -3
- package/src/components/icons/icon.stories.tsx +87 -78
- package/src/components/icons/icon.tsx +57 -52
- package/src/components/icons/index.ts +36 -29
- package/src/components/icons/types.ts +1 -1
- package/src/components/images/figure.stories.tsx +13 -13
- package/src/components/images/img.stories.tsx +12 -12
- package/src/components/link/link.stories.tsx +32 -35
- package/src/components/link/link.tsx +27 -14
- package/src/components/list/list.stories.tsx +16 -16
- package/src/components/modal/dialog.tsx +13 -12
- package/src/components/modal/modal.tsx +28 -30
- package/src/components/nav/nav.stories.tsx +25 -24
- package/src/components/popover/popover.stories.tsx +17 -18
- package/src/components/progress/progress.stories.tsx +14 -20
- package/src/components/tag/tag.stories.tsx +17 -18
- package/src/components/text/text.stories.tsx +28 -29
- package/src/components/text-to-speech/TextToSpeech.stories.tsx +100 -101
- 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/index.ts +31 -30
- package/src/patterns/page/page-header.stories.tsx +17 -21
- package/src/sass/_globals.scss +14 -32
- package/src/sass/_styles.scss +2 -1
- package/src/sass/styles/_colors.scss +13 -0
- 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/breadcrumbs/breadcrumb.css +1 -1
- package/src/styles/buttons/button.css +25 -2
- package/src/styles/buttons/button.css.map +1 -1
- package/src/styles/details/details.css +19 -4
- package/src/styles/details/details.css.map +1 -1
- package/src/styles/dialog/dialog.css +76 -0
- package/src/styles/dialog/dialog.css.map +1 -0
- package/src/styles/icons/icon.css +1 -3
- package/src/styles/icons/icon.css.map +1 -1
- package/src/styles/index.css +213 -60
- package/src/styles/index.css.map +1 -1
- package/libs/chunk-TBM2QIVT.js +0 -8
- package/libs/chunk-TBM2QIVT.js.map +0 -1
- package/libs/chunk-VAH6X2DZ.cjs +0 -31
- package/libs/chunk-VAH6X2DZ.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/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/readme.stories.mdx +0 -7
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Meta } from "@storybook/blocks";
|
|
2
|
+
|
|
3
|
+
<Meta title="FP.REACT Components/Alert/Readme" />
|
|
4
|
+
|
|
5
|
+
# Alert Component
|
|
6
|
+
|
|
7
|
+
## Summary
|
|
8
|
+
|
|
9
|
+
The `Alert` component is a customizable component for displaying status messages
|
|
10
|
+
with different severity levels. It supports multiple severity types (default,
|
|
11
|
+
info, success, warning, error) and can include optional titles and dismissal
|
|
12
|
+
functionality.
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- Multiple severity levels with matching visual indicators
|
|
17
|
+
- Optional title and dismissible states
|
|
18
|
+
- Accessible by default with appropriate ARIA attributes
|
|
19
|
+
- Customizable icons through `iconProps`
|
|
20
|
+
|
|
21
|
+
## Props
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
export type AlertProps = {
|
|
25
|
+
open: boolean;
|
|
26
|
+
severity?: "default" | "info" | "success" | "warning" | "error";
|
|
27
|
+
children: React.ReactNode;
|
|
28
|
+
title?: string;
|
|
29
|
+
dismissible?: boolean;
|
|
30
|
+
onDismiss?: () => void;
|
|
31
|
+
iconProps?: IconProps;
|
|
32
|
+
} & React.ComponentProps<typeof UI>;
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Technical Details
|
|
36
|
+
|
|
37
|
+
The `Alert` component uses the `UI` component for rendering and supports various
|
|
38
|
+
severity levels with corresponding ARIA attributes for accessibility. It also
|
|
39
|
+
allows for custom icons through the `iconProps` prop.
|
|
40
|
+
|
|
41
|
+
## Usage Example
|
|
42
|
+
|
|
43
|
+
### Basic Usage
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import Alert from "#components/alert";
|
|
47
|
+
|
|
48
|
+
<Alert open={true} severity="info">
|
|
49
|
+
This is an info alert.
|
|
50
|
+
</Alert>;
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Advanced Usage
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import Alert from "#components/alert";
|
|
57
|
+
|
|
58
|
+
<Alert
|
|
59
|
+
open={true}
|
|
60
|
+
severity="error"
|
|
61
|
+
title="Error"
|
|
62
|
+
dismissible={true}
|
|
63
|
+
onDismiss={() => console.log("Alert dismissed")}
|
|
64
|
+
iconProps={{ fill: "#000", size: 24 }}
|
|
65
|
+
>
|
|
66
|
+
This is an error alert with a custom icon.
|
|
67
|
+
</Alert>;
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Additional Notes
|
|
71
|
+
|
|
72
|
+
- The `Alert` component is designed to be accessible by default.
|
|
73
|
+
- The `dismissible` prop allows the alert to be dismissed by the user.
|
|
74
|
+
- Custom icons can be provided through the `iconProps` prop.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
[role="alert"] {
|
|
2
|
+
/* Success colors */
|
|
3
|
+
--alert-success-bg: #e6f4ea;
|
|
4
|
+
--alert-success-border: #34a853;
|
|
5
|
+
--alert-success-text: #1e4620;
|
|
6
|
+
|
|
7
|
+
/* Error colors */
|
|
8
|
+
--alert-error-bg: #fdeded;
|
|
9
|
+
--alert-error-border: #d32f2f;
|
|
10
|
+
--alert-error-text: #5f2120;
|
|
11
|
+
|
|
12
|
+
/* Warning colors */
|
|
13
|
+
--alert-warning-bg: #fff4e5;
|
|
14
|
+
--alert-warning-border: #ff9800;
|
|
15
|
+
--alert-warning-text: #663c00;
|
|
16
|
+
|
|
17
|
+
/* Info colors */
|
|
18
|
+
--alert-info-bg: #e5f6fd;
|
|
19
|
+
--alert-info-border: #0288d1;
|
|
20
|
+
--alert-info-text: #084154;
|
|
21
|
+
|
|
22
|
+
--alert-border: thin solid currentColor;
|
|
23
|
+
|
|
24
|
+
background-color: var(--alert-bg, whitesmoke);
|
|
25
|
+
border: var(--alert-border, currentColor);
|
|
26
|
+
color: var(--alert-color, currentColor);
|
|
27
|
+
padding: var(--alert-padding, var(--spc-4));
|
|
28
|
+
margin-block-end: var(--alert-margin-block-end);
|
|
29
|
+
font-size: var(--fs-0);
|
|
30
|
+
line-height: 1.6;
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-direction: row;
|
|
33
|
+
// align-items: center;
|
|
34
|
+
border-radius: var(--alert-border-radius, var(--border-radius));
|
|
35
|
+
border: var(--alert-border);
|
|
36
|
+
gap: var(--alert-gap, var(--spc-4));
|
|
37
|
+
div {
|
|
38
|
+
margin-block-start: 0;
|
|
39
|
+
align-items: center;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.alert-message {
|
|
43
|
+
flex: 2;
|
|
44
|
+
margin-block-start: 0;
|
|
45
|
+
h3 {
|
|
46
|
+
margin-block-end: 0;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&[data-alert~="info"] {
|
|
51
|
+
--alert-bg: var(--alert-info-bg);
|
|
52
|
+
--alert-color: var(--alert-info-text);
|
|
53
|
+
border-color: var(--alert-info-border);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
&[data-alert~="warning"] {
|
|
57
|
+
--alert-bg: var(--alert-warning-bg);
|
|
58
|
+
--alert-color: var(--alert-warning-text);
|
|
59
|
+
border-color: var(--alert-warning-border);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
&[data-alert~="error"] {
|
|
63
|
+
--alert-bg: var(--alert-error-bg);
|
|
64
|
+
--alert-color: var(--alert-error-text);
|
|
65
|
+
border-color: var(--alert-error-border);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
&[data-alert~="success"] {
|
|
69
|
+
--alert-bg: var(--alert-success-bg);
|
|
70
|
+
--alert-color: var(--alert-success-text);
|
|
71
|
+
border-color: var(--alert-success-border);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
button {
|
|
75
|
+
&[data-btn~="icon"] {
|
|
76
|
+
--btn-bg: transparent;
|
|
77
|
+
max-height: fit-content;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { StoryObj, Meta, StoryFn } from "@storybook/react";
|
|
2
|
+
import { within, expect, userEvent, fn } from "@storybook/test";
|
|
3
|
+
|
|
4
|
+
import Alert from "./alert";
|
|
5
|
+
import WithInstructions from "#decorators/instructions.jsx";
|
|
6
|
+
import React from "react";
|
|
7
|
+
|
|
8
|
+
const ButtonDecorator = (Story: StoryFn) => {
|
|
9
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
10
|
+
return (
|
|
11
|
+
<div>
|
|
12
|
+
<button onClick={() => setIsOpen(!isOpen)}>Open Alert</button>
|
|
13
|
+
<Story
|
|
14
|
+
args={{
|
|
15
|
+
open: isOpen,
|
|
16
|
+
title: "Alert Title",
|
|
17
|
+
children: "This is an alert message",
|
|
18
|
+
dismissible: false,
|
|
19
|
+
}}
|
|
20
|
+
/>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const instructions = (
|
|
26
|
+
<>
|
|
27
|
+
<p>We are testing the following interactions on the alert:</p>
|
|
28
|
+
<ul>
|
|
29
|
+
<li>Tab through the alert.</li>
|
|
30
|
+
<li>Check if the button gets focused.</li>
|
|
31
|
+
<li>Click the button to dismiss the alert.</li>
|
|
32
|
+
<li>Check that the alert is no longer in the document.</li>
|
|
33
|
+
</ul>
|
|
34
|
+
</>
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const meta: Meta<typeof Alert> = {
|
|
38
|
+
title: "FP.REACT Components/Alert",
|
|
39
|
+
component: Alert,
|
|
40
|
+
tags: ["beta"],
|
|
41
|
+
parameters: {
|
|
42
|
+
docs: {
|
|
43
|
+
description: {
|
|
44
|
+
component:
|
|
45
|
+
"The Alert component is used to display important messages to users. It can be dismissible and supports various interaction tests",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
args: {
|
|
50
|
+
title: "Alert Title",
|
|
51
|
+
children:
|
|
52
|
+
"The Alert component is used to display important messages to users.",
|
|
53
|
+
dismissible: true,
|
|
54
|
+
onDismiss: () => fn(),
|
|
55
|
+
},
|
|
56
|
+
} as Meta;
|
|
57
|
+
|
|
58
|
+
export default meta;
|
|
59
|
+
type Story = StoryObj<typeof Alert>;
|
|
60
|
+
|
|
61
|
+
export const DefaultAlert: Story = {
|
|
62
|
+
args: {
|
|
63
|
+
open: true,
|
|
64
|
+
},
|
|
65
|
+
play: async ({ canvasElement }) => {
|
|
66
|
+
const canvas = within(canvasElement);
|
|
67
|
+
expect(canvas.getByText(/this is an alert message/i)).toBeInTheDocument();
|
|
68
|
+
},
|
|
69
|
+
} as Story;
|
|
70
|
+
|
|
71
|
+
export const SuccessAlert: Story = {
|
|
72
|
+
args: {
|
|
73
|
+
open: true,
|
|
74
|
+
severity: "success",
|
|
75
|
+
},
|
|
76
|
+
} as Story;
|
|
77
|
+
|
|
78
|
+
export const WarningAlert: Story = {
|
|
79
|
+
args: {
|
|
80
|
+
open: true,
|
|
81
|
+
severity: "warning",
|
|
82
|
+
},
|
|
83
|
+
} as Story;
|
|
84
|
+
|
|
85
|
+
export const ErrorAlert: Story = {
|
|
86
|
+
args: {
|
|
87
|
+
open: true,
|
|
88
|
+
severity: "error",
|
|
89
|
+
},
|
|
90
|
+
} as Story;
|
|
91
|
+
|
|
92
|
+
export const InfoAlert: Story = {
|
|
93
|
+
args: {
|
|
94
|
+
open: true,
|
|
95
|
+
severity: "info",
|
|
96
|
+
},
|
|
97
|
+
} as Story;
|
|
98
|
+
|
|
99
|
+
export const OpenAlert: Story = {
|
|
100
|
+
decorators: [ButtonDecorator],
|
|
101
|
+
} as Story;
|
|
102
|
+
|
|
103
|
+
export const InteractionTest: Story = {
|
|
104
|
+
args: {
|
|
105
|
+
open: true,
|
|
106
|
+
className: "my alert",
|
|
107
|
+
},
|
|
108
|
+
decorators: [WithInstructions(instructions, "Alert interactions test:")],
|
|
109
|
+
play: async ({ canvasElement, step }) => {
|
|
110
|
+
const canvas = within(canvasElement);
|
|
111
|
+
const alert = canvas.getByRole("alert");
|
|
112
|
+
const dismissButton = canvas.getByRole("button", { name: /close alert/i });
|
|
113
|
+
await step(
|
|
114
|
+
"Check that the alert and button are in the document",
|
|
115
|
+
async () => {
|
|
116
|
+
expect(alert).toBeInTheDocument();
|
|
117
|
+
expect(dismissButton).toBeInTheDocument();
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
// await step(
|
|
121
|
+
// "Tab through the alert and check if the button gets focused",
|
|
122
|
+
// async () => {
|
|
123
|
+
// userEvent.tab({ delay: 500 });
|
|
124
|
+
// expect(dismissButton).toHaveFocus();
|
|
125
|
+
// }
|
|
126
|
+
// );
|
|
127
|
+
await step("Click the button to dismiss the alert", async () => {
|
|
128
|
+
await userEvent.click(dismissButton, { delay: 500 });
|
|
129
|
+
expect(alert).not.toBeInTheDocument();
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
} as Story;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import UI from "#components/ui";
|
|
3
|
+
import Icon from "#components/icons/icon";
|
|
4
|
+
import { IconProps } from "#components/icons/types";
|
|
5
|
+
import DismissButton from "./elements/dismiss-button";
|
|
6
|
+
|
|
7
|
+
// First, define a type for the valid severity values
|
|
8
|
+
type Severity = "default" | "info" | "success" | "warning" | "error";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Props for the Alert component.
|
|
12
|
+
*/
|
|
13
|
+
export type AlertProps = {
|
|
14
|
+
/**
|
|
15
|
+
* Whether the alert is open.
|
|
16
|
+
*/
|
|
17
|
+
open: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* The severity level of the alert.
|
|
20
|
+
* @default "default"
|
|
21
|
+
*/
|
|
22
|
+
severity?: Severity;
|
|
23
|
+
/**
|
|
24
|
+
* The main message content.
|
|
25
|
+
*/
|
|
26
|
+
children: React.ReactNode;
|
|
27
|
+
/**
|
|
28
|
+
* Optional title for the alert.
|
|
29
|
+
*/
|
|
30
|
+
title?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Whether the alert can be dismissed.
|
|
33
|
+
* @default false
|
|
34
|
+
*/
|
|
35
|
+
dismissible?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Callback when alert is dismissed.
|
|
38
|
+
*/
|
|
39
|
+
onDismiss?: () => void;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Size of the icon in pixels.
|
|
43
|
+
* @default 32
|
|
44
|
+
*/
|
|
45
|
+
iconSize?: number;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Additional props to pass to the Icon component.
|
|
49
|
+
*/
|
|
50
|
+
iconProps?: IconProps;
|
|
51
|
+
} & React.ComponentProps<typeof UI>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Alert is a customizable component for displaying status messages with different severity levels.
|
|
55
|
+
* It supports multiple severity types (default, info, success, warning, error) and can include
|
|
56
|
+
* optional titles and dismissal functionality.
|
|
57
|
+
*
|
|
58
|
+
* Features:
|
|
59
|
+
* - Multiple severity levels with matching visual indicators
|
|
60
|
+
* - Optional title and dismissible states
|
|
61
|
+
* - Accessible by default with appropriate ARIA attributes
|
|
62
|
+
* - Customizable icons through iconProps
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```tsx
|
|
66
|
+
* <Alert
|
|
67
|
+
* open={true}
|
|
68
|
+
* severity="info"
|
|
69
|
+
* title="Optional Title"
|
|
70
|
+
* dismissible={true}
|
|
71
|
+
* onDismiss={() => console.log('Alert dismissed')}
|
|
72
|
+
* >
|
|
73
|
+
* Alert message content
|
|
74
|
+
* </Alert>
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* @see {@link AlertProps} for available configuration options
|
|
78
|
+
*/ const Alert: React.FC<AlertProps> = ({
|
|
79
|
+
open,
|
|
80
|
+
severity = "default",
|
|
81
|
+
children,
|
|
82
|
+
title,
|
|
83
|
+
dismissible = false,
|
|
84
|
+
onDismiss,
|
|
85
|
+
iconSize,
|
|
86
|
+
iconProps,
|
|
87
|
+
...props
|
|
88
|
+
}) => {
|
|
89
|
+
const [isVisible, setIsVisible] = React.useState(open);
|
|
90
|
+
|
|
91
|
+
React.useEffect(() => {
|
|
92
|
+
setIsVisible(open);
|
|
93
|
+
}, [open]);
|
|
94
|
+
|
|
95
|
+
if (!isVisible) return null;
|
|
96
|
+
|
|
97
|
+
const handleDismiss = (): void => {
|
|
98
|
+
setIsVisible(false);
|
|
99
|
+
onDismiss?.();
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Default props for the Icon component used in the Alert component.
|
|
104
|
+
* These props set the fill color to white and the size to 32 pixels.
|
|
105
|
+
*/
|
|
106
|
+
const defaultIconProps: IconProps = {
|
|
107
|
+
size: iconSize || 32,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Update the severityType object with the type
|
|
111
|
+
const severityType: Record<Severity, "polite" | "assertive"> = {
|
|
112
|
+
default: "polite",
|
|
113
|
+
info: "polite",
|
|
114
|
+
success: "polite",
|
|
115
|
+
warning: "polite",
|
|
116
|
+
error: "assertive",
|
|
117
|
+
} as const;
|
|
118
|
+
|
|
119
|
+
const mergedIconProps = { ...defaultIconProps, ...iconProps };
|
|
120
|
+
|
|
121
|
+
// Update the severityIcons object with the type
|
|
122
|
+
const severityIcons: Record<Severity, JSX.Element> = {
|
|
123
|
+
info: <Icon.InfoSolid {...mergedIconProps} />,
|
|
124
|
+
success: <Icon.SuccessSolid {...mergedIconProps} />,
|
|
125
|
+
warning: <Icon.WarnSolid {...mergedIconProps} />,
|
|
126
|
+
error: <Icon.AlertSolid {...mergedIconProps} />,
|
|
127
|
+
default: <Icon.AlertSquareSolid {...mergedIconProps} />,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<UI
|
|
132
|
+
as="div"
|
|
133
|
+
role="alert"
|
|
134
|
+
aria-live={severityType[severity]}
|
|
135
|
+
aria-atomic="true"
|
|
136
|
+
className={`alert alert-${severity}`}
|
|
137
|
+
data-alert={severity}
|
|
138
|
+
{...props}
|
|
139
|
+
>
|
|
140
|
+
<UI aria-hidden="true">{severityIcons[severity]}</UI>
|
|
141
|
+
<UI as="div" className="alert-message">
|
|
142
|
+
{title && (
|
|
143
|
+
<UI as="h3" className="alert-title">
|
|
144
|
+
{title}
|
|
145
|
+
</UI>
|
|
146
|
+
)}
|
|
147
|
+
<UI as="div">{children}</UI>
|
|
148
|
+
</UI>
|
|
149
|
+
{dismissible && <DismissButton onDismiss={handleDismiss} />}
|
|
150
|
+
</UI>
|
|
151
|
+
);
|
|
152
|
+
};
|
|
153
|
+
export default Alert;
|
|
154
|
+
Alert.displayName = "Alert";
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Meta } from "@storybook/blocks";
|
|
2
|
+
|
|
3
|
+
<Meta title="FP.REACT Elements/DismissButton/Readme" />
|
|
4
|
+
|
|
5
|
+
# DismissButton
|
|
6
|
+
|
|
7
|
+
## Summary
|
|
8
|
+
|
|
9
|
+
The `DismissButton` component is a reusable button designed to dismiss alerts.
|
|
10
|
+
It includes an icon and an optional callback function for handling dismiss
|
|
11
|
+
actions.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- Customizable icon size
|
|
16
|
+
- Callback function for dismiss actions
|
|
17
|
+
- Accessible with `aria-label`
|
|
18
|
+
|
|
19
|
+
## Props
|
|
20
|
+
|
|
21
|
+
| Name | Type | Default | Description |
|
|
22
|
+
| ----------- | ------------ | ------- | ------------------------------------------------ |
|
|
23
|
+
| `onDismiss` | `() => void` | - | Callback function when dismiss button is clicked |
|
|
24
|
+
| `iconSize` | `number` | `16` | Size of the close icon in pixels |
|
|
25
|
+
|
|
26
|
+
## Technical Details
|
|
27
|
+
|
|
28
|
+
The `DismissButton` component uses the `Button` and `Icon` components from the
|
|
29
|
+
`#components` library. It is memoized using `React.memo` for performance
|
|
30
|
+
optimization.
|
|
31
|
+
|
|
32
|
+
## Usage Example
|
|
33
|
+
|
|
34
|
+
### Basic Usage
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import React from "react";
|
|
38
|
+
import DismissButton from "#components/alert/elements/dismiss-button";
|
|
39
|
+
|
|
40
|
+
const handleDismiss = () => {
|
|
41
|
+
console.log("Alert dismissed");
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const AlertComponent = () => (
|
|
45
|
+
<div className="alert">
|
|
46
|
+
<span>Alert message</span>
|
|
47
|
+
<DismissButton onDismiss={handleDismiss} />
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
export default AlertComponent;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Advanced Usage
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import React from "react";
|
|
58
|
+
import DismissButton from "#components/alert/elements/dismiss-button";
|
|
59
|
+
|
|
60
|
+
const handleDismiss = () => {
|
|
61
|
+
// Custom logic for dismissing the alert
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const CustomAlert = () => (
|
|
65
|
+
<div className="custom-alert">
|
|
66
|
+
<span>Custom alert message</span>
|
|
67
|
+
<DismissButton onDismiss={handleDismiss} iconSize={24} />
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
export default CustomAlert;
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Additional Notes
|
|
75
|
+
|
|
76
|
+
- Ensure the `onDismiss` callback is provided to handle the dismiss action.
|
|
77
|
+
- The `iconSize` prop can be adjusted to fit the design requirements.
|
|
@@ -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
|