Table

Denne komponenten er under aktiv utvikling, og vi trenger din feedback!

Er du eventyrlysten, test den og kom med innspill til oss på Slack.

Om komponenten

Table gir brukerne en tydeligere oversikt over data og relasjoner ved å strukturere informasjonen i rader og kolonner.

Egnet til:

  • Vise strukturert data i rader og kolonner
  • Lage sammenligninger (f.eks. produkter eller priser)
  • Presentere statistikk eller rapporter

Uegnet til:

  • Sideoppsett og layout (bruk heller CSS Grid eller Flexbox)
  • Vanlig tekst- og bildeinnhold uten tabellstruktur
  • Interaktivt innhold som knapper, kort eller navigasjon
  • Opplisting av elementer

Varianter

Table har to varianter: default og zebra-stripet. Ved å gi annenhver rad ulik bakgrunnsfarge blir det lettere å følge riktig linje på tvers av tabellen. Dette skaper et tydelig visuelt skille mellom radene, gjør tabellen mer oversiktlig og reduserer risikoen for å lese feil verdier.

Default

<UNSAFE_Table aria-label="Eiendomsforvaltere">
  <UNSAFE_TableHeader>
    <UNSAFE_TableColumn>Navn</UNSAFE_TableColumn>
    <UNSAFE_TableColumn>E-post</UNSAFE_TableColumn>
    <UNSAFE_TableColumn>Område</UNSAFE_TableColumn>
  </UNSAFE_TableHeader>
  <UNSAFE_TableBody>
    <UNSAFE_TableRow>
      <UNSAFE_TableCell>Kari Hansen</UNSAFE_TableCell>
      <UNSAFE_TableCell>kari.hansen@obos.no</UNSAFE_TableCell>
      <UNSAFE_TableCell>Grünerløkka</UNSAFE_TableCell>
    </UNSAFE_TableRow>
    <UNSAFE_TableRow>
      <UNSAFE_TableCell>Lars Olsen</UNSAFE_TableCell>
      <UNSAFE_TableCell>lars.olsen@obos.no</UNSAFE_TableCell>
      <UNSAFE_TableCell>Frogner</UNSAFE_TableCell>
    </UNSAFE_TableRow>
    <UNSAFE_TableRow>
      <UNSAFE_TableCell>Ingrid Svendsen</UNSAFE_TableCell>
      <UNSAFE_TableCell>ingrid.svendsen@obos.no</UNSAFE_TableCell>
      <UNSAFE_TableCell>Majorstuen</UNSAFE_TableCell>
    </UNSAFE_TableRow>
  </UNSAFE_TableBody>
</UNSAFE_Table>

Zebra-striped

<UNSAFE_Table aria-label="Eiendomsforvaltere" variant="zebra-striped">
  <UNSAFE_TableHeader>
    <UNSAFE_TableColumn>Navn</UNSAFE_TableColumn>
    <UNSAFE_TableColumn>E-post</UNSAFE_TableColumn>
    <UNSAFE_TableColumn>Område</UNSAFE_TableColumn>
  </UNSAFE_TableHeader>
  <UNSAFE_TableBody>
    <UNSAFE_TableRow>
      <UNSAFE_TableCell>Kari Hansen</UNSAFE_TableCell>
      <UNSAFE_TableCell>kari.hansen@obos.no</UNSAFE_TableCell>
      <UNSAFE_TableCell>Grünerløkka</UNSAFE_TableCell>
    </UNSAFE_TableRow>
    <UNSAFE_TableRow>
      <UNSAFE_TableCell>Lars Olsen</UNSAFE_TableCell>
      <UNSAFE_TableCell>lars.olsen@obos.no</UNSAFE_TableCell>
      <UNSAFE_TableCell>Frogner</UNSAFE_TableCell>
    </UNSAFE_TableRow>
    <UNSAFE_TableRow>
      <UNSAFE_TableCell>Ingrid Svendsen</UNSAFE_TableCell>
      <UNSAFE_TableCell>ingrid.svendsen@obos.no</UNSAFE_TableCell>
      <UNSAFE_TableCell>Majorstuen</UNSAFE_TableCell>
    </UNSAFE_TableRow>
  </UNSAFE_TableBody>
</UNSAFE_Table>

Ekspanderbar tabell

For år å lage en tabell med ekspanderbare rader, er det mulig å bruke en knapp for toggle radene som skal skjules/vises. Det enkleste er å bruke en controlled `<DisclosureButton>`, her må du huske å sette `aria-expanded`, `aria-controls` og sende med en callback til enten `onPress` eller `onClick` for å toggle staten.

() => {
  const years = [2025, 2026, 2027];
  const [expandedYears, setExpandedYears] = React.useState(
    Object.fromEntries(years.map((year) => [year, false])),
  );

  const months = [
    'januar',
    'februar',
    'mars',
    'april',
    'mai',
    'juni',
    'juli',
    'august',
    'september',
    'oktober',
    'november',
    'desember',
  ];

  return (
    <UNSAFE_TableContainer className="container">
      <UNSAFE_Table aria-label="Lånekostnader" variant="zebra-striped">
        <UNSAFE_TableHeader>
          <UNSAFE_TableColumn maxWidth={100}>Termin</UNSAFE_TableColumn>
          <UNSAFE_TableColumn maxWidth={100}>Renter</UNSAFE_TableColumn>
          <UNSAFE_TableColumn maxWidth={100}>Avdrag</UNSAFE_TableColumn>
          <UNSAFE_TableColumn maxWidth={200}>Månedskostnader</UNSAFE_TableColumn>
        </UNSAFE_TableHeader>
        <UNSAFE_TableBody>
          {years.map((year) => (
            <React.Fragment key={year}>
              <UNSAFE_TableRow className="*:align-middle">
                <UNSAFE_TableCell>{year}</UNSAFE_TableCell>
                <UNSAFE_TableCell>1 200 kr</UNSAFE_TableCell>
                <UNSAFE_TableCell>18 000 kr</UNSAFE_TableCell>
                <UNSAFE_TableCell>
                  <DisclosureButton
                    withChevron
                    aria-controls={months
                      .map((month) => `${year}-${month}`)
                      .join(' ')}
                    aria-expanded={expandedYears[year]}
                    aria-label={`Månedlige kostnader for ${year}`}
                    onPress={() =>
                      setExpandedYears((prevState) => ({
                        ...prevState,
                        [year]: !expandedYears[year],
                      }))
                    }
                    isIconOnly
                  />
                </UNSAFE_TableCell>
              </UNSAFE_TableRow>
              {expandedYears[year] &&
                months.map((month) => (
                  <UNSAFE_TableRow key={`${year}-${month}`} id={`${year}-${month}`}>
                    <UNSAFE_TableCell className="capitalize">{month}</UNSAFE_TableCell>
                    <UNSAFE_TableCell>120 kr</UNSAFE_TableCell>
                    <UNSAFE_TableCell colSpan={2}>1 500 kr</UNSAFE_TableCell>
                  </UNSAFE_TableRow>
                ))}
            </React.Fragment>
          ))}
        </UNSAFE_TableBody>
      </UNSAFE_Table>
    </UNSAFE_TableContainer>
  );
};