Match Angular day-tabs UI + fix flights-map SEO key

Rebuild DayTabs to mirror Angular's flat single-line tab strip: one label
per tab ('17 апр.' siblings, '19 апреля' active), 48px tall, 7 tabs per
page, brand blue label on light-blue background with white active cell.
Single Intl.DateTimeFormat call keeps Russian months in genitive case.

Drop the 60px sticky-content offset to 20px so the date strip aligns
with the left filter column (both already sticky at top:20px).

Correct SEO.FLIGHTS_MAP translation key to SEO.FLIGHTS-MAP.MAIN — the
underscored path never existed in the locale files, so the browser tab
title on /{lang}/flights-map fell back to the raw key.
This commit is contained in:
2026-04-19 14:35:13 +03:00
parent 0bf4e23815
commit f2661768b0
6 changed files with 81 additions and 81 deletions
+3 -3
View File
@@ -14,11 +14,11 @@ const CANONICAL = "https://www.aeroflot.ru";
// ---------------------------------------------------------------------------
describe("buildFlightsMapSeo", () => {
it("uses FLIGHTS_MAP translation keys", () => {
it("uses FLIGHTS-MAP.MAIN translation keys", () => {
const result = buildFlightsMapSeo(stubT, "ru", CANONICAL);
expect(result.title).toBe("SEO.FLIGHTS_MAP.TITLE");
expect(result.description).toBe("SEO.FLIGHTS_MAP.DESCRIPTION");
expect(result.title).toBe("SEO.FLIGHTS-MAP.MAIN.TITLE");
expect(result.description).toBe("SEO.FLIGHTS-MAP.MAIN.DESCRIPTION");
});
it("sets canonical to /{locale}/flights-map", () => {
+2 -2
View File
@@ -37,8 +37,8 @@ export function buildFlightsMapSeo(
locale: string,
canonicalOrigin: string,
): SeoHeadProps {
const title = t("SEO.FLIGHTS_MAP.TITLE");
const description = t("SEO.FLIGHTS_MAP.DESCRIPTION");
const title = t("SEO.FLIGHTS-MAP.MAIN.TITLE");
const description = t("SEO.FLIGHTS-MAP.MAIN.DESCRIPTION");
const canonical = `${canonicalOrigin}/${locale}${PATH_WITHOUT_LOCALE}`;
return {
@@ -9,11 +9,11 @@ describe("DayTabButton", () => {
expect(screen.getByTestId("day-tab-20260416")).toBeTruthy();
});
it("renders weekday, day number, and month", () => {
it("renders day number and month in a single label", () => {
render(<DayTabButton date="20260416" isActive={false} isDisabled={false} locale="en" onClick={() => {}} />);
expect(screen.getByText("16")).toBeTruthy();
expect(screen.getByText(/Apr/i)).toBeTruthy();
expect(screen.getByText(/Thu/i)).toBeTruthy();
const label = screen.getByTestId("day-tab-20260416").textContent ?? "";
expect(label).toMatch(/16/);
expect(label).toMatch(/Apr/i);
});
it("calls onClick with date when enabled", () => {
@@ -18,11 +18,11 @@ export const DayTabButton: FC<DayTabButtonProps> = ({
onClick,
}) => {
const d = parseYyyymmdd(date);
const weekday = new Intl.DateTimeFormat(locale, { weekday: "short" }).format(d);
const day = new Intl.DateTimeFormat(locale, { day: "numeric" }).format(d);
// Angular shows the selected tab with the full month name ('18 апреля'),
// siblings with the abbreviated form ('19 апр.'). Mirror that.
const month = new Intl.DateTimeFormat(locale, {
// Single format call keeps Russian month in the correct genitive
// case ('19 апреля' rather than '19 апрель'), matching Angular's
// ngx-translate output.
const label = new Intl.DateTimeFormat(locale, {
day: "numeric",
month: isActive ? "long" : "short",
}).format(d);
@@ -44,9 +44,7 @@ export const DayTabButton: FC<DayTabButtonProps> = ({
if (!isDisabled) onClick(date);
}}
>
<span className="day-tab__weekday">{weekday}</span>
<span className="day-tab__day">{day}</span>
<span className="day-tab__month">{month}</span>
<span className="day-tab__label">{label}</span>
</button>
);
};
@@ -1,11 +1,67 @@
// Matches Angular's date-tabs row. Single flat row, 48px tall, 7 tabs per
// page flanked by 50px carousel arrows. Active tab has white bg + dark
// text; siblings have light-blue bg + brand link-blue label.
.day-tabs-wrap {
margin-bottom: 8px;
}
.day-tabs {
display: flex;
align-items: stretch;
height: 48px;
border-radius: 8px 8px 0 0;
overflow: hidden;
background: #fff;
&__arrow {
width: 50px;
flex-shrink: 0;
background: #fff;
border: none;
cursor: pointer;
color: #657282;
font-size: 20px;
line-height: 1;
padding: 0;
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
&__list {
flex: 1;
display: grid;
grid-template-columns: repeat(7, 1fr);
min-width: 0;
}
@media (max-width: 768px) {
display: none;
}
}
.day-tab {
padding: 12px 8px;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
padding: 16px 0 15px;
background: #f3f9ff;
color: #1b62b4;
font-size: 12px;
font-weight: 500;
border: 0;
border-right: 1px solid #d1dcea;
border-bottom: 1px solid #d1dcea;
cursor: pointer;
background: #e8f0f7;
color: #2060c0;
border: none;
border-right: 1px solid #d0dae5;
text-align: center;
white-space: nowrap;
&__label {
display: inline-block;
}
&:last-child {
border-right: none;
@@ -13,33 +69,15 @@
&--active {
background: #fff;
color: #1a3a5c;
font-weight: 600;
color: #333;
border-bottom-color: #fff;
cursor: default;
}
&--disabled {
opacity: 0.5;
cursor: not-allowed;
}
// Angular's tabs size each line small: weekday 11px, day number 16px,
// month 12px. Active tab keeps everything the same size but switches to
// #333 and weight 500; siblings stay in the brand link blue.
&__weekday {
font-size: 11px;
display: block;
}
&__day {
font-size: 16px;
font-weight: 500;
display: block;
}
&__month {
font-size: 12px;
display: block;
}
}
.day-select {
@@ -54,39 +92,3 @@
border: 1px solid #d0dae5;
}
}
.day-tabs-wrap {
margin-bottom: 8px;
}
.day-tabs {
display: flex;
align-items: stretch;
background: #e8f0f7;
border-radius: 8px 8px 0 0;
&__arrow {
width: 48px;
flex-shrink: 0;
background: transparent;
border: none;
cursor: pointer;
color: #2060c0;
font-size: 20px;
&:disabled {
opacity: 0.3;
cursor: not-allowed;
}
}
&__list {
flex: 1;
display: grid;
grid-template-columns: repeat(7, 1fr);
}
@media (max-width: 768px) {
display: none;
}
}
+1 -1
View File
@@ -133,7 +133,7 @@
@include screen.gt-mobile {
position: sticky;
z-index: 1000;
top: 60px;
top: 20px;
}
}
}