diff --git a/packages/icons-react-native/src/createReactNativeComponent.ts b/packages/icons-react-native/src/createReactNativeComponent.ts index 3786d5fce4..e14ee93232 100644 --- a/packages/icons-react-native/src/createReactNativeComponent.ts +++ b/packages/icons-react-native/src/createReactNativeComponent.ts @@ -10,7 +10,7 @@ const createReactNativeComponent = ( iconNode: IconNode, ): Icon => { const Component = forwardRef( - ({ color = 'currentColor', size = 24, strokeWidth = 2, children, ...rest }: IconProps, ref) => { + ({ color = 'currentColor', size = 24, strokeWidth = 2, title, children, ...rest }: IconProps, ref) => { const customAttrs = { stroke: color, strokeWidth, @@ -37,7 +37,10 @@ const createReactNativeComponent = ( { ...childDefaultAttributes[type], ...customAttrs, ...attrs } as IconProps, ); }), - ...((Array.isArray(children) ? children : [children]) || []), + [ + title && createElement('title', { key: 'svg-title' }, title), + ...((Array.isArray(children) ? children : [children]) || []) + ], ], ); }, diff --git a/packages/icons-react-native/src/types.ts b/packages/icons-react-native/src/types.ts index d0895b9cbc..ab5dbe72e5 100644 --- a/packages/icons-react-native/src/types.ts +++ b/packages/icons-react-native/src/types.ts @@ -10,6 +10,7 @@ export type SVGAttributes = Partial>; export interface IconProps extends SvgProps { size?: string | number; strokeWidth?: string | number; + title?: string; } export type Icon = ForwardRefExoticComponent; diff --git a/packages/icons-react/src/createReactComponent.ts b/packages/icons-react/src/createReactComponent.ts index 8a28250c88..79a10811f1 100644 --- a/packages/icons-react/src/createReactComponent.ts +++ b/packages/icons-react/src/createReactComponent.ts @@ -10,7 +10,7 @@ const createReactComponent = ( ) => { const Component = forwardRef( ( - { color = 'currentColor', size = 24, stroke = 2, className, children, ...rest }: IconProps, + { color = 'currentColor', size = 24, stroke = 2, title, className, children, ...rest }: IconProps, ref, ) => createElement( @@ -32,6 +32,7 @@ const createReactComponent = ( ...rest, }, [ + title && createElement('title', { key: 'svg-title' }, title), ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)), ...(Array.isArray(children) ? children : [children]), ], diff --git a/packages/icons-react/src/types.ts b/packages/icons-react/src/types.ts index 1f69cfd1d4..04a82ce835 100644 --- a/packages/icons-react/src/types.ts +++ b/packages/icons-react/src/types.ts @@ -7,6 +7,7 @@ export type IconNode = [elementName: keyof ReactSVG, attrs: Record, 'stroke'>> { size?: string | number; stroke?: string | number; + title?: string; } export type Icon = FunctionComponent; diff --git a/packages/icons-react/test.spec.tsx b/packages/icons-react/test.spec.tsx index 8ba094a105..41b611fe13 100644 --- a/packages/icons-react/test.spec.tsx +++ b/packages/icons-react/test.spec.tsx @@ -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 { IconAccessible, IconAccessibleFilled, createReactComponent } from "./src/tabler-icons-react" -import type { IconNode } from './src/tabler-icons-react'; -import { ReactNode } from 'react'; describe("React Icon component", () => { afterEach(() => { @@ -58,6 +56,14 @@ describe("React Icon component", () => { expectTypeOf(IconAccessible).toEqualTypeOf(createReactComponent('outline', 'accessible', 'Accessible', [])); }); + it('should add title child element to svg when title prop is passed', () => { + const { container } = render(); + const svg = container.getElementsByTagName("svg")[0]; + const title = svg.getElementsByTagName("title")[0]; + + expect(title).toHaveTextContent("Accessible Icon"); + }) + it("should match snapshot", () => { const { container } = render() expect(container.innerHTML).toMatchInlineSnapshot(` diff --git a/packages/icons-vue/src/createVueComponent.ts b/packages/icons-vue/src/createVueComponent.ts index 5d23dcb04e..9096ebaff8 100644 --- a/packages/icons-vue/src/createVueComponent.ts +++ b/packages/icons-vue/src/createVueComponent.ts @@ -9,7 +9,9 @@ const createVueComponent = iconNamePascal: string, iconNode: IconNode, ): 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( 'svg', { @@ -28,7 +30,7 @@ const createVueComponent = }), ...rest, }, - [...iconNode.map((child) => h(...child)), ...(slots.default ? [slots.default()] : [])], + children, ); }; diff --git a/packages/icons-vue/src/types.ts b/packages/icons-vue/src/types.ts index 3adec3583f..8455795278 100644 --- a/packages/icons-vue/src/types.ts +++ b/packages/icons-vue/src/types.ts @@ -11,4 +11,5 @@ export type Icon = FunctionalComponent; export interface IconProps extends Partial> { size?: string | number; stroke?: string | number; + title?: string; } diff --git a/packages/icons-vue/test.spec.js b/packages/icons-vue/test.spec.js index c39a4671d2..32e39be235 100644 --- a/packages/icons-vue/test.spec.js +++ b/packages/icons-vue/test.spec.js @@ -67,6 +67,20 @@ describe("Vue Icon component", () => { 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 () => { const onClick = vi.fn() const { container } = render(IconAccessible, { @@ -97,11 +111,11 @@ describe("Vue Icon component", () => { const textElement = getByText(testText) expect(textElement).toBeInTheDocument() - expect(container.innerHTML).toMatchInlineSnapshot(`"Hello World"`); + expect(container.innerHTML).toMatchInlineSnapshot(`"Hello World"`); }); it("should match snapshot", () => { const { container } = render(IconAccessible); - expect(container.innerHTML).toMatchInlineSnapshot(`""`) + expect(container.innerHTML).toMatchInlineSnapshot(`""`) }) })