From c898ade0f03d20cee90f3bdd813afad6e9a2922c Mon Sep 17 00:00:00 2001 From: BG-Software <73077398+BG-Software-BG@users.noreply.github.com> Date: Thu, 13 Jun 2024 22:08:44 +0200 Subject: [PATCH] Add title to icon's as child tag in React, Vue.js and React Native (#1147) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add title as child tag of in icons-react * Add title as child tag of in icons-vue package * Add title as child tag of in icons-react-native package * Test element in React * Prevent adding to if no title prop passed + Test child element. * Fix tests for Vue Stroke attribute added to expected SVG code. --------- Co-authored-by: Bartłomiej Gawęda --- .../src/createReactNativeComponent.ts | 7 +++++-- packages/icons-react-native/src/types.ts | 1 + .../icons-react/src/createReactComponent.ts | 3 ++- packages/icons-react/src/types.ts | 1 + packages/icons-react/test.spec.tsx | 12 +++++++++--- packages/icons-vue/src/createVueComponent.ts | 6 ++++-- packages/icons-vue/src/types.ts | 1 + packages/icons-vue/test.spec.js | 18 ++++++++++++++++-- 8 files changed, 39 insertions(+), 10 deletions(-) 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(`""`) }) })