The key to how sure the map is: the five data states, each as its dot and word, how many vehicles are in it, and one sentence saying what it means in the app's own thresholds, such as "Live 412, Reported by the vehicle in the last 2.5 min", for the panel that explains how much of what the map shows is live.
When to use
Use DataStateKey to explain how sure the map is: each of the five data states with how many vehicles are in it and a sentence on what it means in the app's own thresholds, as in a panel that says how much of what the map shows is live.
| Use instead | When |
|---|---|
| ReportStrip | The counts sit in one row and need no explaining |
| VehicleDotLegend | The dots need their words and nothing is counted |
| VehicleDotLabel | One vehicle's state is shown, as its dot and word in a row or a table |
Props
| Name | Type | Default | Description |
|---|---|---|---|
counts* | DataStateKeyCounts | — | How many vehicles are in each data state, such as { live: 412, ageing: 38, estimated: 61, timetableOnly: 9, noSignal: 3 }. Required. A state left out, null or not a finite number is unknown, and shows "—" and says "Unknown". |
liveThreshold* | number | — | The app's live threshold in milliseconds, the age under which a report is live, such as 150000, filled into the live and ageing sentences as "2.5 min". Required: each app sets its own from its feed (Thresholds). Not finite or below 0, each sentence that holds it shows "—" and says "Unknown" in its place. |
ageingThreshold* | number | — | The app's ageing threshold in milliseconds, the age past which a vehicle has no signal, such as 300000, filled into the ageing and no-signal sentences as "5 min". Required, as liveThreshold is. Not finite or below 0, each sentence that holds it shows "—" and says "Unknown" in its place. |
liveLabel | string | The locale's | Overrides "Live", the word beside the live dot; given to that item's VehicleDotLabel |
ageingLabel | string | The locale's | Overrides "Ageing", the word beside the ageing dot; given to that item's VehicleDotLabel |
estimatedLabel | string | The locale's | Overrides "Estimated", the word beside the estimated dot; given to that item's VehicleDotLabel |
timetableOnlyLabel | string | The locale's | Overrides "Timetable only", the word beside the timetable-only dot; given to that item's VehicleDotLabel |
noSignalLabel | string | The locale's | Overrides "No signal", the word beside the no-signal dot; given to that item's VehicleDotLabel |
liveDescriptionLabel | string | The locale's | Overrides "Reported by the vehicle in the last {liveThreshold}", the live sentence; {liveThreshold} is the live threshold |
ageingDescriptionLabel | string | The locale's | Overrides "Last reported {liveThreshold} to {ageingThreshold} ago", the ageing sentence; {liveThreshold} and {ageingThreshold} are the two thresholds |
estimatedDescriptionLabel | string | The locale's | Overrides "Placed from the timetable and the latest delay", the estimated sentence |
timetableOnlyDescriptionLabel | string | The locale's | Overrides "No real-time data, placed from the timetable alone", the timetable-only sentence |
noSignalDescriptionLabel | string | The locale's | Overrides "Moved on by the timetable, with no report for over {ageingThreshold}", the no-signal sentence; {ageingThreshold} is the ageing threshold |
secondsDurationLabel | string | The locale's | Overrides "{seconds} s", a threshold under 2 min; {seconds} is its whole seconds |
minutesDurationLabel | string | The locale's | Overrides "{minutes} min", a threshold of 2 min or more; {minutes} is its minutes, to one decimal place |
unknownLabel | string | The locale's | Overrides "Unknown", heard in place of "—" for a count no data gives, and in place of a sentence whose threshold no data gives |
className | string | — | Joined after pn-DataStateKey on the root |
style | CSSProperties | — | Reaches the root |
ref | Ref<HTMLUListElement> | — | Reaches the root element |
slotProps | { item?: DetailedHTMLProps<LiHTMLAttributes<HTMLLIElement>, HTMLLIElement> | undefined; label?: Omit<VehicleDotLabelProps, "ageingLabel" | "estimatedLabel" | "line" | "liveLabel" | "noSignalLabel" | "render" | "timetableOnlyLabel" | "variant"> | undefined; count?: DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> | undefined; description?: Omit<TextProps, "children" | "color" | "emphasis" | "render" | "variant"> | undefined; } | — | Props for each part: item, label, count, description. |
counts (required)DataStateKeyCounts, default —- How many vehicles are in each data state, such as
{ live: 412, ageing: 38, estimated: 61, timetableOnly: 9, noSignal: 3 }. Required. A state left out,nullor not a finite number is unknown, and shows "—" and says "Unknown". liveThreshold (required)number, default —- The app's live threshold in milliseconds, the age under which a report is live, such as
150000, filled into the live and ageing sentences as "2.5 min". Required: each app sets its own from its feed (Thresholds). Not finite or below 0, each sentence that holds it shows "—" and says "Unknown" in its place. ageingThreshold (required)number, default —- The app's ageing threshold in milliseconds, the age past which a vehicle has no signal, such as
300000, filled into the ageing and no-signal sentences as "5 min". Required, asliveThresholdis. Not finite or below 0, each sentence that holds it shows "—" and says "Unknown" in its place. liveLabelstring, default The locale's- Overrides "Live", the word beside the live dot; given to that item's VehicleDotLabel
ageingLabelstring, default The locale's- Overrides "Ageing", the word beside the ageing dot; given to that item's VehicleDotLabel
estimatedLabelstring, default The locale's- Overrides "Estimated", the word beside the estimated dot; given to that item's VehicleDotLabel
timetableOnlyLabelstring, default The locale's- Overrides "Timetable only", the word beside the timetable-only dot; given to that item's VehicleDotLabel
noSignalLabelstring, default The locale's- Overrides "No signal", the word beside the no-signal dot; given to that item's VehicleDotLabel
liveDescriptionLabelstring, default The locale's- Overrides "Reported by the vehicle in the last {liveThreshold}", the live sentence;
{liveThreshold}is the live threshold ageingDescriptionLabelstring, default The locale's- Overrides "Last reported {liveThreshold} to {ageingThreshold} ago", the ageing sentence;
{liveThreshold}and{ageingThreshold}are the two thresholds estimatedDescriptionLabelstring, default The locale's- Overrides "Placed from the timetable and the latest delay", the estimated sentence
timetableOnlyDescriptionLabelstring, default The locale's- Overrides "No real-time data, placed from the timetable alone", the timetable-only sentence
noSignalDescriptionLabelstring, default The locale's- Overrides "Moved on by the timetable, with no report for over {ageingThreshold}", the no-signal sentence;
{ageingThreshold}is the ageing threshold secondsDurationLabelstring, default The locale's- Overrides "{seconds} s", a threshold under 2 min;
{seconds}is its whole seconds minutesDurationLabelstring, default The locale's- Overrides "{minutes} min", a threshold of 2 min or more;
{minutes}is its minutes, to one decimal place unknownLabelstring, default The locale's- Overrides "Unknown", heard in place of "—" for a count no data gives, and in place of a sentence whose threshold no data gives
classNamestring, default —- Joined after
pn-DataStateKeyon the root styleCSSProperties, default —- Reaches the root
refRef<HTMLUListElement>, default —- Reaches the root element
slotProps{ item?: DetailedHTMLProps<LiHTMLAttributes<HTMLLIElement>, HTMLLIElement> | undefined; label?: Omit<VehicleDotLabelProps, "ageingLabel" | "estimatedLabel" | "line" | "liveLabel" | "noSignalLabel" | "render" | "timetableOnlyLabel" | "variant"> | undefined; count?: DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> | undefined; description?: Omit<TextProps, "children" | "color" | "emphasis" | "render" | "variant"> | undefined; }, default —- Props for each part:
item,label,count,description.
Use it
import { DataStateKey } from '@mylder/perron/patterns'Ask your agent
search_docs({ query: "DataStateKey" })Keyboard and ARIA
DataStateKey has no keys of its own and nothing in it is focusable: it is read, not operated.
| Key | Does | From |
|---|---|---|
Tab | Passes the key by to the next focusable element: it has no tab stop (CR-12) | Perron |
| Element | Role | Accessible name from | States announced |
|---|---|---|---|
root | list, from the native <ul> | None of its own; the caller's aria-label or aria-labelledby when it gives one (CR-13) | Its number of items, from the <ul> |
item | listitem, from the native <li> | Its text, in order: the state's word, its count or "Unknown", then its sentence | None |
label | None: VehicleDotLabel's generic <span> | Not named; its word is read in place | None |
The dot inside label | hidden: VehicleDot's img, with aria-hidden="true" | None: the word beside it names the state (VehicleDotLabel's CR-2) | None |
count | None: a generic <span> | Not named; its digits are read in place | None |
description | None: Text's generic <span> | Not named; its sentence is read in place | None |
The "—" in count or description, unknown | hidden: aria-hidden="true" | Not read | None |
| The words in its place, when unknown | Text, in VisuallyHidden | The owned string "Unknown", or unknownLabel | None |
An item reads "Live 412 Reported by the vehicle in the last 2.5 min", and one with no count "No signal Unknown Moved on by the timetable, with no report for over 5 min".
The root is a list with no name of its own and no live region, as ReportStrip's is: a caller names it with the heading beside it, such as "How sure are we?", through aria-labelledby; the counts change every few seconds, which would flood a screen reader, and Accessibility asks that only changes of state be announced, in the screen's own polite live region.
What axe won't catch, from the contract, each with how this component meets it:
- Everything the eye is told, the ear is told, for every shape each prop accepts: each dot's state is told to the ear by the word beside it, through VehicleDotLabel; each count is read after its word, and an unknown one as "Unknown" in place of the hidden "—" (CR-5); each sentence is visible text, read in place, and one whose threshold is unknown is "—" for the eye and "Unknown" for the ear (CR-7). No colour carries meaning: the dots are one ink, the text's (CR-3), and no signal's grey comes with its cut and its word.
- Every landmark and named region has an accessible name: none here; the key is a list, not a landmark or region, and a caller names it with
aria-labeloraria-labelledby(CR-13). - Every default label can be overridden:
liveLabel,ageingLabel,estimatedLabel,timetableOnlyLabel,noSignalLabel,liveDescriptionLabel,ageingDescriptionLabel,estimatedDescriptionLabel,timetableOnlyDescriptionLabel,noSignalDescriptionLabel,secondsDurationLabel,minutesDurationLabelandunknownLabel. - Every shape the type accepts renders, in every variant:
countswith all five states, with some left out,null,NaNandInfinity(unknown), with0(a count) and with a count past a thousand (grouped); thresholds under 2 min, at 2 min, past it with a half minute and whole, andNaNor below 0 (unknown); every*Labelstring replaces only its own string, a sentence's override keeps its placeholders filled, and an undefined one keeps the locale's. DataStateKey takes noReactNode. - A hidden copy kept for animation is
aria-hidden, and a collapsed region isinert: none here; nothing animates or collapses. The dots and an unknown figure's "—" arearia-hiddenbecause words beside them say them, not for animation. - Native semantics first: a
<ul>of<li>s, each with a VehicleDotLabel, a<span>of digits and a Text<span>of words; no ARIA of the pattern's own beyond the "—"'saria-hidden, whichUnknownValuesets. - Focus moves in reading order and returns on close: nothing takes focus or opens; Tab passes the key by.
- Every target is at least
size.target-minsquare: no target; nothing in the key is pressed. - Loops stop when the system asks for less motion: none here; nothing moves, and a count changes its text without a transition.
Owned strings
Every word the key shows or announces is its own; the caller gives only numbers. The five state words and "Unknown" are keys packages/perron/src/locale/en.ts already has. The five sentences and the two durations are new: each sentence is worded from what Data states says the state means, so every screen that explains the states says the same thing in the same four languages, and the thresholds are placeholders the app's figures fill (CR-6, CR-7).
The English sentences are "Reported by the vehicle in the last {liveThreshold}", "Last reported {liveThreshold} to {ageingThreshold} ago", "Placed from the timetable and the latest delay", "No real-time data, placed from the timetable alone" and "Moved on by the timetable, with no report for over {ageingThreshold}", with no full stop: each says one state's "Means" from Data states in a plain sentence about the vehicle, as content design asks, live in the ticket's own words; "GPS" is left out, since a vehicle's report is its position whatever the source; "{liveThreshold} to {ageingThreshold}" writes each figure with its own unit, "2.5 min to 5 min", rather than content design's range with one unit at its end, "Every 3 to 4 min", since the two thresholds can take different units, "60 s to 5 min"; no signal's "moved on by the timetable" is the foundation's own and says, as No signal keeps moving asks, that its place is a guess, and it comes first, before the figure, so no punctuation follows a placeholder in any locale: expectOwnedStrings reads a comma straight after a filled figure, "5 min,", as a word no dictionary holds; and with one sentence each and no other in the item, a full stop adds nothing, as the library's other owned strings have none.
| Key | English | Override | Seen or heard where |
|---|---|---|---|
live | Live | liveLabel | The first item's label, seen and read as shown |
ageing | Ageing | ageingLabel | The second item's label, seen and read as shown |
estimated | Estimated | estimatedLabel | The third item's label, seen and read as shown |
timetableOnly | Timetable only | timetableOnlyLabel | The fourth item's label, seen and read as shown |
noSignal | No signal | noSignalLabel | The fifth item's label, seen and read as shown |
liveDescription | Reported by the vehicle in the last {liveThreshold} | liveDescriptionLabel | The first item's description, seen and read as shown; new |
ageingDescription | Last reported {liveThreshold} to {ageingThreshold} ago | ageingDescriptionLabel | The second item's description, seen and read as shown; new |
estimatedDescription | Placed from the timetable and the latest delay | estimatedDescriptionLabel | The third item's description, seen and read as shown; new |
timetableOnlyDescription | No real-time data, placed from the timetable alone | timetableOnlyDescriptionLabel | The fourth item's description, seen and read as shown; new |
noSignalDescription | Moved on by the timetable, with no report for over {ageingThreshold} | noSignalDescriptionLabel | The fifth item's description, seen and read as shown; new |
secondsDuration | {seconds} s | secondsDurationLabel | A threshold under 2 min, inside a sentence, such as "60 s"; new |
minutesDuration | {minutes} min | minutesDurationLabel | A threshold of 2 min or more, inside a sentence, such as "2.5 min"; new |
unknown | Unknown | unknownLabel | A count that is unknown, heard in place of its aria-hidden "—", and a sentence holding an unknown threshold, heard in place of the whole sentence (CR-5, CR-7) |
The new strings join en.ts with a JSDoc each that names its placeholders, and the other dictionaries in Danish, Swedish and Norwegian. In secondsDuration and minutesDuration the space between the figure's placeholder and the unit is a no-break space (U+00A0) in every locale:
| Key | Danish | Swedish | Norwegian Bokmål |
|---|---|---|---|
liveDescription | Meldt af køretøjet inden for de seneste {liveThreshold} | Rapporterad av fordonet under de senaste {liveThreshold} | Meldt av kjøretøyet i løpet av de siste {liveThreshold} |
ageingDescription | Sidst meldt for {liveThreshold} til {ageingThreshold} siden | Senast rapporterad för {liveThreshold} till {ageingThreshold} sedan | Sist meldt for {liveThreshold} til {ageingThreshold} siden |
estimatedDescription | Placeret ud fra køreplanen og den seneste forsinkelse | Placerad utifrån tidtabellen och den senaste förseningen | Plassert ut fra rutetabellen og den siste forsinkelsen |
timetableOnlyDescription | Ingen realtidsdata, placeret ud fra køreplanen alene | Ingen realtidsdata, placerad enbart utifrån tidtabellen | Ingen sanntidsdata, plassert ut fra rutetabellen alene |
noSignalDescription | Flyttet videre efter køreplanen, uden melding i over {ageingThreshold} | Flyttad vidare enligt tidtabellen, utan rapport på över {ageingThreshold} | Flyttet videre etter rutetabellen, uten melding på over {ageingThreshold} |
secondsDuration | {seconds} s | {seconds} s | {seconds} s |
minutesDuration | {minutes} min | {minutes} min | {minutes} min |
Do and don't
For a caller using the pattern, with the reason. Each row is a rule: a must row's Don't is never right, a should row's needs a reason, and Checked by says what catches the Don't in a caller's code.
| Rule | Level | Do | Don't | Checked by |
|---|---|---|---|---|
| DD-1 | must | Pass the thresholds the app works out its states with, liveThreshold and ageingThreshold, in milliseconds, so the sentences say the figures the dots follow | Pass the tokens' defaults when the app's feed sets its own thresholds: the key would tell the rider 60 s while the dots fade at 150 s | review |
| DD-2 | must | Work out each vehicle's state from its report's age with those thresholds, then count | Count a vehicle as live because it is on the map: the key would claim more than the data knows | review |
| DD-3 | must | Leave a state's count out, or pass null, when the data doesn't say it, so the key shows "—" and says "Unknown" | Pass 0 for a count the data doesn't know: 0 says no vehicle is in that state | review |
| DD-4 | must | Name the key with the heading beside it, such as "How sure are we?", and aria-labelledby | Pass aria-hidden to the key: the eye reads its words, counts and sentences, so the ear gets them too | review |
| DD-5 | should | Use ReportStrip where the counts sit in one row and need no explaining, and VehicleDotLegend where the dots need their words and nothing is counted | Write your own sentences beside a ReportStrip or a legend: they drift from the key's, and from the four languages the key says them in | review |
| DD-6 | must | Override a sentence with its *DescriptionLabel prop when a screen's copy needs it, keeping its placeholders | Write the figures into an override, such as "Reported in the last 2.5 min": the sentence would stop following the thresholds the app passes | review |
| DD-7 | must | Put the key in a DataStatePanel while its counts load | Show five unknown counts while the data loads: the panel says it is loading, the key says the data doesn't know | review |