Conversation
- Fix SONAR_PROJECT_KEY (critical bug fix) - Add dependabot.yml for dependency management - Add sonarqube_mcp.instructions.md - Standardize branch triggers and Actions versions - Pin SonarQube actions to commit SHA
- Add comprehensive copilot-instructions.md guiding notification UI development - Align documentation with other frontend packages - Establish clear naming conventions and testing standards - Define module structure and development workflow
… clean, verify, prepublishOnly)
- Add comprehensive instruction files in .github/instructions/ - Includes copilot, testing, bugfix, features, general guidelines - Standardize documentation across all repositories
- Change trigger from tag push to master branch push - Add tag validation using 'git describe --exact-match' - Prevent failed workflows on feature branch tag pushes - Maintain semantic versioning with mandatory tags
* Develop (#1) * ops: updated workflows and unit tests coverage rules * chore: update test coverage rules * chore: setting up env * chore: fixed prettier format errors * ops: updated workflow trigger * 0.0.1 * first version * fixed a style issue and added changeset --------- Co-authored-by: Zaiid Moumni <141942826+Zaiidmo@users.noreply.github.com>
|
There was a problem hiding this comment.
Pull request overview
This PR turns the template package into a functional NotificationKit-UI library by adding a notification store/provider/hook + UI components, publishing a built CSS asset, and updating package/CI metadata for release workflows.
Changes:
- Add notification domain models, store, React context/provider, hooks, and UI components (viewport/container/item/actions/icon/progress).
- Add Tailwind/PostCSS configuration + build pipeline to generate and publish
./style.css. - Replace placeholder exports/docs and update CI workflows (release check + publish), lint config, and formatting config.
Reviewed changes
Copilot reviewed 48 out of 50 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| tailwind.config.mjs | Adds Tailwind config (RTL plugin, zIndex scale). |
| tailwind.config.js | Adds duplicate Tailwind config (same as .mjs). |
| postcss.config.mjs | Adds PostCSS config for Tailwind + autoprefixer. |
| postcss.config.js | Adds duplicate PostCSS config (same as .mjs). |
| src/utils/noop.ts | Removes placeholder noop util. |
| src/utils/index.ts | Removes placeholder exports (now empty module). |
| src/store/notificationStore.ts | Introduces core notification store (state/history/config). |
| src/store/index.ts | Exports store entrypoint. |
| src/models/notification.ts | Introduces notification types/config/state models. |
| src/models/index.ts | Exports models entrypoint. |
| src/index.ts | Updates public API exports to explicit ESM .js paths; adds models/store/utils exports. |
| src/hooks/useNotification.ts | Adds hook to consume notification context. |
| src/hooks/useNoop.ts | Removes placeholder hook. |
| src/hooks/useClearOnNavigate.ts | Adds route-change clearing helper hook. |
| src/hooks/useAccessibility.ts | Adds focus trap + live region helper hooks. |
| src/hooks/index.ts | Replaces placeholder exports with new hooks. |
| src/context/NotificationContext.tsx | Adds notification context + value type. |
| src/components/index.ts | Replaces placeholder exports with notification component exports. |
| src/components/index.css | Adds CSS entry that composes base + notification animation CSS. |
| src/components/NotificationViewport.tsx | Adds portal-based viewport rendering. |
| src/components/NotificationProvider.tsx | Adds provider wiring store → context → viewport. |
| src/components/NotificationProgress.tsx | Adds auto-dismiss progress bar component. |
| src/components/NotificationItem.tsx | Adds per-notification UI + timers + a11y behaviors. |
| src/components/NotificationIcons.css | Adds animation classes/keyframes. |
| src/components/NotificationIcon.tsx | Adds icon rendering (defaults + custom icon support). |
| src/components/NotificationContainer.tsx | Adds position containers and grouping by position. |
| src/components/NotificationActionList.tsx | Adds action button list rendering. |
| src/components/NoopButton.tsx | Removes placeholder component. |
| src/assets/styles/style.css | Adds Tailwind directives + sr-only utility. |
| src/tests/notificationStore.test.ts | Adds unit tests for store behavior. |
| src/tests/NotificationProvider.test.tsx | Adds provider integration tests. |
| src/tests/NotificationItem.a11y.test.tsx | Adds basic a11y regression test for NotificationItem. |
| eslint.config.js | Updates ignore list and adjusts React/TS lint rules. |
| README.md | Rewrites README from template to NotificationKit-UI docs/usage. |
| .prettierrc.json | Expands Prettier configuration. |
| .github/workflows/release-check.yml | Updates PR branch triggers, pins Sonar actions, tweaks permissions/project key. |
| .github/workflows/publish.yml | Changes publish trigger/logic and adds provenance publishing. |
| .github/sonarqube_mcp.instructions.md | Adds SonarQube MCP usage instructions. |
| .github/instructions/testing.instructions.md | Adds testing guidelines doc. |
| .github/instructions/sonarqube_mcp.instructions.md | Adds SonarQube MCP usage instructions (duplicate location). |
| .github/instructions/general.instructions.md | Adds general engineering guidelines doc. |
| .github/instructions/features.instructions.md | Adds feature implementation guidelines doc. |
| .github/instructions/copilot-instructions.md | Adds Copilot usage guidelines doc. |
| .github/instructions/components.instructions.md | Adds component standards doc. |
| .github/instructions/bugfix.instructions.md | Adds bugfix process guidelines doc. |
| .github/dependabot.yml | Adds Dependabot config for npm + GitHub Actions. |
| .github/copilot-instructions.md | Adds top-level Copilot instructions for repo. |
| .changeset/bright-owls-approve.md | Adds changeset for initial functional release. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => { | ||
| // Escape key to dismiss | ||
| if (event.key === 'Escape') { |
There was a problem hiding this comment.
handleKeyDown is typed as React.KeyboardEvent, but React isn’t imported in this file (and jsx: react-jsx doesn’t provide a global React namespace). Import the appropriate type from react (e.g., KeyboardEvent / KeyboardEventHandler) or import type * as React from 'react'.
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
| const { dismissedAt: _dismissedAt, ...rest } = historyItem; | ||
| this.add(rest); | ||
| this.state = { | ||
| ...this.state, | ||
| history: this.state.history.filter((item) => item.id !== id), | ||
| }; | ||
| } |
There was a problem hiding this comment.
restore() mutates this.state.history after calling add(), but never calls this.notify(). Subscribers (e.g., NotificationProvider) won’t re-render to reflect the removed history item. Add a notify after the final state update (or refactor so there’s only one state update + notify).
| // Subscribe to store mutation events — this is the only correct way to react to external store changes | ||
| useEffect(() => { | ||
| return store.subscribe(() => { | ||
| setState(store.getState()); | ||
| }); | ||
| }, [store]); |
There was a problem hiding this comment.
The store subscription sets React state to store.getState() by reference. When setProviderConfig() calls notify() without changing this.state, setState(store.getState()) won’t trigger a re-render, so updated provider config won’t propagate through context. Ensure subscription triggers a new React state object on every notify (e.g., clone the state) or track provider config in React state separately.
| return ( | ||
| <> | ||
| <button onClick={() => notify({ title: 'Test', message: 'Hello' })}>Show</button> | ||
| <button onClick={() => dismiss(state.notifications[0]?.id)}>Dismiss</button> |
There was a problem hiding this comment.
This test calls dismiss(state.notifications[0]?.id), but state.notifications[0]?.id is string | undefined and dismiss expects string. This should fail TypeScript in strict mode. Guard against the empty state (or disable the button / pass a fallback) before calling dismiss.
| <button onClick={() => dismiss(state.notifications[0]?.id)}>Dismiss</button> | |
| <button | |
| onClick={() => { | |
| const firstNotification = state.notifications[0]; | |
| if (!firstNotification?.id) return; | |
| dismiss(firstNotification.id); | |
| }} | |
| > | |
| Dismiss | |
| </button> |
| - name: Build | ||
| run: npm run build --if-present | ||
|
|
||
| - name: Run tests (if present) | ||
| run: npm test --if-present | ||
| continue-on-error: false | ||
| - name: Lint | ||
| run: npm run lint --if-present 2>/dev/null || true | ||
|
|
||
| - name: Build package | ||
| run: npm run build | ||
| - name: Test | ||
| run: npm test --if-present 2>/dev/null || true |
There was a problem hiding this comment.
The publish workflow runs lint/test but explicitly ignores failures (|| true). That allows publishing broken packages. Remove the error-swallowing and let the job fail on lint/test errors (or make these steps conditional, but not ignored).
| * Keeps focus within the container when tabbing. | ||
| */ | ||
| export function useFocusTrap(containerRef: React.RefObject<HTMLElement>) { | ||
| const handleKeyDown = useCallback( |
There was a problem hiding this comment.
useFocusTrap references React.RefObject, but this file does not import the React namespace (and this repo uses jsx: react-jsx, so React.* types aren’t in scope). Import type { RefObject } from react and use RefObject<HTMLElement> (or import type * as React from 'react').
| "clean": "rimraf dist *.tsbuildinfo", | ||
| "build": "tsup", | ||
| "build": "tsup && npm run build:css", | ||
| "build:css": "tailwindcss -i ./src/assets/styles/style.css -o ./dist/style.css --minify", |
There was a problem hiding this comment.
build:css compiles only src/assets/styles/style.css, but the notification animation keyframes/classes live in src/components/NotificationIcons.css (currently only imported by src/components/index.css). As a result, dist/style.css will miss the .animate-notify-* definitions used by components. Consider changing the Tailwind input to src/components/index.css or importing NotificationIcons.css from style.css.
| "build:css": "tailwindcss -i ./src/assets/styles/style.css -o ./dist/style.css --minify", | |
| "build:css": "tailwindcss -i ./src/components/index.css -o ./dist/style.css --minify", |
| "type": "module", | ||
| "sideEffects": false, | ||
| "files": [ | ||
| "dist" | ||
| ], | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "import": "./dist/index.mjs", | ||
| "import": "./dist/index.js", | ||
| "require": "./dist/index.cjs" | ||
| } | ||
| }, | ||
| "./style.css": "./dist/style.css" | ||
| }, |
There was a problem hiding this comment.
With the new published ./style.css asset, "sideEffects": false can cause bundlers (notably webpack) to tree-shake away import '@ciscode/ui-notification-kit/style.css' as “side-effect free”. Consider setting sideEffects to include CSS (e.g., ['*.css']) so consumers reliably receive styles.
| export default { | ||
| plugins: { | ||
| tailwindcss: {}, | ||
| autoprefixer: {}, | ||
| }, | ||
| }; |
There was a problem hiding this comment.
Both postcss.config.js and postcss.config.mjs are present with identical contents. This can lead to ambiguous config resolution depending on the loader. Prefer keeping only one PostCSS config file.
| export default { | |
| plugins: { | |
| tailwindcss: {}, | |
| autoprefixer: {}, | |
| }, | |
| }; | |
| import config from './postcss.config.mjs'; | |
| export default config; |
| # Since develop→master may be a squash merge, look for the latest version tag anywhere in the repo | ||
| # This handles both regular merges and squash merges | ||
| TAG=$(git tag --list --sort=-version:refname 'v*.*.*' | head -1 || echo "") | ||
|
|
||
| if [[ -z "$TAG" ]]; then | ||
| echo "❌ ERROR: No version tag found!" |
There was a problem hiding this comment.
Workflow triggers on every push to master, but the version check uses the latest v*.*.* tag anywhere in the repo (not necessarily pointing to the commit being built). This can publish code that isn’t the tagged release (and can also attempt to re-publish an already-published version on non-release pushes). Consider triggering on tag pushes again, or validate that the release tag is on HEAD/reachable from HEAD before publishing.
| # Since develop→master may be a squash merge, look for the latest version tag anywhere in the repo | |
| # This handles both regular merges and squash merges | |
| TAG=$(git tag --list --sort=-version:refname 'v*.*.*' | head -1 || echo "") | |
| if [[ -z "$TAG" ]]; then | |
| echo "❌ ERROR: No version tag found!" | |
| # Look for the latest version tag that points at the current commit (HEAD) | |
| # This ensures we only publish when the release tag matches the code being built | |
| TAG=$(git tag --points-at HEAD --sort=-version:refname 'v*.*.*' | head -1 || echo "") | |
| if [[ -z "$TAG" ]]; then | |
| echo "❌ ERROR: No version tag found on this commit!" |



Summary
Why
Checklist
npm run lintpassesnpm run typecheckpassesnpm testpassesnpm run buildpassesnpx changeset) if this affects consumersNotes