feat(Footer): create Footer widget with project name and portfolio link
This commit is contained in:
@@ -0,0 +1 @@
|
||||
export { default as Footer } from './ui/Footer.svelte';
|
||||
@@ -0,0 +1,47 @@
|
||||
<script module lang="ts">
|
||||
import { defineMeta } from '@storybook/addon-svelte-csf';
|
||||
import Footer from './Footer.svelte';
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: 'Widgets/Footer',
|
||||
component: Footer,
|
||||
tags: ['autodocs'],
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
'Application footer with project information and portfolio link. Visible only on desktop screens.',
|
||||
},
|
||||
story: { inline: false },
|
||||
},
|
||||
layout: 'fullscreen',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story
|
||||
name="Desktop View"
|
||||
parameters={{
|
||||
viewport: { defaultViewport: 'desktop' },
|
||||
}}
|
||||
>
|
||||
{#snippet template()}
|
||||
<div class="h-[200px] relative bg-neutral-50 dark:bg-neutral-900">
|
||||
<Footer />
|
||||
</div>
|
||||
{/snippet}
|
||||
</Story>
|
||||
|
||||
<Story
|
||||
name="Mobile View (Hidden)"
|
||||
parameters={{
|
||||
viewport: { defaultViewport: 'mobile1' },
|
||||
}}
|
||||
>
|
||||
{#snippet template()}
|
||||
<div class="h-[200px] relative bg-neutral-50 dark:bg-neutral-900">
|
||||
<p class="p-4 text-sm text-neutral-500 italic">Footer should be hidden on mobile.</p>
|
||||
<Footer />
|
||||
</div>
|
||||
{/snippet}
|
||||
</Story>
|
||||
@@ -0,0 +1,36 @@
|
||||
<!--
|
||||
Widget: Footer
|
||||
Application footer with project information and portfolio link.
|
||||
Visible only on desktop screens.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { ResponsiveManager } from '$shared/lib/helpers';
|
||||
import { FooterLink } from '$shared/ui';
|
||||
import { getContext } from 'svelte';
|
||||
|
||||
const responsive = getContext<ResponsiveManager>('responsive');
|
||||
const currentYear = new Date().getFullYear();
|
||||
</script>
|
||||
|
||||
{#if responsive?.isDesktop || responsive?.isDesktopLarge}
|
||||
<footer class="fixed bottom-5 right-5 z-50 flex flex-col items-end gap-1 pointer-events-none">
|
||||
<!-- Portfolio Link (Vertical) -->
|
||||
<div class="pointer-events-auto">
|
||||
<FooterLink
|
||||
text="allmy.work"
|
||||
href="https://allmy.work/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="border border-subtle"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Project Name (Horizontal) -->
|
||||
<div class="pointer-events-auto flex items-center gap-2 bg-surface/80 dark:bg-dark-bg/80 backdrop-blur-sm px-3 py-1 border border-subtle">
|
||||
<div class="w-1.5 h-1.5 bg-brand"></div>
|
||||
<span class="text-2xs font-mono uppercase tracking-wider-mono text-neutral-500 dark:text-neutral-400">
|
||||
GlyphDiff © 2025 — {currentYear}
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
{/if}
|
||||
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
render,
|
||||
screen,
|
||||
} from '@testing-library/svelte';
|
||||
import { setContext } from 'svelte';
|
||||
import Footer from './Footer.svelte';
|
||||
|
||||
// Mock component to provide context
|
||||
import ContextWrapper from '$shared/lib/providers/ResponsiveProvider/ResponsiveProvider.svelte';
|
||||
|
||||
describe('Footer', () => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
it('renders on desktop', () => {
|
||||
// Mock responsive context
|
||||
const mockResponsive = {
|
||||
isDesktop: true,
|
||||
isDesktopLarge: false,
|
||||
};
|
||||
|
||||
const { container } = render(Footer, {
|
||||
context: new Map([['responsive', mockResponsive]]),
|
||||
});
|
||||
|
||||
expect(screen.getByText(`GlyphDiff © 2025 — ${currentYear}`)).toBeInTheDocument();
|
||||
expect(screen.getByText('allmy.work')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders on large desktop', () => {
|
||||
const mockResponsive = {
|
||||
isDesktop: false,
|
||||
isDesktopLarge: true,
|
||||
};
|
||||
|
||||
render(Footer, {
|
||||
context: new Map([['responsive', mockResponsive]]),
|
||||
});
|
||||
|
||||
expect(screen.getByText(`GlyphDiff © 2025 — ${currentYear}`)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render on mobile or tablet', () => {
|
||||
const mockResponsive = {
|
||||
isDesktop: false,
|
||||
isDesktopLarge: false,
|
||||
};
|
||||
|
||||
render(Footer, {
|
||||
context: new Map([['responsive', mockResponsive]]),
|
||||
});
|
||||
|
||||
expect(screen.queryByText(/GlyphDiff/)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user