mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-11-14 13:58:21 +00:00
feat(@lucide/solid): Support for Solid 2.0 RC (#4751)
* Updates needed to support Solid 2 * Switch to the next tag * feat(packages): add `@lucide/solid` package for Solid 2 Solid 2's API is a breaking change from Solid 1 and is still an RC, so `lucide-solid` stays on Solid 1 (`solid-js: ^1.4.7`) untouched and Solid 2 support ships as a new `packages/solid` package named `@lucide/solid` — the same scheme already used by `@lucide/svelte` and `@lucide/angular`. The package is a copy of `lucide-solid` on the `@lucide/shared` build architecture, ported to Solid 2: - `splitProps` -> `omit`, `solid-js/web` -> `@solidjs/web`, `JSXElement` -> `Element`, `Context.Provider` -> `<Context value>` - `jsxImportSource` is `@solidjs/web` in tsconfig, the esbuild step and `ts.createProgram`; rollup externals drop the removed `solid-js/store` - `vite-plugin-solid` -> `@solidjs/vite-plugin` - tests keep upstream's coverage with two Solid 2 adaptations: boolean attribute literals must be strings (`aria-hidden="false"`), and writes are batched so the reactivity tests need a `flush()` before asserting Rendered output is unchanged — the snapshots are byte-identical to `lucide-solid`'s. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(solid): fix React leftovers in the Solid guide The Solid guide was derived from the React guide and three things were not carried across: - `advanced/typescript.md` still told readers the types are for "a TypeScript React project" - its `LucideIcon` example kept React's destructured-props signature. The `: ButtonProps` annotation had also been dropped in the copy, so `ButtonProps` was declared but unused and the parameters were implicitly `any`. Destructuring props breaks reactivity in Solid — verified: with `({ icon: Icon, label })` a changing `label` never reaches the DOM, while `props.label` updates correctly. - `advanced/aliased-names.md` had an orphaned `###` heading; its parent `## Choosing import name style` was dropped during the conversion because it documents React-only subpath exports. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: type the shared Sandpack wrapper with `SandpackProps` The base Sandpack wrapper declared its props as `FunctionalComponent<LucideProps>` with `LucideProps` imported from `@lucide/vue`, which has nothing to do with a Sandpack embed. None of the props the markdown plugin actually passes (`template`, `files`, `options`, `customSetup`, `theme`) were declared, so they only reached the inner `<Sandpack />` via Vue's fallthrough attributes. Declare `SandpackProps` from `sandpack-vue3` instead and forward them explicitly. This also removes the `@lucide/vue` import that made every framework guide — including the Solid one, which uses this wrapper directly — appear to reference the Vue package. Verified with a full `vitepress build`: each framework guide's chunk still carries its own template (react/vue/vite-svelte/vanilla/ vite-solid) and all five props reach the component. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(solid): document the Solid 2 flavour in the getting started guide The Solid guide teaches `lucide-solid` throughout (64 references), so the one-line note about `@lucide/solid` left Solid 2 readers on a page whose every install command and live example is Solid 1 — and the packages list points `@lucide/solid` at this same page. Replace that note with a "Using Solid 2?" callout holding its own tabbed install group, the import-specifier swap, and a table of the Solid 2 changes that affect code written around Lucide (`@solidjs/web`, `jsxImportSource`, `splitProps` -> `omit`, `<Ctx value>`), linking out to Solid's own migration guide for the rest. The claim that both packages expose the same components and props is checked, not assumed: `dist/types/lucide-solid.d.ts` is identical between the two packages, and the only differences anywhere in the public type surface are Solid's own renames (the `JSX` namespace source and `JSXElement` -> `Element`). Keeps one guide section for both packages, matching how upstream handles the two Svelte packages. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(solid): port lucide-solid to Solid 2 - Icon/context/types use Solid 2 APIs: omit() instead of splitProps(), Dynamic and JSX types from @solidjs/web. - solid-js / @solidjs/web 2.0 (rc) as peer and dev dependencies, babel-preset-solid 2; rollup and tsconfig updated to match. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Add integration test * Update rollup file * Fix tests * Revert lucide-solid changes * update lockfile * Fix docs * Format code --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Eric <eric.fennis@gmail.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
Eric
parent
0c0e3ab3ae
commit
4b3f93d9d0
@@ -82,6 +82,7 @@
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'packages/lucide-solid/*'
|
||||
- 'packages/solid/*'
|
||||
|
||||
# For changes in the Lucide astro package
|
||||
🚀 astro package:
|
||||
|
||||
@@ -53,6 +53,7 @@ jobs:
|
||||
'@lucide/angular',
|
||||
'@lucide/astro',
|
||||
'@lucide/icons',
|
||||
'@lucide/solid',
|
||||
'@lucide/svelte',
|
||||
'@lucide/vue',
|
||||
]
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
name: Lucide Solid 2 Checks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- next
|
||||
pull_request:
|
||||
paths:
|
||||
- packages/solid/**
|
||||
- packages/shared/**
|
||||
- tools/build-icons/**
|
||||
- tools/rollup-plugins/**
|
||||
- integrations/lucide-solid-2/**
|
||||
- package.json
|
||||
- pnpm-workspace.yaml
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: pnpm --filter @lucide/solid build
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Test
|
||||
run: pnpm --filter @lucide/solid test
|
||||
|
||||
integration:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Install Chromium
|
||||
run: pnpm --filter @lucide/integration-solid-2-vite exec playwright install --with-deps chromium
|
||||
|
||||
- name: Run integration checks
|
||||
run: pnpm test:integrations:solid-2
|
||||
@@ -23,6 +23,7 @@ packages/svelte/.svelte-kit
|
||||
integrations/**/.react-router
|
||||
integrations/**/next-env.d.ts
|
||||
integrations/**/routeTree.gen.ts
|
||||
integrations/**/file-routes.d.ts
|
||||
integrations/**/.vitest-attachments
|
||||
integrations/**/__screenshots__
|
||||
integrations/**/.output
|
||||
|
||||
@@ -41,7 +41,6 @@ export default eventHandler((event) => {
|
||||
<Diff
|
||||
oldSrc={oldSrc}
|
||||
newSrc={newSrc}
|
||||
showGrid
|
||||
height={height}
|
||||
width={width}
|
||||
>
|
||||
|
||||
@@ -48,7 +48,6 @@ export default eventHandler((event) => {
|
||||
<Diff
|
||||
oldSrc={oldSrc}
|
||||
newSrc={newSrc}
|
||||
showGrid
|
||||
height={height}
|
||||
width={width}
|
||||
>
|
||||
|
||||
@@ -18,11 +18,12 @@ export default eventHandler((event) => {
|
||||
return sendError(event, error);
|
||||
}
|
||||
|
||||
const width = getQuery(event).width || undefined;
|
||||
const height = getQuery(event).height || undefined;
|
||||
const color = getQuery(event).color || undefined;
|
||||
const strokeWidth = getQuery(event).strokeWidth || undefined;
|
||||
const background = getQuery(event).background || undefined;
|
||||
const query = getQuery<Record<string, string | undefined>>(event);
|
||||
const width = query.width || undefined;
|
||||
const height = query.height || undefined;
|
||||
const color = query.color || undefined;
|
||||
const strokeWidth = query.strokeWidth || undefined;
|
||||
const background = query.background || undefined;
|
||||
|
||||
const LucideIcon = createLucideIcon(params.iconName, iconNode);
|
||||
|
||||
|
||||
@@ -85,8 +85,26 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"lucide-react-native": {
|
||||
"@lucide/solid": {
|
||||
"order": 5,
|
||||
"icon": "solid",
|
||||
"docsAlias": "solid",
|
||||
"packageDirname": "solid",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "npm",
|
||||
"src": "https://img.shields.io/npm/v/@lucide/solid",
|
||||
"href": "https://www.npmjs.com/package/@lucide/solid"
|
||||
},
|
||||
{
|
||||
"alt": "npm",
|
||||
"src": "https://img.shields.io/npm/dw/@lucide/solid",
|
||||
"href": "https://www.npmjs.com/package/@lucide/solid"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lucide-react-native": {
|
||||
"order": 6,
|
||||
"icon": "react-native",
|
||||
"docsAlias": "react-native",
|
||||
"shields": [
|
||||
@@ -103,7 +121,7 @@
|
||||
]
|
||||
},
|
||||
"@lucide/angular": {
|
||||
"order": 6,
|
||||
"order": 7,
|
||||
"icon": "angular",
|
||||
"docsAlias": "angular",
|
||||
"packageDirname": "angular",
|
||||
|
||||
@@ -397,10 +397,14 @@ interface SvgPreviewProps extends React.SVGProps<SVGSVGElement> {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const SvgPreview = (
|
||||
{ src, children, height = 24, width = 24, showGrid = false, ...props }: SvgPreviewProps,
|
||||
ref,
|
||||
) => {
|
||||
const SvgPreview = ({
|
||||
src,
|
||||
children,
|
||||
height = 24,
|
||||
width = 24,
|
||||
showGrid = false,
|
||||
...props
|
||||
}: SvgPreviewProps) => {
|
||||
const subGridSize =
|
||||
Math.max(height, width) % 3 === 0
|
||||
? Math.max(height, width) > 24
|
||||
@@ -413,7 +417,6 @@ const SvgPreview = (
|
||||
|
||||
return (
|
||||
<svg
|
||||
ref={ref}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={width}
|
||||
height={height}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
<script lang="ts" setup>
|
||||
import { type FunctionalComponent } from 'vue';
|
||||
import { type LucideProps } from '@lucide/vue/src/types';
|
||||
import { Sandpack } from 'sandpack-vue3';
|
||||
import { Sandpack, type SandpackProps } from 'sandpack-vue3';
|
||||
|
||||
defineProps<FunctionalComponent<LucideProps>>();
|
||||
const props = defineProps<SandpackProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Sandpack />
|
||||
<Sandpack v-bind="props" />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -153,6 +153,7 @@ deno add lucide-solid
|
||||
```
|
||||
|
||||
:::
|
||||
> `@lucide/solid` is for Solid 2, for Solid 1 use the `lucide-solid` package. Solid 2 is still a release candidate.
|
||||
|
||||
For more details, see the [documentation](./solid/index.md).
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
} from "lucide-solid";
|
||||
```
|
||||
|
||||
### Turn off autocomplete in your IDE
|
||||
## Turn off autocomplete in your IDE
|
||||
|
||||
```json [.vscode/settings.json]
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ description: Learn about the different types exported by the `lucide-solid` pack
|
||||
# TypeScript Support
|
||||
|
||||
List of exported types from the `lucide-solid` package.
|
||||
These can be used to type your components when using Lucide icons in a TypeScript React project
|
||||
These can be used to type your components when using Lucide icons in a TypeScript Solid project.
|
||||
|
||||
## `LucideProps`
|
||||
|
||||
@@ -61,10 +61,10 @@ interface ButtonProps {
|
||||
label: string;
|
||||
}
|
||||
|
||||
const IconButton = ({ icon: Icon, label }) => {
|
||||
const IconButton = (props: ButtonProps) => {
|
||||
return (
|
||||
<button aria-label={label}>
|
||||
<Icon size={16} />
|
||||
<button aria-label={props.label}>
|
||||
<props.icon size={16} />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -36,6 +36,52 @@ bun add lucide-solid
|
||||
|
||||
:::
|
||||
|
||||
:::: info Using Solid 2?
|
||||
|
||||
Lucide ships one package per major Solid version. This guide covers **Solid 1** and the `lucide-solid` package. If your app is on Solid 2, install `@lucide/solid` instead:
|
||||
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm add @lucide/solid
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
yarn add @lucide/solid
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npm install @lucide/solid
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun add @lucide/solid
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Both packages export the same icon components with the same props, so the rest of this guide applies unchanged — only the import specifier differs:
|
||||
|
||||
```jsx
|
||||
import { Camera } from '@lucide/solid';
|
||||
```
|
||||
|
||||
What does differ is Solid itself. The changes most likely to affect the code you write around Lucide:
|
||||
|
||||
| | `lucide-solid` | `@lucide/solid` |
|
||||
| -------------------- | ----------------------------- | ----------------------- |
|
||||
| Solid version | `^1.4.7` | `^2.0.0-rc.0` |
|
||||
| DOM runtime package | `solid-js/web` | `@solidjs/web` |
|
||||
| `jsxImportSource` | `solid-js` | `@solidjs/web` |
|
||||
| Splitting icon props | `splitProps(props, ['size'])` | `omit(props, 'size')` |
|
||||
| Providing context | `<Ctx.Provider value={…}>` | `<Ctx value={…}>` |
|
||||
|
||||
See the [Solid 2 migration guide](https://github.com/solidjs/solid/blob/next/documentation/solid-2.0/MIGRATION.md) for the full list.
|
||||
|
||||
Solid 2 is still a release candidate, so treat `@lucide/solid` as pre-release too. The live examples throughout this guide run on Solid 1.
|
||||
|
||||
::::
|
||||
|
||||
## Importing your first icon
|
||||
|
||||
Lucide is built with ES Modules, so it's completely tree-shakable.
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
"@lucide/helpers": "workspace:*",
|
||||
"@lucide/shared": "workspace:*",
|
||||
"@rollup/plugin-replace": "^6.0.3",
|
||||
"@types/react": "^19.3.0",
|
||||
"@types/react-dom": "^19.3.0",
|
||||
"@types/semver": "^7.8.0",
|
||||
"h3": "^1.15.11",
|
||||
"markdown-it": "^15.0.1",
|
||||
|
||||
@@ -34,6 +34,7 @@ export default defineConfig([
|
||||
'docs/**/examples/',
|
||||
'docs/.vitepress/theme/components/editors/preact/index.js',
|
||||
'packages/svelte/.svelte-kit',
|
||||
'integrations/**/file-routes.d.ts',
|
||||
// Tracked in git despite matching a .gitignore pattern, so lint it.
|
||||
'!packages/lucide-react/dynamicIconImports.mjs',
|
||||
],
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# Lucide Solid 2 integrations
|
||||
|
||||
These fixtures exercise the built `@lucide/solid` package in a real Solid 2 (release candidate) consumer app.
|
||||
|
||||
The `vite` fixture was scaffolded with the Solid 2.0 `basic` template:
|
||||
|
||||
```sh
|
||||
npm create solid@latest -- --solid -t basic --ts
|
||||
```
|
||||
|
||||
It keeps the template's turnkey client mode (`@solidjs/vite-plugin` with `start: true`, `filesystem-routing` and `@solidjs/router`), with the demo pages replaced by the icon showcase. The template's tooling versions are aligned with the workspace: TypeScript 6 and the workspace's pinned Vitest 4.
|
||||
|
||||
There is no SolidStart fixture yet, because SolidStart still targets Solid 1. The Solid 1 fixtures for `lucide-solid` live in [`../lucide-solid`](../lucide-solid).
|
||||
|
||||
Every fixture runs three checks:
|
||||
|
||||
- a framework production build;
|
||||
- TypeScript with `noEmit`;
|
||||
- Vitest Browser Mode in Chromium.
|
||||
|
||||
From the repository root, install Chromium once and run the complete suite:
|
||||
|
||||
```sh
|
||||
pnpm --filter @lucide/integration-solid-2-vite exec playwright install chromium
|
||||
pnpm test:integrations:solid-2
|
||||
```
|
||||
|
||||
The fixtures depend on `@lucide/solid` through `workspace:*`, but import only its public package entry points. The root command builds `@lucide/solid` before running any consumer checks.
|
||||
|
||||
## Two TypeScript projects per fixture
|
||||
|
||||
`tsconfig.json` typechecks the fixture's own sources with `skipLibCheck: true`.
|
||||
|
||||
`tsconfig.declarations.json` compiles only `src/IconShowcase.tsx` (the file that imports the public entry points) with `skipLibCheck: false` and no ambient `types`. This makes `tsc` read the declarations shipped in `dist/types`, so a `.d.ts` that points at a file the build never emitted fails the check. Both run under `pnpm typecheck`.
|
||||
|
||||
`file-routes.d.ts` is generated by `filesystem-routing` on every build and is committed so `tsc` can resolve `virtual:file-routes` before a build has run.
|
||||
@@ -0,0 +1,71 @@
|
||||
// Generated by filesystem-routing. Do not edit.
|
||||
// Regenerated whenever a route file is added, changed or removed.
|
||||
|
||||
declare module "virtual:file-routes" {
|
||||
/** A code-split ref: the delivery adapter emits it as a dynamic import. */
|
||||
export interface FileRouteLazyRef<M = Record<string, unknown>> {
|
||||
src: string;
|
||||
import(): Promise<M>;
|
||||
}
|
||||
|
||||
/** An eager ref: its picked exports are imported statically. */
|
||||
export interface FileRouteEagerRef<M = Record<string, unknown>> {
|
||||
/** The source module path — present when the convention emitted the ref lazy and `codeSplitting: false` delivered it eagerly. */
|
||||
src?: string;
|
||||
require(): M;
|
||||
}
|
||||
|
||||
/** A flat entry of the route manifest. */
|
||||
export interface FileRouteEntry {
|
||||
path: string;
|
||||
page?: boolean;
|
||||
/** The page component is a server function; its `$component` is delivered eagerly. */
|
||||
server?: boolean;
|
||||
$component?: FileRouteLazyRef<any> | FileRouteEagerRef<any>;
|
||||
$$route?: FileRouteEagerRef<any>;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** An entry of the nested `pageRoutes` view. */
|
||||
export interface FileRouteTreeEntry extends FileRouteEntry {
|
||||
id: string;
|
||||
children?: readonly FileRouteTreeEntry[];
|
||||
}
|
||||
|
||||
/** The flat route manifest, in scan order. */
|
||||
const routes: readonly [
|
||||
{
|
||||
path: "/about";
|
||||
page: true;
|
||||
$component: FileRouteLazyRef<typeof import("./src/routes/about")>;
|
||||
$$route?: undefined;
|
||||
},
|
||||
{
|
||||
path: "/";
|
||||
page: true;
|
||||
$component: FileRouteLazyRef<typeof import("./src/routes/index")>;
|
||||
$$route?: undefined;
|
||||
}
|
||||
];
|
||||
export default routes;
|
||||
|
||||
/** The page entries, nested by path with grouping segments stripped. */
|
||||
export const pageRoutes: readonly [
|
||||
{
|
||||
path: "/";
|
||||
id: "/";
|
||||
page: true;
|
||||
$component: FileRouteLazyRef<typeof import("./src/routes/index")>;
|
||||
$$route?: undefined;
|
||||
children?: undefined;
|
||||
},
|
||||
{
|
||||
path: "/about";
|
||||
id: "/about";
|
||||
page: true;
|
||||
$component: FileRouteLazyRef<typeof import("./src/routes/about")>;
|
||||
$$route?: undefined;
|
||||
children?: undefined;
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "@lucide/integration-solid-2-vite",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"typecheck": "tsc --noEmit && tsc --noEmit --project tsconfig.declarations.json",
|
||||
"test:browser": "vitest run",
|
||||
"check": "pnpm build && pnpm typecheck && pnpm test:browser"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lucide/solid": "workspace:*",
|
||||
"@solidjs/meta": "1.0.0-next.2",
|
||||
"@solidjs/router": "2.0.0-next.34",
|
||||
"@solidjs/web": "2.0.0-rc.13",
|
||||
"solid-js": "2.0.0-rc.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@solidjs/testing-library": "1.0.0-beta.3",
|
||||
"@solidjs/vite-plugin": "3.0.0-next.47",
|
||||
"@vitest/browser-playwright": "5.0.3",
|
||||
"filesystem-routing": "0.4.0",
|
||||
"playwright": "1.58.2",
|
||||
"typescript": "^6.0.3",
|
||||
"vite": "^8.3.2",
|
||||
"vitest": "5.0.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Loading } from 'solid-js';
|
||||
import { Router } from './router';
|
||||
|
||||
export default function App() {
|
||||
return <Router>{(props) => <Loading>{props.children}</Loading>}</Router>;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { HydrationScript } from '@solidjs/web';
|
||||
import type { ParentProps } from 'solid-js';
|
||||
|
||||
export default function Document(props: ParentProps) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1"
|
||||
/>
|
||||
<title>Lucide Solid 2 Vite integration</title>
|
||||
<HydrationScript />
|
||||
</head>
|
||||
<body>{props.children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Camera, Droplet, Edit2, House, LucideProvider, Pen } from '@lucide/solid';
|
||||
import Circle from '@lucide/solid/icons/circle';
|
||||
|
||||
export default function IconShowcase() {
|
||||
return (
|
||||
<main aria-label="Lucide integration">
|
||||
<Camera data-testid="static-icon" />
|
||||
<Droplet
|
||||
data-testid="custom-icon"
|
||||
class="consumer-icon"
|
||||
color="red"
|
||||
size={48}
|
||||
strokeWidth={4}
|
||||
absoluteStrokeWidth
|
||||
/>
|
||||
<LucideProvider
|
||||
color="purple"
|
||||
size={32}
|
||||
strokeWidth={3}
|
||||
>
|
||||
<House data-testid="provider-icon" />
|
||||
</LucideProvider>
|
||||
<Pen data-testid="alias-icon" />
|
||||
<Edit2 data-testid="canonical-icon" />
|
||||
<Circle
|
||||
aria-label="Deep import circle"
|
||||
data-testid="deep-import-icon"
|
||||
/>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { createRouter } from '@solidjs/router';
|
||||
import { fileRoutes } from '@solidjs/router/fs';
|
||||
import { pageRoutes } from 'virtual:file-routes';
|
||||
|
||||
export const routes = fileRoutes(pageRoutes);
|
||||
|
||||
export const Router = createRouter({ routes });
|
||||
@@ -0,0 +1,10 @@
|
||||
import { ArrowLeft } from '@lucide/solid';
|
||||
|
||||
export default function About() {
|
||||
return (
|
||||
<main>
|
||||
<ArrowLeft data-testid="route-icon" />
|
||||
<a href="/">Home</a>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import IconShowcase from '../IconShowcase';
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<IconShowcase />
|
||||
<a href="/about">About</a>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
/// <reference types="vite/client" />
|
||||
/// <reference types="../file-routes.d.ts" />
|
||||
@@ -0,0 +1,46 @@
|
||||
import { createRouter, memoryHistory } from '@solidjs/router';
|
||||
import { render } from '@solidjs/testing-library';
|
||||
import { Loading } from 'solid-js';
|
||||
import { expect, test } from 'vitest';
|
||||
import { page } from 'vitest/browser';
|
||||
import { routes } from '../src/router';
|
||||
|
||||
test('renders Lucide public APIs and survives router navigation', async () => {
|
||||
const Router = createRouter({ routes, history: memoryHistory('/') });
|
||||
|
||||
render(() => <Router>{(props) => <Loading>{props.children}</Loading>}</Router>);
|
||||
|
||||
const staticIcon = page.getByTestId('static-icon');
|
||||
await expect.element(staticIcon).toBeVisible();
|
||||
await expect.element(staticIcon).toHaveAttribute('width', '24');
|
||||
await expect.element(staticIcon).toHaveAttribute('height', '24');
|
||||
await expect.element(staticIcon).toHaveAttribute('viewBox', '0 0 24 24');
|
||||
await expect.element(staticIcon).toHaveAttribute('aria-hidden', 'true');
|
||||
|
||||
const customIcon = page.getByTestId('custom-icon');
|
||||
await expect.element(customIcon).toBeVisible();
|
||||
await expect.element(customIcon).toHaveAttribute('width', '48');
|
||||
await expect.element(customIcon).toHaveAttribute('height', '48');
|
||||
await expect.element(customIcon).toHaveAttribute('stroke', 'red');
|
||||
await expect.element(customIcon).toHaveAttribute('stroke-width', '2');
|
||||
await expect.element(customIcon).toHaveClass('consumer-icon');
|
||||
|
||||
const providerIcon = page.getByTestId('provider-icon');
|
||||
await expect.element(providerIcon).toHaveAttribute('width', '32');
|
||||
await expect.element(providerIcon).toHaveAttribute('stroke', 'purple');
|
||||
await expect.element(providerIcon).toHaveAttribute('stroke-width', '3');
|
||||
|
||||
const aliasIcon = page.getByTestId('alias-icon');
|
||||
const canonicalIcon = page.getByTestId('canonical-icon');
|
||||
await expect.element(aliasIcon).toBeVisible();
|
||||
await expect.element(canonicalIcon).toBeVisible();
|
||||
expect(aliasIcon.element().innerHTML).toBe(canonicalIcon.element().innerHTML);
|
||||
|
||||
const deepImportIcon = page.getByTestId('deep-import-icon');
|
||||
await expect.element(deepImportIcon).toBeVisible();
|
||||
await expect.element(deepImportIcon).toHaveAttribute('aria-label', 'Deep import circle');
|
||||
await expect.element(deepImportIcon).not.toHaveAttribute('aria-hidden');
|
||||
|
||||
await page.getByRole('link', { name: 'About' }).click();
|
||||
await expect.element(page.getByTestId('route-icon')).toBeVisible();
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"skipLibCheck": false,
|
||||
"types": []
|
||||
},
|
||||
"include": ["src/IconShowcase.tsx"]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "@solidjs/web",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"isolatedModules": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"types": ["vite/client"]
|
||||
},
|
||||
"include": ["src", "tests", "file-routes.d.ts", "vite.config.ts"]
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import solid from '@solidjs/vite-plugin';
|
||||
import { playwright } from '@vitest/browser-playwright';
|
||||
import { fileRoutes } from 'filesystem-routing/vite';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
// Scaffolded with `npm create solid@latest -- --solid -t basic`: turnkey client
|
||||
// mode generates the entries around src/App.tsx (wrapped in src/Document.tsx)
|
||||
// and `vite build` prerenders the shell into dist/client.
|
||||
plugins: [solid({ start: true, extensions: ['.jsx', '.tsx'] }), fileRoutes({ types: true })],
|
||||
test: {
|
||||
browser: {
|
||||
enabled: true,
|
||||
headless: true,
|
||||
provider: playwright(),
|
||||
instances: [{ browser: 'chromium' }],
|
||||
},
|
||||
},
|
||||
build: {
|
||||
target: 'esnext',
|
||||
},
|
||||
});
|
||||
@@ -7,6 +7,7 @@
|
||||
"test:update": "pnpm -r --filter './packages/**' --filter !'./packages/lucide-angular' test -- -u",
|
||||
"test:integrations:react": "pnpm --filter lucide-react build && pnpm -r --workspace-concurrency=1 --filter './integrations/lucide-react/**' check",
|
||||
"test:integrations:solid": "pnpm --filter lucide-solid build && pnpm -r --workspace-concurrency=1 --filter './integrations/lucide-solid/**' check",
|
||||
"test:integrations:solid-2": "pnpm --filter @lucide/solid build && pnpm -r --workspace-concurrency=1 --filter './integrations/lucide-solid-2/**' check",
|
||||
"test:integrations:vue": "pnpm --filter @lucide/vue build && pnpm -r --workspace-concurrency=1 --filter './integrations/lucide-vue/**' check",
|
||||
"test:font": "pnpm --filter build-font test",
|
||||
"lucide": "pnpm --filter lucide",
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<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 2 applications." width="540">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
Lucide icon library for Solid 2 applications.
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://www.npmjs.com/package/@lucide/solid)
|
||||

|
||||
[](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 2 applications.
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
pnpm add @lucide/solid
|
||||
```
|
||||
|
||||
```sh
|
||||
npm install @lucide/solid
|
||||
```
|
||||
|
||||
```sh
|
||||
yarn add @lucide/solid
|
||||
```
|
||||
|
||||
```sh
|
||||
bun add @lucide/solid
|
||||
```
|
||||
|
||||
> `@lucide/solid` is only for Solid 2, for Solid 1 use the [`lucide-solid`](https://www.npmjs.com/package/lucide-solid) package. Solid 2 is still a release candidate.
|
||||
|
||||
## Documentation
|
||||
|
||||
For full documentation, visit [lucide.dev](https://lucide.dev/guide/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).
|
||||
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"name": "@lucide/solid",
|
||||
"description": "A Lucide icon library package for Solid 2 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.29.7",
|
||||
"@babel/preset-env": "^7.29.7",
|
||||
"@babel/preset-typescript": "^7.29.7",
|
||||
"@lucide/build-icons": "workspace:*",
|
||||
"@lucide/rollup-plugins": "workspace:*",
|
||||
"@lucide/shared": "workspace:*",
|
||||
"@rollup/plugin-babel": "^6.1.0",
|
||||
"@solidjs/testing-library": "next",
|
||||
"@solidjs/vite-plugin": "next",
|
||||
"@solidjs/web": "next",
|
||||
"@testing-library/jest-dom": "^7.0.1",
|
||||
"babel-preset-solid": "next",
|
||||
"esbuild": "^0.28.2",
|
||||
"jest-serializer-html": "^7.1.0",
|
||||
"rollup-plugin-dts": "^6.5.1",
|
||||
"rollup": "^4.63.1",
|
||||
"solid-js": "next",
|
||||
"typescript": "^6.0.3",
|
||||
"vite": "^7.3.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"solid-js": "^2.0.0-rc.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import path from 'path';
|
||||
import { babel } from '@rollup/plugin-babel';
|
||||
import esbuild from 'esbuild';
|
||||
import plugins from '@lucide/rollup-plugins';
|
||||
import dts from 'rollup-plugin-dts';
|
||||
|
||||
import pkg from './package.json' with { type: 'json' };
|
||||
|
||||
const dtsOptions = {
|
||||
includeExternal: ['@lucide/shared/types'],
|
||||
tsconfig: './tsconfig.json',
|
||||
};
|
||||
|
||||
const packageName = '@lucide/solid';
|
||||
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: '@solidjs/web',
|
||||
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', '@solidjs/web'],
|
||||
});
|
||||
},
|
||||
}
|
||||
: null,
|
||||
],
|
||||
external: ['solid-js', '@solidjs/web'],
|
||||
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();
|
||||
|
||||
const typesConfig = {
|
||||
input: inputs[0],
|
||||
output: {
|
||||
dir: `${outputDir}/types`,
|
||||
format: 'es',
|
||||
preserveModules: true,
|
||||
preserveModulesRoot: 'src',
|
||||
entryFileNames: '[name].d.ts',
|
||||
},
|
||||
plugins: [dts(dtsOptions)],
|
||||
};
|
||||
|
||||
export default [...configs, typesConfig];
|
||||
@@ -0,0 +1,33 @@
|
||||
import base64SVG from '@lucide/build-icons/utils/base64SVG';
|
||||
import defineExportTemplate from '@lucide/build-icons/utils/defineExportTemplate';
|
||||
|
||||
export default defineExportTemplate(
|
||||
async ({ componentName, iconName, iconData, getSvg, deprecated, deprecationReason }) => {
|
||||
const svgContents = await getSvg();
|
||||
const svgBase64 = base64SVG(svgContents);
|
||||
|
||||
return `
|
||||
import Icon from '../Icon';
|
||||
import type { LucideIconData, LucideProps } from '../types';
|
||||
|
||||
const iconData: LucideIconData = ${JSON.stringify(iconData)};
|
||||
|
||||
/**
|
||||
* @component @name ${componentName}
|
||||
* @description Lucide SVG icon component, renders SVG Element with children.
|
||||
*
|
||||
* @preview  - https://lucide.dev/icons/${iconName}
|
||||
* @see https://lucide.dev/guide/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} icon={iconData} />
|
||||
)
|
||||
|
||||
export default ${componentName};
|
||||
`;
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,75 @@
|
||||
import { createMemo, For, omit, useContext } from 'solid-js';
|
||||
import { Dynamic } from '@solidjs/web';
|
||||
import { buildLucideIconNode, hasA11yProp, mergeClasses } from '@lucide/shared';
|
||||
import { LucideIconData, LucideIconNode, LucideProps } from './types';
|
||||
import { LucideContext } from './context';
|
||||
|
||||
type IconProps =
|
||||
| {
|
||||
icon: LucideIconData;
|
||||
iconNode?: never;
|
||||
}
|
||||
| {
|
||||
icon?: never;
|
||||
iconNode?: LucideIconNode[];
|
||||
};
|
||||
|
||||
const LOCAL_PROP_KEYS = [
|
||||
'color',
|
||||
'size',
|
||||
'width',
|
||||
'height',
|
||||
'strokeWidth',
|
||||
'children',
|
||||
'class',
|
||||
'icon',
|
||||
'iconNode',
|
||||
'absoluteStrokeWidth',
|
||||
'nonScalingStroke',
|
||||
] as const;
|
||||
|
||||
const Icon = (props: LucideProps & IconProps) => {
|
||||
const globalProps = useContext(LucideContext);
|
||||
|
||||
const rest = () => omit(props, ...LOCAL_PROP_KEYS);
|
||||
|
||||
const icon = createMemo<LucideIconData>(
|
||||
() =>
|
||||
props.icon ?? {
|
||||
node: props.iconNode ?? ([] as LucideIconNode[]),
|
||||
size: 24,
|
||||
aliases: [],
|
||||
},
|
||||
);
|
||||
|
||||
const builtIcon = createMemo(() =>
|
||||
buildLucideIconNode(icon(), {
|
||||
color: props.color ?? globalProps.color,
|
||||
width: props.width ?? props.size ?? globalProps.size,
|
||||
height: props.height ?? props.size ?? globalProps.size,
|
||||
strokeWidth: props.strokeWidth ?? globalProps.strokeWidth,
|
||||
absoluteStrokeWidth: props.absoluteStrokeWidth ?? globalProps.absoluteStrokeWidth,
|
||||
nonScalingStroke: props.nonScalingStroke ?? globalProps.nonScalingStroke,
|
||||
className: mergeClasses('lucide-icon', globalProps.class, props.class),
|
||||
hasA11yProp: Boolean(props.children) || hasA11yProp(rest()),
|
||||
attributes: rest(),
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<svg {...builtIcon()[1]}>
|
||||
<For each={builtIcon()[2] ?? []}>
|
||||
{([elementName, attrs]) => {
|
||||
return (
|
||||
<Dynamic
|
||||
component={elementName}
|
||||
{...attrs}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default Icon;
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './aliases';
|
||||
export * from './prefixed';
|
||||
export * from './suffixed';
|
||||
@@ -0,0 +1,37 @@
|
||||
import { createContext, omit, type Element } from 'solid-js';
|
||||
|
||||
export const LucideContext = createContext<{
|
||||
size?: number;
|
||||
color?: string;
|
||||
strokeWidth?: number;
|
||||
/**
|
||||
* @deprecated Use `nonScalingStroke` instead.
|
||||
*/
|
||||
absoluteStrokeWidth?: boolean;
|
||||
nonScalingStroke?: boolean;
|
||||
class?: string;
|
||||
}>({
|
||||
size: 24,
|
||||
color: 'currentColor',
|
||||
strokeWidth: 2,
|
||||
absoluteStrokeWidth: false,
|
||||
nonScalingStroke: false,
|
||||
class: '',
|
||||
});
|
||||
|
||||
interface LucideProviderProps {
|
||||
children: Element;
|
||||
size?: number;
|
||||
color?: string;
|
||||
strokeWidth?: number;
|
||||
/**
|
||||
* @deprecated Use `nonScalingStroke` instead.
|
||||
*/
|
||||
absoluteStrokeWidth?: boolean;
|
||||
nonScalingStroke?: boolean;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
export function LucideProvider(props: LucideProviderProps) {
|
||||
return <LucideContext value={omit(props, 'children')}>{props.children}</LucideContext>;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { defaultAttributes as default } from '@lucide/shared';
|
||||
@@ -0,0 +1 @@
|
||||
Folder for generated icons
|
||||
@@ -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';
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { JSX } from '@solidjs/web';
|
||||
import type {
|
||||
LucideIconData as SharedLucideIconData,
|
||||
LucideIconNode as SharedLucideIconNode,
|
||||
} from '@lucide/shared/types';
|
||||
|
||||
export type SVGAttributes = Partial<JSX.SvgSVGAttributes<SVGSVGElement>>;
|
||||
|
||||
export type LucideIconNode = SharedLucideIconNode<keyof JSX.IntrinsicElements, SVGAttributes>;
|
||||
|
||||
export type LucideIconData = SharedLucideIconData<keyof JSX.IntrinsicElements, SVGAttributes>;
|
||||
|
||||
/**
|
||||
* @deprecated Use LucideIconNode instead.
|
||||
*/
|
||||
export type IconNode = LucideIconNode[];
|
||||
|
||||
export interface LucideProps extends SVGAttributes {
|
||||
key?: string | number;
|
||||
class?: string;
|
||||
size?: string | number;
|
||||
width?: string | number;
|
||||
height?: string | number;
|
||||
color?: string;
|
||||
strokeWidth?: string | number;
|
||||
/**
|
||||
* @deprecated Use `nonScalingStroke` instead.
|
||||
*/
|
||||
absoluteStrokeWidth?: boolean;
|
||||
nonScalingStroke?: boolean;
|
||||
}
|
||||
|
||||
export type LucideIcon = (props: LucideProps) => JSX.Element;
|
||||
@@ -0,0 +1,154 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render } from '@solidjs/testing-library';
|
||||
import { createSignal, flush } from 'solid-js';
|
||||
|
||||
import { airVent } from './testIconNodes';
|
||||
import { Icon } from '../src/lucide-solid';
|
||||
|
||||
describe('Using Icon Component', () => {
|
||||
const airVentIcon = { name: 'air-vent', node: airVent };
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
it('should render icon based on icon data', async () => {
|
||||
const { container } = render(() => (
|
||||
<Icon
|
||||
icon={airVentIcon}
|
||||
size={48}
|
||||
stroke="red"
|
||||
/>
|
||||
));
|
||||
|
||||
expect(container.firstChild).toBeDefined();
|
||||
});
|
||||
|
||||
it('should support nonScalingStroke', async () => {
|
||||
const { container } = render(() => (
|
||||
<Icon
|
||||
icon={airVentIcon}
|
||||
size={48}
|
||||
stroke="red"
|
||||
strokeWidth={2}
|
||||
nonScalingStroke
|
||||
/>
|
||||
));
|
||||
|
||||
expect(container.firstChild?.firstChild).toHaveAttribute('vector-effect', 'non-scaling-stroke');
|
||||
});
|
||||
|
||||
it('should update when reactive props change', async () => {
|
||||
const [size, setSize] = createSignal(24);
|
||||
const { container } = render(() => (
|
||||
<Icon
|
||||
icon={airVentIcon}
|
||||
size={size()}
|
||||
/>
|
||||
));
|
||||
|
||||
expect(container.firstChild).toHaveAttribute('width', '24');
|
||||
expect(container.firstChild).toHaveAttribute('height', '24');
|
||||
|
||||
setSize(48);
|
||||
flush();
|
||||
|
||||
expect(container.firstChild).toHaveAttribute('width', '48');
|
||||
expect(container.firstChild).toHaveAttribute('height', '48');
|
||||
});
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
@@ -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>
|
||||
`;
|
||||
@@ -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-house lucide-home lucide-icon"
|
||||
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>
|
||||
`;
|
||||
@@ -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"
|
||||
width="48"
|
||||
height="48"
|
||||
viewbox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="red"
|
||||
stroke-width="4"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3 lucide-icon"
|
||||
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"
|
||||
width="24"
|
||||
height="24"
|
||||
viewbox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3 lucide-icon"
|
||||
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>
|
||||
`;
|
||||
|
||||
exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="48"
|
||||
height="48"
|
||||
viewbox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="red"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3 lucide-icon"
|
||||
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>
|
||||
`;
|
||||
@@ -0,0 +1,121 @@
|
||||
import { render } from '@solidjs/testing-library';
|
||||
import { createSignal, flush } from 'solid-js';
|
||||
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 update when reactive LucideProvider props change', () => {
|
||||
const [size, setSize] = createSignal(24);
|
||||
const { container } = render(() => (
|
||||
<LucideProvider size={size()}>
|
||||
<House />
|
||||
</LucideProvider>
|
||||
));
|
||||
|
||||
expect(container.firstElementChild).toHaveAttribute('width', '24');
|
||||
expect(container.firstElementChild).toHaveAttribute('height', '24');
|
||||
|
||||
setSize(48);
|
||||
flush();
|
||||
|
||||
expect(container.firstElementChild).toHaveAttribute('width', '48');
|
||||
expect(container.firstElementChild).toHaveAttribute('height', '48');
|
||||
});
|
||||
|
||||
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-house lucide-home lucide-icon provider-class icon-class',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -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 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');
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
@@ -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' }],
|
||||
];
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"paths": {
|
||||
"@lucide/shared": ["../shared/src/index.ts"],
|
||||
"@lucide/shared/types": ["../shared/src/build/types.ts"]
|
||||
},
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "@solidjs/web",
|
||||
"types": ["vite/client", "@testing-library/jest-dom/vitest"],
|
||||
"noEmit": true,
|
||||
"isolatedModules": true,
|
||||
"outDir": "./dist",
|
||||
"declarationDir": "./dist",
|
||||
"declaration": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { defineConfig } from 'vitest/config'
|
||||
import solidPlugin from '@solidjs/vite-plugin';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [solidPlugin()],
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
setupFiles: './tests/setupVitest.js',
|
||||
},
|
||||
});
|
||||
Generated
+625
-19
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user