Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
27 changes: 27 additions & 0 deletions .changeset/bright-owls-approve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
'@ciscode/ui-notification-kit': minor
---

Ship the first functional release of NotificationKit-UI.

### Added

- Notification provider and hook API (`NotificationProvider`, `useNotification`)
- Notification types: success, error, warning, info, loading, default
- Position support: top-left, top-center, top-right, center, bottom-left, bottom-center, bottom-right
- Configurable animations (slide, fade, scale), durations, auto-dismiss, close button, actions, and custom icons
- Store lifecycle with add/update/dismiss/clear/restore and history tracking
- Route-aware clearing support (`clearOnNavigate`, `navigationKey`)
- Accessibility support (live-region announcements, ARIA roles, keyboard escape-to-dismiss)
- Tailwind + RTL styling support and published style asset export (`./style.css`)
- Test coverage for store behavior, provider behavior, and a11y essentials

### Changed

- Package entry exports updated to align with generated build outputs
- Notification rendering moved to a portal to avoid stacking-context issues in host apps
- Layering hardened so notifications stay above dashboard content

### Notes

- Import styles in host apps using: `@ciscode/ui-notification-kit/style.css`
68 changes: 35 additions & 33 deletions .github/instructions/bugfix.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
describe('Bug: Button not disabled when loading', () => {
it('should disable button during loading', () => {
render(<Button isLoading>Click</Button>);

// This SHOULD pass but currently FAILS
expect(screen.getByRole('button')).toBeDisabled();
});
Expand Down Expand Up @@ -59,11 +59,11 @@ useEffect(() => {

### 1. State Management Issues

| Bug Type | Symptoms | Solution |
| --------------------- | ---------------------- | --------------------------- |
| **Stale closure** | Old values in callback | Update dependencies |
| **Infinite loop** | Component re-renders | Fix useEffect dependencies |
| **Lost state** | State resets unexpectedly| Check component key |
| Bug Type | Symptoms | Solution |
| ----------------- | ------------------------- | -------------------------- |
| **Stale closure** | Old values in callback | Update dependencies |
| **Infinite loop** | Component re-renders | Fix useEffect dependencies |
| **Lost state** | State resets unexpectedly | Check component key |

**Example fix:**

Expand All @@ -81,19 +81,19 @@ useEffect(() => {
// ✅ FIX - Functional update
useEffect(() => {
const timer = setInterval(() => {
setCount(prev => prev + 1); // ✅ Uses current count
setCount((prev) => prev + 1); // ✅ Uses current count
}, 1000);
return () => clearInterval(timer);
}, []);
```

### 2. useEffect Issues

| Bug Type | Symptoms | Solution |
| --------------------- | --------------------- | --------------------------- |
| **Memory leak** | Performance degrades | Add cleanup function |
| **Missing cleanup** | Side effects persist | Return cleanup |
| **Wrong dependencies**| Unexpected behavior | Fix dependency array |
| Bug Type | Symptoms | Solution |
| ---------------------- | -------------------- | -------------------- |
| **Memory leak** | Performance degrades | Add cleanup function |
| **Missing cleanup** | Side effects persist | Return cleanup |
| **Wrong dependencies** | Unexpected behavior | Fix dependency array |

**Example fix:**

Expand All @@ -112,11 +112,11 @@ useEffect(() => {

### 3. Event Handler Issues

| Bug Type | Symptoms | Solution |
| --------------------- | --------------------- | --------------------------- |
| **Handler not called**| Click doesn't work | Check event binding |
| **Multiple calls** | Handler fires twice | Remove duplicate listeners |
| **Wrong event** | Unexpected behavior | Use correct event type |
| Bug Type | Symptoms | Solution |
| ---------------------- | ------------------- | -------------------------- |
| **Handler not called** | Click doesn't work | Check event binding |
| **Multiple calls** | Handler fires twice | Remove duplicate listeners |
| **Wrong event** | Unexpected behavior | Use correct event type |

**Example fix:**

Expand All @@ -131,11 +131,11 @@ useEffect(() => {

### 4. Rendering Issues

| Bug Type | Symptoms | Solution |
| --------------------- | --------------------- | --------------------------- |
| **Conditional render**| Component disappears | Fix condition logic |
| **Key prop** | Wrong items update | Use stable unique keys |
| **Forced re-render** | Performance issues | Memoize expensive calcs |
| Bug Type | Symptoms | Solution |
| ---------------------- | -------------------- | ----------------------- |
| **Conditional render** | Component disappears | Fix condition logic |
| **Key prop** | Wrong items update | Use stable unique keys |
| **Forced re-render** | Performance issues | Memoize expensive calcs |

**Example fix:**

Expand All @@ -153,11 +153,11 @@ useEffect(() => {

### 5. Accessibility Bugs

| Bug Type | Symptoms | Solution |
| --------------------- | --------------------- | --------------------------- |
| **Missing ARIA** | Screen reader issues | Add ARIA attributes |
| **No keyboard nav** | Can't use keyboard | Add keyboard handlers |
| **Poor contrast** | Hard to read | Fix colors |
| Bug Type | Symptoms | Solution |
| ------------------- | -------------------- | --------------------- |
| **Missing ARIA** | Screen reader issues | Add ARIA attributes |
| **No keyboard nav** | Can't use keyboard | Add keyboard handlers |
| **Poor contrast** | Hard to read | Fix colors |

**Example fix:**

Expand All @@ -182,9 +182,9 @@ useEffect(() => {
```typescript
it('should fix the bug', async () => {
render(<Component />);

await userEvent.click(screen.getByRole('button'));

expect(screen.getByText(/expected/i)).toBeInTheDocument();
});
```
Expand Down Expand Up @@ -217,10 +217,10 @@ npm run dev
```typescript
/**
* Component that was buggy
*
*
* @fixed v1.2.3 - Fixed click handler issue
*/
export function Component(props: Props): JSX.Element
export function Component(props: Props): JSX.Element;
```

---
Expand All @@ -241,10 +241,12 @@ const sortedItems = [...props.items].sort();

```typescript
// ❌ Bug - Object comparison
if (user === prevUser) { } // Always false (different references)
if (user === prevUser) {
} // Always false (different references)

// ✅ Fix - Compare values
if (user.id === prevUser.id) { }
if (user.id === prevUser.id) {
}
```

### 3. Missing Null Checks
Expand Down
15 changes: 8 additions & 7 deletions .github/instructions/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,25 +97,25 @@ Toast/

### JSDoc for Hooks:

```typescript
````typescript
/**
* Hook for managing notification state
* @returns Notification methods and state
* @example
* ```tsx
* const { notify, dismiss, notifications } = useNotifications();
*
*
* const showSuccess = () => {
* notify({ type: 'success', message: 'Action completed!' });
* };
* ```
*/
export function useNotifications(): UseNotificationsReturn;
```
````

### Component Documentation:

```typescript
````typescript
export interface ToastProps {
/** Toast message content */
message: string;
Expand All @@ -129,7 +129,7 @@ export interface ToastProps {

/**
* Toast notification component
*
*
* @example
* ```tsx
* <Toast
Expand All @@ -140,7 +140,7 @@ export interface ToastProps {
* ```
*/
export function Toast(props: ToastProps): JSX.Element;
```
````

---

Expand Down Expand Up @@ -179,6 +179,7 @@ export type NotificationType = 'success' | 'error' | 'warning' | 'info';
### Creating New Components:

1. **Create component folder**

```
mkdir -p src/components/MyComponent
cd src/components/MyComponent
Expand Down Expand Up @@ -230,7 +231,7 @@ useEffect(() => {
// ✅ Limit queue size
const MAX_NOTIFICATIONS = 5;
const addNotification = (notif) => {
setNotifications(prev => [...prev.slice(-MAX_NOTIFICATIONS + 1), notif]);
setNotifications((prev) => [...prev.slice(-MAX_NOTIFICATIONS + 1), notif]);
};
```

Expand Down
Loading
Loading