Skip to content

Commit cef9d63

Browse files
authored
Merge pull request #25 from luth-v/fix/icon-type
Refactor: Standardize Icon Handle Types to Use `AnimatedIconHandle`
2 parents 9046b7e + 20bc86e commit cef9d63

339 files changed

Lines changed: 2416 additions & 1747 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
# testing
1414
/coverage
15+
/test-consumer
1516

1617
# next.js
1718
/.next/

CONTRIBUTING.md

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,10 @@ Create a new file in `icons/` directory. Follow the exact same pattern as existi
153153

154154
```tsx
155155
import { forwardRef, useImperativeHandle } from "react";
156-
import { AnimatedIconProps } from "./types";
156+
import { AnimatedIconHandle, AnimatedIconProps } from "./types";
157157
import { motion, useAnimate } from "motion/react";
158158

159-
export type YourIconHandle = {
160-
startAnimation: () => void;
161-
stopAnimation: () => void;
162-
};
163-
164-
const YourIcon = forwardRef<YourIconHandle, AnimatedIconProps>(
159+
const YourIcon = forwardRef<AnimatedIconHandle, AnimatedIconProps>(
165160
(
166161
{ size = 24, color = "currentColor", strokeWidth = 2, className = "" },
167162
ref,
@@ -212,9 +207,9 @@ export default YourIcon;
212207

213208
Key requirements:
214209

215-
- Use `forwardRef` and `useImperativeHandle`
210+
- Use `forwardRef` with `AnimatedIconHandle` from `./types`
216211
- Use `motion/react` for animations
217-
- Export type handle for imperative control
212+
- Implement `startAnimation` and `stopAnimation` via `useImperativeHandle`
218213
- Check existing icons like `github-icon.tsx` for reference
219214

220215
### Step 2: Register the Icon
@@ -276,6 +271,89 @@ npm run build
276271

277272
See [Submitting Changes](#submitting-changes) section.
278273

274+
## Testing
275+
276+
Before submitting your changes, verify that icons work correctly:
277+
278+
### Visual Testing (Doc Site)
279+
280+
1. Start the dev server:
281+
282+
```bash
283+
npm run dev
284+
```
285+
286+
2. Open http://localhost:3000/icons in your browser
287+
288+
3. Verify:
289+
- Your icon appears in the gallery
290+
- Hover animations work correctly
291+
- No console errors appear
292+
- Click on the icon to view its detail page
293+
294+
### Testing as a Library Consumer
295+
296+
To verify icons work when installed via the shadcn CLI:
297+
298+
1. Create a test project (outside or inside the repo):
299+
300+
```bash
301+
npx create-next-app@latest test-consumer --typescript --tailwind --app
302+
cd test-consumer
303+
npm install motion
304+
npx shadcn@latest init --defaults
305+
```
306+
307+
2. Add an icon from your local registry:
308+
309+
```bash
310+
npx shadcn@latest add "http://localhost:3000/r/github-icon.json"
311+
```
312+
313+
3. Create a test page (`app/page.tsx`):
314+
315+
```tsx
316+
"use client";
317+
import { useRef } from "react";
318+
import GithubIcon from "@/components/ui/github-icon";
319+
import { AnimatedIconHandle } from "@/components/ui/types";
320+
321+
export default function Home() {
322+
const iconRef = useRef<AnimatedIconHandle>(null);
323+
324+
return (
325+
<div className="flex flex-col items-center gap-4 p-10">
326+
<GithubIcon ref={iconRef} size={48} />
327+
<button onClick={() => iconRef.current?.startAnimation()}>
328+
Start Animation
329+
</button>
330+
</div>
331+
);
332+
}
333+
```
334+
335+
4. Run the test project:
336+
337+
```bash
338+
npm run dev
339+
```
340+
341+
5. Verify:
342+
- Icon renders correctly
343+
- Hover animation works
344+
- Button triggers animation via ref
345+
- No TypeScript errors
346+
347+
### Quick Checklist
348+
349+
```bash
350+
# Run all checks before submitting
351+
npm run check
352+
353+
# Verify registry builds successfully
354+
npm run registry:build
355+
```
356+
279357
## Making Changes
280358

281359
### Branch Strategy

bun.lock

Lines changed: 1613 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

eslint.config.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ const eslintConfig = defineConfig([
1212
"out/**",
1313
"build/**",
1414
"next-env.d.ts",
15+
// Test consumer project
16+
"test-consumer/**",
1517
]),
1618
]);
1719

icons/accessibility-icon.tsx

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
11
import { forwardRef, useImperativeHandle } from "react";
2-
import { AnimatedIconProps } from "./types";
2+
import { AnimatedIconHandle, AnimatedIconProps } from "./types";
33
import { motion, useAnimate } from "motion/react";
44

5-
export type AccessibilityIconHandle = {
6-
startAnimation: () => void;
7-
stopAnimation: () => void;
8-
};
9-
10-
const AccessibilityIcon = forwardRef<
11-
AccessibilityIconHandle,
12-
AnimatedIconProps
13-
>(
5+
const AccessibilityIcon = forwardRef<AnimatedIconHandle, AnimatedIconProps>(
146
(
157
{ size = 24, color = "currentColor", strokeWidth = 2, className = "" },
168
ref,

icons/alarm-clock-plus-icon.tsx

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
11
import { forwardRef, useImperativeHandle } from "react";
2-
import { AnimatedIconProps } from "./types";
2+
import { AnimatedIconHandle, AnimatedIconProps } from "./types";
33
import { motion, useAnimate } from "motion/react";
44

5-
export type AlarmClockPlusIconHandle = {
6-
startAnimation: () => void;
7-
stopAnimation: () => void;
8-
};
9-
10-
const AlarmClockPlusIcon = forwardRef<
11-
AlarmClockPlusIconHandle,
12-
AnimatedIconProps
13-
>(
5+
const AlarmClockPlusIcon = forwardRef<AnimatedIconHandle, AnimatedIconProps>(
146
(
157
{ size = 24, color = "currentColor", strokeWidth = 2, className = "" },
168
ref,

icons/align-center-icon.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
import { forwardRef, useImperativeHandle } from "react";
2-
import { AnimatedIconProps } from "./types";
2+
import { AnimatedIconHandle, AnimatedIconProps } from "./types";
33
import { motion, useAnimate } from "motion/react";
44

5-
export type AlignCenterIconHandle = {
6-
startAnimation: () => void;
7-
stopAnimation: () => void;
8-
};
9-
10-
const AlignCenterIcon = forwardRef<AlignCenterIconHandle, AnimatedIconProps>(
5+
const AlignCenterIcon = forwardRef<AnimatedIconHandle, AnimatedIconProps>(
116
(
127
{ size = 24, color = "currentColor", strokeWidth = 2, className = "" },
138
ref,

icons/align-vertical-space-around-icon.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
import { forwardRef, useImperativeHandle } from "react";
2-
import { AnimatedIconProps } from "./types";
2+
import { AnimatedIconHandle, AnimatedIconProps } from "./types";
33
import { motion, useAnimate } from "motion/react";
44

5-
export type AlignVerticalSpaceAroundIconHandle = {
6-
startAnimation: () => void;
7-
stopAnimation: () => void;
8-
};
9-
105
const AlignVerticalSpaceAroundIcon = forwardRef<
11-
AlignVerticalSpaceAroundIconHandle,
6+
AnimatedIconHandle,
127
AnimatedIconProps
138
>(
149
(

icons/ambulance-icon.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
import { forwardRef, useImperativeHandle } from "react";
2-
import { AnimatedIconProps } from "./types";
2+
import { AnimatedIconHandle, AnimatedIconProps } from "./types";
33
import { motion, useAnimate } from "motion/react";
44

5-
export type AmbulanceIconHandle = {
6-
startAnimation: () => void;
7-
stopAnimation: () => void;
8-
};
9-
10-
const AmbulanceIcon = forwardRef<AmbulanceIconHandle, AnimatedIconProps>(
5+
const AmbulanceIcon = forwardRef<AnimatedIconHandle, AnimatedIconProps>(
116
(
127
{ size = 24, color = "currentColor", strokeWidth = 2, className = "" },
138
ref,

icons/ampersand-icon.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
import { forwardRef, useImperativeHandle } from "react";
2-
import { AnimatedIconProps } from "./types";
2+
import { AnimatedIconHandle, AnimatedIconProps } from "./types";
33
import { motion, useAnimate } from "motion/react";
44

5-
export type AmpersandIconHandle = {
6-
startAnimation: () => void;
7-
stopAnimation: () => void;
8-
};
9-
10-
const AmpersandIcon = forwardRef<AmpersandIconHandle, AnimatedIconProps>(
5+
const AmpersandIcon = forwardRef<AnimatedIconHandle, AnimatedIconProps>(
116
({ size = 40, className = "" }, ref) => {
127
const [scope, animate] = useAnimate();
138

0 commit comments

Comments
 (0)