Jak powstał Destinations (cz. 5): generatory testów i CSS do druku

15.10.2026 · 3 min czytania

Przez cztery części budowaliśmy wokół danych: pomysł i stack, typy, baza danych i logowanie, chronione trasy. Teraz powód, dla którego Destinations w ogóle powstało. Szkoleniowiec klika jeden przycisk, dostaje gotowy test i tabelę odpowiedzi, i drukuje. Na papierze, nie na ekranie.

Losowanie testu#

Test to zestaw losowych wartości z jednego pola destynacji — na przykład kilkanaście kodów IATA albo rozwinięć do rozpoznania. Napisałem do tego jedną funkcję:

import type { Destination } from "./../types";

export function losoweWartosci(
  tablica: Destination[],
  ilosc: number,
  klucz: keyof Destination,
): string[] {
  if (!tablica || !tablica.length || ilosc <= 0) return [];

  const wylosowaneWartosci: string[] = [];
  const pomocniczaTablica = [...tablica];

  for (let i = 0; i < ilosc; i++) {
    if (pomocniczaTablica.length === 0) break;
    const losowyIndeks = Math.floor(Math.random() * pomocniczaTablica.length);
    wylosowaneWartosci.push(pomocniczaTablica[losowyIndeks][klucz]);
    pomocniczaTablica.splice(losowyIndeks, 1);
  }
  return wylosowaneWartosci;
}

Na starcie krótki strażnik: jeśli tablica jest pusta albo ilosc jest zerowa czy ujemna, zwracam od razu []. Potem kopiuję tablicę ([...tablica], żeby nie ruszać oryginału z bazy) i ilosc razy losuję indeks, dorzucam wartość do wyniku, a wylosowany element usuwam przez splice.

Widok do druku#

W Destinations są właściwie dwa wydruki: test (Generator) i tabele do nauki (PrintTables). Oba odpala ten sam, natywny mechanizm — zwykły przycisk z window.print(), zero bibliotek:

<button onClick={() => window.print()}>DRUKUJ</button>

window.print() otwiera systemowe okno drukowania przeglądarki. Cała robota jest w tym, żeby papier wyglądał inaczej niż ekran: bez przycisków, bez selecta, bez paska sterowania. I tym zajmuje się CSS.

CSS, o którym mało kto myśli: @media print#

Najpierw ustawiam samą kartkę, potem chowam na wydruku wszystko, co służy tylko do klikania:

@page {
  margin: 25mm;
  size: a4 portrait;
}

@media print {
  button,
  select,
  .controls {
    display: none !important;
  }
}

Druga sprawa to podział na strony, żeby treść nie urywała się w losowych miejscach:

/* test: zadanie 2 zaczyna się od nowej kartki */
.page:first-of-type {
  page-break-after: always;
}

/* tabele: każda strefa na osobnej kartce */
.schengen-container,
.non-schengen-container {
  break-after: page;
}

@media print {
  tr {
    break-inside: avoid;
  }
}

Koniec serii#

I to tyle. Przez pięć wpisów Destinations przeszło drogę od pomysłu (cz. 1), przez typy (cz. 2), bazę i logowanie (cz. 3), chronione trasy (cz. 4), aż po to, co użytkownik trzyma na końcu w ręku: wydrukowany test (cz. 5).

W kolejnych seriach chcę trochę zwolnić i wrócić do fundamentów: HTML, CSS i JavaScript od podstaw. W końcu dobrze zbudowany dom stoi na mocnych fundamentach. Do zobaczenia w nowej serii!

Więcej z tej serii

  1. 1.Jak powstał Destinations (cz. 1): pomysł, domena i stack
  2. 2.Jak powstał Destinations (cz. 2): z JavaScriptu na TypeScript
  3. 3.Jak powstał Destinations (cz. 3): Firebase — baza i logowanie
  4. 4.Jak powstał Destinations (cz. 4): chronione trasy w React Router
  5. 5.Jak powstał Destinations (cz. 5): generatory testów i CSS do druku