-
Notifications
You must be signed in to change notification settings - Fork 0
* 📦 build: upgrade to Mantine 9, TypeScript 6, and modern tooling #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
ff926dc
* 📦 build: upgrade to Mantine 9, TypeScript 6, and modern tooling
gfazioli b8e5a6f
🧪 Add mock for qrcode module to support SVG rendering in tests
gfazioli bafd008
feat(QRCode): ✨ Add ref forwarding to QRCode component
gfazioli 240434f
refactor(QRCode): 🧹 remove unused ref forwarding
gfazioli File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| { | ||
| "printWidth": 100, | ||
| "singleQuote": true, | ||
| "trailingComma": "es5", | ||
| "importOrder": [ | ||
| ".*styles.css$", | ||
| "", | ||
| "dayjs", | ||
| "^react$", | ||
| "^next$", | ||
| "^next/.*$", | ||
| "<BUILTIN_MODULES>", | ||
| "<THIRD_PARTY_MODULES>", | ||
| "^@mantine/(.*)$", | ||
| "^@mantinex/(.*)$", | ||
| "^@mantine-tests/(.*)$", | ||
| "^@docs/(.*)$", | ||
| "^@/.*$", | ||
| "^../(?!.*\\.css$).*$", | ||
| "^./(?!.*\\.css$).*$", | ||
| "\\.module\\.css$", | ||
| "(?<!\\.module)\\.css$" | ||
| ], | ||
| "sortPackageJson": false, | ||
| "ignorePatterns": [ | ||
| "*.d.ts", | ||
| "*.mdx", | ||
| "*.md", | ||
| "packages/*/*/styles.css", | ||
| "packages/*/*/styles.layer.css", | ||
| "packages/*/*/styles/*.css", | ||
| "docs/.next", | ||
| "docs/out" | ||
| ] | ||
| } | ||
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,17 @@ | ||
| import { dirname, join } from 'path'; | ||
| import type { StorybookConfig } from '@storybook/react-vite'; | ||
|
|
||
| function getAbsolutePath(value: string): any { | ||
| return dirname(require.resolve(join(value, 'package.json'))); | ||
| } | ||
|
|
||
| const config: StorybookConfig = { | ||
| core: { | ||
| disableWhatsNewNotifications: true, | ||
| disableTelemetry: true, | ||
| enableCrashReports: false, | ||
| }, | ||
| stories: ['../package/src/**/*.story.@(js|jsx|mjs|ts|tsx)'], | ||
| addons: [getAbsolutePath('@storybook/addon-essentials'), getAbsolutePath('storybook-dark-mode')], | ||
| addons: ['@storybook/addon-themes'], | ||
| framework: { | ||
| name: getAbsolutePath('@storybook/react-vite'), | ||
| name: '@storybook/react-vite', | ||
| options: {}, | ||
| }, | ||
| docs: { | ||
| autodocs: false, | ||
| }, | ||
| }; | ||
|
|
||
| export default config; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,41 +1,29 @@ | ||
| import type { Preview } from '@storybook/react'; | ||
|
|
||
| import '@mantine/core/styles.css'; | ||
|
|
||
| import React, { useEffect } from 'react'; | ||
| import { addons } from '@storybook/preview-api'; | ||
| import { DARK_MODE_EVENT_NAME } from 'storybook-dark-mode'; | ||
| import { MantineProvider, useMantineColorScheme } from '@mantine/core'; | ||
|
|
||
| const channel = addons.getChannel(); | ||
|
|
||
| function ColorSchemeWrapper({ children }: { children: React.ReactNode }) { | ||
| const { setColorScheme } = useMantineColorScheme(); | ||
| const handleColorScheme = (value: boolean) => setColorScheme(value ? 'dark' : 'light'); | ||
| import { MantineProvider } from '@mantine/core'; | ||
|
|
||
| useEffect(() => { | ||
| channel.on(DARK_MODE_EVENT_NAME, handleColorScheme); | ||
| return () => channel.off(DARK_MODE_EVENT_NAME, handleColorScheme); | ||
| }, [channel]); | ||
|
|
||
| return <>{children}</>; | ||
| } | ||
|
|
||
| export const decorators = [ | ||
| (renderStory: any) => <ColorSchemeWrapper>{renderStory()}</ColorSchemeWrapper>, | ||
| (renderStory: any) => <MantineProvider>{renderStory()}</MantineProvider>, | ||
| ]; | ||
| export const parameters = { | ||
| layout: 'centered', | ||
| }; | ||
|
|
||
| const preview: Preview = { | ||
| parameters: { | ||
| actions: { argTypesRegex: '^on[A-Z].*' }, | ||
| controls: { | ||
| matchers: { | ||
| color: /(background|color)$/i, | ||
| date: /Date$/i, | ||
| }, | ||
| export const globalTypes = { | ||
| theme: { | ||
| name: 'Theme', | ||
| description: 'Mantine color scheme', | ||
| defaultValue: 'light', | ||
| toolbar: { | ||
| icon: 'mirror', | ||
| items: [ | ||
| { value: 'light', title: 'Light' }, | ||
| { value: 'dark', title: 'Dark' }, | ||
| ], | ||
| }, | ||
| }, | ||
| }; | ||
|
|
||
| export default preview; | ||
| export const decorators = [ | ||
| (renderStory: any, context: any) => { | ||
| const scheme = (context.globals.theme || 'light') as 'light' | 'dark'; | ||
| return <MantineProvider forceColorScheme={scheme}>{renderStory()}</MantineProvider>; | ||
| }, | ||
| ]; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,94 +1,51 @@ | ||
| # CLAUDE.md | ||
|
|
||
| This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. | ||
|
|
||
| ## What This Repo Is | ||
|
|
||
| This is the `@gfazioli/mantine-qr-code` component library, part of the Mantine Extensions ecosystem. It provides a QR Code component for React applications built with Mantine. | ||
| ## Project | ||
| `@gfazioli/mantine-qr-code` — A Mantine 9 QR Code component for React with custom dot/finder shapes, image overlay, SVG/PNG download hook, and full Styles API. Requires React 19 and TypeScript 6. | ||
|
|
||
| ## Commands | ||
|
|
||
| | Command | Purpose | | ||
| |---------|---------| | ||
| | `yarn build` | Build the npm package (Rollup + DTS + CSS extraction) | | ||
| | `yarn test` | Full test suite: syncpack → prettier → typecheck → lint → jest | | ||
| | `yarn build` | Build the npm package via Rollup | | ||
| | `yarn dev` | Start the Next.js docs dev server (port 9281) | | ||
| | `yarn test` | Full test suite (syncpack + oxfmt + typecheck + lint + jest) | | ||
| | `yarn jest` | Run only Jest unit tests | | ||
| | `yarn jest --testPathPattern=QRCode` | Run tests for a single component | | ||
| | `yarn dev` | Start docs dev server at http://localhost:9281 | | ||
| | `yarn storybook` | Start Storybook at http://localhost:8271 | | ||
| | `yarn docgen` | Generate `docs/docgen.json` from component TypeScript types | | ||
| | `yarn docs:build` | Run docgen + build Next.js static site | | ||
| | `yarn docs:deploy` | Build docs + deploy to GitHub Pages | | ||
| | `yarn prettier:write` | Auto-fix formatting (run this if prettier check fails after template propagation) | | ||
| | `yarn release:patch` | Bump patch version, publish to npm, deploy docs | | ||
| | `diny yolo` | AI-assisted git commit (stages all, generates message, commits + pushes) | | ||
| | `yarn docgen` | Generate component API docs (docgen.json) | | ||
| | `yarn docs:build` | Build the Next.js docs site for production | | ||
| | `yarn docs:deploy` | Build and deploy docs to GitHub Pages | | ||
| | `yarn lint` | Run oxlint + Stylelint | | ||
| | `yarn format:write` | Format all files with oxfmt | | ||
| | `yarn storybook` | Start Storybook dev server | | ||
| | `yarn clean` | Remove build artifacts | | ||
| | `yarn release:patch` | Bump patch version and deploy docs | | ||
| | `diny yolo` | AI-assisted commit (stage all, generate message, commit + push) | | ||
|
|
||
| > **Important**: After changing the public API, always run `yarn clean && yarn build` before `yarn test`. | ||
|
|
||
| ## Architecture | ||
|
|
||
| **Monorepo with two Yarn workspaces:** | ||
|
|
||
| - `package/` — The publishable npm package. Source lives in `package/src/`. Built artifacts go to `package/dist/` (ESM `.mjs`, CJS `.cjs`, `.d.ts`, `styles.css`). | ||
| - `docs/` — Next.js 15 static site with MDX support. Uses `workspace:*` to reference the local package. Deployed to GitHub Pages via `gh-pages`. | ||
|
|
||
| **Build pipeline** (`yarn build`): | ||
| 1. Rollup bundles `package/src/index.ts` → ESM + CJS with `preserveModules` | ||
| 2. `scripts/generate-dts.ts` produces `.d.ts` and `.d.mts` declarations | ||
| 3. `scripts/prepare-css.ts` extracts CSS into `styles.css` and `styles.layer.css` | ||
|
|
||
| CSS class names are hashed with the prefix `me` via `hash-css-selector`. Non-index chunks get a `'use client';` banner automatically. | ||
|
|
||
| ## Component Authoring Pattern | ||
|
|
||
| Every component follows Mantine's Styles API pattern. Use the QRCode component (`package/src/QRCode.tsx`) as the canonical reference: | ||
|
|
||
| 1. **Factory type** — Define a `PolymorphicFactory` type specifying props, default element, stylesNames, variants, and CSS variables. | ||
| 2. **Props interface** — Extend `BoxProps` + your base props + `StylesApiProps<YourFactory>`. | ||
| 3. **Default props** — Declare a `defaultProps` partial object. | ||
| 4. **CSS Variables resolver** — Use `createVarsResolver<YourFactory>()` to map props to CSS custom properties. | ||
| 5. **Component body** — Use `polymorphicFactory()` with `useProps()` and `useStyles()` hooks. Render via Mantine's `Box` with `getStyles('partName')` and `mod` for data attributes. | ||
| 6. **Attach classes** — Set `Component.classes = classes` and `Component.displayName`. | ||
|
|
||
| ### Exports pattern (`package/src/index.ts`) | ||
| Export the component and its public types (base props, CSS variables type, factory type). Do not export internal types. | ||
|
|
||
| ### CSS Modules (`Component.module.css`) | ||
| - Use CSS custom properties (`--component-*`) for all dynamic values | ||
| - Define sizes via `--component-size-{xs,sm,md,lg,xl}` | ||
| - Use `[data-attribute]` selectors for state/variant styling | ||
| - Animations go in `@keyframes` within the module | ||
|
|
||
| ### Testing (`Component.test.tsx`) | ||
| - Import `render` from `@mantine-tests/core` (not directly from `@testing-library/react`) | ||
| - Test: renders without crashing, forwards ref, data attributes for props/variants/states | ||
| - Jest with jsdom; CSS modules are mocked via `identity-obj-proxy` | ||
|
|
||
| ### Storybook (`Component.story.tsx`) | ||
| Stories live alongside source. Storybook runs on port 8271. | ||
|
|
||
| ## Documentation Site | ||
|
|
||
| - `docs/data.ts` — Package metadata (name, description, repo URL, author) | ||
| - `docs/docs.mdx` — Main documentation content | ||
| - `docs/demos/` — Interactive demos using `@mantinex/demo` | ||
| - `docs/pages/index.tsx` — Assembles Shell, PageHeader, DocsTabs, and the MDX content | ||
| - `docs/styles-api/` — Styles API data for the documentation table | ||
| - `docs/docgen.json` — Auto-generated from TypeScript types (don't edit manually) | ||
|
|
||
| The `next.config.mjs` dynamically sets `basePath` from the repository field in `package/package.json`. | ||
|
|
||
| ## Code Style | ||
|
|
||
| - Prettier: 160 char width, single quotes, trailing commas, sorted imports (styles → react → third-party → @mantine → local) | ||
| - MDX files use 70 char print width | ||
| - ESLint: `eslint-config-mantine` base | ||
| - Stylelint: `stylelint-config-standard-scss` (relaxed) | ||
| - Syncpack enforces consistent dependency versions across workspaces | ||
|
|
||
| ## Tech Stack | ||
|
|
||
| - **Mantine 8.x**, **React 19**, **TypeScript 5.9** | ||
| - **Yarn 4** (node-modules linker, not PnP) | ||
| - **Rollup** for package builds, **esbuild** for transpilation | ||
| - **Next.js 15** with static export for docs | ||
| - **Jest 29** with jsdom for tests | ||
| - **Storybook 8** with React-Vite framework | ||
| ### Workspace Layout | ||
| Yarn workspaces monorepo with two workspaces: `package/` (npm package) and `docs/` (Next.js 15 documentation site). | ||
|
|
||
| ### Package Source (`package/src/`) | ||
| - `QRCode.tsx` -- Main component using Mantine's `polymorphicFactory()` pattern with `useProps()` and `useStyles()` hooks. | ||
| - `QRCode.module.css` -- CSS module with custom properties for dynamic values. | ||
| - `use-qr-code-download.ts` -- Hook for downloading the QR code as an image. | ||
| - `lib/qr-encoder.ts` -- Custom QR code encoding logic. | ||
| - `lib/dot-shapes.ts` -- Configurable dot shape renderers. | ||
| - `lib/finder-shapes.ts` -- Configurable finder pattern shape renderers. | ||
| - `lib/utils.ts` -- Shared utilities. | ||
| - `index.ts` -- Public exports (component, hook, types). | ||
|
|
||
| ### Build Pipeline | ||
| Rollup bundles to dual ESM/CJS with `'use client'` banner. CSS modules hashed with `hash-css-selector` (prefix `me`). TypeScript declarations via `rollup-plugin-dts`. CSS split into `styles.css` and `styles.layer.css`. | ||
|
|
||
| ## Testing | ||
| Jest with `jsdom`, `esbuild-jest` transform, CSS mocked via `identity-obj-proxy`. Tests use `@mantine-tests/core` render helper. | ||
|
|
||
| ## Ecosystem | ||
| This repo is part of the Mantine Extensions ecosystem, derived from the `mantine-base-component` template. See the workspace `CLAUDE.md` (in the parent directory) for: | ||
| - Development checklist (code -> test -> build -> docs -> release) | ||
| - Cross-cutting patterns (compound components, responsive CSS, GitHub sync) | ||
| - Update packages workflow | ||
| - Release process |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,30 @@ | ||
| { | ||
| "compilerOptions": { | ||
| "target": "es5", | ||
| "lib": ["dom", "dom.iterable", "esnext"], | ||
| "target": "es2015", | ||
| "lib": [ | ||
| "dom", | ||
| "dom.iterable", | ||
| "esnext" | ||
| ], | ||
| "allowJs": true, | ||
| "skipLibCheck": true, | ||
| "strict": true, | ||
| "forceConsistentCasingInFileNames": true, | ||
| "noEmit": true, | ||
| "esModuleInterop": true, | ||
| "module": "esnext", | ||
| "moduleResolution": "node", | ||
| "moduleResolution": "bundler", | ||
| "resolveJsonModule": true, | ||
| "isolatedModules": true, | ||
| "jsx": "preserve", | ||
| "incremental": true | ||
| }, | ||
| "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], | ||
| "exclude": ["node_modules"] | ||
| "include": [ | ||
| "next-env.d.ts", | ||
| "**/*.ts", | ||
| "**/*.tsx" | ||
| ], | ||
| "exclude": [ | ||
| "node_modules" | ||
| ] | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.