chore(SetupFont): rename controlManager to typographySettingsStore for better semantic #37
@@ -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