diff --git a/src/shared/api/types.ts b/src/shared/api/types.ts index b612be1..66295e1 100644 --- a/src/shared/api/types.ts +++ b/src/shared/api/types.ts @@ -141,6 +141,10 @@ export type SocialRecord = BaseRecord & { * Full URL for the social profile */ url: string; + /** + * SVG markup string stored in PocketBase + */ + icon: string; }; /** @@ -149,7 +153,7 @@ export type SocialRecord = BaseRecord & { */ export type ContactsRecord = BaseRecord & { /** - * Primary contact email address + * Raw relation ID — use expand?.email for the resolved record */ email: string; /** @@ -157,9 +161,13 @@ export type ContactsRecord = BaseRecord & { */ socials: string[]; /** - * Expanded relation data, present when fetched with expand=socials + * Expanded relation data, present when fetched with expand=email,socials */ expand?: { + /** + * Resolved email contact record + */ + email?: SocialRecord; /** * Resolved social link records */ diff --git a/src/shared/ui/InlineSvg/index.ts b/src/shared/ui/InlineSvg/index.ts new file mode 100644 index 0000000..f3283af --- /dev/null +++ b/src/shared/ui/InlineSvg/index.ts @@ -0,0 +1 @@ +export { InlineSvg } from './ui/InlineSvg'; diff --git a/src/shared/ui/InlineSvg/ui/InlineSvg.tsx b/src/shared/ui/InlineSvg/ui/InlineSvg.tsx new file mode 100644 index 0000000..182dad4 --- /dev/null +++ b/src/shared/ui/InlineSvg/ui/InlineSvg.tsx @@ -0,0 +1,25 @@ +import parse from 'html-react-parser'; +import { cn } from '$shared/lib'; + +type Props = { + /** + * SVG markup string to inline as React elements + */ + svg: string; + /** + * Additional CSS classes on the wrapper span + */ + className?: string; +}; + +/** + * Parses an SVG markup string into React elements. + * Inherits color from parent via currentColor. + */ +export function InlineSvg({ svg, className }: Props) { + if (!svg) { + return null; + } + + return {parse(svg)}; +} diff --git a/src/widgets/Footer/ui/Footer/Footer.test.tsx b/src/widgets/Footer/ui/Footer/Footer.test.tsx index e31eb90..0ea17cb 100644 --- a/src/widgets/Footer/ui/Footer/Footer.test.tsx +++ b/src/widgets/Footer/ui/Footer/Footer.test.tsx @@ -21,9 +21,19 @@ const mockSettings = { collectionName: 'contacts', created: '', updated: '', - email: 'hello@allmy.work', + email: 'e1', socials: ['s1'], expand: { + email: { + id: 'e1', + collectionId: 'contact', + collectionName: 'contact', + created: '', + updated: '', + label: 'hello@allmy.work', + url: 'mailto:hello@allmy.work', + icon: '', + }, socials: [ { id: 's1', @@ -33,6 +43,7 @@ const mockSettings = { updated: '', label: 'GitHub', url: 'https://github.com', + icon: '', }, ], }, @@ -58,19 +69,28 @@ describe('Footer', () => { }); describe('email link', () => { - it('renders the contact email as a mailto link', async () => { + it('renders the contact email link', async () => { + render(await Footer()); + const link = screen.getByRole('link', { name: /hello@allmy\.work/i }); + expect(link).toBeInTheDocument(); + }); + + it('email link points to the mailto url', async () => { render(await Footer()); const link = screen.getByRole('link', { name: /hello@allmy\.work/i }); expect(link).toHaveAttribute('href', 'mailto:hello@allmy.work'); }); - it('does not render email link when contacts.email is missing', async () => { + it('does not render email link when expand.email is missing', async () => { vi.mocked(getFirstRecord).mockResolvedValue({ ...mockSettings, expand: { contacts: { ...mockSettings.expand.contacts, - email: '', + expand: { + ...mockSettings.expand.contacts.expand, + email: undefined, + }, }, }, }); @@ -98,7 +118,10 @@ describe('Footer', () => { expand: { contacts: { ...mockSettings.expand.contacts, - expand: { socials: [] }, + expand: { + ...mockSettings.expand.contacts.expand, + socials: [], + }, }, }, });