Skip to content

Commit

Permalink
format
Browse files Browse the repository at this point in the history
  • Loading branch information
MoustaphaDev committed Jan 4, 2025
1 parent 77fa995 commit ac8eaef
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 115 deletions.
44 changes: 27 additions & 17 deletions packages/lucide-astro/src/createLucideIcon.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
import { mergeClasses, toKebabCase } from "./utils"
import type { AstroComponentFactory } from "astro/runtime/server/render/astro/factory.js"
import { mergeClasses, toKebabCase } from './utils';
import type { AstroComponentFactory } from 'astro/runtime/server/render/astro/factory.js';
import type { IconNode } from './types';
import {
render,
renderSlot,
createAstro,
createComponent,
renderComponent,
} from "astro/compiler-runtime"
} from 'astro/compiler-runtime';
import Icon from './Icon.astro';

export default (iconName: string, iconNode: IconNode): AstroComponentFactory => {
const Component = createComponent(($$result, $$props, $$slots) => {
const $$Astro = createAstro(undefined);
const Astro = $$result.createAstro($$Astro, $$props, $$slots);
const { class: className, ...restProps } = Astro.props;
return render`${renderComponent($$result, 'Icon', Icon, {
class: mergeClasses(
Boolean(iconName) && `lucide-${toKebabCase(iconName)}`,
Boolean(className) && className
),
iconNode,
...restProps,
}, { "default": () => render`${renderSlot($$result, $$slots["default"])}`, })}`;
}, undefined, "none");
const Component = createComponent(
($$result, $$props, $$slots) => {
const $$Astro = createAstro(undefined);
const Astro = $$result.createAstro($$Astro, $$props, $$slots);
const { class: className, ...restProps } = Astro.props;
return render`${renderComponent(
$$result,
'Icon',
Icon,
{
class: mergeClasses(
Boolean(iconName) && `lucide-${toKebabCase(iconName)}`,
Boolean(className) && className,
),
iconNode,
...restProps,
},
{ default: () => render`${renderSlot($$result, $$slots['default'])}` },
)}`;
},
undefined,
'none',
);
return Component;
}
};
2 changes: 1 addition & 1 deletion packages/lucide-astro/src/lucide-astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ export * from './aliases';
export * from './types';

