fix(sf-11): review-pass — DOM.РФ latin URL + Fragment key + a11y keyboard

Per review #285 (HIGH BLOCK):
- DOM.РФ URL: кириллический slug → latin path
  (/services/catalog-newbuildings/object/{id}) для stable behavior
- Replace <> Fragment без key → <Fragment key={signature}>
  (убирает React warning при .map). Closing </> → </Fragment>
- a11y: <tr onClick> теперь имеет tabIndex, role="button",
  aria-expanded, onKeyDown (Enter/Space) — keyboard drill-in

Inline hex tokens (medium) — отложено в follow-up (epic #271 #19).
This commit is contained in:
lekss361 2026-05-17 16:16:24 +03:00
parent d3a51c3657
commit 1959283a41
3 changed files with 18 additions and 8 deletions

View file

@ -32,7 +32,7 @@
"openapi-typescript": "^7.0.0",
"postcss": "^8.4.0",
"tailwindcss": "^4.0.0",
"typescript": "^5.5.0"
"typescript": "5.9.3"
}
},
"node_modules/@alloc/quick-lru": {

View file

@ -35,6 +35,6 @@
"openapi-typescript": "^7.0.0",
"postcss": "^8.4.0",
"tailwindcss": "^4.0.0",
"typescript": "^5.5.0"
"typescript": "5.9.3"
}
}

View file

@ -1,6 +1,6 @@
"use client";
import { useState } from "react";
import { Fragment, useState } from "react";
import { useBestLayouts } from "@/hooks/useBestLayouts";
import { API_BASE_URL } from "@/lib/api";
import type {
@ -109,7 +109,7 @@ function CompetitorDrillIn({
{visible.map((id) => (
<a
key={id}
href={`https://наш.дом.рф/сервисы/каталог-новостроек/объект/${id}`}
href={`https://наш.дом.рф/services/catalog-newbuildings/object/${id}`}
target="_blank"
rel="noopener noreferrer"
className="bg-blue-100 text-blue-700 rounded px-2 py-0.5 font-mono hover:bg-blue-200 transition-colors"
@ -207,12 +207,23 @@ function TopLayoutsTable({ rows }: { rows: TopLayoutRow[] }) {
const hasCompetitors = row.competitor_obj_ids.length > 0;
return (
<>
<Fragment key={row.signature}>
<tr
key={row.signature}
onClick={() => {
if (hasCompetitors) toggleRow(row.signature);
}}
onKeyDown={(e) => {
if (
hasCompetitors &&
(e.key === "Enter" || e.key === " ")
) {
e.preventDefault();
toggleRow(row.signature);
}
}}
tabIndex={hasCompetitors ? 0 : undefined}
role={hasCompetitors ? "button" : undefined}
aria-expanded={hasCompetitors ? isExpanded : undefined}
style={{
background: i % 2 === 0 ? "#fff" : "#fafbfc",
borderBottom: isExpanded ? "none" : "1px solid #f3f4f6",
@ -352,12 +363,11 @@ function TopLayoutsTable({ rows }: { rows: TopLayoutRow[] }) {
</tr>
{isExpanded && hasCompetitors && (
<CompetitorDrillIn
key={`${row.signature}-drill`}
ids={row.competitor_obj_ids}
colSpan={TOTAL_COLS}
/>
)}
</>
</Fragment>
);
})}
</tbody>