mirror of
https://github.com/tabler/tabler-icons.git
synced 2025-08-11 16:44:13 +02:00
Add title to icon's <svg> as child tag in React, Vue.js and React Native (#1147)
* Add title as child tag of <svg> in icons-react * Add title as child tag of <svg> in icons-vue package * Add title as child tag of <svg> in icons-react-native package * Test <title> element in React * Prevent adding <!----> to <svg> if no title prop passed + Test <title> child element. * Fix tests for Vue Stroke attribute added to expected SVG code. --------- Co-authored-by: Bartłomiej Gawęda <bgaweda@abis.krakow.pl>
This commit is contained in:
@@ -10,7 +10,7 @@ const createReactNativeComponent = (
|
|||||||
iconNode: IconNode,
|
iconNode: IconNode,
|
||||||
): Icon => {
|
): Icon => {
|
||||||
const Component = forwardRef<SVGSVGElement, IconProps>(
|
const Component = forwardRef<SVGSVGElement, IconProps>(
|
||||||
({ color = 'currentColor', size = 24, strokeWidth = 2, children, ...rest }: IconProps, ref) => {
|
({ color = 'currentColor', size = 24, strokeWidth = 2, title, children, ...rest }: IconProps, ref) => {
|
||||||
const customAttrs = {
|
const customAttrs = {
|
||||||
stroke: color,
|
stroke: color,
|
||||||
strokeWidth,
|
strokeWidth,
|
||||||
@@ -37,7 +37,10 @@ const createReactNativeComponent = (
|
|||||||
{ ...childDefaultAttributes[type], ...customAttrs, ...attrs } as IconProps,
|
{ ...childDefaultAttributes[type], ...customAttrs, ...attrs } as IconProps,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
...((Array.isArray(children) ? children : [children]) || []),
|
[
|
||||||
|
title && createElement('title', { key: 'svg-title' }, title),
|
||||||
|
...((Array.isArray(children) ? children : [children]) || [])
|
||||||
|
],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@@ -10,6 +10,7 @@ export type SVGAttributes = Partial<SVGProps<SVGSVGElement>>;
|
|||||||
export interface IconProps extends SvgProps {
|
export interface IconProps extends SvgProps {
|
||||||
size?: string | number;
|
size?: string | number;
|
||||||
strokeWidth?: string | number;
|
strokeWidth?: string | number;
|
||||||
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Icon = ForwardRefExoticComponent<IconProps>;
|
export type Icon = ForwardRefExoticComponent<IconProps>;
|
||||||
|
@@ -10,7 +10,7 @@ const createReactComponent = (
|
|||||||
) => {
|
) => {
|
||||||
const Component = forwardRef<Icon, IconProps>(
|
const Component = forwardRef<Icon, IconProps>(
|
||||||
(
|
(
|
||||||
{ color = 'currentColor', size = 24, stroke = 2, className, children, ...rest }: IconProps,
|
{ color = 'currentColor', size = 24, stroke = 2, title, className, children, ...rest }: IconProps,
|
||||||
ref,
|
ref,
|
||||||
) =>
|
) =>
|
||||||
createElement(
|
createElement(
|
||||||
@@ -32,6 +32,7 @@ const createReactComponent = (
|
|||||||
...rest,
|
...rest,
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
|
title && createElement('title', { key: 'svg-title' }, title),
|
||||||
...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
|
...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
|
||||||
...(Array.isArray(children) ? children : [children]),
|
...(Array.isArray(children) ? children : [children]),
|
||||||
],
|
],
|
||||||
|
@@ -7,6 +7,7 @@ export type IconNode = [elementName: keyof ReactSVG, attrs: Record<string, strin
|
|||||||
export interface IconProps extends Partial<Omit<React.ComponentPropsWithoutRef<'svg'>, 'stroke'>> {
|
export interface IconProps extends Partial<Omit<React.ComponentPropsWithoutRef<'svg'>, 'stroke'>> {
|
||||||
size?: string | number;
|
size?: string | number;
|
||||||
stroke?: string | number;
|
stroke?: string | number;
|
||||||
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Icon = FunctionComponent<IconProps>;
|
export type Icon = FunctionComponent<IconProps>;
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
import { describe, it, expect, afterEach, assertType, expectTypeOf } from 'vitest';
|
import { describe, it, expect, afterEach, expectTypeOf } from 'vitest';
|
||||||
import { render, cleanup } from '@testing-library/react'
|
import { render, cleanup } from '@testing-library/react'
|
||||||
import { IconAccessible, IconAccessibleFilled, createReactComponent } from "./src/tabler-icons-react"
|
import { IconAccessible, IconAccessibleFilled, createReactComponent } from "./src/tabler-icons-react"
|
||||||
import type { IconNode } from './src/tabler-icons-react';
|
|
||||||
import { ReactNode } from 'react';
|
|
||||||
|
|
||||||
describe("React Icon component", () => {
|
describe("React Icon component", () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -58,6 +56,14 @@ describe("React Icon component", () => {
|
|||||||
expectTypeOf(IconAccessible).toEqualTypeOf(createReactComponent('outline', 'accessible', 'Accessible', []));
|
expectTypeOf(IconAccessible).toEqualTypeOf(createReactComponent('outline', 'accessible', 'Accessible', []));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should add title child element to svg when title prop is passed', () => {
|
||||||
|
const { container } = render(<IconAccessible title="Accessible Icon"/>);
|
||||||
|
const svg = container.getElementsByTagName("svg")[0];
|
||||||
|
const title = svg.getElementsByTagName("title")[0];
|
||||||
|
|
||||||
|
expect(title).toHaveTextContent("Accessible Icon");
|
||||||
|
})
|
||||||
|
|
||||||
it("should match snapshot", () => {
|
it("should match snapshot", () => {
|
||||||
const { container } = render(<IconAccessible/>)
|
const { container } = render(<IconAccessible/>)
|
||||||
expect(container.innerHTML).toMatchInlineSnapshot(`
|
expect(container.innerHTML).toMatchInlineSnapshot(`
|
||||||
|
@@ -9,7 +9,9 @@ const createVueComponent =
|
|||||||
iconNamePascal: string,
|
iconNamePascal: string,
|
||||||
iconNode: IconNode,
|
iconNode: IconNode,
|
||||||
): Icon =>
|
): Icon =>
|
||||||
({ size, color = 'currentColor', class: classes, stroke, ...rest }: IconProps, { attrs, slots }) => {
|
({ color = 'currentColor', size, stroke, title, class: classes, ...rest }: IconProps, { attrs, slots }) => {
|
||||||
|
let children = [...iconNode.map((child) => h(...child)), ...(slots.default ? [slots.default()] : [])];
|
||||||
|
if (title) children = [h('title', title), ...children];
|
||||||
return h(
|
return h(
|
||||||
'svg',
|
'svg',
|
||||||
{
|
{
|
||||||
@@ -28,7 +30,7 @@ const createVueComponent =
|
|||||||
}),
|
}),
|
||||||
...rest,
|
...rest,
|
||||||
},
|
},
|
||||||
[...iconNode.map((child) => h(...child)), ...(slots.default ? [slots.default()] : [])],
|
children,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -11,4 +11,5 @@ export type Icon = FunctionalComponent<SVGProps>;
|
|||||||
export interface IconProps extends Partial<Omit<SVGProps, 'stroke'>> {
|
export interface IconProps extends Partial<Omit<SVGProps, 'stroke'>> {
|
||||||
size?: string | number;
|
size?: string | number;
|
||||||
stroke?: string | number;
|
stroke?: string | number;
|
||||||
|
title?: string;
|
||||||
}
|
}
|
||||||
|
@@ -67,6 +67,20 @@ describe("Vue Icon component", () => {
|
|||||||
expect(svg.getAttribute("stroke-width")).toBe(null)
|
expect(svg.getAttribute("stroke-width")).toBe(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should add title child element to svg when title prop is passed', () => {
|
||||||
|
const { container } = render(IconAccessible, {
|
||||||
|
props: {
|
||||||
|
title: 'Test Title',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const svg = container.getElementsByTagName("svg")[0]
|
||||||
|
const title = container.getElementsByTagName("title")[0]
|
||||||
|
|
||||||
|
expect(title).toHaveTextContent('Test Title')
|
||||||
|
expect(svg).toContainElement(title)
|
||||||
|
})
|
||||||
|
|
||||||
it('should call the onClick event', async () => {
|
it('should call the onClick event', async () => {
|
||||||
const onClick = vi.fn()
|
const onClick = vi.fn()
|
||||||
const { container } = render(IconAccessible, {
|
const { container } = render(IconAccessible, {
|
||||||
@@ -97,11 +111,11 @@ describe("Vue Icon component", () => {
|
|||||||
const textElement = getByText(testText)
|
const textElement = getByText(testText)
|
||||||
|
|
||||||
expect(textElement).toBeInTheDocument()
|
expect(textElement).toBeInTheDocument()
|
||||||
expect(container.innerHTML).toMatchInlineSnapshot(`"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-accessible"><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"></path><path d="M10 16.5l2 -3l2 3m-2 -3v-2l3 -1m-6 0l3 1"></path><circle cx="12" cy="7.5" r=".5" fill="currentColor"></circle><text>Hello World</text></svg>"`);
|
expect(container.innerHTML).toMatchInlineSnapshot(`"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-accessible"><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"></path><path d="M10 16.5l2 -3l2 3m-2 -3v-2l3 -1m-6 0l3 1"></path><circle cx="12" cy="7.5" r=".5" fill="currentColor"></circle><text>Hello World</text></svg>"`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should match snapshot", () => {
|
it("should match snapshot", () => {
|
||||||
const { container } = render(IconAccessible);
|
const { container } = render(IconAccessible);
|
||||||
expect(container.innerHTML).toMatchInlineSnapshot(`"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-accessible"><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"></path><path d="M10 16.5l2 -3l2 3m-2 -3v-2l3 -1m-6 0l3 1"></path><circle cx="12" cy="7.5" r=".5" fill="currentColor"></circle></svg>"`)
|
expect(container.innerHTML).toMatchInlineSnapshot(`"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-accessible"><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"></path><path d="M10 16.5l2 -3l2 3m-2 -3v-2l3 -1m-6 0l3 1"></path><circle cx="12" cy="7.5" r=".5" fill="currentColor"></circle></svg>"`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user