From 8c57976663969fb159fb7bb4334cb6688aa5ab76 Mon Sep 17 00:00:00 2001 From: Roman Timashev Date: Sun, 8 Feb 2026 03:29:28 +0700 Subject: [PATCH] Ymetrika update (#245) * update claude with message about components * yandex metrika to ts * rm env * add .env example * use account from env * add tests --- .env => .env.example | 2 +- .gitignore | 1 + CLAUDE.md | 12 +++++++ pages/_app.tsx | 6 ++-- src/components/YMetrika.test.ts | 32 +++++++++++++++++++ src/components/{YMetrika.jsx => YMetrika.tsx} | 16 ++++++---- vitest.config.ts | 2 +- 7 files changed, 60 insertions(+), 11 deletions(-) rename .env => .env.example (68%) create mode 100644 src/components/YMetrika.test.ts rename src/components/{YMetrika.jsx => YMetrika.tsx} (80%) diff --git a/.env b/.env.example similarity index 68% rename from .env rename to .env.example index 537be565..380aaaba 100644 --- a/.env +++ b/.env.example @@ -1,2 +1,2 @@ APP_CARD_DEFAULT_IMAGE=https://art.shlisselburg.org/static/img/main.jpg - +NEXT_PUBLIC_YMETRIKA_ACCOUNT=XXX diff --git a/.gitignore b/.gitignore index 817645cf..35ab2d8d 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ dist .vercel .next tsconfig.tsbuildinfo +.env diff --git a/CLAUDE.md b/CLAUDE.md index a9fe8cea..7b9608d3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,6 +26,18 @@ Feature branches are named `issue-XXX` where `XXX` is the related GitHub issue n ESLint 9 flat config (`eslint.config.js`) using `FlatCompat` from `@eslint/eslintrc` to bridge `eslint-config-next` (which doesn't support native flat config in Next.js 15.x). Extends `next/core-web-vitals`. Enforces: no semicolons, double quotes, 4-space indentation, always-multiline trailing commas, `eol-last`. +**React components:** Use regular function declarations (not arrow functions) and `type` for props. Do not use `React.FC`. + +```tsx +export type MyComponentProps = { + value: string +} + +export function MyComponent({ value }: MyComponentProps) { + return
{value}
+} +``` + ## Architecture **Pages Router with SSG/ISR:** All pages use `getStaticProps`/`getStaticPaths` with 30-second revalidation. The catch-all route `pages/[...slug].tsx` handles all dynamic content pages. diff --git a/pages/_app.tsx b/pages/_app.tsx index 0c71cc1d..4554ee69 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -7,7 +7,7 @@ import "src/style/kazimir/video.css" import App from "next/app" import Head from "next/head" import Script from "next/script" -import { YMetrika } from "src/components/YMetrika" +import { YMetrika } from "@/components/YMetrika" import { ThemeColor } from "@/ui/ThemeColor" export default class MyApp extends App { @@ -41,7 +41,9 @@ export default class MyApp extends App { href="/feed.xml" /> - + {process.env.NEXT_PUBLIC_YMETRIKA_ACCOUNT && ( + + )} { + it("should embed account id into metrika counter variable", () => { + const result = script(99999) + expect(result).toContain("w.yaCounter99999") + }) + + it("should embed account id into metrika config", () => { + const result = script(99999) + expect(result).toContain("id:99999") + }) + + it("should include metrika watch.js script url", () => { + const result = script(99999) + expect(result).toContain("https://mc.yandex.ru/metrika/watch.js") + }) + + it("should use yandex_metrika_callbacks", () => { + const result = script(99999) + expect(result).toContain("yandex_metrika_callbacks") + }) + + it("should produce different output for different accounts", () => { + const a = script(11111) + const b = script(22222) + expect(a).toContain("yaCounter11111") + expect(b).toContain("yaCounter22222") + expect(a).not.toContain("yaCounter22222") + }) +}) diff --git a/src/components/YMetrika.jsx b/src/components/YMetrika.tsx similarity index 80% rename from src/components/YMetrika.jsx rename to src/components/YMetrika.tsx index 33d61f26..14c3ee0c 100644 --- a/src/components/YMetrika.jsx +++ b/src/components/YMetrika.tsx @@ -1,6 +1,8 @@ -import React from "react" +export type YMetrikaProps = { + account: number +} -function script(account) { +export function script(account: number) { return ` (function (d, w, c) { (w[c] = w[c] || []).push(function() { @@ -14,14 +16,14 @@ function script(account) { }); } catch(e) { } }); - + var n = d.getElementsByTagName("script")[0], s = d.createElement("script"), f = function () { n.parentNode.insertBefore(s, n); }; s.type = "text/javascript"; s.async = true; s.src = "https://mc.yandex.ru/metrika/watch.js"; - + if (w.opera == "[object Opera]") { d.addEventListener("DOMContentLoaded", f, false); } else { f(); } @@ -29,6 +31,6 @@ function script(account) { ` } -export const YMetrika = (props) => ( -