diff --git a/ci/k8s/apps/umami/configmap.yaml b/ci/k8s/apps/umami/configmap.yaml index 4c524b1..3f7dc03 100644 --- a/ci/k8s/apps/umami/configmap.yaml +++ b/ci/k8s/apps/umami/configmap.yaml @@ -6,4 +6,4 @@ metadata: data: DATABASE_TYPE: "postgresql" CLIENT_IP_HEADER: "X-Forwarded-For" - TRACKER_SCRIPT_NAME: "beacon.js" + TRACKER_SCRIPT_NAME: "trace.js" diff --git a/ci/k8s/apps/umami/deployment.yaml b/ci/k8s/apps/umami/deployment.yaml index 1da65c4..45dd418 100644 --- a/ci/k8s/apps/umami/deployment.yaml +++ b/ci/k8s/apps/umami/deployment.yaml @@ -18,7 +18,7 @@ spec: labels: app: umami annotations: - config/tracker-script-name: "beacon.js" + config/tracker-script-name: "trace.js" spec: containers: - name: umami diff --git a/ci/k8s/infrastructure/networking/ingress-beacon.yaml b/ci/k8s/infrastructure/networking/ingress-beacon.yaml index 59f69a9..52cc026 100644 --- a/ci/k8s/infrastructure/networking/ingress-beacon.yaml +++ b/ci/k8s/infrastructure/networking/ingress-beacon.yaml @@ -20,7 +20,7 @@ spec: - host: www.ermnvldmr.com http: paths: - - path: /beacon + - path: /trace pathType: Prefix backend: service: @@ -30,7 +30,7 @@ spec: - host: docs.ermnvldmr.com http: paths: - - path: /beacon + - path: /trace pathType: Prefix backend: service: diff --git a/ci/k8s/infrastructure/networking/middleware-strip-beacon.yaml b/ci/k8s/infrastructure/networking/middleware-strip-beacon.yaml index 31e5ee0..d33a70f 100644 --- a/ci/k8s/infrastructure/networking/middleware-strip-beacon.yaml +++ b/ci/k8s/infrastructure/networking/middleware-strip-beacon.yaml @@ -6,4 +6,4 @@ metadata: spec: stripPrefix: prefixes: - - /beacon + - /trace diff --git a/packages/ssg/src/lib/config/analytics/umami.test.ts b/packages/ssg/src/lib/config/analytics/umami.test.ts new file mode 100644 index 0000000..604150b --- /dev/null +++ b/packages/ssg/src/lib/config/analytics/umami.test.ts @@ -0,0 +1,56 @@ +import { describe, expect, it } from 'vitest'; + +import { umamiAnalytics } from './umami'; + +describe('umamiAnalytics', () => { + it('suppresses script injection in non-production environments when not forced', () => { + const provider = umamiAnalytics({ + serverUrl: '/pulse', + websiteId: 'test-website-id', + }); + + expect(provider.getHtmlTags(false)).toEqual([]); + }); + + it('injects script tag in non-production when force is true', () => { + const provider = umamiAnalytics({ + serverUrl: '/pulse', + websiteId: 'test-website-id', + force: true, + }); + + const tags = provider.getHtmlTags(false); + expect(tags).toHaveLength(1); + expect(tags[0]?.attrs?.src).toBe('/pulse/script.js'); + }); + + it('generates expected script attributes in production builds', () => { + const provider = umamiAnalytics({ + serverUrl: '/trace/', + scriptName: 'trace.js', + hostUrl: '/trace', + websiteId: '41c7699a-0675-4f49-bcd0-3df47a66b6b1', + domains: 'ermnvldmr.com,www.ermnvldmr.com', + doNotTrack: true, + tag: 'landing', + }); + + const tags = provider.getHtmlTags(true); + expect(tags).toEqual([ + { + tag: 'script', + head: true, + publicPath: false, + attrs: { + defer: true, + src: '/trace/trace.js', + 'data-website-id': '41c7699a-0675-4f49-bcd0-3df47a66b6b1', + 'data-host-url': '/trace', + 'data-domains': 'ermnvldmr.com,www.ermnvldmr.com', + 'data-do-not-track': 'true', + 'data-tag': 'landing', + }, + }, + ]); + }); +}); diff --git a/packages/ui/src/components/Switch/Switch.stories.tsx b/packages/ui/src/components/Switch/Switch.stories.tsx index 03f689a..963f123 100644 --- a/packages/ui/src/components/Switch/Switch.stories.tsx +++ b/packages/ui/src/components/Switch/Switch.stories.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react'; import { Switch } from './Switch'; +import { VStack } from '../VStack/VStack'; import type { Meta, StoryObj } from '@storybook/react'; @@ -8,6 +9,16 @@ const meta: Meta = { title: 'Components/Switch', component: Switch, tags: ['autodocs'], + argTypes: { + size: { + control: 'select', + options: ['s', 'm', 'l'], + }, + variant: { + control: 'select', + options: ['solid', 'outline', 'ghost'], + }, + }, }; export default meta; @@ -19,6 +30,16 @@ export const Default: Story = { }, }; +export const Sizes: Story = { + render: () => ( + + Small Switch + Medium Switch + Large Switch + + ), +}; + export const Selected: Story = { args: { children: 'Selected State', diff --git a/packages/ui/src/components/Switch/Switch.test.tsx b/packages/ui/src/components/Switch/Switch.test.tsx index f7d862a..1b67c4a 100644 --- a/packages/ui/src/components/Switch/Switch.test.tsx +++ b/packages/ui/src/components/Switch/Switch.test.tsx @@ -66,5 +66,18 @@ describe('components/Switch', () => { expect(track).toHaveClass('data-[selected=true]:bg-primary'); expect(thumb).toHaveClass('data-[selected=true]:bg-primary-text'); }); + + it('applies size variant classes to track and thumb', () => { + const { container, rerender } = render(Small); + const track = container.querySelector('[aria-hidden="true"]'); + const thumb = track?.firstElementChild; + + expect(track).toHaveClass('w-7', 'h-4'); + expect(thumb).toHaveClass('w-2.5', 'h-2.5'); + + rerender(Large); + expect(track).toHaveClass('w-14', 'h-8'); + expect(thumb).toHaveClass('w-6', 'h-6'); + }); }); diff --git a/packages/ui/src/components/Switch/Switch.tsx b/packages/ui/src/components/Switch/Switch.tsx index 056df29..efe483c 100644 --- a/packages/ui/src/components/Switch/Switch.tsx +++ b/packages/ui/src/components/Switch/Switch.tsx @@ -14,6 +14,11 @@ import type { AriaSwitchProps } from 'react-aria'; */ export type SwitchVariant = 'solid' | 'outline' | 'ghost'; +/** + * Switch size variants. + */ +export type SwitchSize = 's' | 'm' | 'l'; + /** * Switch color themes. */ @@ -39,12 +44,14 @@ export interface SwitchProps extends AriaSwitchProps, ClassNameProps, DataAttrib children?: React.ReactNode; /** Visual style variant */ variant?: SwitchVariant; + /** Switch size */ + size?: SwitchSize; /** Color theme */ color?: SwitchColor; } const trackVariants = cva( - 'relative w-11 h-6 transition-all duration-200 ease-in-out rounded-full outline-none flex items-center', + 'relative transition-all duration-200 ease-in-out rounded-full outline-none flex items-center', { variants: { variant: { @@ -52,6 +59,11 @@ const trackVariants = cva( outline: 'border-2 data-[selected=false]:bg-transparent data-[selected=false]:border-muted', ghost: 'data-[selected=false]:bg-transparent data-[selected=false]:border-transparent', }, + size: { + s: 'w-7 h-4', + m: 'w-11 h-6', + l: 'w-14 h-8', + }, color: { primary: '', 'primary-negative': '', @@ -118,19 +130,25 @@ const trackVariants = cva( ], defaultVariants: { variant: 'solid', + size: 'm', color: 'primary', }, } ); const thumbVariants = cva( - 'w-4 h-4 transition-all duration-200 ease-in-out transform rounded-full shadow-sm data-[selected=false]:bg-[var(--rb-text)] data-[selected=false]:translate-x-0', + 'transition-all duration-200 ease-in-out transform rounded-full shadow-sm data-[selected=false]:bg-[var(--rb-text)] data-[selected=false]:translate-x-0', { variants: { variant: { - solid: 'ml-1 data-[selected=true]:translate-x-5', - outline: 'ml-0.5 data-[selected=true]:translate-x-4.5', - ghost: 'ml-1 data-[selected=true]:translate-x-5', + solid: '', + outline: '', + ghost: '', + }, + size: { + s: 'w-2.5 h-2.5', + m: 'w-4 h-4', + l: 'w-6 h-6', }, color: { primary: '', @@ -146,6 +164,17 @@ const thumbVariants = cva( }, }, compoundVariants: [ + // size + variant offsets + { size: 's', variant: 'solid', className: 'ml-0.5 data-[selected=true]:translate-x-3.5' }, + { size: 's', variant: 'outline', className: 'ml-0.5 data-[selected=true]:translate-x-2.5' }, + { size: 's', variant: 'ghost', className: 'ml-0.5 data-[selected=true]:translate-x-3.5' }, + { size: 'm', variant: 'solid', className: 'ml-1 data-[selected=true]:translate-x-5' }, + { size: 'm', variant: 'outline', className: 'ml-0.5 data-[selected=true]:translate-x-4.5' }, + { size: 'm', variant: 'ghost', className: 'ml-1 data-[selected=true]:translate-x-5' }, + { size: 'l', variant: 'solid', className: 'ml-1 data-[selected=true]:translate-x-6' }, + { size: 'l', variant: 'outline', className: 'ml-0.5 data-[selected=true]:translate-x-5.5' }, + { size: 'l', variant: 'ghost', className: 'ml-1 data-[selected=true]:translate-x-6' }, + // primary { variant: 'solid', color: 'primary', className: 'data-[selected=true]:bg-primary-text' }, { variant: 'outline', color: 'primary', className: 'data-[selected=true]:bg-primary-text' }, @@ -198,6 +227,7 @@ const thumbVariants = cva( ], defaultVariants: { variant: 'solid', + size: 'm', color: 'primary', }, } @@ -216,7 +246,7 @@ const thumbVariants = cva( * ``` */ export function Switch(props: SwitchProps): React.JSX.Element { - const { children, className, variant = 'solid', color = 'primary' } = props; + const { children, className, variant = 'solid', size = 'm', color = 'primary' } = props; const state = useToggleState(props); const ref = useRef(null); const { inputProps } = useSwitch(props, state, ref); @@ -238,14 +268,14 @@ export function Switch(props: SwitchProps): React.JSX.Element {