Setup solid 2

This commit is contained in:
Eric Fennis
2026-07-10 11:51:42 +02:00
parent ab61fa277b
commit 63353ad207
22 changed files with 1490 additions and 919 deletions

87
packages/solid/README.md Normal file
View File

@@ -0,0 +1,87 @@
<p align="center">
<a href="https://github.com/lucide-icons/lucide">
<img src="https://lucide.dev/package-logos/lucide-solid.svg" alt="Lucide icon library for Solid applications." width="540">
</a>
</p>
<p align="center">
Lucide icon library for Solid v2 applications.
</p>
<div align="center">
[![npm](https://img.shields.io/npm/v/lucide-solid?color=blue)](https://www.npmjs.com/package/lucide-solid)
![NPM Downloads](https://img.shields.io/npm/dw/lucide-solid)
[![License](https://img.shields.io/badge/license-ISC-green)](https://lucide.dev/license)
</div>
<p align="center">
<a href="https://lucide.dev/guide/">About</a>
·
<a href="https://lucide.dev/icons/">Icons</a>
·
<a href="https://lucide.dev/guide/solid">Documentation</a>
·
<a href="https://lucide.dev/license">License</a>
</p>
# Lucide Solid
Implementation of the Lucide icon library for solid applications.
## Installation
```sh
pnpm add @lucide/solid
```
```sh
npm install @lucide/solid
```
```sh
yarn add @lucide/solid
```
```sh
bun add @lucide/solid
```
## Documentation
For full documentation, visit [lucide.dev](https://lucide.dev/guide/packages/lucide-solid)
## Community
Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintainers and other users.
## License
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
<img src="https://lucide.dev/vercel.svg" alt="Powered by Vercel" width="200" />
</a>
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
### Hero backers 🦸
<a href="https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme Open-source PDF generation library built with TypeScript and React." /></a>
<a href="https://www.paxhistoria.co/"><img src="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia An alternate history sandbox game" /></a>
### Backers ☕
<a href="https://www.fina.money/"><img src="https://lucide.dev/sponsors/fina-money.png" width="180" alt="Fina Money Modular Finance Tracker" /></a>
### Other contributors 💸
You can find all our past and non-recurring financial contributors at [our Open Collective page](https://opencollective.com/lucide-icons).

View File

@@ -0,0 +1,94 @@
{
"name": "@lucide/solid",
"description": "A Lucide icon library package for Solid v2 applications.",
"version": "0.0.1",
"license": "ISC",
"homepage": "https://lucide.dev",
"bugs": "https://github.com/lucide-icons/lucide/issues",
"repository": {
"type": "git",
"url": "https://github.com/lucide-icons/lucide.git",
"directory": "packages/solid"
},
"keywords": [
"Lucide",
"Solid",
"SolidJS",
"Feather",
"Icons",
"Icon",
"SVG",
"Feather Icons",
"Fontawesome",
"Font Awesome"
],
"author": "Eric Fennis",
"source": "src/lucide-solid.ts",
"main": "dist/cjs/lucide-solid.js",
"module": "dist/esm/lucide-solid.mjs",
"types": "dist/types/lucide-solid.d.ts",
"files": [
"dist"
],
"exports": {
".": {
"types": "./dist/types/lucide-solid.d.ts",
"solid": "./dist/source/lucide-solid.jsx",
"import": "./dist/esm/lucide-solid.mjs",
"browser": "./dist/esm/lucide-solid.mjs",
"require": "./dist/cjs/lucide-solid.js"
},
"./icons": {
"types": "./dist/types/lucide-solid.d.ts",
"solid": "./dist/source/lucide-solid.jsx",
"import": "./dist/esm/lucide-solid.mjs",
"browser": "./dist/esm/lucide-solid.mjs",
"require": "./dist/cjs/lucide-solid.js"
},
"./icons/*": {
"types": "./dist/types/icons/*.d.ts",
"solid": "./dist/source/icons/*.jsx",
"import": "./dist/esm/icons/*.mjs",
"browser": "./dist/esm/icons/*.mjs",
"require": "./dist/cjs/icons/*.js"
}
},
"sideEffects": false,
"scripts": {
"build": "pnpm clean && pnpm copy:license && pnpm build:icons && pnpm build:bundle",
"copy:license": "cp ../../LICENSE ./LICENSE",
"clean": "rm -rf dist && rm -rf stats && rm -rf ./src/icons/*.ts",
"build:transpile": "tsc --jsx preserve -t es2020 --rootDir src --outDir dist --noEmit false",
"build:version": "node ./scripts/replaceVersion.mjs",
"build:bundle": "rollup -c rollup.config.mjs",
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --withAliases --separateAliasesFile --separateAliasesFileIgnore=fingerprint --aliasesFileExtension=.ts --iconFileExtension=.tsx --exportFileName=index.ts",
"typecheck": "tsc",
"typecheck:watch": "tsc -w",
"test": "pnpm build:icons && vitest run",
"test:watch": "vitest watch"
},
"devDependencies": {
"@babel/core": "^7.23.9",
"@babel/preset-env": "^7.23.9",
"@babel/preset-typescript": "^7.23.3",
"@lucide/build-icons": "workspace:*",
"@lucide/rollup-plugins": "workspace:*",
"@lucide/shared": "workspace:*",
"@rollup/plugin-babel": "^6.0.4",
"@solidjs/testing-library": "1.0.0-beta.2",
"@solidjs/web": "2.0.0-beta.16",
"@testing-library/jest-dom": "^6.8.0",
"babel-preset-solid": "2.0.0-beta.16",
"esbuild": "^0.28.1",
"jest-serializer-html": "^7.1.0",
"rollup": "^4.59.0",
"solid-js": "2.0.0-beta.16",
"typescript": "^5.8.3",
"vite": "^7.2.4",
"vite-plugin-solid": "^2.11.6"
},
"peerDependencies": {
"solid-js": "2.0.0-beta.16",
"@solidjs/web": "2.0.0-beta.16"
}
}

View File

@@ -0,0 +1,135 @@
import path from 'path';
import { babel } from '@rollup/plugin-babel';
import esbuild from 'esbuild';
import plugins from '@lucide/rollup-plugins';
import ts from 'typescript';
import pkg from './package.json' with { type: 'json' };
const packageName = 'LucideSolid';
const outputFileName = 'lucide-solid';
const outputDir = 'dist';
const inputs = ['src/lucide-solid.ts'];
const bundles = [
{
format: 'cjs',
inputs,
outputDir,
preserveModules: true,
},
{
format: 'esm',
inputs,
outputDir,
preserveModules: true,
extension: 'mjs',
},
];
const configs = bundles
.map(({ inputs, outputDir, format, preserveModules, extension = 'js' }) =>
inputs.map((input) => ({
input,
plugins: [
babel({
extensions: ['.ts', '.tsx', '.js', '.jsx'],
babelHelpers: 'bundled',
presets: [
'babel-preset-solid',
'@babel/preset-typescript',
['@babel/preset-env', { bugfixes: true, targets: 'last 2 years' }],
],
}),
...plugins({
pkg,
withEsbuild: false,
}),
format === 'esm'
? {
name: 'ts',
buildEnd() {
// Transpile typescript to './dist/source'
esbuild.build({
entryPoints: ['./src/**/*.tsx', './src/**/*.ts'],
outdir: './dist/source',
outExtension: {
'.js': '.jsx',
},
loader: {
'.js': 'jsx',
},
jsx: 'preserve',
jsxImportSource: 'solid-js',
bundle: true,
format: 'esm',
sourcemap: true,
target: ['esnext'],
banner: {
js: `/**
* @license ${pkg.name} v${pkg.version} - ${pkg.license}
*
* This source code is licensed under the ${pkg.license} license.
* See the LICENSE file in the root directory of this source tree.
*/`,
},
plugins: [
{
name: 'externalize-everything-except-own-dependencies',
setup(build) {
build.onResolve({ filter: /(.*)/ }, (args) => {
const modulePath = path.join(args.resolveDir, args.path);
if (
args.kind === 'import-statement' &&
args.path !== '@lucide/shared' &&
!modulePath.includes('packages/shared')
) {
return { path: args.path, external: true };
}
});
},
},
],
external: ['solid-js'],
});
// Generate types
const program = ts.createProgram([pkg.source], {
target: ts.ScriptTarget.ESNext,
module: ts.ModuleKind.ESNext,
moduleResolution: ts.ModuleResolutionKind.NodeJs,
jsx: ts.JsxEmit.Preserve,
jsxImportSource: 'solid-js',
allowSyntheticDefaultImports: true,
esModuleInterop: true,
declarationDir: `dist/types`,
declaration: true,
emitDeclarationOnly: true,
});
program.emit();
},
}
: null,
],
external: ['solid-js', '@solidjs/web', 'solid-js/store'],
output: {
name: packageName,
...(preserveModules
? {
dir: `${outputDir}/${format}`,
exports: 'auto',
entryFileNames: `[name].${extension}`,
}
: {
file: `${outputDir}/${format}/${outputFileName}.${extension}`,
}),
format: format === 'source' ? 'esm' : format,
preserveModules,
preserveModulesRoot: 'src',
sourcemap: true,
},
})),
)
.flat();
export default configs;

View File

@@ -0,0 +1,38 @@
import base64SVG from '@lucide/build-icons/utils/base64SVG';
import defineExportTemplate from '@lucide/build-icons/utils/defineExportTemplate';
export default defineExportTemplate(async ({
componentName,
iconName,
children,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `
import Icon from '../Icon';
import type { IconNode, LucideProps } from '../types';
const iconNode: IconNode = ${JSON.stringify(children)};
/**
* @component @name ${componentName}
* @description Lucide SVG icon component, renders SVG Element with children.
*
* @preview ![img](data:image/svg+xml;base64,${svgBase64}) - https://lucide.dev/icons/${iconName}
* @see https://lucide.dev/guide/packages/lucide-solid - Documentation
*
* @param {Object} props - Lucide icons props and any valid SVG attribute
* @returns {JSX.Element} JSX Element
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/
const ${componentName} = (props: LucideProps) => (
<Icon {...props} iconNode={iconNode} name="${iconName}" />
)
export default ${componentName};
`;
});

View File

@@ -0,0 +1,78 @@
import { For, omit, useContext } from 'solid-js';
import { Dynamic } from '@solidjs/web';
import defaultAttributes from './defaultAttributes';
import { IconNode, LucideProps } from './types';
import { LucideContext } from './context';
import { hasA11yProp, mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared';
import { JSX } from '@solidjs/web';
interface IconProps {
name?: string;
iconNode: IconNode;
}
const Icon = (props: LucideProps & IconProps) => {
const [localProps, rest] = omit(props, [
'color',
'size',
'strokeWidth',
'children',
'class',
'name',
'iconNode',
'absoluteStrokeWidth',
]);
const globalProps = useContext(LucideContext);
return (
<svg
{...defaultAttributes}
width={localProps.size ?? globalProps.size ?? defaultAttributes.width}
height={localProps.size ?? globalProps.size ?? defaultAttributes.height}
stroke={localProps.color ?? globalProps.color ?? defaultAttributes.stroke}
stroke-width={
(localProps.absoluteStrokeWidth ?? globalProps.absoluteStrokeWidth) === true
? (Number(
localProps.strokeWidth ??
globalProps.strokeWidth ??
defaultAttributes['stroke-width'],
) *
24) /
Number(localProps.size ?? globalProps.size)
: Number(
localProps.strokeWidth ??
globalProps.strokeWidth ??
defaultAttributes['stroke-width'],
)
}
class={mergeClasses(
'lucide',
'lucide-icon',
globalProps.class,
...(localProps.name != null
? [
`lucide-${toKebabCase(toPascalCase(localProps.name))}`,
`lucide-${toKebabCase(localProps.name)}`,
]
: []),
localProps.class,
)}
aria-hidden={!localProps.children && !hasA11yProp(rest) ? 'true' : undefined}
{...rest}
>
<For each={localProps.iconNode}>
{([elementName, attrs]) => {
return (
<Dynamic
component={elementName}
{...attrs}
/>
);
}}
</For>
</svg>
);
};
export default Icon;

View File

@@ -0,0 +1,3 @@
export * from './aliases';
export * from './prefixed';
export * from './suffixed';

View File

@@ -0,0 +1,37 @@
import { createContext, omit } from 'solid-js';
import { type JSX } from '@solidjs/web';
export const LucideContext = createContext<{
size?: number;
color?: string;
strokeWidth?: number;
absoluteStrokeWidth?: boolean;
class?: string;
}>({
size: 24,
color: 'currentColor',
strokeWidth: 2,
absoluteStrokeWidth: false,
class: '',
});
interface LucideProviderProps {
children: JSX.Element;
size?: number;
color?: string;
strokeWidth?: number;
absoluteStrokeWidth?: boolean;
class?: string;
}
export function LucideProvider(props: LucideProviderProps) {
const [value, rest] = omit(props, [
'size',
'color',
'strokeWidth',
'absoluteStrokeWidth',
'class',
]);
return <LucideContext.Provider value={value}>{rest.children}</LucideContext.Provider>;
}

View File

@@ -0,0 +1,15 @@
import { type SVGAttributes } from './types';
const defaultAttributes: SVGAttributes = {
xmlns: 'http://www.w3.org/2000/svg',
width: 24,
height: 24,
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': 2,
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
};
export default defaultAttributes;

View File

@@ -0,0 +1 @@
Folder for generated icons

View File

@@ -0,0 +1,7 @@
export * from './icons';
export * as icons from './icons';
export * from './aliases';
export * from './types';
export * from './context';
export { default as Icon } from './Icon';

View File

@@ -0,0 +1,15 @@
import { type JSX } from '@solidjs/web';
export type IconNode = [elementName: keyof JSX.IntrinsicElements, attrs: Record<string, string>][];
export type SVGAttributes = Partial<JSX.SvgSVGAttributes<SVGSVGElement>>;
export interface LucideProps extends SVGAttributes {
key?: string | number;
class?: string;
size?: string | number;
color?: string;
strokeWidth?: string | number;
absoluteStrokeWidth?: boolean;
}
export type LucideIcon = (props: LucideProps) => JSX.Element;

View File

@@ -0,0 +1,106 @@
import { describe, it, expect } from 'vitest';
import { render } 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(() => (
<Icon
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>
));
expect(container.firstChild).toBeDefined();
});
it('should render icon and match snapshot', async () => {
const { container } = render(() => (
<Icon
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>
));
expect(container.firstChild).toMatchSnapshot();
});
});
describe('Icon Component Accessibility', () => {
it('should have aria-hidden prop when no aria prop is present', async () => {
const { container } = render(() => (
<Icon
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
/>
));
expect(container.firstChild).toHaveAttribute('aria-hidden', 'true');
});
it('should not have aria-hidden prop when aria prop is present', async () => {
const { container } = render(() => (
<Icon
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
aria-label="Air conditioning"
/>
));
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
});
it('should not have aria-hidden prop when title prop is present', async () => {
const { container } = render(() => (
<Icon
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
// @ts-expect-error
title="Air conditioning"
/>
));
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
});
it('should not have aria-hidden prop when there are children that could be a <title> element', async () => {
const { container } = render(() => (
<Icon
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
>
<title>Some title</title>
</Icon>
));
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
});
it('should never override aria-hidden prop', async () => {
const { container } = render(() => (
<Icon
iconNode={airVent}
size={48}
stroke="red"
absoluteStrokeWidth
aria-hidden={false}
/>
));
expect(container.firstChild).toHaveAttribute('aria-hidden', 'false');
});
});

View File

@@ -0,0 +1,34 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using Icon Component > should render icon and match snapshot 1`] = `
<svg
aria-hidden="true"
class="lucide lucide-icon"
fill="none"
height="48"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
>
<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"
/>
</svg>
`;

View File

@@ -0,0 +1,26 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using LucideProvider > should render the icon with LucideProvider 1`] = `
<svg
aria-hidden="true"
class="lucide lucide-icon lucide-house"
fill="none"
height="48"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"
key="5wwlr5"
/>
<path
d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
key="r6nss1"
/>
</svg>
`;

View File

@@ -0,0 +1,123 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using lucide icon components > should adjust the size, stroke color and stroke width 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
viewbox="0 0 24 24"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
width="48"
height="48"
stroke="red"
stroke-width="4"
class="lucide lucide-icon lucide-grid3x3 lucide-grid-3x3"
aria-hidden="true"
data-testid="grid-icon"
>
<rect width="18"
height="18"
x="3"
y="3"
rx="2"
key="afitv7"
>
</rect>
<path d="M3 9h18"
key="1pudct"
>
</path>
<path d="M3 15h18"
key="5xshup"
>
</path>
<path d="M9 3v18"
key="fh3hqa"
>
</path>
<path d="M15 3v18"
key="14nvp0"
>
</path>
</svg>
`;
exports[`Using lucide icon components > should not scale the strokeWidth when absoluteStrokeWidth is set 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
viewbox="0 0 24 24"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
width="48"
height="48"
stroke="red"
stroke-width="1"
class="lucide lucide-icon lucide-grid3x3 lucide-grid-3x3"
aria-hidden="true"
data-testid="grid-icon"
>
<rect width="18"
height="18"
x="3"
y="3"
rx="2"
key="afitv7"
>
</rect>
<path d="M3 9h18"
key="1pudct"
>
</path>
<path d="M3 15h18"
key="5xshup"
>
</path>
<path d="M9 3v18"
key="fh3hqa"
>
</path>
<path d="M15 3v18"
key="14nvp0"
>
</path>
</svg>
`;
exports[`Using lucide icon components > should render a component 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
viewbox="0 0 24 24"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
width="24"
height="24"
stroke="currentColor"
stroke-width="2"
class="lucide lucide-icon lucide-grid3x3 lucide-grid-3x3"
aria-hidden="true"
>
<rect width="18"
height="18"
x="3"
y="3"
rx="2"
key="afitv7"
>
</rect>
<path d="M3 9h18"
key="1pudct"
>
</path>
<path d="M3 15h18"
key="5xshup"
>
</path>
<path d="M9 3v18"
key="fh3hqa"
>
</path>
<path d="M15 3v18"
key="14nvp0"
>
</path>
</svg>
`;

View File

@@ -0,0 +1,102 @@
import { render } from '@solidjs/testing-library';
import { describe, expect, it } from 'vitest';
import { House, LucideProvider } from '../src/lucide-solid';
describe('Using LucideProvider', () => {
it('should render the icon with LucideProvider', () => {
const { container } = render(() => (
<LucideProvider
size={48}
color="red"
>
<House />
</LucideProvider>
));
expect(container.firstChild).toMatchSnapshot();
});
it('should render the icon with default props when no provider is used', () => {
const { container } = render(() => <House />);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '24');
expect(IconComponent).toHaveAttribute('height', '24');
expect(IconComponent).toHaveAttribute('stroke', 'currentColor');
expect(IconComponent).toHaveAttribute('stroke-width', '2');
});
it('should render the icon with LucideProvider and custom strokeWidth', () => {
const { container } = render(() => (
<LucideProvider
size={48}
color="red"
strokeWidth={4}
>
<House />
</LucideProvider>
));
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '48');
expect(IconComponent).toHaveAttribute('height', '48');
expect(IconComponent).toHaveAttribute('stroke', 'red');
expect(IconComponent).toHaveAttribute('stroke-width', '4');
});
it('should render the icon with LucideProvider and custom absoluteStrokeWidth', () => {
const { container } = render(() => (
<LucideProvider
size={48}
color="red"
absoluteStrokeWidth
>
<House />
</LucideProvider>
));
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('stroke-width', '1');
});
it("should override the provider's global props when passing props to the icon", () => {
const { container } = render(() => (
<LucideProvider
size={48}
color="red"
strokeWidth={4}
>
<House
size={24}
color="blue"
strokeWidth={2}
/>
</LucideProvider>
));
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '24');
expect(IconComponent).toHaveAttribute('height', '24');
expect(IconComponent).toHaveAttribute('stroke', 'blue');
expect(IconComponent).toHaveAttribute('stroke-width', '2');
});
it('should merge className from provider and icon', () => {
const { container } = render(() => (
<LucideProvider class="provider-class">
<House class="icon-class" />
</LucideProvider>
));
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute(
'class',
'lucide lucide-icon provider-class lucide-house icon-class',
);
});
});

View File

@@ -0,0 +1,90 @@
import { describe, it, expect } from 'vitest';
import { render, cleanup } from '@solidjs/testing-library';
import { Edit2, Grid, Pen, Droplet } from '../src/lucide-solid';
describe('Using lucide icon components', () => {
it('should render a component', () => {
const { container } = render(() => <Grid />);
expect(container.innerHTML).toMatchSnapshot();
});
it('should adjust the size, stroke color and stroke width', async () => {
const testId = 'grid-icon';
const { container, getByTestId } = render(() => (
<Grid
data-testid={testId}
size={48}
color="red"
strokeWidth={4}
/>
));
const { attributes } = (await getByTestId(testId)) as unknown as {
attributes: Record<string, { value: string }>;
};
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');
expect(container.innerHTML).toMatchSnapshot();
});
it('should render the alias icon', () => {
const testId = 'pen-icon';
const { container } = render(() => (
<Pen
data-testid={testId}
size={48}
stroke="red"
strokeWidth={4}
/>
));
const PenIconRenderedHTML = container.innerHTML;
cleanup();
const { container: Edit2Container } = render(() => (
<Edit2
data-testid={testId}
size={48}
stroke="red"
strokeWidth={4}
/>
));
expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML);
});
it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => {
const testId = 'grid-icon';
const { container, getByTestId } = render(() => (
<Grid
data-testid={testId}
size={48}
color="red"
absoluteStrokeWidth
/>
));
const { attributes } = getByTestId(testId) as unknown as {
attributes: Record<string, { value: string }>;
};
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();
});
it('should add all classes to the element', () => {
const testClass = 'my-class';
const { container } = render(() => <Droplet class={testClass} />);
expect(container.firstChild).toHaveClass(testClass);
expect(container.firstChild).toHaveClass('lucide');
expect(container.firstChild).toHaveClass('lucide-droplet');
});
});

View File

@@ -0,0 +1,10 @@
import { expect, afterEach } from 'vitest';
import { cleanup } from '@solidjs/testing-library';
import '@testing-library/jest-dom/vitest';
import htmlSerializer from 'jest-serializer-html';
expect.addSnapshotSerializer(htmlSerializer);
afterEach(() => {
cleanup();
});

View File

@@ -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' }],
];

View File

@@ -0,0 +1,18 @@
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"jsx": "preserve",
"jsxImportSource": "@solidjs/web",
"types": ["vite/client", "@testing-library/jest-dom"],
"noEmit": true,
"isolatedModules": true,
"outDir": "./dist",
"declarationDir": "./dist",
"declaration": true,
},
}

View File

@@ -0,0 +1,13 @@
import { defineConfig } from 'vitest/config'
import solidPlugin from 'vite-plugin-solid';
export default defineConfig({
// TODO: Remove this when Solid testing library has support for Vitest 1.0, see: https://github.com/solidjs/solid-testing-library/issues/52
// @ts-ignore
plugins: [solidPlugin()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './tests/setupVitest.js',
},
});

1355
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff