Skip to content
design
Components

WiFi network list

The WiFi networks in range, to choose which the vessel joins, with each one's signal, details, password and DNS.

On this page

The page receives: nothing yet

The networks at a marina: Starlink connected, the club's saved, the marina's and a cafe's in range.
Code
import {
  WifiNetworkList,
  type WifiConnectRequest,
  type WifiConnectResult,
} from "@bosunkit/components/wifi-network-list";
import { useState, type Dispatch, type ReactElement, type SetStateAction } from "react";
import { NETWORKS } from "./networks";

export const description: string =
  "Choose a network to see its details, type its password and choose its DNS; this example connects nothing.";

/** A connection that succeeds after a moment and changes nothing. */
function pretendConnect(): Promise<WifiConnectResult> {
  return new Promise<WifiConnectResult>((resolve: (result: WifiConnectResult) => void): void => {
    setTimeout((): void => {
      resolve({ status: "connected" });
    }, 800);
  });
}

export default function Showcase(): ReactElement {
  const [last, setLast]: [string, Dispatch<SetStateAction<string>>] = useState<string>("nothing yet");
  return (
    <div style={{ display: "grid", gap: "var(--space-sm)", maxInlineSize: "36rem" }}>
      <WifiNetworkList
        networks={NETWORKS}
        connectAction={(request: WifiConnectRequest): Promise<WifiConnectResult> => {
          setLast(`connect to ${request.ssid} with DNS ${request.dns.mode}`);
          return pretendConnect();
        }}
        disconnectAction={(ssid: string): Promise<void> => {
          setLast(`disconnect from ${ssid}`);
          return Promise.resolve();
        }}
        forgetAction={(ssid: string): Promise<void> => {
          setLast(`forget ${ssid}`);
          return Promise.resolve();
        }}
        onAutoJoinChange={(ssid: string, on: boolean): void => {
          setLast(`join ${ssid} automatically: ${String(on)}`);
        }}
        rescanAction={(): Promise<void> => new Promise<void>((resolve: () => void): void => void setTimeout(resolve, 1200))}
      />
      <p style={{ margin: 0, color: "var(--text-dim)" }}>The page receives: {last}</p>
    </div>
  );
}

A WiFi network list shows the networks a scan found, to choose which the vessel joins. Each row has the network's name, its signal, its band and whether it needs a password, with the connected network first, then the saved ones, then the rest by strength; access points that share a name show as one network. Choosing a network opens its details (signal, security, band, channel, its access points or the access point's address, and the vessel's address on it when connected) and what can be done with it.

  • A network that needs a password shows a password field for the key the marina or the owner gave. The key is checked before anything is sent, by the rules bosund's core/passwordpolicy applies: 8 to 63 printable characters or 64 hex digits for WPA, WPA2 and WPA3, and 5 or 13 characters or 10 or 26 hex digits for WEP; a wrong one says what is wrong and to check it.
  • An open network warns that anyone nearby can see what goes over it, and connects without a password.
  • A saved network connects without asking, can be forgotten, and has a switch for joining it by itself when it is in range, so the crew choose which networks the vessel joins.
  • Every network has a DNS picker: the network's own servers, Cloudflare, Google or custom ones, sent with the connection or reported for a saved network.
  • Join another network joins one that does not broadcast its name: its name, security, password and DNS.

The list never scans or connects itself. connectAction joins and resolves connected or refused with bosund's reason, which shows under the password; rescanAction, disconnectAction and forgetAction do the rest, each shown pending until it settles, and a connection is announced to a screen reader.

Examples

A refused password

A connection bosund refuses: choose Marina Guest, type a password and connect to see the reason under the field.
Code
import { WifiNetworkList, type WifiConnectResult } from "@bosunkit/components/wifi-network-list";
import type { ReactElement } from "react";
import { NETWORKS } from "./networks";

export const description: string =
  "A connection bosund refuses: choose Marina Guest, type a password and connect to see the reason under the field.";

export default function Refused(): ReactElement {
  return (
    <div style={{ maxInlineSize: "36rem" }}>
      <WifiNetworkList
        networks={NETWORKS}
        connectAction={(): Promise<WifiConnectResult> =>
          Promise.resolve({
            status: "rejected",
            message: "Marina Guest didn't accept that password. Check it with the marina office and try again.",
          })
        }
      />
    </div>
  );
}

No networks in range

No networks in range

Move closer to the marina's access point, or scan again.
A scan that found nothing: it says so and offers to scan again.
Code
import { WifiNetworkList } from "@bosunkit/components/wifi-network-list";
import type { ReactElement } from "react";

export const description: string = "A scan that found nothing: it says so and offers to scan again.";

export default function Empty(): ReactElement {
  return (
    <div style={{ maxInlineSize: "36rem" }}>
      <WifiNetworkList networks={[]} rescanAction={(): Promise<void> => Promise.resolve()} />
    </div>
  );
}

Props

PropTypeDefaultDescription
networksrequiredreadonly WifiNetwork[]–The access points the last scan found, in any order: the list merges those that share a name and sorts them.
connectAction((request: WifiConnectRequest) => Promise<WifiConnectResult>) | undefined–Joins a network; the list shows it pending until the promise settles, and a refusal's message under the field.
disconnectAction((ssid: string) => Promise<void>) | undefined–Leaves the connected network.
forgetAction((ssid: string) => Promise<void>) | undefined–Forgets a saved network's password.
onAutoJoinChange((ssid: string, on: boolean) => void) | undefined–Called when the crew turn a saved network's automatic joining on or off.
onDnsChange((ssid: string, dns: DnsSetting) => void) | undefined–Called when the crew change a saved network's DNS.
rescanAction(() => Promise<void>) | undefined–Scans again; the button shows it busy until the promise settles.
scanningboolean | undefinedfalseWhether a scan is running now, started elsewhere.
allowOtherboolean | undefinedtrueWhether the crew can join a network that does not broadcast its name; on unless set.
labelstring | undefined–The list's name, translated by the caller; "WiFi networks" unless set.
classNamestring | undefined–Classes added to the root's own.
testIdstring | undefined–Becomes data-testid on the root.