export { default as defaultAttributes } from './defaultAttributes';
export { default as createLucideIcon } from "./createLucideIcon"
export { default as createLucideIcon } from './createLucideIcon';
export { default as Icon } from './Icon.astro';
4 changes: 2 additions & 2 deletions packages/lucide-astro/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export type AstroComponent = (_props: IconProps) => any;
export interface IconProps extends SVGAttributes {
color?: string;
size?: number | string;
"stroke-width"?: number | string;
'stroke-width'?: number | string;
absoluteStrokeWidth?: boolean;
class?: string;
iconNode?: IconNode;
Expand Down Expand Up @@ -73,4 +73,4 @@ type SVGElements =
| 'textPath'
| 'tspan'
| 'use'
| 'view'
| 'view';
6 changes: 3 additions & 3 deletions packages/lucide-astro/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* @returns {string} A kebabized string
*/
export const toKebabCase = (string: string): string =>
string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();

/**
* Merges classes into a single string
Expand All @@ -20,9 +20,9 @@ export const mergeClasses = <ClassType = string | undefined | null>(
.filter((className, index, array) => {
return (
Boolean(className) &&
(className as string).trim() !== "" &&
(className as string).trim() !== '' &&
array.indexOf(className) === index
);
})
.join(" ")
.join(' ')
.trim();
11 changes: 5 additions & 6 deletions packages/lucide-astro/tests/Icon.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { describe, it, expect } from 'vitest';
import { airVent } from './testIconNodes';
import { render } from "./utils";
import { Icon } from "../src/lucide-astro"
import { render } from './utils';
import { Icon } from '../src/lucide-astro';

describe('Using Icon Component', async () => {
const { container } = await render(Icon, { props: { iconNode: airVent, size: 48, stroke: 'red', absoluteStrokeWidth: true } })
const { container } = await render(Icon, {
props: { iconNode: airVent, size: 48, stroke: 'red', absoluteStrokeWidth: true },
});

it('should render icon and match snapshot', async () => {
expect(container.innerHTML).toMatchSnapshot();
Expand All @@ -13,7 +15,4 @@ describe('Using Icon Component', async () => {
it('should render icon based on a iconNode', async () => {
expect(container.innerHTML).toBeDefined();
});

});


94 changes: 40 additions & 54 deletions packages/lucide-astro/tests/lucide-astro.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,29 @@ describe('Using lucide icon components', () => {

it('should render the icon with default attributes', async () => {
const { container } = await render(Grid);
const SVGElement = container.firstElementChild
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"]);
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']);

expect(container.innerHTML).toMatchSnapshot();
});

it('should adjust the size, stroke color and stroke width', async () => {
const { container } = await render(
Grid,
{
props: {
size: 48,
stroke: "red",
"stroke-width": 4
}
}
);
const { container } = await render(Grid, {
props: {
size: 48,
stroke: 'red',
'stroke-width': 4,
},
});

const SVGElement = container.firstElementChild;

Expand All @@ -48,43 +45,33 @@ describe('Using lucide icon components', () => {
});

it('should render the alias icon', async () => {
const { container: PenIconContainer } = await render(
Pen,
{
props: {
size: 48,
stroke: "red",
"stroke-width": 4
}
}
);


const { container: Edit2Container } = await render(
Edit2,
{
props: {
size: 48,
stroke: "red",
"stroke-width": 4
}
}
);
const { container: PenIconContainer } = await render(Pen, {
props: {
size: 48,
stroke: 'red',
'stroke-width': 4,
},
});

const { container: Edit2Container } = await render(Edit2, {
props: {
size: 48,
stroke: 'red',
'stroke-width': 4,
},
});

expect(PenIconContainer.innerHTML).toBe(Edit2Container.innerHTML);
});

it('should not scale the strokeWidth when absoluteStrokeWidth is set', async () => {
const { container } = await render(
Grid,
{
props: {
size: 48,
stroke: "red",
absoluteStrokeWidth: true
}
}
);
const { container } = await render(Grid, {
props: {
size: 48,
stroke: 'red',
absoluteStrokeWidth: true,
},
});

const SVGElement = container.firstElementChild;

Expand All @@ -110,15 +97,14 @@ describe('Using lucide icon components', () => {
expect(container.innerHTML).toMatchSnapshot();
});


it('should pass children to the icon slot', async () => {
const { getByText, container } = await render(Smile, {
slots: {
default: createAstroHTMLString("<p>Hello World</p>")
default: createAstroHTMLString('<p>Hello World</p>'),
},
});

const textElement = getByText("Hello World");
const textElement = getByText('Hello World');

expect(textElement).toBeInTheDocument();

Expand All @@ -128,15 +114,15 @@ describe('Using lucide icon components', () => {
it('should apply all classes to the element', async () => {
const { container } = await render(Droplet, {
props: {
class: "my-icon"
}
class: 'my-icon',
},
});
const SVGElement = container.firstElementChild
const SVGElement = container.firstElementChild;

expect(SVGElement).toHaveClass("my-icon");
expect(SVGElement).toHaveClass('my-icon');
expect(SVGElement).toHaveClass('lucide');
expect(SVGElement).toHaveClass('lucide-droplet');

expect(container.innerHTML).toMatchSnapshot()
expect(container.innerHTML).toMatchSnapshot();
});
});
2 changes: 1 addition & 1 deletion packages/lucide-astro/tests/testIconNodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { 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' }],
['path', { d: 'M6 8h12', }],
['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' }],
];
Expand Down
4 changes: 2 additions & 2 deletions packages/lucide-astro/tests/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from "./render"
export * from "./types"
export * from './render';
export * from './types';
25 changes: 14 additions & 11 deletions packages/lucide-astro/tests/utils/render.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { parseHTML } from "linkedom"
import { getQueriesForElement } from '@testing-library/dom'
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { type AstroComponentFactory, HTMLString } from "astro/runtime/server/index.js";
import type { RenderFn } from "./types";
import { parseHTML } from 'linkedom';
import { getQueriesForElement } from '@testing-library/dom';
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import { type AstroComponentFactory, HTMLString } from 'astro/runtime/server/index.js';
import type { RenderFn } from './types';

const ASTRO_DEBUGGING_ATTRIBUTE = "data-astro-source";
const ASTRO_DEBUGGING_ATTRIBUTE = 'data-astro-source';

export const render = (async function (AstroComponent, options?) {
export const render = async function (AstroComponent, options?) {
const astroContainer = await AstroContainer.create();
const htmlString = await astroContainer.renderToString(AstroComponent as AstroComponentFactory, options);
const htmlString = await astroContainer.renderToString(
AstroComponent as AstroComponentFactory,
options,
);

const { document } = parseHTML(injectMissingParentTags(htmlString));
const container = document.body;
Expand All @@ -17,17 +20,17 @@ export const render = (async function (AstroComponent, options?) {
removeIrrelevantAttributes(container.firstElementChild!);

return { container, html: htmlString, ...getQueriesForElement(container) };
}) satisfies RenderFn
} satisfies RenderFn;

// html strings need to be marked with this class
// otherwise, Astro is gonna escape the the html entities
export function createAstroHTMLString(htmlString: string) {
return new HTMLString(htmlString)
return new HTMLString(htmlString);
}

// needed so linkedom generates the tree correctly
function injectMissingParentTags(markup: string) {
return `<html><body>${markup}</body></html>`
return `<html><body>${markup}</body></html>`;
}

// remove irrelevant metadata generated by the astro dev container
Expand Down
40 changes: 25 additions & 15 deletions packages/lucide-astro/tests/utils/types.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
import type { queries, BoundFunctions } from '@testing-library/dom'
import type { ContainerRenderOptions } from "astro/container";
import type { ComponentProps } from "astro/types"
import type { AstroComponentFactory } from "astro/runtime/server/index.js";
import type { queries, BoundFunctions } from '@testing-library/dom';
import type { ContainerRenderOptions } from 'astro/container';
import type { ComponentProps } from 'astro/types';
import type { AstroComponentFactory } from 'astro/runtime/server/index.js';

export type RenderFn = <T = PossibleComponentType>(AstroComponent: T, options?: RenderFnOptions<T>) => RenderResult
export type RenderFn = <T = PossibleComponentType>(
AstroComponent: T,
options?: RenderFnOptions<T>,
) => RenderResult;

type RenderFnOptions<T> = T extends AstroComponentVirtualType ? ContainerRenderOptionsWithInferedProps<T> : ContainerRenderOptions
type RenderFnOptions<T> = T extends AstroComponentVirtualType
? ContainerRenderOptionsWithInferedProps<T>
: ContainerRenderOptions;

type RenderResult = Promise<{
container: HTMLElement;
html: string;
} & Queries>
type RenderResult = Promise<
{
container: HTMLElement;
html: string;
} & Queries
>;

type ContainerRenderOptionsWithInferedProps<T extends AstroComponentVirtualType> = Omit<ContainerRenderOptions, "props"> & {
props?: ComponentProps<T>
}
type ContainerRenderOptionsWithInferedProps<T extends AstroComponentVirtualType> = Omit<
ContainerRenderOptions,
'props'
> & {
props?: ComponentProps<T>;
};

// types may either be coming from the compiler (through the language tools)
// or from the Astro component's signature itself
type PossibleComponentType = AstroComponentFactory | AstroComponentVirtualType
type PossibleComponentType = AstroComponentFactory | AstroComponentVirtualType;
type AstroComponentVirtualType = (args: any) => any;

type Queries = BoundFunctions<typeof queries>
type Queries = BoundFunctions<typeof queries>;
Loading

0 comments on commit ac8eaef

Please sign in to comment.