From 69f86e3553c31a63f73ceef3dc33dde07915cea7 Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Tue, 23 Jan 2024 11:19:36 +0100 Subject: [PATCH 01/27] Add useIconComponent, lucide-react --- packages/lucide-react/src/useIconComponent.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 packages/lucide-react/src/useIconComponent.ts diff --git a/packages/lucide-react/src/useIconComponent.ts b/packages/lucide-react/src/useIconComponent.ts new file mode 100644 index 0000000000..fbd211737f --- /dev/null +++ b/packages/lucide-react/src/useIconComponent.ts @@ -0,0 +1,27 @@ +import { LucideIcon } from "../dist/lucide-react"; +import createLucideIcon, { IconNode } from "./createLucideIcon"; + +type CamelToPascal = + T extends `${infer FirstChar}${infer Rest}` ? `${Capitalize}${Rest}` : never + +type ComponentList = { + [Prop in keyof T as CamelToPascal]: LucideIcon +} + +const toPascalCase = (string: T): CamelToPascal => + string.replace(/(\w)(\w*)(_|-|\s*)/g, (g0, g1, g2) => g1.toUpperCase() + g2.toLowerCase()) as CamelToPascal; + +const useIconComponent = >(icons: Icons) => { + const iconNodeEntries = Object.entries(icons) + const iconComponents = iconNodeEntries.reduce((acc, [iconName, iconNode]) => { + const componentName = toPascalCase(iconName) as keyof ComponentList; + + acc[componentName] = createLucideIcon(componentName as string, iconNode) as ComponentList[typeof componentName]; + + return acc; + }, {} as ComponentList) + + return iconComponents +} + +export default useIconComponent From a1a912637bdfe998d61346a3f6347bc1380278fc Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Tue, 23 Jan 2024 17:30:24 +0100 Subject: [PATCH 02/27] Add concept useIconComponent --- packages/lucide-react/src/useIconComponent.ts | 15 ++- .../dynamicImports.spec.tsx.snap | 36 +++++++ .../useIconComponent.spec.tsx.snap | 96 +++++++++++++++++++ .../tests/dynamicImports.spec.tsx | 40 ++++++++ .../lucide-react/tests/lucide-react.spec.tsx | 37 +------ packages/lucide-react/tests/testIconNodes.ts | 22 +++++ .../tests/useIconComponent.spec.tsx | 29 ++++++ 7 files changed, 238 insertions(+), 37 deletions(-) create mode 100644 packages/lucide-react/tests/__snapshots__/dynamicImports.spec.tsx.snap create mode 100644 packages/lucide-react/tests/__snapshots__/useIconComponent.spec.tsx.snap create mode 100644 packages/lucide-react/tests/dynamicImports.spec.tsx create mode 100644 packages/lucide-react/tests/testIconNodes.ts create mode 100644 packages/lucide-react/tests/useIconComponent.spec.tsx diff --git a/packages/lucide-react/src/useIconComponent.ts b/packages/lucide-react/src/useIconComponent.ts index fbd211737f..9ce28f941f 100644 --- a/packages/lucide-react/src/useIconComponent.ts +++ b/packages/lucide-react/src/useIconComponent.ts @@ -1,5 +1,4 @@ -import { LucideIcon } from "../dist/lucide-react"; -import createLucideIcon, { IconNode } from "./createLucideIcon"; +import createLucideIcon, { IconNode, LucideIcon } from "./createLucideIcon"; type CamelToPascal = T extends `${infer FirstChar}${infer Rest}` ? `${Capitalize}${Rest}` : never @@ -8,10 +7,18 @@ type ComponentList = { [Prop in keyof T as CamelToPascal]: LucideIcon } -const toPascalCase = (string: T): CamelToPascal => - string.replace(/(\w)(\w*)(_|-|\s*)/g, (g0, g1, g2) => g1.toUpperCase() + g2.toLowerCase()) as CamelToPascal; +export const toPascalCase = (string: T): CamelToPascal => { + const camelCase = string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => + p2 ? p2.toUpperCase() : p1.toLowerCase(), + ); + + return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal; +}; const useIconComponent = >(icons: Icons) => { + // TODO: throw error if iconNodes are incorrect + // TODO: throw error if this function is executed inside a react component render function + const iconNodeEntries = Object.entries(icons) const iconComponents = iconNodeEntries.reduce((acc, [iconName, iconNode]) => { const componentName = toPascalCase(iconName) as keyof ComponentList; diff --git a/packages/lucide-react/tests/__snapshots__/dynamicImports.spec.tsx.snap b/packages/lucide-react/tests/__snapshots__/dynamicImports.spec.tsx.snap new file mode 100644 index 0000000000..7a3b51faae --- /dev/null +++ b/packages/lucide-react/tests/__snapshots__/dynamicImports.spec.tsx.snap @@ -0,0 +1,36 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Using dynamicImports > should render icons dynamically by using the dynamicIconImports module 1`] = ` + + + + + + + + + + +`; diff --git a/packages/lucide-react/tests/__snapshots__/useIconComponent.spec.tsx.snap b/packages/lucide-react/tests/__snapshots__/useIconComponent.spec.tsx.snap new file mode 100644 index 0000000000..4c63dd69d3 --- /dev/null +++ b/packages/lucide-react/tests/__snapshots__/useIconComponent.spec.tsx.snap @@ -0,0 +1,96 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Using iconNode component generator > should create a component from an iconNode 1`] = ` + + + + + + +`; + +exports[`Using iconNode component generator > should create multiple components 1`] = ` +
+ + + + + + + + + + + + + +
+`; diff --git a/packages/lucide-react/tests/dynamicImports.spec.tsx b/packages/lucide-react/tests/dynamicImports.spec.tsx new file mode 100644 index 0000000000..d1cbf019b9 --- /dev/null +++ b/packages/lucide-react/tests/dynamicImports.spec.tsx @@ -0,0 +1,40 @@ +import { describe, it, expect } from 'vitest'; +import { Suspense, lazy } from 'react'; +import { render, waitFor } from '@testing-library/react'; + +import dynamicIconImports from '../src/dynamicIconImports'; +import { LucideProps } from '../src/createLucideIcon'; + +describe('Using dynamicImports', () => { + + it('should render icons dynamically by using the dynamicIconImports module', async () => { + interface IconProps extends Omit { + name: keyof typeof dynamicIconImports; + } + + const Icon = ({ name, ...props }: IconProps) => { + const LucideIcon = lazy(dynamicIconImports[name]); + + return ( + + + + ); + } + + const { container, getByLabelText } = render( + , + ); + + await waitFor(() => getByLabelText('smile')) + + expect( container.innerHTML ).toMatchSnapshot(); + + }); +}) diff --git a/packages/lucide-react/tests/lucide-react.spec.tsx b/packages/lucide-react/tests/lucide-react.spec.tsx index d5da181e41..fb002550fa 100644 --- a/packages/lucide-react/tests/lucide-react.spec.tsx +++ b/packages/lucide-react/tests/lucide-react.spec.tsx @@ -1,8 +1,6 @@ import { describe, it, expect } from 'vitest'; -import { render, cleanup, waitFor } from '@testing-library/react' -import { Pen, Edit2, Grid, LucideProps, Droplet } from '../src/lucide-react'; -import { Suspense, lazy } from 'react'; -import dynamicIconImports from '../src/dynamicIconImports'; +import { render, cleanup } from '@testing-library/react' +import { Pen, Edit2, Grid, Droplet } from '../src/lucide-react'; describe('Using lucide icon components', () => { it('should render an component', () => { @@ -87,34 +85,7 @@ describe('Using lucide icon components', () => { expect(container.firstChild).toHaveClass('lucide-droplet'); }); - it('should render icons dynamically by using the dynamicIconImports module', async () => { - interface IconProps extends Omit { - name: keyof typeof dynamicIconImports; - } - - const Icon = ({ name, ...props }: IconProps) => { - const LucideIcon = lazy(dynamicIconImports[name]); - - return ( - - - - ); - } - - const { container, getByLabelText } = render( - , - ); + it('should create components from iconNodes', async () => { - await waitFor(() => getByLabelText('smile')) - - expect( container.innerHTML ).toMatchSnapshot(); - - }); + }) }) diff --git a/packages/lucide-react/tests/testIconNodes.ts b/packages/lucide-react/tests/testIconNodes.ts new file mode 100644 index 0000000000..80280c9a81 --- /dev/null +++ b/packages/lucide-react/tests/testIconNodes.ts @@ -0,0 +1,22 @@ +import { IconNode } from "../src/createLucideIcon" + +export const airVent: IconNode = [ + [ + 'path', + { + d: 'M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2', + key: 'larmp2', + }, + ], + ['path', { d: 'M6 8h12', key: '6g4wlu' }], + ['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }], + ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }] +] + +export const coffee: IconNode = [ + ['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }], + ['path', { d: 'M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z', key: '1bxrl0' }], + ['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }], + ['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }], + ['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }], +] diff --git a/packages/lucide-react/tests/useIconComponent.spec.tsx b/packages/lucide-react/tests/useIconComponent.spec.tsx new file mode 100644 index 0000000000..4bb1af0cb1 --- /dev/null +++ b/packages/lucide-react/tests/useIconComponent.spec.tsx @@ -0,0 +1,29 @@ +import { describe, it, expect } from 'vitest'; +import useIconComponent from '../src/useIconComponent'; +import { airVent, coffee } from './testIconNodes'; +import { render } from '@testing-library/react'; + +describe('Using iconNode component generator', () => { + it('should create a component from an iconNode', () => { + const { AirVent } = useIconComponent({ airVent }) + + const { container } = render( ); + + expect( container.firstChild ).toMatchSnapshot(); + expect( container.firstChild ).toBeDefined(); + }); + + it('should create multiple components', () => { + const { AirVent, Coffee } = useIconComponent({ airVent, coffee }) + + const { container } = render( + <> + + + + ); + + expect( container ).toMatchSnapshot(); + expect( container ).toBeDefined(); + }); +}) From 76ff2d5b54322998e8295daf4c84a03826e5a76c Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Sun, 28 Jan 2024 21:16:02 +0100 Subject: [PATCH 03/27] add useIconComponents to packages --- packages/lucide-preact/src/lucide-preact.ts | 2 + .../lucide-preact/src/useIconComponent.ts | 44 +++++++++ .../useIconComponent.spec.tsx.snap | 96 +++++++++++++++++++ packages/lucide-preact/tests/setupVitest.js | 9 +- packages/lucide-preact/tests/testIconNodes.ts | 22 +++++ .../tests/useIconComponent.spec.tsx | 29 ++++++ .../src/useIconComponent.ts | 45 +++++++++ packages/lucide-react/src/useIconComponent.ts | 15 ++- .../__snapshots__/lucide-react.spec.tsx.snap | 35 ------- packages/lucide-solid/src/lucide-solid.ts | 3 + packages/lucide-solid/src/types.ts | 2 + .../lucide-solid/src/useIconComponent.tsx | 53 ++++++++++ packages/lucide-svelte/src/lucide-svelte.ts | 1 + .../lucide-vue-next/src/createLucideIcon.ts | 11 ++- .../lucide-vue-next/src/lucide-vue-next.ts | 3 + .../lucide-vue-next/src/useIconComponent.ts | 44 +++++++++ .../useIconComponent.spec.ts.snap | 29 ++++++ .../lucide-vue-next/tests/testIconNodes.ts | 22 +++++ .../tests/useIconComponent.spec.ts | 27 ++++++ 19 files changed, 449 insertions(+), 43 deletions(-) create mode 100644 packages/lucide-preact/src/useIconComponent.ts create mode 100644 packages/lucide-preact/tests/__snapshots__/useIconComponent.spec.tsx.snap create mode 100644 packages/lucide-preact/tests/testIconNodes.ts create mode 100644 packages/lucide-preact/tests/useIconComponent.spec.tsx create mode 100644 packages/lucide-react-native/src/useIconComponent.ts create mode 100644 packages/lucide-solid/src/useIconComponent.tsx create mode 100644 packages/lucide-vue-next/src/useIconComponent.ts create mode 100644 packages/lucide-vue-next/tests/__snapshots__/useIconComponent.spec.ts.snap create mode 100644 packages/lucide-vue-next/tests/testIconNodes.ts create mode 100644 packages/lucide-vue-next/tests/useIconComponent.spec.ts diff --git a/packages/lucide-preact/src/lucide-preact.ts b/packages/lucide-preact/src/lucide-preact.ts index 2ddd5eb314..e7f28ebe70 100644 --- a/packages/lucide-preact/src/lucide-preact.ts +++ b/packages/lucide-preact/src/lucide-preact.ts @@ -1,4 +1,6 @@ export * from './icons'; export * as icons from './icons'; export * from './aliases'; + export { default as createLucideIcon } from './createLucideIcon'; +export { default as useIconComponent } from './useIconComponent'; diff --git a/packages/lucide-preact/src/useIconComponent.ts b/packages/lucide-preact/src/useIconComponent.ts new file mode 100644 index 0000000000..ffc904522d --- /dev/null +++ b/packages/lucide-preact/src/useIconComponent.ts @@ -0,0 +1,44 @@ +import createLucideIcon, { IconNode, LucideIcon } from "./createLucideIcon"; + +type CamelToPascal = + T extends `${infer FirstChar}${infer Rest}` ? `${Capitalize}${Rest}` : never + +type ComponentList = { + [Prop in keyof T as CamelToPascal]: LucideIcon +} + +export const toPascalCase = (string: T): CamelToPascal => { + const camelCase = string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => + p2 ? p2.toUpperCase() : p1.toLowerCase(), + ); + + return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal; +}; + +const useIconComponent = >(icons: Icons) => { + if (typeof icons !== 'object') { + console.error('[lucide-preact]: useIconComponent expects an object as argument') + } + + const iconNodeEntries = Object.entries(icons) + + const iconNodesHasCorrectType = iconNodeEntries.every( + ([, iconNode]) => Array.isArray(iconNode) + ) + + if (!iconNodesHasCorrectType) { + console.error('[lucide-preact]: Passed icons object has incorrect type') + } + + const iconComponents = iconNodeEntries.reduce((acc, [iconName, iconNode]) => { + const componentName = toPascalCase(iconName) as keyof ComponentList; + + acc[componentName] = createLucideIcon(componentName as string, iconNode) as ComponentList[typeof componentName]; + + return acc; + }, {} as ComponentList) + + return iconComponents +} + +export default useIconComponent diff --git a/packages/lucide-preact/tests/__snapshots__/useIconComponent.spec.tsx.snap b/packages/lucide-preact/tests/__snapshots__/useIconComponent.spec.tsx.snap new file mode 100644 index 0000000000..4c63dd69d3 --- /dev/null +++ b/packages/lucide-preact/tests/__snapshots__/useIconComponent.spec.tsx.snap @@ -0,0 +1,96 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Using iconNode component generator > should create a component from an iconNode 1`] = ` + + + + + + +`; + +exports[`Using iconNode component generator > should create multiple components 1`] = ` +
+ + + + + + + + + + + + + +
+`; diff --git a/packages/lucide-preact/tests/setupVitest.js b/packages/lucide-preact/tests/setupVitest.js index 7dfeb1c3b3..ec8087900d 100644 --- a/packages/lucide-preact/tests/setupVitest.js +++ b/packages/lucide-preact/tests/setupVitest.js @@ -1,5 +1,10 @@ -import { expect } from 'vitest' -import '@testing-library/jest-dom'; +import { expect, afterEach } from 'vitest'; +import { cleanup } from '@testing-library/preact'; +import '@testing-library/jest-dom/vitest'; import htmlSerializer from 'jest-serializer-html' expect.addSnapshotSerializer(htmlSerializer) + +afterEach(() => { + cleanup(); +}); diff --git a/packages/lucide-preact/tests/testIconNodes.ts b/packages/lucide-preact/tests/testIconNodes.ts new file mode 100644 index 0000000000..80280c9a81 --- /dev/null +++ b/packages/lucide-preact/tests/testIconNodes.ts @@ -0,0 +1,22 @@ +import { IconNode } from "../src/createLucideIcon" + +export const airVent: IconNode = [ + [ + 'path', + { + d: 'M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2', + key: 'larmp2', + }, + ], + ['path', { d: 'M6 8h12', key: '6g4wlu' }], + ['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }], + ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }] +] + +export const coffee: IconNode = [ + ['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }], + ['path', { d: 'M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z', key: '1bxrl0' }], + ['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }], + ['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }], + ['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }], +] diff --git a/packages/lucide-preact/tests/useIconComponent.spec.tsx b/packages/lucide-preact/tests/useIconComponent.spec.tsx new file mode 100644 index 0000000000..202d8b2cd7 --- /dev/null +++ b/packages/lucide-preact/tests/useIconComponent.spec.tsx @@ -0,0 +1,29 @@ +import { describe, it, expect } from 'vitest'; +import { useIconComponent } from '../src/lucide-preact'; +import { airVent, coffee } from './testIconNodes'; +import { render } from '@testing-library/preact' + +describe('Using iconNode component generator', () => { + it('should create a component from an iconNode', () => { + const { AirVent } = useIconComponent({ airVent }) + + const { container } = render( ); + + expect( container.firstChild ).toMatchSnapshot(); + expect( container.firstChild ).toBeDefined(); + }); + + it('should create multiple components', () => { + const { AirVent, Coffee } = useIconComponent({ airVent, coffee }) + + const { container } = render( + <> + + + + ); + + expect( container ).toMatchSnapshot(); + expect( container ).toBeDefined(); + }); +}) diff --git a/packages/lucide-react-native/src/useIconComponent.ts b/packages/lucide-react-native/src/useIconComponent.ts new file mode 100644 index 0000000000..84fb300c74 --- /dev/null +++ b/packages/lucide-react-native/src/useIconComponent.ts @@ -0,0 +1,45 @@ +import createLucideIcon, { IconNode, LucideIcon } from "./createLucideIcon"; + +type CamelToPascal = + T extends `${infer FirstChar}${infer Rest}` ? `${Capitalize}${Rest}` : never + +type ComponentList = { + [Prop in keyof T as CamelToPascal]: LucideIcon +} + +export const toPascalCase = (string: T): CamelToPascal => { + const camelCase = string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => + p2 ? p2.toUpperCase() : p1.toLowerCase(), + ); + + return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal; +}; + +const useIconComponent = >(icons: Icons) => { + if (typeof icons !== 'object') { + console.error('[lucide-react-native]: useIconComponent expects an object as argument') + } + + const iconNodeEntries = Object.entries(icons) + + const iconNodesHasCorrectType = iconNodeEntries.every( + ([, iconNode]) => Array.isArray(iconNode) + ) + + if (!iconNodesHasCorrectType) { + console.error('[lucide-react-native]: Passed icons object has incorrect type') + } + // TODO: throw error if this function is executed inside a react component render function + + const iconComponents = iconNodeEntries.reduce((acc, [iconName, iconNode]) => { + const componentName = toPascalCase(iconName) as keyof ComponentList; + + acc[componentName] = createLucideIcon(componentName as string, iconNode) as ComponentList[typeof componentName]; + + return acc; + }, {} as ComponentList) + + return iconComponents +} + +export default useIconComponent diff --git a/packages/lucide-react/src/useIconComponent.ts b/packages/lucide-react/src/useIconComponent.ts index 9ce28f941f..28712c6add 100644 --- a/packages/lucide-react/src/useIconComponent.ts +++ b/packages/lucide-react/src/useIconComponent.ts @@ -16,10 +16,21 @@ export const toPascalCase = (string: T): CamelToPascal => { }; const useIconComponent = >(icons: Icons) => { - // TODO: throw error if iconNodes are incorrect - // TODO: throw error if this function is executed inside a react component render function + if (typeof icons !== 'object') { + console.error('[lucide-react]: useIconComponent expects an object as argument') + } const iconNodeEntries = Object.entries(icons) + + const iconNodesHasCorrectType = iconNodeEntries.every( + ([, iconNode]) => Array.isArray(iconNode) + ) + + if (!iconNodesHasCorrectType) { + console.error('[lucide-react]: Passed icons object has incorrect type') + } + // TODO: throw error if this function is executed inside a react component render function + const iconComponents = iconNodeEntries.reduce((acc, [iconName, iconNode]) => { const componentName = toPascalCase(iconName) as keyof ComponentList; diff --git a/packages/lucide-react/tests/__snapshots__/lucide-react.spec.tsx.snap b/packages/lucide-react/tests/__snapshots__/lucide-react.spec.tsx.snap index 31ab33829f..6df3af2c0f 100644 --- a/packages/lucide-react/tests/__snapshots__/lucide-react.spec.tsx.snap +++ b/packages/lucide-react/tests/__snapshots__/lucide-react.spec.tsx.snap @@ -91,38 +91,3 @@ exports[`Using lucide icon components > should render an component 1`] = ` `; - -exports[`Using lucide icon components > should render icons dynamically by using the dynamicIconImports module 1`] = ` - - - - - - - - - - -`; diff --git a/packages/lucide-solid/src/lucide-solid.ts b/packages/lucide-solid/src/lucide-solid.ts index f5d8e914bf..311ff3d489 100644 --- a/packages/lucide-solid/src/lucide-solid.ts +++ b/packages/lucide-solid/src/lucide-solid.ts @@ -5,3 +5,6 @@ export * from './icons'; export * as icons from './icons'; export * from './aliases'; + +export { default as Icon } from './Icon'; +export { default as useIconComponent } from './useIconComponent'; diff --git a/packages/lucide-solid/src/types.ts b/packages/lucide-solid/src/types.ts index 3435c5a21e..a52f5f00dc 100644 --- a/packages/lucide-solid/src/types.ts +++ b/packages/lucide-solid/src/types.ts @@ -11,3 +11,5 @@ export interface LucideProps extends SVGAttributes { class?: string absoluteStrokeWidth?: boolean } + +export type LucideIcon = (props: LucideProps) => JSX.Element diff --git a/packages/lucide-solid/src/useIconComponent.tsx b/packages/lucide-solid/src/useIconComponent.tsx new file mode 100644 index 0000000000..06a3694be7 --- /dev/null +++ b/packages/lucide-solid/src/useIconComponent.tsx @@ -0,0 +1,53 @@ +import Icon from "./Icon"; +import { LucideProps, IconNode, LucideIcon } from "./types"; + +type CamelToPascal = + T extends `${infer FirstChar}${infer Rest}` ? `${Capitalize}${Rest}` : never + +type ComponentList = { + [Prop in keyof T as CamelToPascal]: LucideIcon +} + +export const toPascalCase = (string: T): CamelToPascal => { + const camelCase = string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => + p2 ? p2.toUpperCase() : p1.toLowerCase(), + ); + + return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal; +}; + +const useIconComponent = >(icons: Icons) => { + if (typeof icons !== 'object') { + console.error('[lucide-solid]: useIconComponent expects an object as argument') + } + + const iconNodeEntries = Object.entries(icons) + + const iconNodesHasCorrectType = iconNodeEntries.every( + ([, iconNode]) => Array.isArray(iconNode) + ) + + if (!iconNodesHasCorrectType) { + console.error('[lucide-solid]: Passed icons object has incorrect type') + } + + const iconComponents = iconNodeEntries.reduce((acc, [iconName, iconNode]) => { + const componentName = toPascalCase(iconName) as keyof ComponentList; + + acc[componentName] = ( + (props: LucideProps) => ( + + ) + ) as ComponentList[typeof componentName]; + + return acc; + }, {} as ComponentList) + + return iconComponents +} + +export default useIconComponent diff --git a/packages/lucide-svelte/src/lucide-svelte.ts b/packages/lucide-svelte/src/lucide-svelte.ts index 77e474d300..c1567a365b 100644 --- a/packages/lucide-svelte/src/lucide-svelte.ts +++ b/packages/lucide-svelte/src/lucide-svelte.ts @@ -3,3 +3,4 @@ export * as icons from './icons/index.js'; export * from './aliases.js'; export { default as defaultAttributes } from './defaultAttributes.js'; export * from './types.js'; +export { default as Icon } from './Icon.svelte'; diff --git a/packages/lucide-vue-next/src/createLucideIcon.ts b/packages/lucide-vue-next/src/createLucideIcon.ts index 13bbf3e92b..af223738dc 100644 --- a/packages/lucide-vue-next/src/createLucideIcon.ts +++ b/packages/lucide-vue-next/src/createLucideIcon.ts @@ -3,15 +3,18 @@ import type { SVGAttributes, FunctionalComponent, DefineComponent } from 'vue'; import defaultAttributes from './defaultAttributes'; // Create interface extending SVGAttributes -export interface SVGProps extends Partial { +export interface LucideProps extends Partial { size?: 24 | number strokeWidth?: number | string absoluteStrokeWidth?: boolean } - export type IconNode = [elementName: string, attrs: Record][] -export type Icon = FunctionalComponent +export type LucideIcon = FunctionalComponent + +// Legacy exports +export type SVGProps = LucideProps +export type Icon = LucideIcon /** * Converts string to KebabCase * Copied from scripts/helper. If anyone knows how to properly import it here @@ -22,7 +25,7 @@ export type Icon = FunctionalComponent */ export const toKebabCase = (string: string) => string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); -const createLucideIcon = (iconName: string, iconNode: IconNode): Icon => ( +const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => ( { size, strokeWidth = 2, absoluteStrokeWidth, color, class: classes, ...props }, // props { attrs, slots } // context ) => { diff --git a/packages/lucide-vue-next/src/lucide-vue-next.ts b/packages/lucide-vue-next/src/lucide-vue-next.ts index 2e28382436..e7f28ebe70 100644 --- a/packages/lucide-vue-next/src/lucide-vue-next.ts +++ b/packages/lucide-vue-next/src/lucide-vue-next.ts @@ -1,3 +1,6 @@ export * from './icons'; export * as icons from './icons'; export * from './aliases'; + +export { default as createLucideIcon } from './createLucideIcon'; +export { default as useIconComponent } from './useIconComponent'; diff --git a/packages/lucide-vue-next/src/useIconComponent.ts b/packages/lucide-vue-next/src/useIconComponent.ts new file mode 100644 index 0000000000..105e18b486 --- /dev/null +++ b/packages/lucide-vue-next/src/useIconComponent.ts @@ -0,0 +1,44 @@ +import createLucideIcon, { IconNode, LucideIcon } from "./createLucideIcon"; + +type CamelToPascal = + T extends `${infer FirstChar}${infer Rest}` ? `${Capitalize}${Rest}` : never + +type ComponentList = { + [Prop in keyof T as CamelToPascal]: LucideIcon +} + +export const toPascalCase = (string: T): CamelToPascal => { + const camelCase = string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => + p2 ? p2.toUpperCase() : p1.toLowerCase(), + ); + + return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal; +}; + +const useIconComponent = >(icons: Icons) => { + if (typeof icons !== 'object') { + throw new Error('[lucide-vue-next]: useIconComponent expects an object as argument') + } + + const iconNodeEntries = Object.entries(icons) + + const iconNodesHasCorrectType = iconNodeEntries.every( + ([, iconNode]) => Array.isArray(iconNode) + ) + + if (!iconNodesHasCorrectType) { + throw new Error('[lucide-vue-next]: Passed icons object has incorrect type') + } + + const iconComponents = iconNodeEntries.reduce((acc, [iconName, iconNode]) => { + const componentName = toPascalCase(iconName) as keyof ComponentList; + + acc[componentName] = createLucideIcon(componentName as string, iconNode) as ComponentList[typeof componentName]; + + return acc; + }, {} as ComponentList) + + return iconComponents +} + +export default useIconComponent diff --git a/packages/lucide-vue-next/tests/__snapshots__/useIconComponent.spec.ts.snap b/packages/lucide-vue-next/tests/__snapshots__/useIconComponent.spec.ts.snap new file mode 100644 index 0000000000..ad1dea2c57 --- /dev/null +++ b/packages/lucide-vue-next/tests/__snapshots__/useIconComponent.spec.ts.snap @@ -0,0 +1,29 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Using iconNode component generator > should create a component from an iconNode 1`] = ` + + + + + + +`; diff --git a/packages/lucide-vue-next/tests/testIconNodes.ts b/packages/lucide-vue-next/tests/testIconNodes.ts new file mode 100644 index 0000000000..80280c9a81 --- /dev/null +++ b/packages/lucide-vue-next/tests/testIconNodes.ts @@ -0,0 +1,22 @@ +import { IconNode } from "../src/createLucideIcon" + +export const airVent: IconNode = [ + [ + 'path', + { + d: 'M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2', + key: 'larmp2', + }, + ], + ['path', { d: 'M6 8h12', key: '6g4wlu' }], + ['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }], + ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }] +] + +export const coffee: IconNode = [ + ['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }], + ['path', { d: 'M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z', key: '1bxrl0' }], + ['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }], + ['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }], + ['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }], +] diff --git a/packages/lucide-vue-next/tests/useIconComponent.spec.ts b/packages/lucide-vue-next/tests/useIconComponent.spec.ts new file mode 100644 index 0000000000..91fff44595 --- /dev/null +++ b/packages/lucide-vue-next/tests/useIconComponent.spec.ts @@ -0,0 +1,27 @@ +import { describe, it, expect } from 'vitest'; +import { useIconComponent } from '../src/lucide-vue-next'; +import { airVent, coffee } from './testIconNodes'; +import { cleanup, render } from '@testing-library/vue' + +describe('Using iconNode component generator', () => { + it('should create a component from an iconNode', () => { + const { AirVent } = useIconComponent({ airVent }) + + const { container } = render(AirVent); + + expect( container.firstChild ).toMatchSnapshot(); + expect( container.firstChild ).toBeDefined(); + }); + + it('should create multiple components', () => { + const { AirVent, Coffee } = useIconComponent({ airVent, coffee }) + + const { container } = render(AirVent); + expect( container ).toBeDefined(); + + cleanup(); + + const { container: container2 } = render(Coffee); + expect( container2 ).toBeDefined(); + }); +}) From 736a6cd8b8ccb1b0b2d0e299a0237e17385de41a Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Fri, 8 Mar 2024 13:53:51 +0100 Subject: [PATCH 04/27] Add icon component --- packages/lucide-react/src/Icon.tsx | 42 +++++++++++++++++++ packages/lucide-react/src/createLucideIcon.ts | 17 +------- packages/lucide-react/src/types.ts | 18 ++++++++ packages/lucide-react/src/useIconComponent.ts | 36 +++++++--------- .../lucide-react/tests/lucide-react.spec.tsx | 4 -- packages/shared/src/index.ts | 1 + packages/shared/src/utility-types.ts | 15 +++++++ packages/shared/src/utils.ts | 19 ++++++++- 8 files changed, 109 insertions(+), 43 deletions(-) create mode 100644 packages/lucide-react/src/Icon.tsx create mode 100644 packages/lucide-react/src/types.ts create mode 100644 packages/shared/src/utility-types.ts diff --git a/packages/lucide-react/src/Icon.tsx b/packages/lucide-react/src/Icon.tsx new file mode 100644 index 0000000000..6c37bb524a --- /dev/null +++ b/packages/lucide-react/src/Icon.tsx @@ -0,0 +1,42 @@ +import { toKebabCase } from "@lucide/shared"; +import { createElement, forwardRef } from "react"; +import defaultAttributes from "./defaultAttributes"; + +interface IconComponentProps { + +} + +const Icon = forwardRef( + ( + { + color = 'currentColor', + size = 24, + strokeWidth = 2, + absoluteStrokeWidth, + className = '', + children, + ...rest + }, + ref, + ) => { + return createElement( + 'svg', + { + ref, + ...defaultAttributes, + width: size, + height: size, + stroke: color, + strokeWidth: absoluteStrokeWidth + ? (Number(strokeWidth) * 24) / Number(size) + : strokeWidth, + className: ['lucide', `lucide-${toKebabCase(iconName)}`, className].join(' '), + ...rest, + }, + [ + ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)), + ...(Array.isArray(children) ? children : [children]), + ], + ); + }, +); diff --git a/packages/lucide-react/src/createLucideIcon.ts b/packages/lucide-react/src/createLucideIcon.ts index 5207168afc..71dc2dbaf1 100644 --- a/packages/lucide-react/src/createLucideIcon.ts +++ b/packages/lucide-react/src/createLucideIcon.ts @@ -1,25 +1,10 @@ import { forwardRef, createElement, - ReactSVG, - SVGProps, - ForwardRefExoticComponent, - RefAttributes, } from 'react'; import defaultAttributes from './defaultAttributes'; import { toKebabCase } from '@lucide/shared'; - -export type IconNode = [elementName: keyof ReactSVG, attrs: Record][]; - -export type SVGAttributes = Partial>; -type ComponentAttributes = RefAttributes & SVGAttributes; - -export interface LucideProps extends ComponentAttributes { - size?: string | number; - absoluteStrokeWidth?: boolean; -} - -export type LucideIcon = ForwardRefExoticComponent; +import { IconNode, LucideIcon, LucideProps } from './types'; const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => { const Component = forwardRef( diff --git a/packages/lucide-react/src/types.ts b/packages/lucide-react/src/types.ts new file mode 100644 index 0000000000..6d21b5d6fc --- /dev/null +++ b/packages/lucide-react/src/types.ts @@ -0,0 +1,18 @@ +import { + ReactSVG, + SVGProps, + ForwardRefExoticComponent, + RefAttributes, +} from 'react'; + +export type IconNode = [elementName: keyof ReactSVG, attrs: Record][]; + +export type SVGAttributes = Partial>; +type ComponentAttributes = RefAttributes & SVGAttributes; + +export interface LucideProps extends ComponentAttributes { + size?: string | number; + absoluteStrokeWidth?: boolean; +} + +export type LucideIcon = ForwardRefExoticComponent; diff --git a/packages/lucide-react/src/useIconComponent.ts b/packages/lucide-react/src/useIconComponent.ts index 28712c6add..38cc1e554a 100644 --- a/packages/lucide-react/src/useIconComponent.ts +++ b/packages/lucide-react/src/useIconComponent.ts @@ -1,26 +1,18 @@ +import { CamelToPascal, ComponentList, toPascalCase } from "@lucide/shared"; import createLucideIcon, { IconNode, LucideIcon } from "./createLucideIcon"; -type CamelToPascal = - T extends `${infer FirstChar}${infer Rest}` ? `${Capitalize}${Rest}` : never - -type ComponentList = { - [Prop in keyof T as CamelToPascal]: LucideIcon -} - -export const toPascalCase = (string: T): CamelToPascal => { - const camelCase = string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => - p2 ? p2.toUpperCase() : p1.toLowerCase(), - ); - - return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal; -}; - -const useIconComponent = >(icons: Icons) => { - if (typeof icons !== 'object') { +/** + * Create a list (object) of icon components from a list (object) of icon nodes + * + * @param icons + * @returns Object of icon components + */ +const useIconComponent = >(iconNodes: Icons) => { + if (typeof iconNodes !== 'object') { console.error('[lucide-react]: useIconComponent expects an object as argument') } - const iconNodeEntries = Object.entries(icons) + const iconNodeEntries = Object.entries(iconNodes) const iconNodesHasCorrectType = iconNodeEntries.every( ([, iconNode]) => Array.isArray(iconNode) @@ -29,15 +21,15 @@ const useIconComponent = >(icons: Icons) if (!iconNodesHasCorrectType) { console.error('[lucide-react]: Passed icons object has incorrect type') } - // TODO: throw error if this function is executed inside a react component render function + // TODO: Optional throw an warning if this function is executed inside a react component render function, because this harms performance const iconComponents = iconNodeEntries.reduce((acc, [iconName, iconNode]) => { - const componentName = toPascalCase(iconName) as keyof ComponentList; + const componentName = toPascalCase(iconName) as keyof ComponentList; - acc[componentName] = createLucideIcon(componentName as string, iconNode) as ComponentList[typeof componentName]; + acc[componentName] = createLucideIcon(componentName as string, iconNode) as ComponentList[typeof componentName]; return acc; - }, {} as ComponentList) + }, {} as ComponentList) return iconComponents } diff --git a/packages/lucide-react/tests/lucide-react.spec.tsx b/packages/lucide-react/tests/lucide-react.spec.tsx index 5fc566df9c..48a5beafe9 100644 --- a/packages/lucide-react/tests/lucide-react.spec.tsx +++ b/packages/lucide-react/tests/lucide-react.spec.tsx @@ -85,8 +85,4 @@ describe('Using lucide icon components', () => { expect(container.firstChild).toHaveClass('lucide'); expect(container.firstChild).toHaveClass('lucide-droplet'); }); - - it('should create components from iconNodes', async () => { - - }) }) diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 04bca77e0d..e1a8199185 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1 +1,2 @@ export * from './utils'; +export * from './utility-types'; diff --git a/packages/shared/src/utility-types.ts b/packages/shared/src/utility-types.ts new file mode 100644 index 0000000000..5bd1d88bd3 --- /dev/null +++ b/packages/shared/src/utility-types.ts @@ -0,0 +1,15 @@ +/** + * Convert a type string from camelCase to PascalCase + * + * @example + * type Test = CamelToPascal<'fooBar'> // 'FooBar' + */ +export type CamelToPascal = + T extends `${infer FirstChar}${infer Rest}` ? `${Capitalize}${Rest}` : never + +/** + * Creates a list of components from a list of component names and a component type + */ +export type ComponentList = { + [Prop in keyof ComponentNames as CamelToPascal]: ComponentType +} diff --git a/packages/shared/src/utils.ts b/packages/shared/src/utils.ts index 9e45977f2e..b5bbdd618e 100644 --- a/packages/shared/src/utils.ts +++ b/packages/shared/src/utils.ts @@ -1,8 +1,25 @@ +import { CamelToPascal } from "./utility-types"; + /** - * Converts string to KebabCase + * Converts string to kebab case * * @param {string} string * @returns {string} A kebabized string */ export const toKebabCase = (string: string) => string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); + + +/** + * Converts string to pascal case + * + * @param {string} string + * @returns {string} A pascalized string + */ +export const toPascalCase = (string: T): CamelToPascal => { + const camelCase = string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => + p2 ? p2.toUpperCase() : p1.toLowerCase(), + ); + + return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal; +}; From 4b675991b9717c9677d09c1de2762c76d0c1cb5d Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Fri, 15 Mar 2024 11:14:20 +0100 Subject: [PATCH 05/27] Add icon component --- .../lucide-react/src/{Icon.tsx => Icon.ts} | 12 +++-- packages/lucide-react/src/createLucideIcon.ts | 45 +++++-------------- packages/lucide-react/src/lucide-react.ts | 10 ++--- packages/lucide-react/src/types.ts | 6 +-- packages/lucide-react/src/useIconComponent.ts | 4 +- packages/lucide-react/tests/Icon.spec.tsx | 22 +++++++++ .../tests/dynamicImports.spec.tsx | 2 +- .../lucide-react/tests/lucide-react.spec.tsx | 4 +- packages/lucide-react/tests/testIconNodes.ts | 2 +- 9 files changed, 55 insertions(+), 52 deletions(-) rename packages/lucide-react/src/{Icon.tsx => Icon.ts} (79%) create mode 100644 packages/lucide-react/tests/Icon.spec.tsx diff --git a/packages/lucide-react/src/Icon.tsx b/packages/lucide-react/src/Icon.ts similarity index 79% rename from packages/lucide-react/src/Icon.tsx rename to packages/lucide-react/src/Icon.ts index 6c37bb524a..62b45f6133 100644 --- a/packages/lucide-react/src/Icon.tsx +++ b/packages/lucide-react/src/Icon.ts @@ -1,9 +1,9 @@ -import { toKebabCase } from "@lucide/shared"; import { createElement, forwardRef } from "react"; import defaultAttributes from "./defaultAttributes"; +import { IconNode, LucideProps } from "./types"; -interface IconComponentProps { - +interface IconComponentProps extends LucideProps { + iconNode: IconNode } const Icon = forwardRef( @@ -15,10 +15,12 @@ const Icon = forwardRef( absoluteStrokeWidth, className = '', children, + iconNode, ...rest }, ref, ) => { + return createElement( 'svg', { @@ -30,7 +32,7 @@ const Icon = forwardRef( strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth, - className: ['lucide', `lucide-${toKebabCase(iconName)}`, className].join(' '), + className: ['lucide', className].join(' '), ...rest, }, [ @@ -40,3 +42,5 @@ const Icon = forwardRef( ); }, ); + +export default Icon diff --git a/packages/lucide-react/src/createLucideIcon.ts b/packages/lucide-react/src/createLucideIcon.ts index 71dc2dbaf1..18e20216bb 100644 --- a/packages/lucide-react/src/createLucideIcon.ts +++ b/packages/lucide-react/src/createLucideIcon.ts @@ -1,45 +1,22 @@ import { - forwardRef, createElement, + forwardRef, } from 'react'; -import defaultAttributes from './defaultAttributes'; import { toKebabCase } from '@lucide/shared'; -import { IconNode, LucideIcon, LucideProps } from './types'; +import { IconNode, LucideProps } from './types'; +import Icon from './Icon'; -const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => { +const createLucideIcon = (iconName: string, iconNode: IconNode) => { const Component = forwardRef( - ( + ({ className, ...props}, ref) => createElement( + Icon, { - color = 'currentColor', - size = 24, - strokeWidth = 2, - absoluteStrokeWidth, - className = '', - children, - ...rest + ref, + iconNode, + className: [`lucide-${toKebabCase(iconName)}`, className].join(' '), + ...props, }, - ref, - ) => { - return createElement( - 'svg', - { - ref, - ...defaultAttributes, - width: size, - height: size, - stroke: color, - strokeWidth: absoluteStrokeWidth - ? (Number(strokeWidth) * 24) / Number(size) - : strokeWidth, - className: ['lucide', `lucide-${toKebabCase(iconName)}`, className].join(' '), - ...rest, - }, - [ - ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)), - ...(Array.isArray(children) ? children : [children]), - ], - ); - }, + ) ); Component.displayName = `${iconName}`; diff --git a/packages/lucide-react/src/lucide-react.ts b/packages/lucide-react/src/lucide-react.ts index a024114981..6636c22592 100644 --- a/packages/lucide-react/src/lucide-react.ts +++ b/packages/lucide-react/src/lucide-react.ts @@ -1,9 +1,7 @@ export * from './icons'; export * as icons from './icons'; export * from './aliases'; -export { - default as createLucideIcon, - type IconNode, - type LucideProps, - type LucideIcon, -} from './createLucideIcon'; +export * from './types'; + +export { default as createLucideIcon } from './createLucideIcon'; +export { default as Icon } from './Icon'; diff --git a/packages/lucide-react/src/types.ts b/packages/lucide-react/src/types.ts index 6d21b5d6fc..fe3833f72c 100644 --- a/packages/lucide-react/src/types.ts +++ b/packages/lucide-react/src/types.ts @@ -8,11 +8,11 @@ import { export type IconNode = [elementName: keyof ReactSVG, attrs: Record][]; export type SVGAttributes = Partial>; -type ComponentAttributes = RefAttributes & SVGAttributes; +type ElementAttributes = RefAttributes & SVGAttributes; -export interface LucideProps extends ComponentAttributes { +export interface LucideProps extends ElementAttributes { size?: string | number; absoluteStrokeWidth?: boolean; } -export type LucideIcon = ForwardRefExoticComponent; +export type LucideIcon = ForwardRefExoticComponent & RefAttributes> diff --git a/packages/lucide-react/src/useIconComponent.ts b/packages/lucide-react/src/useIconComponent.ts index 38cc1e554a..b9f7b04fa9 100644 --- a/packages/lucide-react/src/useIconComponent.ts +++ b/packages/lucide-react/src/useIconComponent.ts @@ -1,5 +1,7 @@ import { CamelToPascal, ComponentList, toPascalCase } from "@lucide/shared"; -import createLucideIcon, { IconNode, LucideIcon } from "./createLucideIcon"; +import createLucideIcon from "./createLucideIcon"; + +import { IconNode, LucideIcon } from "./types"; /** * Create a list (object) of icon components from a list (object) of icon nodes diff --git a/packages/lucide-react/tests/Icon.spec.tsx b/packages/lucide-react/tests/Icon.spec.tsx new file mode 100644 index 0000000000..10259f2a16 --- /dev/null +++ b/packages/lucide-react/tests/Icon.spec.tsx @@ -0,0 +1,22 @@ +import { describe, it, expect } from 'vitest'; +import { render, waitFor } from '@testing-library/react'; + +import { airVent } from './testIconNodes'; +import Icon from '../src/Icon'; + +describe('Using Icon Component', () => { + it('should render icon based on a iconNode', async () => { + const { container, getByLabelText } = render( + , + ); + + await waitFor(() => getByLabelText('smile')) + + expect( container.innerHTML ).toMatchSnapshot(); + }); +}) diff --git a/packages/lucide-react/tests/dynamicImports.spec.tsx b/packages/lucide-react/tests/dynamicImports.spec.tsx index d1cbf019b9..e0d98889fa 100644 --- a/packages/lucide-react/tests/dynamicImports.spec.tsx +++ b/packages/lucide-react/tests/dynamicImports.spec.tsx @@ -3,7 +3,7 @@ import { Suspense, lazy } from 'react'; import { render, waitFor } from '@testing-library/react'; import dynamicIconImports from '../src/dynamicIconImports'; -import { LucideProps } from '../src/createLucideIcon'; +import { LucideProps } from '../src/types'; describe('Using dynamicImports', () => { diff --git a/packages/lucide-react/tests/lucide-react.spec.tsx b/packages/lucide-react/tests/lucide-react.spec.tsx index 48a5beafe9..61d1638aee 100644 --- a/packages/lucide-react/tests/lucide-react.spec.tsx +++ b/packages/lucide-react/tests/lucide-react.spec.tsx @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; -import { render, cleanup, waitFor } from '@testing-library/react' -import { Pen, Edit2, Grid, Droplet, LucideProps } from '../src/lucide-react'; +import { render, cleanup } from '@testing-library/react' +import { Pen, Edit2, Grid, Droplet } from '../src/lucide-react'; describe('Using lucide icon components', () => { it('should render an component', () => { diff --git a/packages/lucide-react/tests/testIconNodes.ts b/packages/lucide-react/tests/testIconNodes.ts index 80280c9a81..e48a1b8332 100644 --- a/packages/lucide-react/tests/testIconNodes.ts +++ b/packages/lucide-react/tests/testIconNodes.ts @@ -1,4 +1,4 @@ -import { IconNode } from "../src/createLucideIcon" +import { IconNode } from "../src/types" export const airVent: IconNode = [ [ From c56e29d90435cce6d070e4ecac3a4db6dc8d9662 Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Mon, 18 Mar 2024 18:53:05 +0100 Subject: [PATCH 06/27] Add tests for react packages --- packages/lucide-preact/src/Icon.ts | 53 ++++++++++++++ .../lucide-preact/src/createLucideIcon.ts | 33 ++++----- .../lucide-preact/src/useIconComponent.ts | 2 +- packages/lucide-preact/tests/Icon.spec.tsx | 33 +++++++++ .../tests/__snapshots__/Icon.spec.tsx.snap | 29 ++++++++ .../createLucideIcon.spec.tsx.snap | 29 ++++++++ .../__snapshots__/lucide-preact.spec.tsx.snap | 8 +-- .../useIconComponent.spec.tsx.snap | 6 +- .../tests/createLucideIcon.spec.tsx | 15 ++++ .../tests/lucide-preact.spec.tsx | 50 +++++++++----- packages/lucide-react-native/package.json | 1 + packages/lucide-react-native/src/Icon.ts | 69 +++++++++++++++++++ .../src/createLucideIcon.ts | 13 +--- .../src/lucide-react-native.ts | 4 +- packages/lucide-react-native/src/types.ts | 12 ++++ .../src/useIconComponent.ts | 45 +++++------- .../lucide-react-native.spec.tsx.snap | 2 - .../tests/lucide-react-native.spec.tsx | 36 +++++----- .../tests/testIconNodes.ts | 22 ++++++ .../tests/useIconComponent.spec.tsx | 31 +++++++++ packages/lucide-react/src/Icon.ts | 19 ++++- packages/lucide-react/src/createLucideIcon.ts | 10 ++- packages/lucide-react/src/useIconComponent.ts | 4 +- packages/lucide-react/tests/Icon.spec.tsx | 19 +++-- .../tests/__snapshots__/Icon.spec.tsx.snap | 29 ++++++++ .../createLucideIcon.spec.tsx.snap | 29 ++++++++ .../dynamicImports.spec.tsx.snap | 2 +- .../__snapshots__/lucide-react.spec.tsx.snap | 8 +-- .../useIconComponent.spec.tsx.snap | 6 +- .../tests/createLucideIcon.spec.tsx | 15 ++++ .../lucide-react/tests/lucide-react.spec.tsx | 49 ++++++++----- packages/shared/src/utils.ts | 8 +++ pnpm-lock.yaml | 5 ++ tools/build-icons/{main.mjs => cli.mjs} | 0 tools/build-icons/package.json | 4 +- tools/test-utils/package.json | 11 +++ tools/test-utils/src/getElementAttributes.ts | 0 tools/test-utils/src/getOriginalIconSVG.ts | 17 +++++ tools/test-utils/src/main.ts | 1 + 39 files changed, 578 insertions(+), 151 deletions(-) create mode 100644 packages/lucide-preact/src/Icon.ts create mode 100644 packages/lucide-preact/tests/Icon.spec.tsx create mode 100644 packages/lucide-preact/tests/__snapshots__/Icon.spec.tsx.snap create mode 100644 packages/lucide-preact/tests/__snapshots__/createLucideIcon.spec.tsx.snap create mode 100644 packages/lucide-preact/tests/createLucideIcon.spec.tsx create mode 100644 packages/lucide-react-native/src/Icon.ts create mode 100644 packages/lucide-react-native/src/types.ts create mode 100644 packages/lucide-react-native/tests/testIconNodes.ts create mode 100644 packages/lucide-react-native/tests/useIconComponent.spec.tsx create mode 100644 packages/lucide-react/tests/__snapshots__/Icon.spec.tsx.snap create mode 100644 packages/lucide-react/tests/__snapshots__/createLucideIcon.spec.tsx.snap create mode 100644 packages/lucide-react/tests/createLucideIcon.spec.tsx rename tools/build-icons/{main.mjs => cli.mjs} (100%) create mode 100644 tools/test-utils/package.json create mode 100644 tools/test-utils/src/getElementAttributes.ts create mode 100644 tools/test-utils/src/getOriginalIconSVG.ts create mode 100644 tools/test-utils/src/main.ts diff --git a/packages/lucide-preact/src/Icon.ts b/packages/lucide-preact/src/Icon.ts new file mode 100644 index 0000000000..0250d26c6c --- /dev/null +++ b/packages/lucide-preact/src/Icon.ts @@ -0,0 +1,53 @@ +import { h, toChildArray } from 'preact'; +import defaultAttributes from './defaultAttributes'; +import { IconNode, LucideProps } from './createLucideIcon'; + +interface IconComponentProps extends LucideProps { + iconNode: IconNode +} + +/** + * Lucide icon component + * + * @component Icon + * @param {object} props + * @param {string} props.color - The color of the icon + * @param {number} props.size - The size of the icon + * @param {number} props.strokeWidth - The stroke width of the icon + * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width + * @param {string} props.class - The class name of the icon + * @param {IconNode} props.children - The children of the icon + * @param {IconNode} props.iconNode - The icon node of the icon + * + * @returns {ForwardRefExoticComponent} LucideIcon + */ +const Icon = ({ + color = 'currentColor', + size = 24, + strokeWidth = 2, + absoluteStrokeWidth, + children, + iconNode, + class: classes = '', + ...rest +}: IconComponentProps) => + h( + 'svg', + { + ...defaultAttributes, + width: String(size), + height: size, + stroke: color, + ['stroke-width' as 'strokeWidth']: absoluteStrokeWidth + ? (Number(strokeWidth) * 24) / Number(size) + : strokeWidth, + class: ['lucide', classes].join(' '), + ...rest, + }, + [ + ...iconNode.map(([tag, attrs]) => h(tag, attrs)), + ...toChildArray(children) + ], + ); + +export default Icon diff --git a/packages/lucide-preact/src/createLucideIcon.ts b/packages/lucide-preact/src/createLucideIcon.ts index e4eb95f3b6..024eea9c3b 100644 --- a/packages/lucide-preact/src/createLucideIcon.ts +++ b/packages/lucide-preact/src/createLucideIcon.ts @@ -1,6 +1,6 @@ -import { type FunctionComponent, h, type JSX, toChildArray } from 'preact'; -import defaultAttributes from './defaultAttributes'; -import { toKebabCase } from '@lucide/shared'; +import { type FunctionComponent, h, type JSX } from 'preact'; +import { mergeClasses, toKebabCase } from '@lucide/shared'; +import Icon from './Icon'; export type IconNode = [elementName: keyof JSX.IntrinsicElements, attrs: Record][]; @@ -21,28 +21,21 @@ export type LucideIcon = FunctionComponent; */ const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => { const Component = ({ - color = 'currentColor', - size = 24, - strokeWidth = 2, - absoluteStrokeWidth, - children, class: classes = '', - ...rest + children, + ...props }: LucideProps) => h( - 'svg', + Icon, { - ...defaultAttributes, - width: String(size), - height: size, - stroke: color, - ['stroke-width' as 'strokeWidth']: absoluteStrokeWidth - ? (Number(strokeWidth) * 24) / Number(size) - : strokeWidth, - class: ['lucide', `lucide-${toKebabCase(iconName)}`, classes].join(' '), - ...rest, + ...props, + iconNode, + class: mergeClasses>( + `lucide-${toKebabCase(iconName)}`, + classes + ), }, - [...iconNode.map(([tag, attrs]) => h(tag, attrs)), ...toChildArray(children)], + children, ); Component.displayName = `${iconName}`; diff --git a/packages/lucide-preact/src/useIconComponent.ts b/packages/lucide-preact/src/useIconComponent.ts index ffc904522d..0d7f66085d 100644 --- a/packages/lucide-preact/src/useIconComponent.ts +++ b/packages/lucide-preact/src/useIconComponent.ts @@ -27,7 +27,7 @@ const useIconComponent = >(icons: Icons) ) if (!iconNodesHasCorrectType) { - console.error('[lucide-preact]: Passed icons object has incorrect type') + console.error('[lucide-preact]: Passed icons object has an incorrect children type') } const iconComponents = iconNodeEntries.reduce((acc, [iconName, iconNode]) => { diff --git a/packages/lucide-preact/tests/Icon.spec.tsx b/packages/lucide-preact/tests/Icon.spec.tsx new file mode 100644 index 0000000000..778e56a277 --- /dev/null +++ b/packages/lucide-preact/tests/Icon.spec.tsx @@ -0,0 +1,33 @@ +import { describe, it, expect } from 'vitest'; +import { render } from '@testing-library/preact'; + +import { airVent } from './testIconNodes'; +import Icon from '../src/Icon'; + +describe('Using Icon Component', () => { + it('should render icon based on a iconNode', async () => { + const { container } = render( + , + ); + + expect( container.firstChild ).toBeDefined() + }); + + it('should render icon and match snapshot', async () => { + const { container } = render( + , + ); + + expect( container.firstChild ).toMatchSnapshot(); + }); +}) diff --git a/packages/lucide-preact/tests/__snapshots__/Icon.spec.tsx.snap b/packages/lucide-preact/tests/__snapshots__/Icon.spec.tsx.snap new file mode 100644 index 0000000000..2e30bf9a62 --- /dev/null +++ b/packages/lucide-preact/tests/__snapshots__/Icon.spec.tsx.snap @@ -0,0 +1,29 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Using Icon Component > should render icon and match snapshot 1`] = ` + + + + + + +`; diff --git a/packages/lucide-preact/tests/__snapshots__/createLucideIcon.spec.tsx.snap b/packages/lucide-preact/tests/__snapshots__/createLucideIcon.spec.tsx.snap new file mode 100644 index 0000000000..acf4f97390 --- /dev/null +++ b/packages/lucide-preact/tests/__snapshots__/createLucideIcon.spec.tsx.snap @@ -0,0 +1,29 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Using createLucideIcon > should create a component from an iconNode 1`] = ` + + + + + + +`; diff --git a/packages/lucide-preact/tests/__snapshots__/lucide-preact.spec.tsx.snap b/packages/lucide-preact/tests/__snapshots__/lucide-preact.spec.tsx.snap index 6df3af2c0f..a3cc086577 100644 --- a/packages/lucide-preact/tests/__snapshots__/lucide-preact.spec.tsx.snap +++ b/packages/lucide-preact/tests/__snapshots__/lucide-preact.spec.tsx.snap @@ -10,8 +10,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and stroke-width="4" stroke-linecap="round" stroke-linejoin="round" - class="lucide lucide-grid3x3 " - data-testid="grid-icon" + class="lucide lucide-grid3x3" > should not scale the strokeWidth when ab stroke-width="1" stroke-linecap="round" stroke-linejoin="round" - class="lucide lucide-grid3x3 " - data-testid="grid-icon" + class="lucide lucide-grid3x3" > should render an component 1`] = ` stroke-width="2" stroke-linecap="round" stroke-linejoin="round" - class="lucide lucide-grid3x3 " + class="lucide lucide-grid3x3" > should create a component from an iconNode 1`] = ` should create a component from an exports[`Using iconNode component generator > should create multiple components 1`] = `
should create multiple components /> { + it('should create a component from an iconNode', () => { + const AirVent = createLucideIcon('AirVent', airVent) + + const { container } = render( ); + + expect( container.firstChild ).toMatchSnapshot(); + expect( container.firstChild ).toBeDefined(); + }); +}) diff --git a/packages/lucide-preact/tests/lucide-preact.spec.tsx b/packages/lucide-preact/tests/lucide-preact.spec.tsx index c0e6d06eb9..ab7adcba8c 100644 --- a/packages/lucide-preact/tests/lucide-preact.spec.tsx +++ b/packages/lucide-preact/tests/lucide-preact.spec.tsx @@ -1,6 +1,7 @@ import { describe, it, expect } from 'vitest'; import { render, cleanup } from '@testing-library/preact'; import { Pen, Edit2, Grid, Droplet } from '../src/lucide-preact'; +import defaultAttributes from '../src/defaultAttributes'; type AttributesAssertion = { attributes: Record }; @@ -11,30 +12,43 @@ describe('Using lucide icon components', () => { expect(container.innerHTML).toMatchSnapshot(); }); + it('should render the icon with the default attributes', () => { + const { container } = render(); + + const SVGElement = container.firstElementChild + + expect(SVGElement).toHaveAttribute('xmlns', defaultAttributes.xmlns); + expect(SVGElement).toHaveAttribute('width', String(defaultAttributes.width)); + expect(SVGElement).toHaveAttribute('height', String(defaultAttributes.height)); + expect(SVGElement).toHaveAttribute('viewBox', defaultAttributes.viewBox); + expect(SVGElement).toHaveAttribute('fill', defaultAttributes.fill); + expect(SVGElement).toHaveAttribute('stroke', defaultAttributes.stroke); + expect(SVGElement).toHaveAttribute('stroke-width', String(defaultAttributes['stroke-width'])); + expect(SVGElement).toHaveAttribute('stroke-linecap', defaultAttributes['stroke-linecap']); + expect(SVGElement).toHaveAttribute('stroke-linejoin', defaultAttributes['stroke-linejoin']); + }); + it('should adjust the size, stroke color and stroke width', () => { - const testId = 'grid-icon'; - const { container, getByTestId } = render( + const { container } = render( , ); - const { attributes } = getByTestId(testId) as unknown as AttributesAssertion; - expect(attributes.stroke.value).toBe('red'); - expect(attributes.width.value).toBe('48'); - expect(attributes.height.value).toBe('48'); - expect(attributes['stroke-width'].value).toBe('4'); + const SVGElement = container.firstElementChild + + expect(SVGElement).toHaveAttribute('stroke', 'red'); + expect(SVGElement).toHaveAttribute('width', '48'); + expect(SVGElement).toHaveAttribute('height', '48'); + expect(SVGElement).toHaveAttribute('stroke-width', '4'); expect(container.innerHTML).toMatchSnapshot(); }); it('should render the alias icon', () => { - const testId = 'pen-icon'; const { container } = render( { const { container: Edit2Container } = render( { }); it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { - const testId = 'grid-icon'; - const { container, getByTestId } = render( + const { container } = render( , ); - const { attributes } = getByTestId(testId) as unknown as AttributesAssertion; + const SVGElement = container.firstElementChild + + expect(SVGElement).toHaveAttribute('stroke', 'red'); + expect(SVGElement).toHaveAttribute('width', '48'); + expect(SVGElement).toHaveAttribute('height', '48'); + expect(SVGElement).toHaveAttribute('stroke-width', '1'); - expect(attributes.stroke.value).toBe('red'); - expect(attributes.width.value).toBe('48'); - expect(attributes.height.value).toBe('48'); - expect(attributes['stroke-width'].value).toBe('1'); expect(container.innerHTML).toMatchSnapshot(); }); diff --git a/packages/lucide-react-native/package.json b/packages/lucide-react-native/package.json index 4329a6c949..f813242f92 100644 --- a/packages/lucide-react-native/package.json +++ b/packages/lucide-react-native/package.json @@ -45,6 +45,7 @@ "devDependencies": { "@lucide/rollup-plugins": "workspace:*", "@lucide/build-icons": "workspace:*", + "@lucide/shared": "workspace:*", "@testing-library/jest-dom": "^6.1.6", "@testing-library/react": "^14.1.2", "@types/prop-types": "^15.7.5", diff --git a/packages/lucide-react-native/src/Icon.ts b/packages/lucide-react-native/src/Icon.ts new file mode 100644 index 0000000000..c76767747c --- /dev/null +++ b/packages/lucide-react-native/src/Icon.ts @@ -0,0 +1,69 @@ +import { createElement, forwardRef, type FunctionComponent } from "react"; +import * as NativeSvg from 'react-native-svg'; +import defaultAttributes, { childDefaultAttributes } from "./defaultAttributes"; +import { IconNode, LucideProps } from "./types"; + +interface IconComponentProps extends LucideProps { + iconNode: IconNode +} + +/** + * Lucide icon component + * + * @component Icon + * @param {object} props + * @param {string} props.color - The color of the icon + * @param {number} props.size - The size of the icon + * @param {number} props.strokeWidth - The stroke width of the icon + * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width + * @param {string} props.className - The class name of the icon + * @param {IconNode} props.children - The children of the icon + * @param {IconNode} props.iconNode - The icon node of the icon + * + * @returns {ForwardRefExoticComponent} LucideIcon + */ +const Icon = forwardRef( + ( + { + color = 'currentColor', + size = 24, + strokeWidth = 2, + absoluteStrokeWidth, + children, + iconNode, + ...rest + }, + ref, + ) => { + const customAttrs = { + stroke: color, + strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth, + ...rest, + }; + + return createElement( + NativeSvg.Svg as unknown as string, + { + ref, + ...defaultAttributes, + width: size, + height: size, + ...customAttrs, + }, + [ + ...iconNode.map(([tag, attrs]) => { + const upperCasedTag = (tag.charAt(0).toUpperCase() + + tag.slice(1)) as keyof typeof NativeSvg; + // duplicating the attributes here because generating the OTA update bundles don't inherit the SVG properties from parent (codepush, expo-updates) + return createElement( + NativeSvg[upperCasedTag] as FunctionComponent, + { ...childDefaultAttributes, ...customAttrs, ...attrs } as LucideProps, + ); + }), + ...((Array.isArray(children) ? children : [children]) || []), + ], + ); + }, +); + +export default Icon diff --git a/packages/lucide-react-native/src/createLucideIcon.ts b/packages/lucide-react-native/src/createLucideIcon.ts index e824659bde..f862b585bc 100644 --- a/packages/lucide-react-native/src/createLucideIcon.ts +++ b/packages/lucide-react-native/src/createLucideIcon.ts @@ -7,17 +7,8 @@ import { } from 'react'; import * as NativeSvg from 'react-native-svg'; import defaultAttributes, { childDefaultAttributes } from './defaultAttributes'; -import type { SvgProps } from 'react-native-svg'; +import { LucideProps } from './types'; -export type IconNode = [elementName: keyof ReactSVG, attrs: Record][]; - -export interface LucideProps extends SvgProps { - size?: string | number; - absoluteStrokeWidth?: boolean; - 'data-testid'?: string; -} - -export type LucideIcon = ForwardRefExoticComponent; const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => { const Component = forwardRef( @@ -28,7 +19,6 @@ const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => { strokeWidth = 2, absoluteStrokeWidth, children, - 'data-testid': dataTestId, ...rest }: LucideProps, ref, @@ -46,7 +36,6 @@ const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => { ...defaultAttributes, width: size, height: size, - 'data-testid': dataTestId, ...customAttrs, }, [ diff --git a/packages/lucide-react-native/src/lucide-react-native.ts b/packages/lucide-react-native/src/lucide-react-native.ts index a024114981..3d55f91f7f 100644 --- a/packages/lucide-react-native/src/lucide-react-native.ts +++ b/packages/lucide-react-native/src/lucide-react-native.ts @@ -1,9 +1,7 @@ export * from './icons'; export * as icons from './icons'; export * from './aliases'; +export * from './types' export { default as createLucideIcon, - type IconNode, - type LucideProps, - type LucideIcon, } from './createLucideIcon'; diff --git a/packages/lucide-react-native/src/types.ts b/packages/lucide-react-native/src/types.ts new file mode 100644 index 0000000000..fd534414ac --- /dev/null +++ b/packages/lucide-react-native/src/types.ts @@ -0,0 +1,12 @@ +import type { ForwardRefExoticComponent, ReactSVG } from 'react'; +import type { SvgProps } from 'react-native-svg'; + +export type IconNode = [elementName: keyof ReactSVG, attrs: Record][]; + +export interface LucideProps extends SvgProps { + size?: string | number; + absoluteStrokeWidth?: boolean; + 'data-testid'?: string; +} + +export type LucideIcon = ForwardRefExoticComponent; diff --git a/packages/lucide-react-native/src/useIconComponent.ts b/packages/lucide-react-native/src/useIconComponent.ts index 84fb300c74..a98b0f5ebc 100644 --- a/packages/lucide-react-native/src/useIconComponent.ts +++ b/packages/lucide-react-native/src/useIconComponent.ts @@ -1,43 +1,36 @@ -import createLucideIcon, { IconNode, LucideIcon } from "./createLucideIcon"; - -type CamelToPascal = - T extends `${infer FirstChar}${infer Rest}` ? `${Capitalize}${Rest}` : never - -type ComponentList = { - [Prop in keyof T as CamelToPascal]: LucideIcon -} - -export const toPascalCase = (string: T): CamelToPascal => { - const camelCase = string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => - p2 ? p2.toUpperCase() : p1.toLowerCase(), - ); - - return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal; -}; - -const useIconComponent = >(icons: Icons) => { - if (typeof icons !== 'object') { - console.error('[lucide-react-native]: useIconComponent expects an object as argument') +import { type ComponentList, toPascalCase } from "@lucide/shared"; +import createLucideIcon from "./createLucideIcon"; + +import { IconNode, LucideIcon } from "./types"; + +/** + * Create a list (object) of icon components from a list (object) of icon nodes + * + * @param icons + * @returns Object of icon components + */ +const useIconComponent = >(iconNodes: Icons) => { + if (typeof iconNodes !== 'object') { + console.error('[lucide-react-react]: useIconComponent expects an object as argument') } - const iconNodeEntries = Object.entries(icons) + const iconNodeEntries = Object.entries(iconNodes) const iconNodesHasCorrectType = iconNodeEntries.every( ([, iconNode]) => Array.isArray(iconNode) ) if (!iconNodesHasCorrectType) { - console.error('[lucide-react-native]: Passed icons object has incorrect type') + console.error('[lucide-react-react]: Passed icons object has incorrect type') } - // TODO: throw error if this function is executed inside a react component render function const iconComponents = iconNodeEntries.reduce((acc, [iconName, iconNode]) => { - const componentName = toPascalCase(iconName) as keyof ComponentList; + const componentName = toPascalCase(iconName) as keyof ComponentList; - acc[componentName] = createLucideIcon(componentName as string, iconNode) as ComponentList[typeof componentName]; + acc[componentName] = createLucideIcon(componentName as string, iconNode) as ComponentList[typeof componentName]; return acc; - }, {} as ComponentList) + }, {} as ComponentList) return iconComponents } diff --git a/packages/lucide-react-native/tests/__snapshots__/lucide-react-native.spec.tsx.snap b/packages/lucide-react-native/tests/__snapshots__/lucide-react-native.spec.tsx.snap index 90dca01ac4..14af0d45ef 100644 --- a/packages/lucide-react-native/tests/__snapshots__/lucide-react-native.spec.tsx.snap +++ b/packages/lucide-react-native/tests/__snapshots__/lucide-react-native.spec.tsx.snap @@ -10,7 +10,6 @@ exports[`Using lucide icon components > should adjust the size, stroke color and stroke-width="4" stroke-linecap="round" stroke-linejoin="round" - data-testid="grid-icon" > should not scale the strokeWidth when ab stroke-width="1" stroke-linecap="round" stroke-linejoin="round" - data-testid="grid-icon" > { }); it('should adjust the size, stroke color and stroke width', () => { - const testId = 'grid-icon'; - const { container, getByTestId } = render( + const { container } = render( , ); - const { attributes } = getByTestId(testId); - expect((attributes as unknown as Attributes).stroke.value).toBe('red'); - expect((attributes as unknown as Attributes).width.value).toBe('48'); - expect((attributes as unknown as Attributes).height.value).toBe('48'); - expect((attributes as unknown as Attributes)['stroke-width'].value).toBe('4'); + const SVGElement = container.firstElementChild + + expect(SVGElement).toHaveAttribute('stroke', 'red'); + expect(SVGElement).toHaveAttribute('width', '48'); + expect(SVGElement).toHaveAttribute('height', '48'); + expect(SVGElement).toHaveAttribute('stroke-width', '4'); expect(container.innerHTML).toMatchSnapshot(); }); @@ -61,23 +60,20 @@ describe('Using lucide icon components', () => { }); it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { - const testId = 'grid-icon'; - const { container, getByTestId } = render( + const { container } = render( , ); - const { attributes } = getByTestId(testId) as unknown as { - attributes: Record; - }; - expect(attributes.stroke.value).toBe('red'); - expect(attributes.width.value).toBe('48'); - expect(attributes.height.value).toBe('48'); - expect(attributes['stroke-width'].value).toBe('1'); + const SVGElement = container.firstElementChild + + expect(SVGElement).toHaveAttribute('stroke', 'red'); + expect(SVGElement).toHaveAttribute('width', '48'); + expect(SVGElement).toHaveAttribute('height', '48'); + expect(SVGElement).toHaveAttribute('stroke-width', '1'); expect(container.innerHTML).toMatchSnapshot(); }); @@ -91,8 +87,8 @@ describe('Using lucide icon components', () => { , ); - const { children } = getByTestId(testId) as unknown as { children: HTMLCollection }; - const lastChild = children[children.length - 1]; + const { children } = container.firstElementChild ?? {} + const lastChild = children?.[children.length - 1]; expect(lastChild).toEqual(getByTestId(childId)); expect(container.innerHTML).toMatchSnapshot(); diff --git a/packages/lucide-react-native/tests/testIconNodes.ts b/packages/lucide-react-native/tests/testIconNodes.ts new file mode 100644 index 0000000000..e48a1b8332 --- /dev/null +++ b/packages/lucide-react-native/tests/testIconNodes.ts @@ -0,0 +1,22 @@ +import { IconNode } from "../src/types" + +export const airVent: IconNode = [ + [ + 'path', + { + d: 'M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2', + key: 'larmp2', + }, + ], + ['path', { d: 'M6 8h12', key: '6g4wlu' }], + ['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }], + ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }] +] + +export const coffee: IconNode = [ + ['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }], + ['path', { d: 'M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z', key: '1bxrl0' }], + ['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }], + ['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }], + ['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }], +] diff --git a/packages/lucide-react-native/tests/useIconComponent.spec.tsx b/packages/lucide-react-native/tests/useIconComponent.spec.tsx new file mode 100644 index 0000000000..3d78690c61 --- /dev/null +++ b/packages/lucide-react-native/tests/useIconComponent.spec.tsx @@ -0,0 +1,31 @@ +import { describe, it, expect, vi } from 'vitest'; +import useIconComponent from '../src/useIconComponent'; +import { airVent, coffee } from './testIconNodes'; +import { render } from '@testing-library/react'; + +vi.mock('react-native-svg'); + +describe('Using iconNode component generator', () => { + it('should create a component from an iconNode', () => { + const { AirVent } = useIconComponent({ airVent }) + + const { container } = render( ); + + expect( container.firstChild ).toMatchSnapshot(); + expect( container.firstChild ).toBeDefined(); + }); + + it('should create multiple components', () => { + const { AirVent, Coffee } = useIconComponent({ airVent, coffee }) + + const { container } = render( + <> + + + + ); + + expect( container ).toMatchSnapshot(); + expect( container ).toBeDefined(); + }); +}) diff --git a/packages/lucide-react/src/Icon.ts b/packages/lucide-react/src/Icon.ts index 62b45f6133..2e7b3ee70e 100644 --- a/packages/lucide-react/src/Icon.ts +++ b/packages/lucide-react/src/Icon.ts @@ -1,11 +1,27 @@ import { createElement, forwardRef } from "react"; import defaultAttributes from "./defaultAttributes"; import { IconNode, LucideProps } from "./types"; +import { mergeClasses } from "@lucide/shared"; interface IconComponentProps extends LucideProps { iconNode: IconNode } +/** + * Lucide icon component + * + * @component Icon + * @param {object} props + * @param {string} props.color - The color of the icon + * @param {number} props.size - The size of the icon + * @param {number} props.strokeWidth - The stroke width of the icon + * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width + * @param {string} props.className - The class name of the icon + * @param {IconNode} props.children - The children of the icon + * @param {IconNode} props.iconNode - The icon node of the icon + * + * @returns {ForwardRefExoticComponent} LucideIcon + */ const Icon = forwardRef( ( { @@ -20,7 +36,6 @@ const Icon = forwardRef( }, ref, ) => { - return createElement( 'svg', { @@ -32,7 +47,7 @@ const Icon = forwardRef( strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth, - className: ['lucide', className].join(' '), + className: mergeClasses('lucide', className), ...rest, }, [ diff --git a/packages/lucide-react/src/createLucideIcon.ts b/packages/lucide-react/src/createLucideIcon.ts index 18e20216bb..0373548048 100644 --- a/packages/lucide-react/src/createLucideIcon.ts +++ b/packages/lucide-react/src/createLucideIcon.ts @@ -2,10 +2,16 @@ import { createElement, forwardRef, } from 'react'; -import { toKebabCase } from '@lucide/shared'; +import { mergeClasses, toKebabCase } from '@lucide/shared'; import { IconNode, LucideProps } from './types'; import Icon from './Icon'; +/** + * Create a Lucide icon component + * @param {string} iconName + * @param {array} iconNode + * @returns {ForwardRefExoticComponent} LucideIcon + */ const createLucideIcon = (iconName: string, iconNode: IconNode) => { const Component = forwardRef( ({ className, ...props}, ref) => createElement( @@ -13,7 +19,7 @@ const createLucideIcon = (iconName: string, iconNode: IconNode) => { { ref, iconNode, - className: [`lucide-${toKebabCase(iconName)}`, className].join(' '), + className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className), ...props, }, ) diff --git a/packages/lucide-react/src/useIconComponent.ts b/packages/lucide-react/src/useIconComponent.ts index b9f7b04fa9..594208565f 100644 --- a/packages/lucide-react/src/useIconComponent.ts +++ b/packages/lucide-react/src/useIconComponent.ts @@ -1,4 +1,4 @@ -import { CamelToPascal, ComponentList, toPascalCase } from "@lucide/shared"; +import { type ComponentList, toPascalCase } from "@lucide/shared"; import createLucideIcon from "./createLucideIcon"; import { IconNode, LucideIcon } from "./types"; @@ -23,7 +23,7 @@ const useIconComponent = >(iconNodes: Ico if (!iconNodesHasCorrectType) { console.error('[lucide-react]: Passed icons object has incorrect type') } - // TODO: Optional throw an warning if this function is executed inside a react component render function, because this harms performance + // TODO: Optional throw an warning if this function is executed inside a React component render function, because this harms performance const iconComponents = iconNodeEntries.reduce((acc, [iconName, iconNode]) => { const componentName = toPascalCase(iconName) as keyof ComponentList; diff --git a/packages/lucide-react/tests/Icon.spec.tsx b/packages/lucide-react/tests/Icon.spec.tsx index 10259f2a16..6b9a90a66d 100644 --- a/packages/lucide-react/tests/Icon.spec.tsx +++ b/packages/lucide-react/tests/Icon.spec.tsx @@ -1,12 +1,12 @@ import { describe, it, expect } from 'vitest'; -import { render, waitFor } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { airVent } from './testIconNodes'; import Icon from '../src/Icon'; describe('Using Icon Component', () => { it('should render icon based on a iconNode', async () => { - const { container, getByLabelText } = render( + const { container } = render( { />, ); - await waitFor(() => getByLabelText('smile')) + expect( container.firstChild ).toBeDefined() + }); + + it('should render icon and match snapshot', async () => { + const { container } = render( + , + ); - expect( container.innerHTML ).toMatchSnapshot(); + expect( container.firstChild ).toMatchSnapshot(); }); }) diff --git a/packages/lucide-react/tests/__snapshots__/Icon.spec.tsx.snap b/packages/lucide-react/tests/__snapshots__/Icon.spec.tsx.snap new file mode 100644 index 0000000000..635ca428ad --- /dev/null +++ b/packages/lucide-react/tests/__snapshots__/Icon.spec.tsx.snap @@ -0,0 +1,29 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Using Icon Component > should render icon and match snapshot 1`] = ` + + + + + + +`; diff --git a/packages/lucide-react/tests/__snapshots__/createLucideIcon.spec.tsx.snap b/packages/lucide-react/tests/__snapshots__/createLucideIcon.spec.tsx.snap new file mode 100644 index 0000000000..acf4f97390 --- /dev/null +++ b/packages/lucide-react/tests/__snapshots__/createLucideIcon.spec.tsx.snap @@ -0,0 +1,29 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Using createLucideIcon > should create a component from an iconNode 1`] = ` + + + + + + +`; diff --git a/packages/lucide-react/tests/__snapshots__/dynamicImports.spec.tsx.snap b/packages/lucide-react/tests/__snapshots__/dynamicImports.spec.tsx.snap index 7a3b51faae..81acb42979 100644 --- a/packages/lucide-react/tests/__snapshots__/dynamicImports.spec.tsx.snap +++ b/packages/lucide-react/tests/__snapshots__/dynamicImports.spec.tsx.snap @@ -10,7 +10,7 @@ exports[`Using dynamicImports > should render icons dynamically by using the dyn stroke-width="1" stroke-linecap="round" stroke-linejoin="round" - class="lucide lucide-smile " + class="lucide lucide-smile" aria-label="smile" > should adjust the size, stroke color and stroke-width="4" stroke-linecap="round" stroke-linejoin="round" - class="lucide lucide-grid3x3 " - data-testid="grid-icon" + class="lucide lucide-grid3x3" > should not scale the strokeWidth when ab stroke-width="1" stroke-linecap="round" stroke-linejoin="round" - class="lucide lucide-grid3x3 " - data-testid="grid-icon" + class="lucide lucide-grid3x3" > should render an component 1`] = ` stroke-width="2" stroke-linecap="round" stroke-linejoin="round" - class="lucide lucide-grid3x3 " + class="lucide lucide-grid3x3" > should create a component from an iconNode 1`] = ` should create a component from an exports[`Using iconNode component generator > should create multiple components 1`] = `
should create multiple components /> { + it('should create a component from an iconNode', () => { + const AirVent = createLucideIcon('AirVent', airVent) + + const { container } = render( ); + + expect( container.firstChild ).toMatchSnapshot(); + expect( container.firstChild ).toBeDefined(); + }); +}) diff --git a/packages/lucide-react/tests/lucide-react.spec.tsx b/packages/lucide-react/tests/lucide-react.spec.tsx index 61d1638aee..87fef0cff7 100644 --- a/packages/lucide-react/tests/lucide-react.spec.tsx +++ b/packages/lucide-react/tests/lucide-react.spec.tsx @@ -1,6 +1,7 @@ import { describe, it, expect } from 'vitest'; import { render, cleanup } from '@testing-library/react' import { Pen, Edit2, Grid, Droplet } from '../src/lucide-react'; +import defaultAttributes from '../src/defaultAttributes'; describe('Using lucide icon components', () => { it('should render an component', () => { @@ -9,24 +10,37 @@ describe('Using lucide icon components', () => { expect(container.innerHTML).toMatchSnapshot(); }); + it('should render the icon with default attributes', () => { + const { container } = render(); + + const SVGElement = container.firstElementChild + + expect(SVGElement).toHaveAttribute('xmlns', defaultAttributes.xmlns); + expect(SVGElement).toHaveAttribute('width', String(defaultAttributes.width)); + expect(SVGElement).toHaveAttribute('height', String(defaultAttributes.height)); + expect(SVGElement).toHaveAttribute('viewBox', defaultAttributes.viewBox); + expect(SVGElement).toHaveAttribute('fill', defaultAttributes.fill); + expect(SVGElement).toHaveAttribute('stroke', defaultAttributes.stroke); + expect(SVGElement).toHaveAttribute('stroke-width', String(defaultAttributes.strokeWidth)); + expect(SVGElement).toHaveAttribute('stroke-linecap', defaultAttributes.strokeLinecap); + expect(SVGElement).toHaveAttribute('stroke-linejoin', defaultAttributes.strokeLinejoin); + }); + it('should adjust the size, stroke color and stroke width', () => { - const testId = 'grid-icon'; - const { container, getByTestId } = render( + const { container } = render( , ); - const { attributes } = getByTestId(testId) as unknown as { - attributes: Record; - }; - expect(attributes.stroke.value).toBe('red'); - expect(attributes.width.value).toBe('48'); - expect(attributes.height.value).toBe('48'); - expect(attributes['stroke-width'].value).toBe('4'); + const SVGElement = container.firstElementChild + + expect(SVGElement).toHaveAttribute('stroke', 'red'); + expect(SVGElement).toHaveAttribute('width', '48'); + expect(SVGElement).toHaveAttribute('height', '48'); + expect(SVGElement).toHaveAttribute('stroke-width', '4'); expect(container.innerHTML).toMatchSnapshot(); }); @@ -56,23 +70,20 @@ describe('Using lucide icon components', () => { }); it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { - const testId = 'grid-icon'; const { container, getByTestId } = render( , ); - const { attributes } = getByTestId(testId) as unknown as { - attributes: Record; - }; - expect(attributes.stroke.value).toBe('red'); - expect(attributes.width.value).toBe('48'); - expect(attributes.height.value).toBe('48'); - expect(attributes['stroke-width'].value).toBe('1'); + const SVGElement = container.firstElementChild + + expect(SVGElement).toHaveAttribute('stroke', 'red'); + expect(SVGElement).toHaveAttribute('width', '48'); + expect(SVGElement).toHaveAttribute('height', '48'); + expect(SVGElement).toHaveAttribute('stroke-width', '1'); expect(container.innerHTML).toMatchSnapshot(); }); diff --git a/packages/shared/src/utils.ts b/packages/shared/src/utils.ts index b5bbdd618e..17a804c14a 100644 --- a/packages/shared/src/utils.ts +++ b/packages/shared/src/utils.ts @@ -23,3 +23,11 @@ export const toPascalCase = (string: T): CamelToPascal => { return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal; }; + +/** + * Merges classes into a single string + * + * @param {array} classes + * @returns {string} A string of classes + */ +export const mergeClasses = (...classes: ClassType[]) => classes.filter(Boolean).join(' '); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f6c38a6ee..5ce2fb692f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -457,6 +457,9 @@ importers: '@lucide/rollup-plugins': specifier: workspace:* version: link:../../tools/rollup-plugins + '@lucide/shared': + specifier: workspace:* + version: link:../shared '@testing-library/jest-dom': specifier: ^6.1.6 version: 6.4.2(vitest@1.2.2) @@ -775,6 +778,8 @@ importers: specifier: ^5.12.0 version: 5.12.0(rollup@4.9.6) + tools/test-utils: {} + packages: /@aashutoshrathi/word-wrap@1.2.6: diff --git a/tools/build-icons/main.mjs b/tools/build-icons/cli.mjs similarity index 100% rename from tools/build-icons/main.mjs rename to tools/build-icons/cli.mjs diff --git a/tools/build-icons/package.json b/tools/build-icons/package.json index 56e70c0cd2..2f901be293 100644 --- a/tools/build-icons/package.json +++ b/tools/build-icons/package.json @@ -6,10 +6,10 @@ "main": "index.mjs", "type": "module", "scripts": { - "start": "node ./main.mjs" + "start": "node ./cli.mjs" }, "bin": { - "build-icons": "./main.mjs" + "build-icons": "./cli.mjs" }, "engines": { "node": ">= 16" diff --git a/tools/test-utils/package.json b/tools/test-utils/package.json new file mode 100644 index 0000000000..10fcf94f2d --- /dev/null +++ b/tools/test-utils/package.json @@ -0,0 +1,11 @@ +{ + "name": "@lucide/test-utils", + "license": "ISC", + "private": true, + "version": "1.0.0", + "description": "", + "type": "module", + "main": "src/main.ts", + "types": "src/main.ts", + "author": "Eric Fennis" +} diff --git a/tools/test-utils/src/getElementAttributes.ts b/tools/test-utils/src/getElementAttributes.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/test-utils/src/getOriginalIconSVG.ts b/tools/test-utils/src/getOriginalIconSVG.ts new file mode 100644 index 0000000000..03b1109e2f --- /dev/null +++ b/tools/test-utils/src/getOriginalIconSVG.ts @@ -0,0 +1,17 @@ +import fs from 'fs'; +import path from 'path'; +import { parseSync, stringify } from 'svgson'; + +const ICONS_DIR = path.resolve(__dirname, '../../../icons'); + +const getOriginalIconSVG = (iconName:string, aliasName:string) => { + const svgContent = fs.readFileSync(path.join(ICONS_DIR, `${iconName}.svg`), 'utf8'); + const svgParsed = parseSync(svgContent); + + svgParsed.attributes['data-lucide'] = aliasName ?? iconName; + svgParsed.attributes['class'] = `lucide lucide-${aliasName ?? iconName}`; + + return stringify(svgParsed, { selfClose: false }); +}; + +export default getOriginalIconSVG; diff --git a/tools/test-utils/src/main.ts b/tools/test-utils/src/main.ts new file mode 100644 index 0000000000..d2b4ad12a9 --- /dev/null +++ b/tools/test-utils/src/main.ts @@ -0,0 +1 @@ +export { default as getOriginalIconSVG } from './getOriginalIconSVG'; From 00de13cf2fd4493e323be63116a0968fd77ccab0 Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Mon, 18 Mar 2024 18:59:03 +0100 Subject: [PATCH 07/27] Reset changes in icons --- icons/ambulance.svg | 5 +++-- icons/brain-cog.svg | 2 +- icons/captions.svg | 4 ++-- icons/circle-fading-plus.svg | 2 +- icons/guitar.svg | 2 +- icons/headset.svg | 2 +- icons/hop-off.svg | 2 +- icons/hop.svg | 10 +++++----- icons/key-round.svg | 2 +- icons/package-open.svg | 4 ++-- icons/palette.svg | 10 +++++----- icons/pickaxe.svg | 2 +- icons/scatter-chart.svg | 10 +++++----- icons/shield-off.svg | 2 +- icons/square-radical.svg | 2 +- icons/tag.svg | 2 +- icons/tags.svg | 2 +- icons/truck.svg | 2 +- icons/vault.svg | 8 ++++---- 19 files changed, 38 insertions(+), 37 deletions(-) diff --git a/icons/ambulance.svg b/icons/ambulance.svg index 7e08bedb20..c192da79ce 100644 --- a/icons/ambulance.svg +++ b/icons/ambulance.svg @@ -11,9 +11,10 @@ > - + - + \ No newline at end of file diff --git a/icons/brain-cog.svg b/icons/brain-cog.svg index f46a29d0b5..079ffac6d0 100644 --- a/icons/brain-cog.svg +++ b/icons/brain-cog.svg @@ -9,7 +9,7 @@ stroke-linecap="round" stroke-linejoin="round" > - + diff --git a/icons/captions.svg b/icons/captions.svg index b97781e966..fdf03617b2 100644 --- a/icons/captions.svg +++ b/icons/captions.svg @@ -10,5 +10,5 @@ stroke-linejoin="round" > - - + + \ No newline at end of file diff --git a/icons/circle-fading-plus.svg b/icons/circle-fading-plus.svg index 5c9ef7499b..16be9dcff5 100644 --- a/icons/circle-fading-plus.svg +++ b/icons/circle-fading-plus.svg @@ -16,4 +16,4 @@ - + \ No newline at end of file diff --git a/icons/guitar.svg b/icons/guitar.svg index d2f86707f0..129613cd4a 100644 --- a/icons/guitar.svg +++ b/icons/guitar.svg @@ -11,7 +11,7 @@ > - + diff --git a/icons/headset.svg b/icons/headset.svg index bf0db141e0..72cd078061 100644 --- a/icons/headset.svg +++ b/icons/headset.svg @@ -9,6 +9,6 @@ stroke-linecap="round" stroke-linejoin="round" > - + diff --git a/icons/hop-off.svg b/icons/hop-off.svg index 53ed5fe9d0..d2db146bb7 100644 --- a/icons/hop-off.svg +++ b/icons/hop-off.svg @@ -12,7 +12,7 @@ - + diff --git a/icons/hop.svg b/icons/hop.svg index cc7e7af085..eae2af0fcb 100644 --- a/icons/hop.svg +++ b/icons/hop.svg @@ -11,10 +11,10 @@ > - - - - + + + + - + diff --git a/icons/key-round.svg b/icons/key-round.svg index 5749f92e1d..16819eaf65 100644 --- a/icons/key-round.svg +++ b/icons/key-round.svg @@ -10,5 +10,5 @@ stroke-linejoin="round" > - + diff --git a/icons/package-open.svg b/icons/package-open.svg index 7d2ef26f2c..645f567236 100644 --- a/icons/package-open.svg +++ b/icons/package-open.svg @@ -10,7 +10,7 @@ stroke-linejoin="round" > - + - + diff --git a/icons/palette.svg b/icons/palette.svg index c59676d44a..74ae304021 100644 --- a/icons/palette.svg +++ b/icons/palette.svg @@ -9,9 +9,9 @@ stroke-linecap="round" stroke-linejoin="round" > - - - - - + + + + + diff --git a/icons/pickaxe.svg b/icons/pickaxe.svg index e51f247487..fd134ab991 100644 --- a/icons/pickaxe.svg +++ b/icons/pickaxe.svg @@ -12,5 +12,5 @@ - + diff --git a/icons/scatter-chart.svg b/icons/scatter-chart.svg index f0e46ba641..4beec1f156 100644 --- a/icons/scatter-chart.svg +++ b/icons/scatter-chart.svg @@ -9,10 +9,10 @@ stroke-linecap="round" stroke-linejoin="round" > - - - - - + + + + + diff --git a/icons/shield-off.svg b/icons/shield-off.svg index 040698e046..d7cc4443d5 100644 --- a/icons/shield-off.svg +++ b/icons/shield-off.svg @@ -11,5 +11,5 @@ > - + diff --git a/icons/square-radical.svg b/icons/square-radical.svg index d26d00d2dc..70bd53f615 100644 --- a/icons/square-radical.svg +++ b/icons/square-radical.svg @@ -10,5 +10,5 @@ stroke-linejoin="round" > - + diff --git a/icons/tag.svg b/icons/tag.svg index 127b9c24bf..7a073e9f35 100644 --- a/icons/tag.svg +++ b/icons/tag.svg @@ -10,5 +10,5 @@ stroke-linejoin="round" > - + diff --git a/icons/tags.svg b/icons/tags.svg index 61a2bee805..afe87a8596 100644 --- a/icons/tags.svg +++ b/icons/tags.svg @@ -11,5 +11,5 @@ > - + diff --git a/icons/truck.svg b/icons/truck.svg index 57e03d6c94..cef64e21b1 100644 --- a/icons/truck.svg +++ b/icons/truck.svg @@ -14,4 +14,4 @@ - + \ No newline at end of file diff --git a/icons/vault.svg b/icons/vault.svg index 95e51027a5..0cb6662f58 100644 --- a/icons/vault.svg +++ b/icons/vault.svg @@ -10,13 +10,13 @@ stroke-linejoin="round" > - + - + - + - + From b9e086414da8221d06edaf86e8cc0560abc607ca Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Mon, 18 Mar 2024 19:38:13 +0100 Subject: [PATCH 08/27] Add types --- packages/lucide-react-native/src/createLucideIcon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lucide-react-native/src/createLucideIcon.ts b/packages/lucide-react-native/src/createLucideIcon.ts index f862b585bc..71a33a9bcb 100644 --- a/packages/lucide-react-native/src/createLucideIcon.ts +++ b/packages/lucide-react-native/src/createLucideIcon.ts @@ -7,7 +7,7 @@ import { } from 'react'; import * as NativeSvg from 'react-native-svg'; import defaultAttributes, { childDefaultAttributes } from './defaultAttributes'; -import { LucideProps } from './types'; +import { IconNode, LucideIcon, LucideProps } from './types'; const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => { From 40b30e03cdd79605ac15041bbc806da8e5ec0c3c Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Fri, 22 Mar 2024 14:23:02 +0100 Subject: [PATCH 09/27] Add support for Icon components in Lucide Vue Next --- packages/lucide-vue-next/package.json | 9 +- packages/lucide-vue-next/src/Icon.ts | 44 ++ .../lucide-vue-next/src/createLucideIcon.ts | 49 +-- packages/lucide-vue-next/src/types.ts | 13 + .../lucide-vue-next.spec.ts.snap | 2 - .../tests/lucide-vue-next.spec.ts | 43 +- packages/shared/src/utils.ts | 6 +- pnpm-lock.yaml | 387 ++++++++++++++---- 8 files changed, 406 insertions(+), 147 deletions(-) create mode 100644 packages/lucide-vue-next/src/Icon.ts create mode 100644 packages/lucide-vue-next/src/types.ts diff --git a/packages/lucide-vue-next/package.json b/packages/lucide-vue-next/package.json index 78b0472576..3fc318c599 100644 --- a/packages/lucide-vue-next/package.json +++ b/packages/lucide-vue-next/package.json @@ -41,6 +41,7 @@ "build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mjs --renderUniqueKey --withAliases --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts", "build:bundles": "rollup -c ./rollup.config.mjs", "test": "vitest run", + "test:watch": "vitest watch", "version": "pnpm version --git-tag-version=false" }, "devDependencies": { @@ -48,14 +49,14 @@ "@lucide/rollup-plugins": "workspace:*", "@lucide/shared": "workspace:*", "@testing-library/jest-dom": "^6.1.6", - "@testing-library/vue": "^8.0.1", + "@testing-library/vue": "^8.0.3", "@vitejs/plugin-vue": "^4.6.2", - "@vue/test-utils": "2.4.3", + "@vue/test-utils": "2.4.5", "rollup": "^4.9.2", "rollup-plugin-dts": "^6.1.0", "vite": "5.0.12", - "vitest": "^1.1.1", - "vue": "^3.0.1" + "vitest": "^1.4.0", + "vue": "^3.4.21" }, "peerDependencies": { "vue": ">=3.0.1" diff --git a/packages/lucide-vue-next/src/Icon.ts b/packages/lucide-vue-next/src/Icon.ts new file mode 100644 index 0000000000..52fac40218 --- /dev/null +++ b/packages/lucide-vue-next/src/Icon.ts @@ -0,0 +1,44 @@ +import { type FunctionalComponent, h } from "vue"; +import { mergeClasses, toKebabCase } from "@lucide/shared"; +import defaultAttributes from "./defaultAttributes"; +import { IconNode, LucideProps } from "./types"; + +interface IconProps { + iconNode: IconNode; + name: string +} + +const Icon: FunctionalComponent = ( + { + size, + strokeWidth = 2, + absoluteStrokeWidth, + color, + iconNode, + name, + class: classes, + ...props + }, // props + { slots }, // context +) => { + return h( + 'svg', + { + ...defaultAttributes, + width: size || defaultAttributes.width, + height: size || defaultAttributes.height, + stroke: color || defaultAttributes.stroke, + 'stroke-width': absoluteStrokeWidth + ? (Number(strokeWidth) * 24) / Number(size) + : strokeWidth, + class: ['lucide', `lucide-${toKebabCase(name ?? 'icon')}`], + ...props, + }, + [ + ...iconNode.map((child) => h(...child)), + ...(slots.default ? [slots.default()] : []) + ], + ); +} + +export default Icon diff --git a/packages/lucide-vue-next/src/createLucideIcon.ts b/packages/lucide-vue-next/src/createLucideIcon.ts index 26c300d1d4..c2d9b5bdd4 100644 --- a/packages/lucide-vue-next/src/createLucideIcon.ts +++ b/packages/lucide-vue-next/src/createLucideIcon.ts @@ -1,49 +1,26 @@ import { h } from 'vue'; import type { SVGAttributes, FunctionalComponent } from 'vue'; -import defaultAttributes from './defaultAttributes'; -import { toKebabCase } from '@lucide/shared'; +import { mergeClasses, toKebabCase } from '@lucide/shared'; +import { IconNode, LucideIcon, LucideProps } from './types'; +import Icon from './Icon'; // Create interface extending SVGAttributes -export interface LucideProps extends Partial { - size?: 24 | number - strokeWidth?: number | string - absoluteStrokeWidth?: boolean -} -export type IconNode = [elementName: string, attrs: Record][] -export type LucideIcon = FunctionalComponent - -// Legacy exports -export type SVGProps = LucideProps -export type Icon = LucideIcon /** * Create a Lucide icon component * @param {string} iconName * @param {array} iconNode * @returns {FunctionalComponent} LucideIcon */ -const createLucideIcon = - (iconName: string, iconNode: IconNode): Icon => - ( - { size, strokeWidth = 2, absoluteStrokeWidth, color, class: classes, ...props }, // props - { attrs, slots }, // context - ) => { - return h( - 'svg', - { - ...defaultAttributes, - width: size || defaultAttributes.width, - height: size || defaultAttributes.height, - stroke: color || defaultAttributes.stroke, - 'stroke-width': absoluteStrokeWidth - ? (Number(strokeWidth) * 24) / Number(size) - : strokeWidth, - ...attrs, - class: ['lucide', `lucide-${toKebabCase(iconName)}`], - ...props, - }, - [...iconNode.map((child) => h(...child)), ...(slots.default ? [slots.default()] : [])], - ); - }; +const createLucideIcon = (iconName: string, iconNode: IconNode): FunctionalComponent => + (props, { slots }) => h( + Icon, + { + ...props, + iconNode, + name: iconName, + }, + slots + ) export default createLucideIcon; diff --git a/packages/lucide-vue-next/src/types.ts b/packages/lucide-vue-next/src/types.ts new file mode 100644 index 0000000000..95ce13a5be --- /dev/null +++ b/packages/lucide-vue-next/src/types.ts @@ -0,0 +1,13 @@ +import type { FunctionalComponent, SVGAttributes } from "vue"; + +export interface LucideProps extends Partial { + size?: 24 | number + strokeWidth?: number | string + absoluteStrokeWidth?: boolean +} + +export type IconNode = [elementName: string, attrs: Record][] +export type LucideIcon = FunctionalComponent + +// Legacy exports +export type SVGProps = LucideProps diff --git a/packages/lucide-vue-next/tests/__snapshots__/lucide-vue-next.spec.ts.snap b/packages/lucide-vue-next/tests/__snapshots__/lucide-vue-next.spec.ts.snap index 9a609fb1d6..c9e6de1970 100644 --- a/packages/lucide-vue-next/tests/__snapshots__/lucide-vue-next.spec.ts.snap +++ b/packages/lucide-vue-next/tests/__snapshots__/lucide-vue-next.spec.ts.snap @@ -81,10 +81,8 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
{ afterEach(() => cleanup()); @@ -10,6 +11,22 @@ describe('Using lucide icon components', () => { expect(container).toMatchSnapshot(); }); + it('should render the icon with the default attributes', () => { + const { container } = render(Smile) + + const SVGElement = container.firstElementChild + + expect(SVGElement).toHaveAttribute('xmlns', defaultAttributes.xmlns); + expect(SVGElement).toHaveAttribute('width', String(defaultAttributes.width)); + expect(SVGElement).toHaveAttribute('height', String(defaultAttributes.height)); + expect(SVGElement).toHaveAttribute('viewBox', defaultAttributes.viewBox); + expect(SVGElement).toHaveAttribute('fill', defaultAttributes.fill); + expect(SVGElement).toHaveAttribute('stroke', defaultAttributes.stroke); + expect(SVGElement).toHaveAttribute('stroke-width', String(defaultAttributes['stroke-width'])); + expect(SVGElement).toHaveAttribute('stroke-linecap', defaultAttributes['stroke-linecap']); + expect(SVGElement).toHaveAttribute('stroke-linejoin', defaultAttributes['stroke-linejoin']); + }); + it('should adjust the size, stroke color and stroke width', () => { const { container } = render(Smile, { props: { @@ -19,11 +36,11 @@ describe('Using lucide icon components', () => { }, }); - const [icon] = document.getElementsByClassName('lucide'); + const SVGElement = container.firstElementChild - expect(icon.getAttribute('width')).toBe('48'); - expect(icon.getAttribute('stroke')).toBe('red'); - expect(icon.getAttribute('stroke-width')).toBe('4'); + expect(SVGElement).toHaveAttribute('width', '48'); + expect(SVGElement).toHaveAttribute('stroke', 'red'); + expect(SVGElement).toHaveAttribute('stroke-width', '4'); expect(container).toMatchSnapshot(); }); @@ -37,7 +54,7 @@ describe('Using lucide icon components', () => { expect(container).toMatchSnapshot(); - const [icon] = document.getElementsByClassName('my-icon'); + const icon = container.firstElementChild expect(icon).toHaveClass('my-icon'); expect(icon).toHaveClass('lucide'); @@ -53,20 +70,20 @@ describe('Using lucide icon components', () => { expect(container).toMatchSnapshot(); - const [icon] = document.getElementsByClassName('lucide'); + const icon = container.firstElementChild expect(icon).toHaveStyle({ position: 'absolute' }); }); it('should call the onClick event', async () => { const onClick = vi.fn(); - render(Smile, { + const { container } = render(Smile, { attrs: { onClick, }, }); - const [icon] = document.getElementsByClassName('lucide'); + const icon = container.firstElementChild await fireEvent.click(icon); @@ -116,7 +133,7 @@ describe('Using lucide icon components', () => { }); it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { - render(Pen, { + const { container } = render(Pen, { props: { size: 48, color: 'red', @@ -124,10 +141,10 @@ describe('Using lucide icon components', () => { }, }); - const [icon] = document.getElementsByClassName('lucide'); + const icon = container.firstElementChild - expect(icon.getAttribute('width')).toBe('48'); - expect(icon.getAttribute('stroke')).toBe('red'); - expect(icon.getAttribute('stroke-width')).toBe('1'); + expect(icon).toHaveAttribute('width', '48'); + expect(icon).toHaveAttribute('stroke', 'red'); + expect(icon).toHaveAttribute('stroke-width', '1'); }); }); diff --git a/packages/shared/src/utils.ts b/packages/shared/src/utils.ts index 17a804c14a..2c70e39a1b 100644 --- a/packages/shared/src/utils.ts +++ b/packages/shared/src/utils.ts @@ -30,4 +30,8 @@ export const toPascalCase = (string: T): CamelToPascal => { * @param {array} classes * @returns {string} A string of classes */ -export const mergeClasses = (...classes: ClassType[]) => classes.filter(Boolean).join(' '); +export const mergeClasses = ( + ...classes: ClassType[] +) => classes.filter((className, index, array) => { + return Boolean(className) && array.indexOf(className) === index; +}).join(' '); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ce2fb692f..d30442b41b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -589,6 +589,9 @@ importers: '@lucide/build-icons': specifier: workspace:* version: link:../../tools/build-icons + '@lucide/shared': + specifier: workspace:* + version: link:../shared '@sveltejs/package': specifier: ^2.2.3 version: 2.2.6(svelte@4.1.2)(typescript@5.1.6) @@ -684,16 +687,16 @@ importers: version: link:../shared '@testing-library/jest-dom': specifier: ^6.1.6 - version: 6.4.2(vitest@1.2.2) + version: 6.4.2(vitest@1.4.0) '@testing-library/vue': - specifier: ^8.0.1 - version: 8.0.2(vue@3.4.18) + specifier: ^8.0.3 + version: 8.0.3(vue@3.4.21) '@vitejs/plugin-vue': specifier: ^4.6.2 - version: 4.6.2(vite@5.0.12)(vue@3.4.18) + version: 4.6.2(vite@5.0.12)(vue@3.4.21) '@vue/test-utils': - specifier: 2.4.3 - version: 2.4.3(vue@3.4.18) + specifier: 2.4.5 + version: 2.4.5 rollup: specifier: ^4.9.2 version: 4.9.6 @@ -704,11 +707,11 @@ importers: specifier: 5.0.12 version: 5.0.12 vitest: - specifier: ^1.1.1 - version: 1.2.2(jsdom@20.0.3) + specifier: ^1.4.0 + version: 1.4.0 vue: - specifier: ^3.0.1 - version: 3.4.18(typescript@4.9.5) + specifier: ^3.4.21 + version: 3.4.21(typescript@4.9.5) packages/shared: {} @@ -7457,6 +7460,38 @@ packages: vitest: 1.2.2(jsdom@20.0.3) dev: true + /@testing-library/jest-dom@6.4.2(vitest@1.4.0): + resolution: {integrity: sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + peerDependencies: + '@jest/globals': '>= 28' + '@types/bun': latest + '@types/jest': '>= 28' + jest: '>= 28' + vitest: '>= 0.32' + peerDependenciesMeta: + '@jest/globals': + optional: true + '@types/bun': + optional: true + '@types/jest': + optional: true + jest: + optional: true + vitest: + optional: true + dependencies: + '@adobe/css-tools': 4.3.3 + '@babel/runtime': 7.23.9 + aria-query: 5.3.0 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + lodash: 4.17.21 + redent: 3.0.0 + vitest: 1.4.0 + dev: true + /@testing-library/preact@3.2.3(preact@10.19.4): resolution: {integrity: sha512-y6Kklp1XK3f1X2fWCbujmJyzkf+1BgLYXNgAx21j9+D4CoqMTz5qC4SQufb1L6q/jxLGACzrQ90ewVOTBvHOfg==} engines: {node: '>= 12'} @@ -7505,8 +7540,8 @@ packages: vue-template-compiler: 2.7.14(vue@2.7.14) dev: true - /@testing-library/vue@8.0.2(vue@3.4.18): - resolution: {integrity: sha512-A8wWX+qQn0o0izpQWnGCpwQt8wAdpsVP8vPP2h5Q/jcGhZ5yKXz9PPUqhQv+45LTFaWlyRf8bArTVaB/KFFd5A==} + /@testing-library/vue@8.0.3(vue@3.4.21): + resolution: {integrity: sha512-wSsbNlZ69ZFQgVlHMtc/ZC/g9BHO7MhyDrd4nHyfEubtMr3kToN/w4/BsSBknGIF8w9UmPbsgbIuq/CbdBHzCA==} engines: {node: '>=14'} peerDependencies: '@vue/compiler-sfc': '>= 3' @@ -7517,10 +7552,8 @@ packages: dependencies: '@babel/runtime': 7.23.9 '@testing-library/dom': 9.3.4 - '@vue/test-utils': 2.4.3(vue@3.4.18) - vue: 3.4.18(typescript@4.9.5) - transitivePeerDependencies: - - '@vue/server-renderer' + '@vue/test-utils': 2.4.5 + vue: 3.4.21(typescript@4.9.5) dev: true /@tokenizer/token@0.3.0: @@ -8382,7 +8415,7 @@ packages: vue: 2.7.14 dev: true - /@vitejs/plugin-vue@4.6.2(vite@5.0.12)(vue@3.4.18): + /@vitejs/plugin-vue@4.6.2(vite@5.0.12)(vue@3.4.21): resolution: {integrity: sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -8390,7 +8423,7 @@ packages: vue: ^3.2.25 dependencies: vite: 5.0.12 - vue: 3.4.18(typescript@4.9.5) + vue: 3.4.21(typescript@4.9.5) dev: true /@vitejs/plugin-vue@5.0.3(vite@5.0.12)(vue@3.4.18): @@ -8420,6 +8453,14 @@ packages: chai: 4.4.1 dev: true + /@vitest/expect@1.4.0: + resolution: {integrity: sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==} + dependencies: + '@vitest/spy': 1.4.0 + '@vitest/utils': 1.4.0 + chai: 4.4.1 + dev: true + /@vitest/runner@0.32.4: resolution: {integrity: sha512-cHOVCkiRazobgdKLnczmz2oaKK9GJOw6ZyRcaPdssO1ej+wzHVIkWiCiNacb3TTYPdzMddYkCgMjZ4r8C0JFCw==} dependencies: @@ -8436,6 +8477,14 @@ packages: pathe: 1.1.2 dev: true + /@vitest/runner@1.4.0: + resolution: {integrity: sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==} + dependencies: + '@vitest/utils': 1.4.0 + p-limit: 5.0.0 + pathe: 1.1.2 + dev: true + /@vitest/snapshot@0.32.4: resolution: {integrity: sha512-IRpyqn9t14uqsFlVI2d7DFMImGMs1Q9218of40bdQQgMePwVdmix33yMNnebXcTzDU5eiV3eUsoxxH5v0x/IQA==} dependencies: @@ -8452,6 +8501,14 @@ packages: pretty-format: 29.7.0 dev: true + /@vitest/snapshot@1.4.0: + resolution: {integrity: sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==} + dependencies: + magic-string: 0.30.7 + pathe: 1.1.2 + pretty-format: 29.7.0 + dev: true + /@vitest/spy@0.32.4: resolution: {integrity: sha512-oA7rCOqVOOpE6rEoXuCOADX7Lla1LIa4hljI2MSccbpec54q+oifhziZIJXxlE/CvI2E+ElhBHzVu0VEvJGQKQ==} dependencies: @@ -8464,6 +8521,12 @@ packages: tinyspy: 2.2.1 dev: true + /@vitest/spy@1.4.0: + resolution: {integrity: sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==} + dependencies: + tinyspy: 2.2.1 + dev: true + /@vitest/utils@0.32.4: resolution: {integrity: sha512-Gwnl8dhd1uJ+HXrYyV0eRqfmk9ek1ASE/LWfTCuWMw+d07ogHqp4hEAV28NiecimK6UY9DpSEPh+pXBA5gtTBg==} dependencies: @@ -8481,6 +8544,15 @@ packages: pretty-format: 29.7.0 dev: true + /@vitest/utils@1.4.0: + resolution: {integrity: sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==} + dependencies: + diff-sequences: 29.6.3 + estree-walker: 3.0.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + dev: true + /@vue/compiler-core@3.4.18: resolution: {integrity: sha512-F7YK8lMK0iv6b9/Gdk15A67wM0KKZvxDxed0RR60C1z9tIJTKta+urs4j0RTN5XqHISzI3etN3mX0uHhjmoqjQ==} dependencies: @@ -8490,17 +8562,34 @@ packages: estree-walker: 2.0.2 source-map-js: 1.0.2 + /@vue/compiler-core@3.4.21: + resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==} + dependencies: + '@babel/parser': 7.23.9 + '@vue/shared': 3.4.21 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.0.2 + dev: true + /@vue/compiler-dom@3.4.18: resolution: {integrity: sha512-24Eb8lcMfInefvQ6YlEVS18w5Q66f4+uXWVA+yb7praKbyjHRNuKVWGuinfSSjM0ZIiPi++QWukhkgznBaqpEA==} dependencies: '@vue/compiler-core': 3.4.18 '@vue/shared': 3.4.18 + /@vue/compiler-dom@3.4.21: + resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==} + dependencies: + '@vue/compiler-core': 3.4.21 + '@vue/shared': 3.4.21 + dev: true + /@vue/compiler-sfc@2.7.14: resolution: {integrity: sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==} dependencies: '@babel/parser': 7.23.9 - postcss: 8.4.27 + postcss: 8.4.35 source-map: 0.6.1 dev: true @@ -8517,12 +8606,33 @@ packages: postcss: 8.4.35 source-map-js: 1.0.2 + /@vue/compiler-sfc@3.4.21: + resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==} + dependencies: + '@babel/parser': 7.23.9 + '@vue/compiler-core': 3.4.21 + '@vue/compiler-dom': 3.4.21 + '@vue/compiler-ssr': 3.4.21 + '@vue/shared': 3.4.21 + estree-walker: 2.0.2 + magic-string: 0.30.7 + postcss: 8.4.35 + source-map-js: 1.0.2 + dev: true + /@vue/compiler-ssr@3.4.18: resolution: {integrity: sha512-hSlv20oUhPxo2UYUacHgGaxtqP0tvFo6ixxxD6JlXIkwzwoZ9eKK6PFQN4hNK/R13JlNyldwWt/fqGBKgWJ6nQ==} dependencies: '@vue/compiler-dom': 3.4.18 '@vue/shared': 3.4.18 + /@vue/compiler-ssr@3.4.21: + resolution: {integrity: sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==} + dependencies: + '@vue/compiler-dom': 3.4.21 + '@vue/shared': 3.4.21 + dev: true + /@vue/devtools-api@6.5.1: resolution: {integrity: sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==} dev: true @@ -8532,12 +8642,25 @@ packages: dependencies: '@vue/shared': 3.4.18 + /@vue/reactivity@3.4.21: + resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==} + dependencies: + '@vue/shared': 3.4.21 + dev: true + /@vue/runtime-core@3.4.18: resolution: {integrity: sha512-7mU9diCa+4e+8/wZ7Udw5pwTH10A11sZ1nldmHOUKJnzCwvZxfJqAtw31mIf4T5H2FsLCSBQT3xgioA9vIjyDQ==} dependencies: '@vue/reactivity': 3.4.18 '@vue/shared': 3.4.18 + /@vue/runtime-core@3.4.21: + resolution: {integrity: sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==} + dependencies: + '@vue/reactivity': 3.4.21 + '@vue/shared': 3.4.21 + dev: true + /@vue/runtime-dom@3.4.18: resolution: {integrity: sha512-2y1Mkzcw1niSfG7z3Qx+2ir9Gb4hdTkZe5p/I8x1aTIKQE0vY0tPAEUPhZm5tx6183gG3D/KwHG728UR0sIufA==} dependencies: @@ -8545,6 +8668,14 @@ packages: '@vue/shared': 3.4.18 csstype: 3.1.3 + /@vue/runtime-dom@3.4.21: + resolution: {integrity: sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==} + dependencies: + '@vue/runtime-core': 3.4.21 + '@vue/shared': 3.4.21 + csstype: 3.1.3 + dev: true + /@vue/server-renderer@3.4.18(vue@3.4.18): resolution: {integrity: sha512-YJd1wa7mzUN3NRqLEsrwEYWyO+PUBSROIGlCc3J/cvn7Zu6CxhNLgXa8Z4zZ5ja5/nviYO79J1InoPeXgwBTZA==} peerDependencies: @@ -8554,9 +8685,23 @@ packages: '@vue/shared': 3.4.18 vue: 3.4.18(typescript@4.9.5) + /@vue/server-renderer@3.4.21(vue@3.4.21): + resolution: {integrity: sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==} + peerDependencies: + vue: 3.4.21 + dependencies: + '@vue/compiler-ssr': 3.4.21 + '@vue/shared': 3.4.21 + vue: 3.4.21(typescript@4.9.5) + dev: true + /@vue/shared@3.4.18: resolution: {integrity: sha512-CxouGFxxaW5r1WbrSmWwck3No58rApXgRSBxrqgnY1K+jk20F6DrXJkHdH9n4HVT+/B6G2CAn213Uq3npWiy8Q==} + /@vue/shared@3.4.21: + resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==} + dev: true + /@vue/test-utils@1.3.0(vue-template-compiler@2.7.14)(vue@2.7.14): resolution: {integrity: sha512-Xk2Xiyj2k5dFb8eYUKkcN9PzqZSppTlx7LaQWBbdA8tqh3jHr/KHX2/YLhNFc/xwDrgeLybqd+4ZCPJSGPIqeA==} peerDependencies: @@ -8570,18 +8715,11 @@ packages: vue-template-compiler: 2.7.14(vue@2.7.14) dev: true - /@vue/test-utils@2.4.3(vue@3.4.18): - resolution: {integrity: sha512-F4K7mF+ad++VlTrxMJVRnenKSJmO6fkQt2wpRDiKDesQMkfpniGWsqEi/JevxGBo2qEkwwjvTUAoiGJLNx++CA==} - peerDependencies: - '@vue/server-renderer': ^3.0.1 - vue: ^3.0.1 - peerDependenciesMeta: - '@vue/server-renderer': - optional: true + /@vue/test-utils@2.4.5: + resolution: {integrity: sha512-oo2u7vktOyKUked36R93NB7mg2B+N7Plr8lxp2JBGwr18ch6EggFjixSCdIVVLkT6Qr0z359Xvnafc9dcKyDUg==} dependencies: js-beautify: 1.14.9 - vue: 3.4.18(typescript@4.9.5) - vue-component-type-helpers: 1.8.27 + vue-component-type-helpers: 2.0.7 dev: true /@vueuse/components@10.7.2(vue@3.4.18): @@ -9215,13 +9353,6 @@ packages: engines: {node: '>=0.10.0'} dev: false - /array-buffer-byte-length@1.0.0: - resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} - dependencies: - call-bind: 1.0.6 - is-array-buffer: 3.0.4 - dev: true - /array-buffer-byte-length@1.0.1: resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} engines: {node: '>= 0.4'} @@ -10705,7 +10836,7 @@ packages: css-select: 4.3.0 parse5: 6.0.1 parse5-htmlparser2-tree-adapter: 6.0.1 - postcss: 8.4.27 + postcss: 8.4.35 pretty-bytes: 5.6.0 dev: true @@ -10767,12 +10898,12 @@ packages: peerDependencies: webpack: ^5.0.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.27) - postcss: 8.4.27 - postcss-modules-extract-imports: 3.0.0(postcss@8.4.27) - postcss-modules-local-by-default: 4.0.3(postcss@8.4.27) - postcss-modules-scope: 3.0.0(postcss@8.4.27) - postcss-modules-values: 4.0.0(postcss@8.4.27) + icss-utils: 5.1.0(postcss@8.4.35) + postcss: 8.4.35 + postcss-modules-extract-imports: 3.0.0(postcss@8.4.35) + postcss-modules-local-by-default: 4.0.3(postcss@8.4.35) + postcss-modules-scope: 3.0.0(postcss@8.4.35) + postcss-modules-values: 4.0.0(postcss@8.4.35) postcss-value-parser: 4.2.0 semver: 7.5.4 webpack: 5.76.1(esbuild@0.14.22) @@ -11017,12 +11148,12 @@ packages: /deep-equal@2.2.2: resolution: {integrity: sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==} dependencies: - array-buffer-byte-length: 1.0.0 + array-buffer-byte-length: 1.0.1 call-bind: 1.0.6 es-get-iterator: 1.1.3 get-intrinsic: 1.2.4 is-arguments: 1.1.1 - is-array-buffer: 3.0.2 + is-array-buffer: 3.0.4 is-date-object: 1.0.5 is-regex: 1.1.4 is-shared-array-buffer: 1.0.2 @@ -11030,11 +11161,11 @@ packages: object-is: 1.1.5 object-keys: 1.1.1 object.assign: 4.1.4 - regexp.prototype.flags: 1.5.0 + regexp.prototype.flags: 1.5.1 side-channel: 1.0.4 which-boxed-primitive: 1.0.2 which-collection: 1.0.1 - which-typed-array: 1.1.11 + which-typed-array: 1.1.14 dev: true /deep-is@0.1.4: @@ -13938,13 +14069,13 @@ packages: dependencies: safer-buffer: 2.1.2 - /icss-utils@5.1.0(postcss@8.4.27): + /icss-utils@5.1.0(postcss@8.4.35): resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.27 + postcss: 8.4.35 dev: true /ieee754@1.2.1: @@ -14160,14 +14291,6 @@ packages: has-tostringtag: 1.0.2 dev: true - /is-array-buffer@3.0.2: - resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} - dependencies: - call-bind: 1.0.6 - get-intrinsic: 1.2.4 - is-typed-array: 1.1.12 - dev: true - /is-array-buffer@3.0.4: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} @@ -14767,6 +14890,10 @@ packages: /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + /js-tokens@8.0.3: + resolution: {integrity: sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==} + dev: true + /js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true @@ -17903,45 +18030,45 @@ packages: postcss: 8.4.5 dev: true - /postcss-modules-extract-imports@3.0.0(postcss@8.4.27): + /postcss-modules-extract-imports@3.0.0(postcss@8.4.35): resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.27 + postcss: 8.4.35 dev: true - /postcss-modules-local-by-default@4.0.3(postcss@8.4.27): + /postcss-modules-local-by-default@4.0.3(postcss@8.4.35): resolution: {integrity: sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.27) - postcss: 8.4.27 + icss-utils: 5.1.0(postcss@8.4.35) + postcss: 8.4.35 postcss-selector-parser: 6.0.13 postcss-value-parser: 4.2.0 dev: true - /postcss-modules-scope@3.0.0(postcss@8.4.27): + /postcss-modules-scope@3.0.0(postcss@8.4.35): resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.27 + postcss: 8.4.35 postcss-selector-parser: 6.0.13 dev: true - /postcss-modules-values@4.0.0(postcss@8.4.27): + /postcss-modules-values@4.0.0(postcss@8.4.35): resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.27) - postcss: 8.4.27 + icss-utils: 5.1.0(postcss@8.4.35) + postcss: 8.4.35 dev: true /postcss-nesting@10.2.0(postcss@8.4.27): @@ -18764,15 +18891,6 @@ packages: resolution: {integrity: sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==} dev: true - /regexp.prototype.flags@1.5.0: - resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.6 - define-properties: 1.2.1 - functions-have-names: 1.2.3 - dev: true - /regexp.prototype.flags@1.5.1: resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} engines: {node: '>= 0.4'} @@ -18862,7 +18980,7 @@ packages: adjust-sourcemap-loader: 4.0.0 convert-source-map: 1.9.0 loader-utils: 2.0.4 - postcss: 8.4.27 + postcss: 8.4.35 source-map: 0.6.1 dev: true @@ -20227,6 +20345,12 @@ packages: acorn: 8.11.3 dev: true + /strip-literal@2.0.0: + resolution: {integrity: sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==} + dependencies: + js-tokens: 8.0.3 + dev: true + /strnum@1.0.5: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} dev: true @@ -21579,6 +21703,27 @@ packages: - terser dev: true + /vite-node@1.4.0: + resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + pathe: 1.1.2 + picocolors: 1.0.0 + vite: 5.0.12 + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vite-plugin-singlefile@0.5.1(vite@5.0.12): resolution: {integrity: sha512-yA9lWd6bSet0Br4/s34YPNnVBlDl2MbxlHDRrLrBCncD7q+HO5GGsw29Ymp+ydZ3eb4UU2GECgX2MJZW+qnoeQ==} peerDependencies: @@ -21644,7 +21789,7 @@ packages: dependencies: '@types/node': 20.4.5 esbuild: 0.18.20 - postcss: 8.4.27 + postcss: 8.4.35 rollup: 3.29.4 optionalDependencies: fsevents: 2.3.3 @@ -21884,6 +22029,61 @@ packages: - terser dev: true + /vitest@1.4.0: + resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 1.4.0 + '@vitest/ui': 1.4.0 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@vitest/expect': 1.4.0 + '@vitest/runner': 1.4.0 + '@vitest/snapshot': 1.4.0 + '@vitest/spy': 1.4.0 + '@vitest/utils': 1.4.0 + acorn-walk: 8.3.2 + chai: 4.4.1 + debug: 4.3.4 + execa: 8.0.1 + local-pkg: 0.5.0 + magic-string: 0.30.7 + pathe: 1.1.2 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 2.0.0 + tinybench: 2.6.0 + tinypool: 0.8.2 + vite: 5.0.12 + vite-node: 1.4.0 + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vlq@1.0.1: resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==} dev: true @@ -21893,8 +22093,8 @@ packages: engines: {node: '>=0.10.0'} dev: true - /vue-component-type-helpers@1.8.27: - resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==} + /vue-component-type-helpers@2.0.7: + resolution: {integrity: sha512-7e12Evdll7JcTIocojgnCgwocX4WzIYStGClBQ+QuWPinZo/vQolv2EMq4a3lg16TKfwWafLimG77bxb56UauA==} dev: true /vue-demi@0.14.5(vue@3.4.18): @@ -21958,6 +22158,22 @@ packages: '@vue/shared': 3.4.18 typescript: 4.9.5 + /vue@3.4.21(typescript@4.9.5): + resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@vue/compiler-dom': 3.4.21 + '@vue/compiler-sfc': 3.4.21 + '@vue/runtime-dom': 3.4.21 + '@vue/server-renderer': 3.4.21(vue@3.4.21) + '@vue/shared': 3.4.21 + typescript: 4.9.5 + dev: true + /w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} dev: false @@ -22218,17 +22434,6 @@ packages: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} dev: true - /which-typed-array@1.1.11: - resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==} - engines: {node: '>= 0.4'} - dependencies: - available-typed-arrays: 1.0.6 - call-bind: 1.0.6 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.2 - dev: true - /which-typed-array@1.1.14: resolution: {integrity: sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==} engines: {node: '>= 0.4'} From 57e8edc1e3ea20af1fc68d401ce432cacdb0d49f Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Fri, 22 Mar 2024 14:31:08 +0100 Subject: [PATCH 10/27] update tests --- .../lucide-react-native.spec.tsx.snap | 291 +----------------- .../tests/lucide-react-native.spec.tsx | 4 +- 2 files changed, 8 insertions(+), 287 deletions(-) diff --git a/packages/lucide-react-native/tests/__snapshots__/lucide-react-native.spec.tsx.snap b/packages/lucide-react-native/tests/__snapshots__/lucide-react-native.spec.tsx.snap index 14af0d45ef..11cbcf6c75 100644 --- a/packages/lucide-react-native/tests/__snapshots__/lucide-react-native.spec.tsx.snap +++ b/packages/lucide-react-native/tests/__snapshots__/lucide-react-native.spec.tsx.snap @@ -75,6 +75,7 @@ exports[`Using lucide icon components > should duplicate properties to children stroke-width="10" stroke-linecap="round" stroke-linejoin="round" + data-testid="multiple-children" width="18" height="18" x="3" @@ -87,6 +88,7 @@ exports[`Using lucide icon components > should duplicate properties to children stroke-width="10" stroke-linecap="round" stroke-linejoin="round" + data-testid="multiple-children" d="M3 9h18" > @@ -95,6 +97,7 @@ exports[`Using lucide icon components > should duplicate properties to children stroke-width="10" stroke-linecap="round" stroke-linejoin="round" + data-testid="multiple-children" d="M3 15h18" > @@ -103,6 +106,7 @@ exports[`Using lucide icon components > should duplicate properties to children stroke-width="10" stroke-linecap="round" stroke-linejoin="round" + data-testid="multiple-children" d="M9 3v18" > @@ -111,6 +115,7 @@ exports[`Using lucide icon components > should duplicate properties to children stroke-width="10" stroke-linecap="round" stroke-linejoin="round" + data-testid="multiple-children" d="M15 3v18" > @@ -232,289 +237,3 @@ exports[`Using lucide icon components > should render an component 1`] = ` `; - -exports[`Using lucide icon components > should work with a single child component 1`] = ` - - - - - - - - - - - - - - - - - - - - - - - - -`; - -exports[`Using lucide icon components > should work with several children components 1`] = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -`; diff --git a/packages/lucide-react-native/tests/lucide-react-native.spec.tsx b/packages/lucide-react-native/tests/lucide-react-native.spec.tsx index 7dcfd3abf5..2cc3fc7ed7 100644 --- a/packages/lucide-react-native/tests/lucide-react-native.spec.tsx +++ b/packages/lucide-react-native/tests/lucide-react-native.spec.tsx @@ -112,6 +112,8 @@ describe('Using lucide icon components', () => { expect(child1).toEqual(getByTestId(childId1)); expect(child2).toEqual(getByTestId(childId2)); expect(container.innerHTML).toMatchSnapshot(); + + cleanup() }); it('should duplicate properties to children components', () => { @@ -129,7 +131,7 @@ describe('Using lucide icon components', () => { strokeWidth={strokeWidth} />, ); - const { children = [] } = getByTestId(testId) as unknown as { children: HTMLCollection }; + const { children = [] } = container.firstChild for (let i = 0; i < children.length; i++) { const child = children[i]; expect(child.getAttribute('fill')).toBe(fill); From 6a9bd80fc82b68e7bb646ae8a2a46ad80bb724fb Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Fri, 22 Mar 2024 14:31:31 +0100 Subject: [PATCH 11/27] Update tests --- .../useIconComponent.spec.tsx.snap | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 packages/lucide-react-native/tests/__snapshots__/useIconComponent.spec.tsx.snap diff --git a/packages/lucide-react-native/tests/__snapshots__/useIconComponent.spec.tsx.snap b/packages/lucide-react-native/tests/__snapshots__/useIconComponent.spec.tsx.snap new file mode 100644 index 0000000000..aeae002f12 --- /dev/null +++ b/packages/lucide-react-native/tests/__snapshots__/useIconComponent.spec.tsx.snap @@ -0,0 +1,158 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Using iconNode component generator > should create a component from an iconNode 1`] = ` + + + + + + +`; + +exports[`Using iconNode component generator > should create multiple components 1`] = ` +
+ + + + + + + + + + + + + +
+`; From 34e647d8f1a6e0a03a29c81a8572e091cc182a36 Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Fri, 22 Mar 2024 14:31:55 +0100 Subject: [PATCH 12/27] Enable Svelte component --- packages/lucide-svelte/package.json | 2 ++ packages/lucide-svelte/src/Icon.svelte | 12 ++++++++++-- .../tests/__snapshots__/lucide-svelte.spec.ts.snap | 8 ++++---- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/lucide-svelte/package.json b/packages/lucide-svelte/package.json index 3b9bab8eb4..325ae5ba69 100644 --- a/packages/lucide-svelte/package.json +++ b/packages/lucide-svelte/package.json @@ -52,10 +52,12 @@ "build:package": "svelte-package --input ./src", "build:license": "node ./scripts/appendBlockComments.mjs", "test": "vitest run", + "test:watch": "vitest watch", "version": "pnpm version --git-tag-version=false" }, "devDependencies": { "@lucide/build-icons": "workspace:*", + "@lucide/shared": "workspace:*", "@sveltejs/package": "^2.2.3", "@sveltejs/vite-plugin-svelte": "^2.4.2", "@testing-library/jest-dom": "^6.1.4", diff --git a/packages/lucide-svelte/src/Icon.svelte b/packages/lucide-svelte/src/Icon.svelte index 4249751b40..64bcb73eab 100644 --- a/packages/lucide-svelte/src/Icon.svelte +++ b/packages/lucide-svelte/src/Icon.svelte @@ -1,8 +1,9 @@ - - -``` - -You can pass additional props to adjust the icon. - -```sv - - - -``` - -### Available props - -| name | type | default | -| ------------- | -------- | ------------ | -| `size` | _Number_ | 24 | -| `color` | _String_ | currentColor | -| `strokeWidth` | _Number_ | 2 | -| `*` | _String_ | - | - -- All SVGProps are available to style the svgs. See the list of SVG Presentation Attributes on [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation) - -### Example of custom props - -```sv - - - -``` - -This results a filled phone icon. - -### One generic icon component - -It is possible to create one generic icon component to load icons. - -> :warning: Example below importing all EsModules, caution using this example, not recommended when you bundle your application,the build size will grow strongly. Because it will import all the icons. - -#### Icon Component Example - -```svelte - - - -``` - -##### Then you can use it like this - -```svelte - - - -``` +For full documentation, visit [lucide.dev](https://lucide.dev/guide/packages/lucide-svelte) diff --git a/packages/lucide-vue-next/README.md b/packages/lucide-vue-next/README.md index 565dc7c490..9e6688032e 100644 --- a/packages/lucide-vue-next/README.md +++ b/packages/lucide-vue-next/README.md @@ -18,89 +18,6 @@ or npm install lucide-vue-next ``` -## How to use +## Documentation -It's build with ESmodules so it's completely tree-shakable. -Each icon can be imported as a vue component. - -### Example - -You can pass additional props to adjust the icon. - -```vue - - - -``` - -### Props - -| name | type | default | -| ----------------------- | --------- | ------------ | -| `size` | *number* | 24 | -| `color` | *string* | currentColor | -| `stroke-width` | *number* | 2 | -| `absolute-stroke-width` | *boolean* | false | -| `default-class` | *string* | lucide-icon | - -### Custom props - -You can also pass custom props that will be added in the svg as attributes. - -```vue - -``` - -### One generic icon component - -It is possible to create one generic icon component to load icons. - -> :warning: Example below importing all EsModules, caution using this example, not recommended when you using bundlers, your application build size will grow strongly. - -#### Icon Component Example - -```vue - - - -``` - -##### Then you can use it like this - -```vue - -``` +For full documentation, visit [lucide.dev](https://lucide.dev/guide/packages/lucide-vue-next) diff --git a/packages/lucide/README.md b/packages/lucide/README.md index b75cd96374..4623e6bc71 100644 --- a/packages/lucide/README.md +++ b/packages/lucide/README.md @@ -26,99 +26,6 @@ yarn add lucide ``` -## Usage +## Documentation -### With unpkg - -Here is a complete example with unpkg - -```html - - - - - - - - - -``` - -### With ESModules - -To reduce bundle size, lucide is built to be fully tree-shakable. -The `createIcons` function will search for HTMLElements with the attribute `data-lucide` and replace it with the svg from the given icon name. - -```html - - -``` - -```js -import { createIcons, icons } from 'lucide'; - -// Caution, this will import all the icons and bundle them. -createIcons({ icons }); - -// Recommended way, to include only the icons you need. -import { createIcons, Menu, ArrowRight, Globe } from 'lucide'; - -createIcons({ - icons: { - Menu, - ArrowRight, - Globe - } -}); -``` - -#### Additional Options - -In the `createIcons` function you can pass some extra parameters to adjust the `nameAttr` or add custom attributes like for example classes. - -Here is a full example: - -```js -import { createIcons } from 'lucide'; - -createIcons({ - attrs: { - class: ['my-custom-class', 'icon'], - 'stroke-width': 1, - stroke: '#333' - }, - nameAttr: 'data-lucide' // attribute for the icon name. -}); -``` - -#### Treeshake the library, only use the icons you use - -```js -import { createIcons, Menu, ArrowRight, Globe } from 'lucide'; - -createIcons({ - icons: { - Menu, - ArrowRight, - Globe - } -}); -``` - -#### Custom Element binding - -```js -import { createElement, Menu } from 'lucide'; - -const menuIcon = createElement(Menu); // Returns HTMLElement (svg) - -// set custom attributes with browser native functions -menuIcon.setAttribute('stroke', '#333'); -menuIcon.classList.add('my-icon-class'); - -// Append HTMLElement in webpage -const myApp = document.getElementById('app'); -myApp.appendChild(menuIcon); -``` +For full documentation, visit [lucide.dev](https://lucide.dev/guide/packages/lucide) From adc4d476c83d946802b2f8c3e21fac641e15fb0c Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Mon, 1 Apr 2024 11:44:37 +0200 Subject: [PATCH 17/27] add docs --- docs/guide/packages/lucide-preact.md | 10 ++--- docs/guide/packages/lucide-react-native.md | 38 ++++++++++++++++ docs/guide/packages/lucide-react.md | 38 ++++++++++++++++ docs/guide/packages/lucide-solid.md | 23 ++++++++++ docs/guide/packages/lucide-svelte.md | 23 ++++++++++ docs/guide/packages/lucide-vue-next.md | 50 ++++++++++++++++++++-- docs/guide/packages/lucide.md | 2 - 7 files changed, 173 insertions(+), 11 deletions(-) diff --git a/docs/guide/packages/lucide-preact.md b/docs/guide/packages/lucide-preact.md index b3e2cce1ac..f113fe0eaa 100644 --- a/docs/guide/packages/lucide-preact.md +++ b/docs/guide/packages/lucide-preact.md @@ -69,12 +69,12 @@ const App = () => { ## With Lucide lab or custom icons -Lucide lab is a collection of icons that are not part of the official lucide library. +Lucide Lab is a collection of icons that are not part of the official lucide library. They can be used by using the `useIconComponent` or the `Icon` component. -For both methods all props like regular lucide icons can be passed to adjust the icon appearance. +For both methods, all props like regular lucide icons can be passed to adjust the icon appearance. -## Using `useIconComponent` +### Using `useIconComponent` This creates multiple icons based on the iconNodes passed in an object and returns an object with components that can be used as Lucide components. @@ -92,7 +92,7 @@ const App = () => ( ); ``` -## Using the Icon component +### Using the `Icon` component This creates a single icon based on the iconNode passed and renders a Lucide icon component. @@ -101,7 +101,7 @@ import { Icon } from 'lucide-preact'; import { burger } from '@lucide/lab'; const App = () => ( - + ); ``` diff --git a/docs/guide/packages/lucide-react-native.md b/docs/guide/packages/lucide-react-native.md index 112e33230e..771af33895 100644 --- a/docs/guide/packages/lucide-react-native.md +++ b/docs/guide/packages/lucide-react-native.md @@ -61,6 +61,44 @@ const App = () => { }; ``` +## With Lucide lab or custom icons + +Lucide Lab is a collection of icons that are not part of the official lucide library. + +They can be used by using the `useIconComponent` or the `Icon` component. +For both methods, all props like regular lucide icons can be passed to adjust the icon appearance. + +### Using `useIconComponent` + +This creates multiple icons based on the iconNodes passed in an object and returns an object with components that can be used as Lucide components. + +```jsx +import { useIconComponent } from 'lucide-react-native'; +import { burger, sausage } from '@lucide/lab'; + +const { Burger, Pizza } = useIconComponent({ burger, sausage }) + +const App = () => ( + <> + + + +); +``` + +### Using the `Icon` component + +This creates a single icon based on the iconNode passed and renders a Lucide icon component. + +```jsx +import { Icon } from 'lucide-react-native'; +import { burger } from '@lucide/lab'; + +const App = () => ( + +); +``` + ## One generic icon component It is possible to create one generic icon component to load icons, but it is not recommended. diff --git a/docs/guide/packages/lucide-react.md b/docs/guide/packages/lucide-react.md index 87f78f3b98..7705eafb27 100644 --- a/docs/guide/packages/lucide-react.md +++ b/docs/guide/packages/lucide-react.md @@ -61,6 +61,44 @@ const App = () => { }; ``` +## With Lucide lab or custom icons + +Lucide Lab is a collection of icons that are not part of the official lucide library. + +They can be used by using the `useIconComponent` or the `Icon` component. +For both methods, all props like regular lucide icons can be passed to adjust the icon appearance. + +### Using `useIconComponent` + +This creates multiple icons based on the iconNodes passed in an object and returns an object with components that can be used as Lucide components. + +```jsx +import { useIconComponent } from 'lucide-react'; +import { burger, sausage } from '@lucide/lab'; + +const { Burger, Pizza } = useIconComponent({ burger, sausage }) + +const App = () => ( + <> + + + +); +``` + +### Using the `Icon` component + +This creates a single icon based on the iconNode passed and renders a Lucide icon component. + +```jsx +import { Icon } from 'lucide-react'; +import { burger } from '@lucide/lab'; + +const App = () => ( + +); +``` + ## One generic icon component It is possible to create one generic icon component to load icons, but it is not recommended. diff --git a/docs/guide/packages/lucide-solid.md b/docs/guide/packages/lucide-solid.md index 94087df819..7eacbb5e8a 100644 --- a/docs/guide/packages/lucide-solid.md +++ b/docs/guide/packages/lucide-solid.md @@ -61,6 +61,29 @@ const App = () => { }; ``` +## With Lucide lab or custom icons + +Lucide Lab is a collection of icons that are not part of the official lucide library. + +They can be used by using the `Icon` component. +All props like the regular Lucide icons can be passed to adjust the icon appearance. + +### Using the `Icon` component + +This creates a single icon based on the iconNode passed and renders a Lucide icon component. + +```jsx +import { Icon } from 'lucide-solid'; +import { burger, sausage } from '@lucide/lab'; + +const App = () => ( + <> + + + +); +``` + ## One generic icon component It is possible to create one generic icon component to load icons. It's not recommended. diff --git a/docs/guide/packages/lucide-svelte.md b/docs/guide/packages/lucide-svelte.md index 184aa2076c..3829c65421 100644 --- a/docs/guide/packages/lucide-svelte.md +++ b/docs/guide/packages/lucide-svelte.md @@ -166,6 +166,29 @@ The package includes type definitions for all icons. This is useful if you want For more details about typing the `svelte:component` directive, see the [Svelte documentation](https://svelte.dev/docs/typescript#types-componenttype). +## With Lucide lab or custom icons + +Lucide Lab is a collection of icons that are not part of the official lucide library. + +They can be used by using the `Icon` component. +All props like the regular Lucide icons can be passed to adjust the icon appearance. + +### Using the `Icon` component + +This creates a single icon based on the iconNode passed and renders a Lucide icon component. + +```jsx +import { Icon } from 'lucide-svelte'; +import { burger, sausage } from '@lucide/lab'; + +const App = () => ( + <> + + + +); +``` + ## One generic icon component It is possible to create one generic icon component to load icons, but it is not recommended. diff --git a/docs/guide/packages/lucide-vue-next.md b/docs/guide/packages/lucide-vue-next.md index acf1c2448b..18b86604fe 100644 --- a/docs/guide/packages/lucide-vue-next.md +++ b/docs/guide/packages/lucide-vue-next.md @@ -37,16 +37,16 @@ Each icon can be imported as a Vue component, which renders an inline SVG Elemen You can pass additional props to adjust the icon. ```vue + + - - ``` ## Props @@ -69,6 +69,48 @@ To customize the appearance of an icon, you can pass custom properties as props ``` +## With Lucide lab or custom icons + +Lucide Lab is a collection of icons that are not part of the official lucide library. + +They can be used by using the `useIconComponent` or the `Icon` component. +For both methods, all props like regular lucide icons can be passed to adjust the icon appearance. + +### Using `useIconComponent` + +This creates multiple icons based on the iconNodes passed in an object and returns an object with components that can be used as Lucide components. + +```vue + + + +``` + +### Using the `Icon` component + +This creates a single icon based on the iconNode passed and renders a Lucide icon component. + +```vue + + + +``` + ## One generic icon component It is possible to create one generic icon component to load icons, but it is not recommended. diff --git a/docs/guide/packages/lucide.md b/docs/guide/packages/lucide.md index 9aeb5e1ae4..b3fc734ccb 100644 --- a/docs/guide/packages/lucide.md +++ b/docs/guide/packages/lucide.md @@ -138,8 +138,6 @@ You can use them in the same way as the official icons. -```sh - ```js import { burger } from '@lucide/lab'; From 23710e445f3c2bfd7a6d800c1c1366308e71cf0a Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Mon, 1 Apr 2024 12:05:14 +0200 Subject: [PATCH 18/27] Update tests --- packages/lucide-preact/src/lucide-preact.ts | 1 + packages/lucide-preact/tests/Icon.spec.tsx | 2 +- .../src/lucide-react-native.ts | 7 +- .../lucide-react-native/tests/Icon.spec.tsx | 35 ++++++ .../tests/__snapshots__/Icon.spec.tsx.snap | 48 ++++++++ .../tests/useIconComponent.spec.tsx | 2 +- packages/lucide-react/src/lucide-react.ts | 1 + packages/lucide-react/tests/Icon.spec.tsx | 2 +- .../tests/useIconComponent.spec.tsx | 2 +- packages/lucide-solid/tests/Icon.spec.tsx | 33 ++++++ .../tests/__snapshots__/Icon.spec.tsx.snap | 33 ++++++ .../useIconComponent.spec.tsx.snap | 109 ++++++++++++++++++ packages/lucide-solid/tests/testIconNodes.ts | 22 ++++ .../tests/useIconComponent.spec.tsx | 29 +++++ packages/lucide-svelte/src/Icon.svelte | 2 +- packages/lucide-svelte/tests/Icon.spec.ts | 33 ++++++ .../tests/__snapshots__/Icon.spec.ts.snap | 36 ++++++ packages/lucide-svelte/tests/testIconNodes.ts | 21 ++++ packages/lucide-vue-next/tests/Icon.spec.ts | 33 ++++++ .../tests/__snapshots__/Icon.spec.ts.snap | 29 +++++ 20 files changed, 472 insertions(+), 8 deletions(-) create mode 100644 packages/lucide-react-native/tests/Icon.spec.tsx create mode 100644 packages/lucide-react-native/tests/__snapshots__/Icon.spec.tsx.snap create mode 100644 packages/lucide-solid/tests/Icon.spec.tsx create mode 100644 packages/lucide-solid/tests/__snapshots__/Icon.spec.tsx.snap create mode 100644 packages/lucide-solid/tests/__snapshots__/useIconComponent.spec.tsx.snap create mode 100644 packages/lucide-solid/tests/testIconNodes.ts create mode 100644 packages/lucide-solid/tests/useIconComponent.spec.tsx create mode 100644 packages/lucide-svelte/tests/Icon.spec.ts create mode 100644 packages/lucide-svelte/tests/__snapshots__/Icon.spec.ts.snap create mode 100644 packages/lucide-svelte/tests/testIconNodes.ts create mode 100644 packages/lucide-vue-next/tests/Icon.spec.ts create mode 100644 packages/lucide-vue-next/tests/__snapshots__/Icon.spec.ts.snap diff --git a/packages/lucide-preact/src/lucide-preact.ts b/packages/lucide-preact/src/lucide-preact.ts index e7f28ebe70..d648cd6af4 100644 --- a/packages/lucide-preact/src/lucide-preact.ts +++ b/packages/lucide-preact/src/lucide-preact.ts @@ -4,3 +4,4 @@ export * from './aliases'; export { default as createLucideIcon } from './createLucideIcon'; export { default as useIconComponent } from './useIconComponent'; +export { default as Icon } from './Icon'; diff --git a/packages/lucide-preact/tests/Icon.spec.tsx b/packages/lucide-preact/tests/Icon.spec.tsx index 778e56a277..f3a0810e1b 100644 --- a/packages/lucide-preact/tests/Icon.spec.tsx +++ b/packages/lucide-preact/tests/Icon.spec.tsx @@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest'; import { render } from '@testing-library/preact'; import { airVent } from './testIconNodes'; -import Icon from '../src/Icon'; +import { Icon } from '../src/lucide-preact'; describe('Using Icon Component', () => { it('should render icon based on a iconNode', async () => { diff --git a/packages/lucide-react-native/src/lucide-react-native.ts b/packages/lucide-react-native/src/lucide-react-native.ts index 3d55f91f7f..53d94599ec 100644 --- a/packages/lucide-react-native/src/lucide-react-native.ts +++ b/packages/lucide-react-native/src/lucide-react-native.ts @@ -2,6 +2,7 @@ export * from './icons'; export * as icons from './icons'; export * from './aliases'; export * from './types' -export { - default as createLucideIcon, -} from './createLucideIcon'; + +export { default as createLucideIcon } from './createLucideIcon'; +export { default as useIconComponent } from './useIconComponent'; +export { default as Icon } from './Icon'; diff --git a/packages/lucide-react-native/tests/Icon.spec.tsx b/packages/lucide-react-native/tests/Icon.spec.tsx new file mode 100644 index 0000000000..cc0383d61e --- /dev/null +++ b/packages/lucide-react-native/tests/Icon.spec.tsx @@ -0,0 +1,35 @@ +import { describe, it, expect, vi } from 'vitest'; +import { render } from '@testing-library/react'; + +import { airVent } from './testIconNodes'; +import { Icon } from '../src/lucide-react-native'; + +vi.mock('react-native-svg'); + +describe('Using Icon Component', () => { + it('should render icon based on a iconNode', async () => { + const { container } = render( + , + ); + + expect( container.firstChild ).toBeDefined() + }); + + it('should render icon and match snapshot', async () => { + const { container } = render( + , + ); + + expect( container.firstChild ).toMatchSnapshot(); + }); +}) diff --git a/packages/lucide-react-native/tests/__snapshots__/Icon.spec.tsx.snap b/packages/lucide-react-native/tests/__snapshots__/Icon.spec.tsx.snap new file mode 100644 index 0000000000..fd704741d4 --- /dev/null +++ b/packages/lucide-react-native/tests/__snapshots__/Icon.spec.tsx.snap @@ -0,0 +1,48 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Using Icon Component > should render icon and match snapshot 1`] = ` + + + + + + +`; diff --git a/packages/lucide-react-native/tests/useIconComponent.spec.tsx b/packages/lucide-react-native/tests/useIconComponent.spec.tsx index 3d78690c61..6765882361 100644 --- a/packages/lucide-react-native/tests/useIconComponent.spec.tsx +++ b/packages/lucide-react-native/tests/useIconComponent.spec.tsx @@ -1,5 +1,5 @@ import { describe, it, expect, vi } from 'vitest'; -import useIconComponent from '../src/useIconComponent'; +import { useIconComponent } from '../src/lucide-react-native'; import { airVent, coffee } from './testIconNodes'; import { render } from '@testing-library/react'; diff --git a/packages/lucide-react/src/lucide-react.ts b/packages/lucide-react/src/lucide-react.ts index 6636c22592..98cd424a0b 100644 --- a/packages/lucide-react/src/lucide-react.ts +++ b/packages/lucide-react/src/lucide-react.ts @@ -4,4 +4,5 @@ export * from './aliases'; export * from './types'; export { default as createLucideIcon } from './createLucideIcon'; +export { default as useIconComponent } from './useIconComponent'; export { default as Icon } from './Icon'; diff --git a/packages/lucide-react/tests/Icon.spec.tsx b/packages/lucide-react/tests/Icon.spec.tsx index 6b9a90a66d..25d9d2a286 100644 --- a/packages/lucide-react/tests/Icon.spec.tsx +++ b/packages/lucide-react/tests/Icon.spec.tsx @@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest'; import { render } from '@testing-library/react'; import { airVent } from './testIconNodes'; -import Icon from '../src/Icon'; +import { Icon } from '../src/lucide-react'; describe('Using Icon Component', () => { it('should render icon based on a iconNode', async () => { diff --git a/packages/lucide-react/tests/useIconComponent.spec.tsx b/packages/lucide-react/tests/useIconComponent.spec.tsx index 4bb1af0cb1..53720d06d0 100644 --- a/packages/lucide-react/tests/useIconComponent.spec.tsx +++ b/packages/lucide-react/tests/useIconComponent.spec.tsx @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import useIconComponent from '../src/useIconComponent'; +import { useIconComponent } from '../src/lucide-react'; import { airVent, coffee } from './testIconNodes'; import { render } from '@testing-library/react'; diff --git a/packages/lucide-solid/tests/Icon.spec.tsx b/packages/lucide-solid/tests/Icon.spec.tsx new file mode 100644 index 0000000000..eda5f8a485 --- /dev/null +++ b/packages/lucide-solid/tests/Icon.spec.tsx @@ -0,0 +1,33 @@ +import { describe, it, expect } from 'vitest'; +import { render, cleanup } from '@solidjs/testing-library'; + +import { airVent } from './testIconNodes'; +import { Icon } from '../src/lucide-solid'; + +describe('Using Icon Component', () => { + it('should render icon based on a iconNode', async () => { + const { container } = render(() => + , + ); + + expect( container.firstChild ).toBeDefined() + }); + + it('should render icon and match snapshot', async () => { + const { container } = render(() => + , + ); + + expect( container.firstChild ).toMatchSnapshot(); + }); +}) diff --git a/packages/lucide-solid/tests/__snapshots__/Icon.spec.tsx.snap b/packages/lucide-solid/tests/__snapshots__/Icon.spec.tsx.snap new file mode 100644 index 0000000000..3ec6f852d3 --- /dev/null +++ b/packages/lucide-solid/tests/__snapshots__/Icon.spec.tsx.snap @@ -0,0 +1,33 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Using Icon Component > should render icon and match snapshot 1`] = ` + + + + + + +`; diff --git a/packages/lucide-solid/tests/__snapshots__/useIconComponent.spec.tsx.snap b/packages/lucide-solid/tests/__snapshots__/useIconComponent.spec.tsx.snap new file mode 100644 index 0000000000..2c2a52db67 --- /dev/null +++ b/packages/lucide-solid/tests/__snapshots__/useIconComponent.spec.tsx.snap @@ -0,0 +1,109 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Using iconNode component generator > should create a component from an iconNode 1`] = ` + + + + + + +`; + +exports[`Using iconNode component generator > should create multiple components 1`] = ` +
+ + + + + + + + + + + + + +
+`; diff --git a/packages/lucide-solid/tests/testIconNodes.ts b/packages/lucide-solid/tests/testIconNodes.ts new file mode 100644 index 0000000000..e48a1b8332 --- /dev/null +++ b/packages/lucide-solid/tests/testIconNodes.ts @@ -0,0 +1,22 @@ +import { IconNode } from "../src/types" + +export const airVent: IconNode = [ + [ + 'path', + { + d: 'M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2', + key: 'larmp2', + }, + ], + ['path', { d: 'M6 8h12', key: '6g4wlu' }], + ['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }], + ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }] +] + +export const coffee: IconNode = [ + ['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }], + ['path', { d: 'M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z', key: '1bxrl0' }], + ['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }], + ['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }], + ['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }], +] diff --git a/packages/lucide-solid/tests/useIconComponent.spec.tsx b/packages/lucide-solid/tests/useIconComponent.spec.tsx new file mode 100644 index 0000000000..5696723411 --- /dev/null +++ b/packages/lucide-solid/tests/useIconComponent.spec.tsx @@ -0,0 +1,29 @@ +import { describe, it, expect } from 'vitest'; +import { useIconComponent } from '../src/lucide-solid'; +import { airVent, coffee } from './testIconNodes'; +import { render } from '@solidjs/testing-library'; + +describe('Using iconNode component generator', () => { + it('should create a component from an iconNode', () => { + const { AirVent } = useIconComponent({ airVent }) + + const { container } = render(() => ); + + expect( container.firstChild ).toMatchSnapshot(); + expect( container.firstChild ).toBeDefined(); + }); + + it('should create multiple components', () => { + const { AirVent, Coffee } = useIconComponent({ airVent, coffee }) + + const { container } = render(() => + <> + + + + ); + + expect( container ).toMatchSnapshot(); + expect( container ).toBeDefined(); + }); +}) diff --git a/packages/lucide-svelte/src/Icon.svelte b/packages/lucide-svelte/src/Icon.svelte index 64bcb73eab..6395c855cc 100644 --- a/packages/lucide-svelte/src/Icon.svelte +++ b/packages/lucide-svelte/src/Icon.svelte @@ -3,7 +3,7 @@ import defaultAttributes from './defaultAttributes' import type { IconNode } from './types'; - export let name: string | undefined + export let name: string | undefined = undefined export let color = 'currentColor' export let size: number | string = 24 export let strokeWidth: number | string = 2 diff --git a/packages/lucide-svelte/tests/Icon.spec.ts b/packages/lucide-svelte/tests/Icon.spec.ts new file mode 100644 index 0000000000..e7756c9c24 --- /dev/null +++ b/packages/lucide-svelte/tests/Icon.spec.ts @@ -0,0 +1,33 @@ +import { describe, it, expect } from 'vitest'; +import { render } from '@testing-library/svelte'; +import { Icon } from '../src/lucide-svelte'; + +import { airVent } from './testIconNodes'; + +describe('Using Icon Component', () => { + it('should render icon based on a iconNode', async () => { + const { container } = render(Icon, { + props: { + iconNode: airVent, + size: 48, + color: 'red', + absoluteStrokeWidth: true, + } + }); + + expect( container.firstChild ).toBeDefined() + }); + + it('should render icon and match snapshot', async () => { + const { container } = render(Icon, { + props: { + iconNode: airVent, + size: 48, + color: 'red', + absoluteStrokeWidth: true, + }, + }); + + expect( container.firstChild ).toMatchSnapshot(); + }); +}) diff --git a/packages/lucide-svelte/tests/__snapshots__/Icon.spec.ts.snap b/packages/lucide-svelte/tests/__snapshots__/Icon.spec.ts.snap new file mode 100644 index 0000000000..bd3dddc7f1 --- /dev/null +++ b/packages/lucide-svelte/tests/__snapshots__/Icon.spec.ts.snap @@ -0,0 +1,36 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Using Icon Component > should render icon and match snapshot 1`] = ` +
+ + + + + + + + + + + +
+`; diff --git a/packages/lucide-svelte/tests/testIconNodes.ts b/packages/lucide-svelte/tests/testIconNodes.ts new file mode 100644 index 0000000000..453d769c13 --- /dev/null +++ b/packages/lucide-svelte/tests/testIconNodes.ts @@ -0,0 +1,21 @@ +import type { IconNode } from "../src/lucide-svelte" + +export const airVent: IconNode = [ + [ + 'path', + { + d: 'M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2', + }, + ], + ['path', { d: 'M6 8h12' }], + ['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12' }], + ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5' }] +] + +export const coffee: IconNode = [ + ['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1' }], + ['path', { d: 'M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z' }], + ['line', { x1: '6', x2: '6', y1: '2', y2: '4' }], + ['line', { x1: '10', x2: '10', y1: '2', y2: '4' }], + ['line', { x1: '14', x2: '14', y1: '2', y2: '4' }], +] diff --git a/packages/lucide-vue-next/tests/Icon.spec.ts b/packages/lucide-vue-next/tests/Icon.spec.ts new file mode 100644 index 0000000000..d500b928b4 --- /dev/null +++ b/packages/lucide-vue-next/tests/Icon.spec.ts @@ -0,0 +1,33 @@ +import { describe, it, expect } from 'vitest'; +import { render } from '@testing-library/vue'; + +import { airVent } from './testIconNodes'; +import { Icon } from '../src/lucide-vue-next'; + +describe('Using Icon Component', () => { + it('should render icon based on a iconNode', async () => { + const { container } = render(Icon, { + props: { + iconNode: airVent, + size: 48, + color: 'red', + absoluteStrokeWidth: true, + } + }); + + expect( container.firstChild ).toBeDefined() + }); + + it('should render icon and match snapshot', async () => { + const { container } = render(Icon, { + props: { + iconNode: airVent, + size: 48, + color: 'red', + absoluteStrokeWidth: true, + } + }); + + expect( container.firstChild ).toMatchSnapshot(); + }); +}) diff --git a/packages/lucide-vue-next/tests/__snapshots__/Icon.spec.ts.snap b/packages/lucide-vue-next/tests/__snapshots__/Icon.spec.ts.snap new file mode 100644 index 0000000000..5e38eec073 --- /dev/null +++ b/packages/lucide-vue-next/tests/__snapshots__/Icon.spec.ts.snap @@ -0,0 +1,29 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Using Icon Component > should render icon and match snapshot 1`] = ` + + + + + + +`; From 78c5bd292cdec2eac5711f777f0d98eba10ada3a Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Mon, 1 Apr 2024 12:16:30 +0200 Subject: [PATCH 19/27] Formatting --- packages/lucide-preact/src/Icon.ts | 9 ++--- .../lucide-preact/src/createLucideIcon.ts | 8 ++--- .../lucide-preact/src/useIconComponent.ts | 34 +++++++++--------- packages/lucide-preact/tests/Icon.spec.tsx | 6 ++-- .../tests/createLucideIcon.spec.tsx | 12 +++---- .../tests/lucide-preact.spec.tsx | 6 ++-- packages/lucide-preact/tests/setupVitest.js | 4 +-- packages/lucide-preact/tests/testIconNodes.ts | 8 ++--- .../tests/useIconComponent.spec.tsx | 24 ++++++------- packages/lucide-react-native/src/Icon.ts | 10 +++--- .../src/createLucideIcon.ts | 1 - packages/lucide-react/src/createLucideIcon.ts | 22 +++++------- packages/lucide-react/tests/Icon.spec.tsx | 6 ++-- .../tests/createLucideIcon.spec.tsx | 12 +++---- .../lucide-react/tests/lucide-react.spec.tsx | 10 +++--- packages/lucide-react/tests/testIconNodes.ts | 8 ++--- .../tests/useIconComponent.spec.tsx | 22 ++++++------ packages/lucide-solid/tests/Icon.spec.tsx | 18 +++++----- .../tests/useIconComponent.spec.tsx | 24 ++++++------- packages/lucide-svelte/tests/Icon.spec.ts | 8 ++--- packages/lucide-svelte/tests/testIconNodes.ts | 8 ++--- .../lucide-vue-next/src/createLucideIcon.ts | 22 ++++++------ .../lucide-vue-next/src/useIconComponent.ts | 36 ++++++++++--------- packages/lucide-vue-next/tests/Icon.spec.ts | 10 +++--- .../tests/lucide-vue-next.spec.ts | 14 ++++---- packages/lucide-vue-next/tests/setupVitest.js | 2 +- .../lucide-vue-next/tests/testIconNodes.ts | 8 ++--- .../tests/useIconComponent.spec.ts | 16 ++++----- 28 files changed, 180 insertions(+), 188 deletions(-) diff --git a/packages/lucide-preact/src/Icon.ts b/packages/lucide-preact/src/Icon.ts index 0250d26c6c..b7bae1e5f5 100644 --- a/packages/lucide-preact/src/Icon.ts +++ b/packages/lucide-preact/src/Icon.ts @@ -3,7 +3,7 @@ import defaultAttributes from './defaultAttributes'; import { IconNode, LucideProps } from './createLucideIcon'; interface IconComponentProps extends LucideProps { - iconNode: IconNode + iconNode: IconNode; } /** @@ -44,10 +44,7 @@ const Icon = ({ class: ['lucide', classes].join(' '), ...rest, }, - [ - ...iconNode.map(([tag, attrs]) => h(tag, attrs)), - ...toChildArray(children) - ], + [...iconNode.map(([tag, attrs]) => h(tag, attrs)), ...toChildArray(children)], ); -export default Icon +export default Icon; diff --git a/packages/lucide-preact/src/createLucideIcon.ts b/packages/lucide-preact/src/createLucideIcon.ts index 024eea9c3b..5e77e8cfa2 100644 --- a/packages/lucide-preact/src/createLucideIcon.ts +++ b/packages/lucide-preact/src/createLucideIcon.ts @@ -20,11 +20,7 @@ export type LucideIcon = FunctionComponent; * @returns {FunctionComponent} LucideIcon */ const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => { - const Component = ({ - class: classes = '', - children, - ...props - }: LucideProps) => + const Component = ({ class: classes = '', children, ...props }: LucideProps) => h( Icon, { @@ -32,7 +28,7 @@ const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => { iconNode, class: mergeClasses>( `lucide-${toKebabCase(iconName)}`, - classes + classes, ), }, children, diff --git a/packages/lucide-preact/src/useIconComponent.ts b/packages/lucide-preact/src/useIconComponent.ts index 0d7f66085d..3082dd06ea 100644 --- a/packages/lucide-preact/src/useIconComponent.ts +++ b/packages/lucide-preact/src/useIconComponent.ts @@ -1,11 +1,12 @@ -import createLucideIcon, { IconNode, LucideIcon } from "./createLucideIcon"; +import createLucideIcon, { IconNode, LucideIcon } from './createLucideIcon'; -type CamelToPascal = - T extends `${infer FirstChar}${infer Rest}` ? `${Capitalize}${Rest}` : never +type CamelToPascal = T extends `${infer FirstChar}${infer Rest}` + ? `${Capitalize}${Rest}` + : never; type ComponentList = { - [Prop in keyof T as CamelToPascal]: LucideIcon -} + [Prop in keyof T as CamelToPascal]: LucideIcon; +}; export const toPascalCase = (string: T): CamelToPascal => { const camelCase = string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => @@ -17,28 +18,29 @@ export const toPascalCase = (string: T): CamelToPascal => { const useIconComponent = >(icons: Icons) => { if (typeof icons !== 'object') { - console.error('[lucide-preact]: useIconComponent expects an object as argument') + console.error('[lucide-preact]: useIconComponent expects an object as argument'); } - const iconNodeEntries = Object.entries(icons) + const iconNodeEntries = Object.entries(icons); - const iconNodesHasCorrectType = iconNodeEntries.every( - ([, iconNode]) => Array.isArray(iconNode) - ) + const iconNodesHasCorrectType = iconNodeEntries.every(([, iconNode]) => Array.isArray(iconNode)); if (!iconNodesHasCorrectType) { - console.error('[lucide-preact]: Passed icons object has an incorrect children type') + console.error('[lucide-preact]: Passed icons object has an incorrect children type'); } const iconComponents = iconNodeEntries.reduce((acc, [iconName, iconNode]) => { const componentName = toPascalCase(iconName) as keyof ComponentList; - acc[componentName] = createLucideIcon(componentName as string, iconNode) as ComponentList[typeof componentName]; + acc[componentName] = createLucideIcon( + componentName as string, + iconNode, + ) as ComponentList[typeof componentName]; return acc; - }, {} as ComponentList) + }, {} as ComponentList); - return iconComponents -} + return iconComponents; +}; -export default useIconComponent +export default useIconComponent; diff --git a/packages/lucide-preact/tests/Icon.spec.tsx b/packages/lucide-preact/tests/Icon.spec.tsx index f3a0810e1b..d43760ba93 100644 --- a/packages/lucide-preact/tests/Icon.spec.tsx +++ b/packages/lucide-preact/tests/Icon.spec.tsx @@ -15,7 +15,7 @@ describe('Using Icon Component', () => { />, ); - expect( container.firstChild ).toBeDefined() + expect(container.firstChild).toBeDefined(); }); it('should render icon and match snapshot', async () => { @@ -28,6 +28,6 @@ describe('Using Icon Component', () => { />, ); - expect( container.firstChild ).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); -}) +}); diff --git a/packages/lucide-preact/tests/createLucideIcon.spec.tsx b/packages/lucide-preact/tests/createLucideIcon.spec.tsx index 523d018815..883f6f424d 100644 --- a/packages/lucide-preact/tests/createLucideIcon.spec.tsx +++ b/packages/lucide-preact/tests/createLucideIcon.spec.tsx @@ -1,15 +1,15 @@ import { describe, it, expect } from 'vitest'; import { createLucideIcon } from '../src/lucide-preact'; import { airVent } from './testIconNodes'; -import { render } from '@testing-library/preact' +import { render } from '@testing-library/preact'; describe('Using createLucideIcon', () => { it('should create a component from an iconNode', () => { - const AirVent = createLucideIcon('AirVent', airVent) + const AirVent = createLucideIcon('AirVent', airVent); - const { container } = render( ); + const { container } = render(); - expect( container.firstChild ).toMatchSnapshot(); - expect( container.firstChild ).toBeDefined(); + expect(container.firstChild).toMatchSnapshot(); + expect(container.firstChild).toBeDefined(); }); -}) +}); diff --git a/packages/lucide-preact/tests/lucide-preact.spec.tsx b/packages/lucide-preact/tests/lucide-preact.spec.tsx index ab7adcba8c..819ebfe728 100644 --- a/packages/lucide-preact/tests/lucide-preact.spec.tsx +++ b/packages/lucide-preact/tests/lucide-preact.spec.tsx @@ -15,7 +15,7 @@ describe('Using lucide icon components', () => { it('should render the icon with the default attributes', () => { const { container } = render(); - const SVGElement = container.firstElementChild + const SVGElement = container.firstElementChild; expect(SVGElement).toHaveAttribute('xmlns', defaultAttributes.xmlns); expect(SVGElement).toHaveAttribute('width', String(defaultAttributes.width)); @@ -37,7 +37,7 @@ describe('Using lucide icon components', () => { />, ); - const SVGElement = container.firstElementChild + const SVGElement = container.firstElementChild; expect(SVGElement).toHaveAttribute('stroke', 'red'); expect(SVGElement).toHaveAttribute('width', '48'); @@ -79,7 +79,7 @@ describe('Using lucide icon components', () => { />, ); - const SVGElement = container.firstElementChild + const SVGElement = container.firstElementChild; expect(SVGElement).toHaveAttribute('stroke', 'red'); expect(SVGElement).toHaveAttribute('width', '48'); diff --git a/packages/lucide-preact/tests/setupVitest.js b/packages/lucide-preact/tests/setupVitest.js index ec8087900d..4e0ce2a49d 100644 --- a/packages/lucide-preact/tests/setupVitest.js +++ b/packages/lucide-preact/tests/setupVitest.js @@ -1,9 +1,9 @@ import { expect, afterEach } from 'vitest'; import { cleanup } from '@testing-library/preact'; import '@testing-library/jest-dom/vitest'; -import htmlSerializer from 'jest-serializer-html' +import htmlSerializer from 'jest-serializer-html'; -expect.addSnapshotSerializer(htmlSerializer) +expect.addSnapshotSerializer(htmlSerializer); afterEach(() => { cleanup(); diff --git a/packages/lucide-preact/tests/testIconNodes.ts b/packages/lucide-preact/tests/testIconNodes.ts index 80280c9a81..8c593572d5 100644 --- a/packages/lucide-preact/tests/testIconNodes.ts +++ b/packages/lucide-preact/tests/testIconNodes.ts @@ -1,4 +1,4 @@ -import { IconNode } from "../src/createLucideIcon" +import { IconNode } from '../src/createLucideIcon'; export const airVent: IconNode = [ [ @@ -10,8 +10,8 @@ export const airVent: IconNode = [ ], ['path', { d: 'M6 8h12', key: '6g4wlu' }], ['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }], - ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }] -] + ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }], +]; export const coffee: IconNode = [ ['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }], @@ -19,4 +19,4 @@ export const coffee: IconNode = [ ['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }], ['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }], ['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }], -] +]; diff --git a/packages/lucide-preact/tests/useIconComponent.spec.tsx b/packages/lucide-preact/tests/useIconComponent.spec.tsx index 202d8b2cd7..b93a3cb9e8 100644 --- a/packages/lucide-preact/tests/useIconComponent.spec.tsx +++ b/packages/lucide-preact/tests/useIconComponent.spec.tsx @@ -1,29 +1,29 @@ import { describe, it, expect } from 'vitest'; import { useIconComponent } from '../src/lucide-preact'; import { airVent, coffee } from './testIconNodes'; -import { render } from '@testing-library/preact' +import { render } from '@testing-library/preact'; describe('Using iconNode component generator', () => { it('should create a component from an iconNode', () => { - const { AirVent } = useIconComponent({ airVent }) + const { AirVent } = useIconComponent({ airVent }); - const { container } = render( ); + const { container } = render(); - expect( container.firstChild ).toMatchSnapshot(); - expect( container.firstChild ).toBeDefined(); + expect(container.firstChild).toMatchSnapshot(); + expect(container.firstChild).toBeDefined(); }); it('should create multiple components', () => { - const { AirVent, Coffee } = useIconComponent({ airVent, coffee }) + const { AirVent, Coffee } = useIconComponent({ airVent, coffee }); const { container } = render( <> - - - + + + , ); - expect( container ).toMatchSnapshot(); - expect( container ).toBeDefined(); + expect(container).toMatchSnapshot(); + expect(container).toBeDefined(); }); -}) +}); diff --git a/packages/lucide-react-native/src/Icon.ts b/packages/lucide-react-native/src/Icon.ts index c76767747c..ee43a1b0d7 100644 --- a/packages/lucide-react-native/src/Icon.ts +++ b/packages/lucide-react-native/src/Icon.ts @@ -1,10 +1,10 @@ -import { createElement, forwardRef, type FunctionComponent } from "react"; +import { createElement, forwardRef, type FunctionComponent } from 'react'; import * as NativeSvg from 'react-native-svg'; -import defaultAttributes, { childDefaultAttributes } from "./defaultAttributes"; -import { IconNode, LucideProps } from "./types"; +import defaultAttributes, { childDefaultAttributes } from './defaultAttributes'; +import { IconNode, LucideProps } from './types'; interface IconComponentProps extends LucideProps { - iconNode: IconNode + iconNode: IconNode; } /** @@ -66,4 +66,4 @@ const Icon = forwardRef( }, ); -export default Icon +export default Icon; diff --git a/packages/lucide-react-native/src/createLucideIcon.ts b/packages/lucide-react-native/src/createLucideIcon.ts index 725d853219..9eb27bd972 100644 --- a/packages/lucide-react-native/src/createLucideIcon.ts +++ b/packages/lucide-react-native/src/createLucideIcon.ts @@ -9,7 +9,6 @@ import * as NativeSvg from 'react-native-svg'; import defaultAttributes, { childDefaultAttributes } from './defaultAttributes'; import { IconNode, LucideIcon, LucideProps } from './types'; - const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => { const Component = forwardRef( ( diff --git a/packages/lucide-react/src/createLucideIcon.ts b/packages/lucide-react/src/createLucideIcon.ts index 0373548048..b209293824 100644 --- a/packages/lucide-react/src/createLucideIcon.ts +++ b/packages/lucide-react/src/createLucideIcon.ts @@ -1,7 +1,4 @@ -import { - createElement, - forwardRef, -} from 'react'; +import { createElement, forwardRef } from 'react'; import { mergeClasses, toKebabCase } from '@lucide/shared'; import { IconNode, LucideProps } from './types'; import Icon from './Icon'; @@ -13,16 +10,13 @@ import Icon from './Icon'; * @returns {ForwardRefExoticComponent} LucideIcon */ const createLucideIcon = (iconName: string, iconNode: IconNode) => { - const Component = forwardRef( - ({ className, ...props}, ref) => createElement( - Icon, - { - ref, - iconNode, - className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className), - ...props, - }, - ) + const Component = forwardRef(({ className, ...props }, ref) => + createElement(Icon, { + ref, + iconNode, + className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className), + ...props, + }), ); Component.displayName = `${iconName}`; diff --git a/packages/lucide-react/tests/Icon.spec.tsx b/packages/lucide-react/tests/Icon.spec.tsx index 25d9d2a286..906f0c3117 100644 --- a/packages/lucide-react/tests/Icon.spec.tsx +++ b/packages/lucide-react/tests/Icon.spec.tsx @@ -15,7 +15,7 @@ describe('Using Icon Component', () => { />, ); - expect( container.firstChild ).toBeDefined() + expect(container.firstChild).toBeDefined(); }); it('should render icon and match snapshot', async () => { @@ -28,6 +28,6 @@ describe('Using Icon Component', () => { />, ); - expect( container.firstChild ).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); -}) +}); diff --git a/packages/lucide-react/tests/createLucideIcon.spec.tsx b/packages/lucide-react/tests/createLucideIcon.spec.tsx index 6340887487..7c1b5e475b 100644 --- a/packages/lucide-react/tests/createLucideIcon.spec.tsx +++ b/packages/lucide-react/tests/createLucideIcon.spec.tsx @@ -1,15 +1,15 @@ import { describe, it, expect } from 'vitest'; import { createLucideIcon } from '../src/lucide-react'; import { airVent } from './testIconNodes'; -import { render } from '@testing-library/react' +import { render } from '@testing-library/react'; describe('Using createLucideIcon', () => { it('should create a component from an iconNode', () => { - const AirVent = createLucideIcon('AirVent', airVent) + const AirVent = createLucideIcon('AirVent', airVent); - const { container } = render( ); + const { container } = render(); - expect( container.firstChild ).toMatchSnapshot(); - expect( container.firstChild ).toBeDefined(); + expect(container.firstChild).toMatchSnapshot(); + expect(container.firstChild).toBeDefined(); }); -}) +}); diff --git a/packages/lucide-react/tests/lucide-react.spec.tsx b/packages/lucide-react/tests/lucide-react.spec.tsx index 87fef0cff7..8365d96f4a 100644 --- a/packages/lucide-react/tests/lucide-react.spec.tsx +++ b/packages/lucide-react/tests/lucide-react.spec.tsx @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { render, cleanup } from '@testing-library/react' +import { render, cleanup } from '@testing-library/react'; import { Pen, Edit2, Grid, Droplet } from '../src/lucide-react'; import defaultAttributes from '../src/defaultAttributes'; @@ -13,7 +13,7 @@ describe('Using lucide icon components', () => { it('should render the icon with default attributes', () => { const { container } = render(); - const SVGElement = container.firstElementChild + const SVGElement = container.firstElementChild; expect(SVGElement).toHaveAttribute('xmlns', defaultAttributes.xmlns); expect(SVGElement).toHaveAttribute('width', String(defaultAttributes.width)); @@ -35,7 +35,7 @@ describe('Using lucide icon components', () => { />, ); - const SVGElement = container.firstElementChild + const SVGElement = container.firstElementChild; expect(SVGElement).toHaveAttribute('stroke', 'red'); expect(SVGElement).toHaveAttribute('width', '48'); @@ -78,7 +78,7 @@ describe('Using lucide icon components', () => { />, ); - const SVGElement = container.firstElementChild + const SVGElement = container.firstElementChild; expect(SVGElement).toHaveAttribute('stroke', 'red'); expect(SVGElement).toHaveAttribute('width', '48'); @@ -96,4 +96,4 @@ describe('Using lucide icon components', () => { expect(container.firstChild).toHaveClass('lucide'); expect(container.firstChild).toHaveClass('lucide-droplet'); }); -}) +}); diff --git a/packages/lucide-react/tests/testIconNodes.ts b/packages/lucide-react/tests/testIconNodes.ts index e48a1b8332..c93721961c 100644 --- a/packages/lucide-react/tests/testIconNodes.ts +++ b/packages/lucide-react/tests/testIconNodes.ts @@ -1,4 +1,4 @@ -import { IconNode } from "../src/types" +import { IconNode } from '../src/types'; export const airVent: IconNode = [ [ @@ -10,8 +10,8 @@ export const airVent: IconNode = [ ], ['path', { d: 'M6 8h12', key: '6g4wlu' }], ['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }], - ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }] -] + ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }], +]; export const coffee: IconNode = [ ['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }], @@ -19,4 +19,4 @@ export const coffee: IconNode = [ ['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }], ['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }], ['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }], -] +]; diff --git a/packages/lucide-react/tests/useIconComponent.spec.tsx b/packages/lucide-react/tests/useIconComponent.spec.tsx index 53720d06d0..3b661baf5d 100644 --- a/packages/lucide-react/tests/useIconComponent.spec.tsx +++ b/packages/lucide-react/tests/useIconComponent.spec.tsx @@ -5,25 +5,25 @@ import { render } from '@testing-library/react'; describe('Using iconNode component generator', () => { it('should create a component from an iconNode', () => { - const { AirVent } = useIconComponent({ airVent }) + const { AirVent } = useIconComponent({ airVent }); - const { container } = render( ); + const { container } = render(); - expect( container.firstChild ).toMatchSnapshot(); - expect( container.firstChild ).toBeDefined(); + expect(container.firstChild).toMatchSnapshot(); + expect(container.firstChild).toBeDefined(); }); it('should create multiple components', () => { - const { AirVent, Coffee } = useIconComponent({ airVent, coffee }) + const { AirVent, Coffee } = useIconComponent({ airVent, coffee }); const { container } = render( <> - - - + + + , ); - expect( container ).toMatchSnapshot(); - expect( container ).toBeDefined(); + expect(container).toMatchSnapshot(); + expect(container).toBeDefined(); }); -}) +}); diff --git a/packages/lucide-solid/tests/Icon.spec.tsx b/packages/lucide-solid/tests/Icon.spec.tsx index eda5f8a485..c43ab284f0 100644 --- a/packages/lucide-solid/tests/Icon.spec.tsx +++ b/packages/lucide-solid/tests/Icon.spec.tsx @@ -6,28 +6,28 @@ import { Icon } from '../src/lucide-solid'; describe('Using Icon Component', () => { it('should render icon based on a iconNode', async () => { - const { container } = render(() => + const { container } = render(() => ( , - ); + /> + )); - expect( container.firstChild ).toBeDefined() + expect(container.firstChild).toBeDefined(); }); it('should render icon and match snapshot', async () => { - const { container } = render(() => + const { container } = render(() => ( , - ); + /> + )); - expect( container.firstChild ).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); -}) +}); diff --git a/packages/lucide-solid/tests/useIconComponent.spec.tsx b/packages/lucide-solid/tests/useIconComponent.spec.tsx index 5696723411..e9daac74c9 100644 --- a/packages/lucide-solid/tests/useIconComponent.spec.tsx +++ b/packages/lucide-solid/tests/useIconComponent.spec.tsx @@ -5,25 +5,25 @@ import { render } from '@solidjs/testing-library'; describe('Using iconNode component generator', () => { it('should create a component from an iconNode', () => { - const { AirVent } = useIconComponent({ airVent }) + const { AirVent } = useIconComponent({ airVent }); - const { container } = render(() => ); + const { container } = render(() => ); - expect( container.firstChild ).toMatchSnapshot(); - expect( container.firstChild ).toBeDefined(); + expect(container.firstChild).toMatchSnapshot(); + expect(container.firstChild).toBeDefined(); }); it('should create multiple components', () => { - const { AirVent, Coffee } = useIconComponent({ airVent, coffee }) + const { AirVent, Coffee } = useIconComponent({ airVent, coffee }); - const { container } = render(() => + const { container } = render(() => ( <> - - + + - ); + )); - expect( container ).toMatchSnapshot(); - expect( container ).toBeDefined(); + expect(container).toMatchSnapshot(); + expect(container).toBeDefined(); }); -}) +}); diff --git a/packages/lucide-svelte/tests/Icon.spec.ts b/packages/lucide-svelte/tests/Icon.spec.ts index e7756c9c24..e5f0eb3244 100644 --- a/packages/lucide-svelte/tests/Icon.spec.ts +++ b/packages/lucide-svelte/tests/Icon.spec.ts @@ -12,10 +12,10 @@ describe('Using Icon Component', () => { size: 48, color: 'red', absoluteStrokeWidth: true, - } + }, }); - expect( container.firstChild ).toBeDefined() + expect(container.firstChild).toBeDefined(); }); it('should render icon and match snapshot', async () => { @@ -28,6 +28,6 @@ describe('Using Icon Component', () => { }, }); - expect( container.firstChild ).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); -}) +}); diff --git a/packages/lucide-svelte/tests/testIconNodes.ts b/packages/lucide-svelte/tests/testIconNodes.ts index 453d769c13..ff00c70fbb 100644 --- a/packages/lucide-svelte/tests/testIconNodes.ts +++ b/packages/lucide-svelte/tests/testIconNodes.ts @@ -1,4 +1,4 @@ -import type { IconNode } from "../src/lucide-svelte" +import type { IconNode } from '../src/lucide-svelte'; export const airVent: IconNode = [ [ @@ -9,8 +9,8 @@ export const airVent: IconNode = [ ], ['path', { d: 'M6 8h12' }], ['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12' }], - ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5' }] -] + ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5' }], +]; export const coffee: IconNode = [ ['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1' }], @@ -18,4 +18,4 @@ export const coffee: IconNode = [ ['line', { x1: '6', x2: '6', y1: '2', y2: '4' }], ['line', { x1: '10', x2: '10', y1: '2', y2: '4' }], ['line', { x1: '14', x2: '14', y1: '2', y2: '4' }], -] +]; diff --git a/packages/lucide-vue-next/src/createLucideIcon.ts b/packages/lucide-vue-next/src/createLucideIcon.ts index c2d9b5bdd4..86f14f1fce 100644 --- a/packages/lucide-vue-next/src/createLucideIcon.ts +++ b/packages/lucide-vue-next/src/createLucideIcon.ts @@ -12,15 +12,17 @@ import Icon from './Icon'; * @param {array} iconNode * @returns {FunctionalComponent} LucideIcon */ -const createLucideIcon = (iconName: string, iconNode: IconNode): FunctionalComponent => - (props, { slots }) => h( - Icon, - { - ...props, - iconNode, - name: iconName, - }, - slots - ) +const createLucideIcon = + (iconName: string, iconNode: IconNode): FunctionalComponent => + (props, { slots }) => + h( + Icon, + { + ...props, + iconNode, + name: iconName, + }, + slots, + ); export default createLucideIcon; diff --git a/packages/lucide-vue-next/src/useIconComponent.ts b/packages/lucide-vue-next/src/useIconComponent.ts index d0b387e5be..04286e5906 100644 --- a/packages/lucide-vue-next/src/useIconComponent.ts +++ b/packages/lucide-vue-next/src/useIconComponent.ts @@ -1,12 +1,13 @@ -import createLucideIcon from "./createLucideIcon"; -import { LucideIcon, IconNode } from "./types"; +import createLucideIcon from './createLucideIcon'; +import { LucideIcon, IconNode } from './types'; -type CamelToPascal = - T extends `${infer FirstChar}${infer Rest}` ? `${Capitalize}${Rest}` : never +type CamelToPascal = T extends `${infer FirstChar}${infer Rest}` + ? `${Capitalize}${Rest}` + : never; type ComponentList = { - [Prop in keyof T as CamelToPascal]: LucideIcon -} + [Prop in keyof T as CamelToPascal]: LucideIcon; +}; export const toPascalCase = (string: T): CamelToPascal => { const camelCase = string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => @@ -18,28 +19,29 @@ export const toPascalCase = (string: T): CamelToPascal => { const useIconComponent = >(icons: Icons) => { if (typeof icons !== 'object') { - throw new Error('[lucide-vue-next]: useIconComponent expects an object as argument') + throw new Error('[lucide-vue-next]: useIconComponent expects an object as argument'); } - const iconNodeEntries = Object.entries(icons) + const iconNodeEntries = Object.entries(icons); - const iconNodesHasCorrectType = iconNodeEntries.every( - ([, iconNode]) => Array.isArray(iconNode) - ) + const iconNodesHasCorrectType = iconNodeEntries.every(([, iconNode]) => Array.isArray(iconNode)); if (!iconNodesHasCorrectType) { - throw new Error('[lucide-vue-next]: Passed icons object has incorrect type') + throw new Error('[lucide-vue-next]: Passed icons object has incorrect type'); } const iconComponents = iconNodeEntries.reduce((acc, [iconName, iconNode]) => { const componentName = toPascalCase(iconName) as keyof ComponentList; - acc[componentName] = createLucideIcon(componentName as string, iconNode) as ComponentList[typeof componentName]; + acc[componentName] = createLucideIcon( + componentName as string, + iconNode, + ) as ComponentList[typeof componentName]; return acc; - }, {} as ComponentList) + }, {} as ComponentList); - return iconComponents -} + return iconComponents; +}; -export default useIconComponent +export default useIconComponent; diff --git a/packages/lucide-vue-next/tests/Icon.spec.ts b/packages/lucide-vue-next/tests/Icon.spec.ts index d500b928b4..6546744ad0 100644 --- a/packages/lucide-vue-next/tests/Icon.spec.ts +++ b/packages/lucide-vue-next/tests/Icon.spec.ts @@ -12,10 +12,10 @@ describe('Using Icon Component', () => { size: 48, color: 'red', absoluteStrokeWidth: true, - } + }, }); - expect( container.firstChild ).toBeDefined() + expect(container.firstChild).toBeDefined(); }); it('should render icon and match snapshot', async () => { @@ -25,9 +25,9 @@ describe('Using Icon Component', () => { size: 48, color: 'red', absoluteStrokeWidth: true, - } + }, }); - expect( container.firstChild ).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); -}) +}); diff --git a/packages/lucide-vue-next/tests/lucide-vue-next.spec.ts b/packages/lucide-vue-next/tests/lucide-vue-next.spec.ts index ea46a942ab..5e5138fc5f 100644 --- a/packages/lucide-vue-next/tests/lucide-vue-next.spec.ts +++ b/packages/lucide-vue-next/tests/lucide-vue-next.spec.ts @@ -12,9 +12,9 @@ describe('Using lucide icon components', () => { }); it('should render the icon with the default attributes', () => { - const { container } = render(Smile) + const { container } = render(Smile); - const SVGElement = container.firstElementChild + const SVGElement = container.firstElementChild; expect(SVGElement).toHaveAttribute('xmlns', defaultAttributes.xmlns); expect(SVGElement).toHaveAttribute('width', String(defaultAttributes.width)); @@ -36,7 +36,7 @@ describe('Using lucide icon components', () => { }, }); - const SVGElement = container.firstElementChild + const SVGElement = container.firstElementChild; expect(SVGElement).toHaveAttribute('width', '48'); expect(SVGElement).toHaveAttribute('stroke', 'red'); @@ -54,7 +54,7 @@ describe('Using lucide icon components', () => { expect(container).toMatchSnapshot(); - const icon = container.firstElementChild + const icon = container.firstElementChild; expect(icon).toHaveClass('my-icon'); expect(icon).toHaveClass('lucide'); @@ -70,7 +70,7 @@ describe('Using lucide icon components', () => { expect(container).toMatchSnapshot(); - const icon = container.firstElementChild + const icon = container.firstElementChild; expect(icon).toHaveStyle({ position: 'absolute' }); }); @@ -83,7 +83,7 @@ describe('Using lucide icon components', () => { }, }); - const icon = container.firstElementChild + const icon = container.firstElementChild; await fireEvent.click(icon); @@ -141,7 +141,7 @@ describe('Using lucide icon components', () => { }, }); - const icon = container.firstElementChild + const icon = container.firstElementChild; expect(icon).toHaveAttribute('width', '48'); expect(icon).toHaveAttribute('stroke', 'red'); diff --git a/packages/lucide-vue-next/tests/setupVitest.js b/packages/lucide-vue-next/tests/setupVitest.js index 7b0828bfa8..bb02c60cd0 100644 --- a/packages/lucide-vue-next/tests/setupVitest.js +++ b/packages/lucide-vue-next/tests/setupVitest.js @@ -1 +1 @@ -import '@testing-library/jest-dom'; +import '@testing-library/jest-dom/vitest'; diff --git a/packages/lucide-vue-next/tests/testIconNodes.ts b/packages/lucide-vue-next/tests/testIconNodes.ts index 80280c9a81..8c593572d5 100644 --- a/packages/lucide-vue-next/tests/testIconNodes.ts +++ b/packages/lucide-vue-next/tests/testIconNodes.ts @@ -1,4 +1,4 @@ -import { IconNode } from "../src/createLucideIcon" +import { IconNode } from '../src/createLucideIcon'; export const airVent: IconNode = [ [ @@ -10,8 +10,8 @@ export const airVent: IconNode = [ ], ['path', { d: 'M6 8h12', key: '6g4wlu' }], ['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }], - ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }] -] + ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }], +]; export const coffee: IconNode = [ ['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }], @@ -19,4 +19,4 @@ export const coffee: IconNode = [ ['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }], ['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }], ['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }], -] +]; diff --git a/packages/lucide-vue-next/tests/useIconComponent.spec.ts b/packages/lucide-vue-next/tests/useIconComponent.spec.ts index 91fff44595..3f20472415 100644 --- a/packages/lucide-vue-next/tests/useIconComponent.spec.ts +++ b/packages/lucide-vue-next/tests/useIconComponent.spec.ts @@ -1,27 +1,27 @@ import { describe, it, expect } from 'vitest'; import { useIconComponent } from '../src/lucide-vue-next'; import { airVent, coffee } from './testIconNodes'; -import { cleanup, render } from '@testing-library/vue' +import { cleanup, render } from '@testing-library/vue'; describe('Using iconNode component generator', () => { it('should create a component from an iconNode', () => { - const { AirVent } = useIconComponent({ airVent }) + const { AirVent } = useIconComponent({ airVent }); const { container } = render(AirVent); - expect( container.firstChild ).toMatchSnapshot(); - expect( container.firstChild ).toBeDefined(); + expect(container.firstChild).toMatchSnapshot(); + expect(container.firstChild).toBeDefined(); }); it('should create multiple components', () => { - const { AirVent, Coffee } = useIconComponent({ airVent, coffee }) + const { AirVent, Coffee } = useIconComponent({ airVent, coffee }); const { container } = render(AirVent); - expect( container ).toBeDefined(); + expect(container).toBeDefined(); cleanup(); const { container: container2 } = render(Coffee); - expect( container2 ).toBeDefined(); + expect(container2).toBeDefined(); }); -}) +}); From 30ee113f49479666b773b803245dc7db34145bce Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Mon, 1 Apr 2024 12:21:30 +0200 Subject: [PATCH 20/27] Formatting --- .../src/useIconComponent.ts | 38 ++++++++++--------- .../lucide-react-native/tests/Icon.spec.tsx | 6 +-- .../tests/lucide-react-native.spec.tsx | 6 +-- .../tests/testIconNodes.ts | 8 ++-- .../tests/useIconComponent.spec.tsx | 22 +++++------ packages/lucide-react/src/types.ts | 11 ++---- packages/lucide-react/src/useIconComponent.ts | 38 ++++++++++--------- .../tests/dynamicImports.spec.tsx | 10 ++--- packages/lucide-solid/tests/Icon.spec.tsx | 2 +- packages/lucide-solid/tests/testIconNodes.ts | 8 ++-- packages/lucide-vue-next/src/types.ts | 14 +++---- packages/shared/src/utility-types.ts | 9 +++-- packages/shared/src/utils.ts | 14 +++---- tools/test-utils/package.json | 11 ------ tools/test-utils/src/getElementAttributes.ts | 0 tools/test-utils/src/getOriginalIconSVG.ts | 17 --------- tools/test-utils/src/main.ts | 1 - 17 files changed, 95 insertions(+), 120 deletions(-) delete mode 100644 tools/test-utils/package.json delete mode 100644 tools/test-utils/src/getElementAttributes.ts delete mode 100644 tools/test-utils/src/getOriginalIconSVG.ts delete mode 100644 tools/test-utils/src/main.ts diff --git a/packages/lucide-react-native/src/useIconComponent.ts b/packages/lucide-react-native/src/useIconComponent.ts index a98b0f5ebc..70f6e1b095 100644 --- a/packages/lucide-react-native/src/useIconComponent.ts +++ b/packages/lucide-react-native/src/useIconComponent.ts @@ -1,7 +1,7 @@ -import { type ComponentList, toPascalCase } from "@lucide/shared"; -import createLucideIcon from "./createLucideIcon"; +import { type ComponentList, toPascalCase } from '@lucide/shared'; +import createLucideIcon from './createLucideIcon'; -import { IconNode, LucideIcon } from "./types"; +import { IconNode, LucideIcon } from './types'; /** * Create a list (object) of icon components from a list (object) of icon nodes @@ -11,28 +11,32 @@ import { IconNode, LucideIcon } from "./types"; */ const useIconComponent = >(iconNodes: Icons) => { if (typeof iconNodes !== 'object') { - console.error('[lucide-react-react]: useIconComponent expects an object as argument') + console.error('[lucide-react-react]: useIconComponent expects an object as argument'); } - const iconNodeEntries = Object.entries(iconNodes) + const iconNodeEntries = Object.entries(iconNodes); - const iconNodesHasCorrectType = iconNodeEntries.every( - ([, iconNode]) => Array.isArray(iconNode) - ) + const iconNodesHasCorrectType = iconNodeEntries.every(([, iconNode]) => Array.isArray(iconNode)); if (!iconNodesHasCorrectType) { - console.error('[lucide-react-react]: Passed icons object has incorrect type') + console.error('[lucide-react-react]: Passed icons object has incorrect type'); } - const iconComponents = iconNodeEntries.reduce((acc, [iconName, iconNode]) => { - const componentName = toPascalCase(iconName) as keyof ComponentList; + const iconComponents = iconNodeEntries.reduce( + (acc, [iconName, iconNode]) => { + const componentName = toPascalCase(iconName) as keyof ComponentList; - acc[componentName] = createLucideIcon(componentName as string, iconNode) as ComponentList[typeof componentName]; + acc[componentName] = createLucideIcon(componentName as string, iconNode) as ComponentList< + Icons, + LucideIcon + >[typeof componentName]; - return acc; - }, {} as ComponentList) + return acc; + }, + {} as ComponentList, + ); - return iconComponents -} + return iconComponents; +}; -export default useIconComponent +export default useIconComponent; diff --git a/packages/lucide-react-native/tests/Icon.spec.tsx b/packages/lucide-react-native/tests/Icon.spec.tsx index cc0383d61e..0bf4e886ad 100644 --- a/packages/lucide-react-native/tests/Icon.spec.tsx +++ b/packages/lucide-react-native/tests/Icon.spec.tsx @@ -17,7 +17,7 @@ describe('Using Icon Component', () => { />, ); - expect( container.firstChild ).toBeDefined() + expect(container.firstChild).toBeDefined(); }); it('should render icon and match snapshot', async () => { @@ -30,6 +30,6 @@ describe('Using Icon Component', () => { />, ); - expect( container.firstChild ).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); -}) +}); diff --git a/packages/lucide-react-native/tests/lucide-react-native.spec.tsx b/packages/lucide-react-native/tests/lucide-react-native.spec.tsx index 7dcfd3abf5..de59709815 100644 --- a/packages/lucide-react-native/tests/lucide-react-native.spec.tsx +++ b/packages/lucide-react-native/tests/lucide-react-native.spec.tsx @@ -22,7 +22,7 @@ describe('Using lucide icon components', () => { />, ); - const SVGElement = container.firstElementChild + const SVGElement = container.firstElementChild; expect(SVGElement).toHaveAttribute('stroke', 'red'); expect(SVGElement).toHaveAttribute('width', '48'); @@ -68,7 +68,7 @@ describe('Using lucide icon components', () => { />, ); - const SVGElement = container.firstElementChild + const SVGElement = container.firstElementChild; expect(SVGElement).toHaveAttribute('stroke', 'red'); expect(SVGElement).toHaveAttribute('width', '48'); @@ -87,7 +87,7 @@ describe('Using lucide icon components', () => { , ); - const { children } = container.firstElementChild ?? {} + const { children } = container.firstElementChild ?? {}; const lastChild = children?.[children.length - 1]; expect(lastChild).toEqual(getByTestId(childId)); diff --git a/packages/lucide-react-native/tests/testIconNodes.ts b/packages/lucide-react-native/tests/testIconNodes.ts index e48a1b8332..c93721961c 100644 --- a/packages/lucide-react-native/tests/testIconNodes.ts +++ b/packages/lucide-react-native/tests/testIconNodes.ts @@ -1,4 +1,4 @@ -import { IconNode } from "../src/types" +import { IconNode } from '../src/types'; export const airVent: IconNode = [ [ @@ -10,8 +10,8 @@ export const airVent: IconNode = [ ], ['path', { d: 'M6 8h12', key: '6g4wlu' }], ['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }], - ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }] -] + ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }], +]; export const coffee: IconNode = [ ['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }], @@ -19,4 +19,4 @@ export const coffee: IconNode = [ ['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }], ['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }], ['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }], -] +]; diff --git a/packages/lucide-react-native/tests/useIconComponent.spec.tsx b/packages/lucide-react-native/tests/useIconComponent.spec.tsx index 6765882361..fd1539804c 100644 --- a/packages/lucide-react-native/tests/useIconComponent.spec.tsx +++ b/packages/lucide-react-native/tests/useIconComponent.spec.tsx @@ -7,25 +7,25 @@ vi.mock('react-native-svg'); describe('Using iconNode component generator', () => { it('should create a component from an iconNode', () => { - const { AirVent } = useIconComponent({ airVent }) + const { AirVent } = useIconComponent({ airVent }); - const { container } = render( ); + const { container } = render(); - expect( container.firstChild ).toMatchSnapshot(); - expect( container.firstChild ).toBeDefined(); + expect(container.firstChild).toMatchSnapshot(); + expect(container.firstChild).toBeDefined(); }); it('should create multiple components', () => { - const { AirVent, Coffee } = useIconComponent({ airVent, coffee }) + const { AirVent, Coffee } = useIconComponent({ airVent, coffee }); const { container } = render( <> - - - + + + , ); - expect( container ).toMatchSnapshot(); - expect( container ).toBeDefined(); + expect(container).toMatchSnapshot(); + expect(container).toBeDefined(); }); -}) +}); diff --git a/packages/lucide-react/src/types.ts b/packages/lucide-react/src/types.ts index fe3833f72c..ed686b315b 100644 --- a/packages/lucide-react/src/types.ts +++ b/packages/lucide-react/src/types.ts @@ -1,9 +1,4 @@ -import { - ReactSVG, - SVGProps, - ForwardRefExoticComponent, - RefAttributes, -} from 'react'; +import { ReactSVG, SVGProps, ForwardRefExoticComponent, RefAttributes } from 'react'; export type IconNode = [elementName: keyof ReactSVG, attrs: Record][]; @@ -15,4 +10,6 @@ export interface LucideProps extends ElementAttributes { absoluteStrokeWidth?: boolean; } -export type LucideIcon = ForwardRefExoticComponent & RefAttributes> +export type LucideIcon = ForwardRefExoticComponent< + Omit & RefAttributes +>; diff --git a/packages/lucide-react/src/useIconComponent.ts b/packages/lucide-react/src/useIconComponent.ts index 594208565f..5216646d60 100644 --- a/packages/lucide-react/src/useIconComponent.ts +++ b/packages/lucide-react/src/useIconComponent.ts @@ -1,7 +1,7 @@ -import { type ComponentList, toPascalCase } from "@lucide/shared"; -import createLucideIcon from "./createLucideIcon"; +import { type ComponentList, toPascalCase } from '@lucide/shared'; +import createLucideIcon from './createLucideIcon'; -import { IconNode, LucideIcon } from "./types"; +import { IconNode, LucideIcon } from './types'; /** * Create a list (object) of icon components from a list (object) of icon nodes @@ -11,29 +11,33 @@ import { IconNode, LucideIcon } from "./types"; */ const useIconComponent = >(iconNodes: Icons) => { if (typeof iconNodes !== 'object') { - console.error('[lucide-react]: useIconComponent expects an object as argument') + console.error('[lucide-react]: useIconComponent expects an object as argument'); } - const iconNodeEntries = Object.entries(iconNodes) + const iconNodeEntries = Object.entries(iconNodes); - const iconNodesHasCorrectType = iconNodeEntries.every( - ([, iconNode]) => Array.isArray(iconNode) - ) + const iconNodesHasCorrectType = iconNodeEntries.every(([, iconNode]) => Array.isArray(iconNode)); if (!iconNodesHasCorrectType) { - console.error('[lucide-react]: Passed icons object has incorrect type') + console.error('[lucide-react]: Passed icons object has incorrect type'); } // TODO: Optional throw an warning if this function is executed inside a React component render function, because this harms performance - const iconComponents = iconNodeEntries.reduce((acc, [iconName, iconNode]) => { - const componentName = toPascalCase(iconName) as keyof ComponentList; + const iconComponents = iconNodeEntries.reduce( + (acc, [iconName, iconNode]) => { + const componentName = toPascalCase(iconName) as keyof ComponentList; - acc[componentName] = createLucideIcon(componentName as string, iconNode) as ComponentList[typeof componentName]; + acc[componentName] = createLucideIcon(componentName as string, iconNode) as ComponentList< + Icons, + LucideIcon + >[typeof componentName]; - return acc; - }, {} as ComponentList) + return acc; + }, + {} as ComponentList, + ); - return iconComponents -} + return iconComponents; +}; -export default useIconComponent +export default useIconComponent; diff --git a/packages/lucide-react/tests/dynamicImports.spec.tsx b/packages/lucide-react/tests/dynamicImports.spec.tsx index e0d98889fa..00f1eab8e4 100644 --- a/packages/lucide-react/tests/dynamicImports.spec.tsx +++ b/packages/lucide-react/tests/dynamicImports.spec.tsx @@ -6,7 +6,6 @@ import dynamicIconImports from '../src/dynamicIconImports'; import { LucideProps } from '../src/types'; describe('Using dynamicImports', () => { - it('should render icons dynamically by using the dynamicIconImports module', async () => { interface IconProps extends Omit { name: keyof typeof dynamicIconImports; @@ -20,7 +19,7 @@ describe('Using dynamicImports', () => { ); - } + }; const { container, getByLabelText } = render( { />, ); - await waitFor(() => getByLabelText('smile')) - - expect( container.innerHTML ).toMatchSnapshot(); + await waitFor(() => getByLabelText('smile')); + expect(container.innerHTML).toMatchSnapshot(); }); -}) +}); diff --git a/packages/lucide-solid/tests/Icon.spec.tsx b/packages/lucide-solid/tests/Icon.spec.tsx index c43ab284f0..74c3936abb 100644 --- a/packages/lucide-solid/tests/Icon.spec.tsx +++ b/packages/lucide-solid/tests/Icon.spec.tsx @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { render, cleanup } from '@solidjs/testing-library'; +import { render } from '@solidjs/testing-library'; import { airVent } from './testIconNodes'; import { Icon } from '../src/lucide-solid'; diff --git a/packages/lucide-solid/tests/testIconNodes.ts b/packages/lucide-solid/tests/testIconNodes.ts index e48a1b8332..c93721961c 100644 --- a/packages/lucide-solid/tests/testIconNodes.ts +++ b/packages/lucide-solid/tests/testIconNodes.ts @@ -1,4 +1,4 @@ -import { IconNode } from "../src/types" +import { IconNode } from '../src/types'; export const airVent: IconNode = [ [ @@ -10,8 +10,8 @@ export const airVent: IconNode = [ ], ['path', { d: 'M6 8h12', key: '6g4wlu' }], ['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }], - ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }] -] + ['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }], +]; export const coffee: IconNode = [ ['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }], @@ -19,4 +19,4 @@ export const coffee: IconNode = [ ['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }], ['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }], ['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }], -] +]; diff --git a/packages/lucide-vue-next/src/types.ts b/packages/lucide-vue-next/src/types.ts index 95ce13a5be..638af5b7fe 100644 --- a/packages/lucide-vue-next/src/types.ts +++ b/packages/lucide-vue-next/src/types.ts @@ -1,13 +1,13 @@ -import type { FunctionalComponent, SVGAttributes } from "vue"; +import type { FunctionalComponent, SVGAttributes } from 'vue'; export interface LucideProps extends Partial { - size?: 24 | number - strokeWidth?: number | string - absoluteStrokeWidth?: boolean + size?: 24 | number; + strokeWidth?: number | string; + absoluteStrokeWidth?: boolean; } -export type IconNode = [elementName: string, attrs: Record][] -export type LucideIcon = FunctionalComponent +export type IconNode = [elementName: string, attrs: Record][]; +export type LucideIcon = FunctionalComponent; // Legacy exports -export type SVGProps = LucideProps +export type SVGProps = LucideProps; diff --git a/packages/shared/src/utility-types.ts b/packages/shared/src/utility-types.ts index 5bd1d88bd3..892014723d 100644 --- a/packages/shared/src/utility-types.ts +++ b/packages/shared/src/utility-types.ts @@ -4,12 +4,13 @@ * @example * type Test = CamelToPascal<'fooBar'> // 'FooBar' */ -export type CamelToPascal = - T extends `${infer FirstChar}${infer Rest}` ? `${Capitalize}${Rest}` : never +export type CamelToPascal = T extends `${infer FirstChar}${infer Rest}` + ? `${Capitalize}${Rest}` + : never; /** * Creates a list of components from a list of component names and a component type */ export type ComponentList = { - [Prop in keyof ComponentNames as CamelToPascal]: ComponentType -} + [Prop in keyof ComponentNames as CamelToPascal]: ComponentType; +}; diff --git a/packages/shared/src/utils.ts b/packages/shared/src/utils.ts index 2c70e39a1b..f068a5714e 100644 --- a/packages/shared/src/utils.ts +++ b/packages/shared/src/utils.ts @@ -1,4 +1,4 @@ -import { CamelToPascal } from "./utility-types"; +import { CamelToPascal } from './utility-types'; /** * Converts string to kebab case @@ -9,7 +9,6 @@ import { CamelToPascal } from "./utility-types"; export const toKebabCase = (string: string) => string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); - /** * Converts string to pascal case * @@ -30,8 +29,9 @@ export const toPascalCase = (string: T): CamelToPascal => { * @param {array} classes * @returns {string} A string of classes */ -export const mergeClasses = ( - ...classes: ClassType[] -) => classes.filter((className, index, array) => { - return Boolean(className) && array.indexOf(className) === index; -}).join(' '); +export const mergeClasses = (...classes: ClassType[]) => + classes + .filter((className, index, array) => { + return Boolean(className) && array.indexOf(className) === index; + }) + .join(' '); diff --git a/tools/test-utils/package.json b/tools/test-utils/package.json deleted file mode 100644 index 10fcf94f2d..0000000000 --- a/tools/test-utils/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "@lucide/test-utils", - "license": "ISC", - "private": true, - "version": "1.0.0", - "description": "", - "type": "module", - "main": "src/main.ts", - "types": "src/main.ts", - "author": "Eric Fennis" -} diff --git a/tools/test-utils/src/getElementAttributes.ts b/tools/test-utils/src/getElementAttributes.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tools/test-utils/src/getOriginalIconSVG.ts b/tools/test-utils/src/getOriginalIconSVG.ts deleted file mode 100644 index 03b1109e2f..0000000000 --- a/tools/test-utils/src/getOriginalIconSVG.ts +++ /dev/null @@ -1,17 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { parseSync, stringify } from 'svgson'; - -const ICONS_DIR = path.resolve(__dirname, '../../../icons'); - -const getOriginalIconSVG = (iconName:string, aliasName:string) => { - const svgContent = fs.readFileSync(path.join(ICONS_DIR, `${iconName}.svg`), 'utf8'); - const svgParsed = parseSync(svgContent); - - svgParsed.attributes['data-lucide'] = aliasName ?? iconName; - svgParsed.attributes['class'] = `lucide lucide-${aliasName ?? iconName}`; - - return stringify(svgParsed, { selfClose: false }); -}; - -export default getOriginalIconSVG; diff --git a/tools/test-utils/src/main.ts b/tools/test-utils/src/main.ts deleted file mode 100644 index d2b4ad12a9..0000000000 --- a/tools/test-utils/src/main.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as getOriginalIconSVG } from './getOriginalIconSVG'; From 4ae0d4b97618db8067c0bb1bd05a7e17475f48bf Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Mon, 1 Apr 2024 12:24:03 +0200 Subject: [PATCH 21/27] Update package lock --- packages/lucide-react-native/src/lucide-react-native.ts | 2 +- pnpm-lock.yaml | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/lucide-react-native/src/lucide-react-native.ts b/packages/lucide-react-native/src/lucide-react-native.ts index 53d94599ec..98cd424a0b 100644 --- a/packages/lucide-react-native/src/lucide-react-native.ts +++ b/packages/lucide-react-native/src/lucide-react-native.ts @@ -1,7 +1,7 @@ export * from './icons'; export * as icons from './icons'; export * from './aliases'; -export * from './types' +export * from './types'; export { default as createLucideIcon } from './createLucideIcon'; export { default as useIconComponent } from './useIconComponent'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d30442b41b..3e6ab511ee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -781,8 +781,6 @@ importers: specifier: ^5.12.0 version: 5.12.0(rollup@4.9.6) - tools/test-utils: {} - packages: /@aashutoshrathi/word-wrap@1.2.6: From 2a792b824545c5fe925b03a8a6bba917e83d4210 Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Fri, 5 Apr 2024 10:27:14 +0200 Subject: [PATCH 22/27] Remove `useIconComponent` --- docs/guide/packages/lucide-preact.md | 20 +-- docs/guide/packages/lucide-react-native.md | 20 +-- docs/guide/packages/lucide-react.md | 20 +-- docs/guide/packages/lucide-vue-next.md | 21 +-- packages/lucide-preact/src/lucide-preact.ts | 1 - .../lucide-preact/src/useIconComponent.ts | 46 ----- .../useIconComponent.spec.tsx.snap | 96 ----------- .../tests/useIconComponent.spec.tsx | 29 ---- .../src/lucide-react-native.ts | 1 - .../src/useIconComponent.ts | 42 ----- .../useIconComponent.spec.tsx.snap | 158 ------------------ .../tests/useIconComponent.spec.tsx | 31 ---- packages/lucide-react/src/lucide-react.ts | 1 - packages/lucide-react/src/useIconComponent.ts | 43 ----- .../useIconComponent.spec.tsx.snap | 96 ----------- .../tests/useIconComponent.spec.tsx | 29 ---- packages/lucide-solid/src/lucide-solid.ts | 1 - .../lucide-solid/src/useIconComponent.tsx | 53 ------ .../useIconComponent.spec.tsx.snap | 109 ------------ .../tests/useIconComponent.spec.tsx | 29 ---- .../lucide-vue-next/src/lucide-vue-next.ts | 1 - .../lucide-vue-next/src/useIconComponent.ts | 47 ------ .../useIconComponent.spec.ts.snap | 29 ---- .../tests/useIconComponent.spec.ts | 27 --- 24 files changed, 4 insertions(+), 946 deletions(-) delete mode 100644 packages/lucide-preact/src/useIconComponent.ts delete mode 100644 packages/lucide-preact/tests/__snapshots__/useIconComponent.spec.tsx.snap delete mode 100644 packages/lucide-preact/tests/useIconComponent.spec.tsx delete mode 100644 packages/lucide-react-native/src/useIconComponent.ts delete mode 100644 packages/lucide-react-native/tests/__snapshots__/useIconComponent.spec.tsx.snap delete mode 100644 packages/lucide-react-native/tests/useIconComponent.spec.tsx delete mode 100644 packages/lucide-react/src/useIconComponent.ts delete mode 100644 packages/lucide-react/tests/__snapshots__/useIconComponent.spec.tsx.snap delete mode 100644 packages/lucide-react/tests/useIconComponent.spec.tsx delete mode 100644 packages/lucide-solid/src/useIconComponent.tsx delete mode 100644 packages/lucide-solid/tests/__snapshots__/useIconComponent.spec.tsx.snap delete mode 100644 packages/lucide-solid/tests/useIconComponent.spec.tsx delete mode 100644 packages/lucide-vue-next/src/useIconComponent.ts delete mode 100644 packages/lucide-vue-next/tests/__snapshots__/useIconComponent.spec.ts.snap delete mode 100644 packages/lucide-vue-next/tests/useIconComponent.spec.ts diff --git a/docs/guide/packages/lucide-preact.md b/docs/guide/packages/lucide-preact.md index f113fe0eaa..ec884b9676 100644 --- a/docs/guide/packages/lucide-preact.md +++ b/docs/guide/packages/lucide-preact.md @@ -71,27 +71,9 @@ const App = () => { Lucide Lab is a collection of icons that are not part of the official lucide library. -They can be used by using the `useIconComponent` or the `Icon` component. +They can be used by using the `Icon` component. For both methods, all props like regular lucide icons can be passed to adjust the icon appearance. -### Using `useIconComponent` - -This creates multiple icons based on the iconNodes passed in an object and returns an object with components that can be used as Lucide components. - -```jsx -import { useIconComponent } from 'lucide-preact'; -import { burger, sausage } from '@lucide/lab'; - -const { Burger, Pizza } = useIconComponent({ burger, sausage }) - -const App = () => ( - <> - - - -); -``` - ### Using the `Icon` component This creates a single icon based on the iconNode passed and renders a Lucide icon component. diff --git a/docs/guide/packages/lucide-react-native.md b/docs/guide/packages/lucide-react-native.md index 771af33895..39885c0e6b 100644 --- a/docs/guide/packages/lucide-react-native.md +++ b/docs/guide/packages/lucide-react-native.md @@ -65,27 +65,9 @@ const App = () => { Lucide Lab is a collection of icons that are not part of the official lucide library. -They can be used by using the `useIconComponent` or the `Icon` component. +They can be used by using the `Icon` component. For both methods, all props like regular lucide icons can be passed to adjust the icon appearance. -### Using `useIconComponent` - -This creates multiple icons based on the iconNodes passed in an object and returns an object with components that can be used as Lucide components. - -```jsx -import { useIconComponent } from 'lucide-react-native'; -import { burger, sausage } from '@lucide/lab'; - -const { Burger, Pizza } = useIconComponent({ burger, sausage }) - -const App = () => ( - <> - - - -); -``` - ### Using the `Icon` component This creates a single icon based on the iconNode passed and renders a Lucide icon component. diff --git a/docs/guide/packages/lucide-react.md b/docs/guide/packages/lucide-react.md index 7705eafb27..d40e5b55aa 100644 --- a/docs/guide/packages/lucide-react.md +++ b/docs/guide/packages/lucide-react.md @@ -65,27 +65,9 @@ const App = () => { Lucide Lab is a collection of icons that are not part of the official lucide library. -They can be used by using the `useIconComponent` or the `Icon` component. +They can be used by using the `Icon` component. For both methods, all props like regular lucide icons can be passed to adjust the icon appearance. -### Using `useIconComponent` - -This creates multiple icons based on the iconNodes passed in an object and returns an object with components that can be used as Lucide components. - -```jsx -import { useIconComponent } from 'lucide-react'; -import { burger, sausage } from '@lucide/lab'; - -const { Burger, Pizza } = useIconComponent({ burger, sausage }) - -const App = () => ( - <> - - - -); -``` - ### Using the `Icon` component This creates a single icon based on the iconNode passed and renders a Lucide icon component. diff --git a/docs/guide/packages/lucide-vue-next.md b/docs/guide/packages/lucide-vue-next.md index 18b86604fe..98310aa337 100644 --- a/docs/guide/packages/lucide-vue-next.md +++ b/docs/guide/packages/lucide-vue-next.md @@ -73,26 +73,9 @@ To customize the appearance of an icon, you can pass custom properties as props Lucide Lab is a collection of icons that are not part of the official lucide library. -They can be used by using the `useIconComponent` or the `Icon` component. +They can be used by using the `Icon` component. For both methods, all props like regular lucide icons can be passed to adjust the icon appearance. -### Using `useIconComponent` - -This creates multiple icons based on the iconNodes passed in an object and returns an object with components that can be used as Lucide components. - -```vue - - - -``` ### Using the `Icon` component @@ -102,8 +85,6 @@ This creates a single icon based on the iconNode passed and renders a Lucide ico