Lesson 2: Start Trip Form Inputs
🏳️ Haven't completed the previous lesson?
No worries! You can pickup from here:
git checkout tags/lesson-2
This lesson focuses on building the first step of the trip logging form, the "Start Trip" page. We'll use components from the U.S. Web Design System (USWDS) library (@trussworks/react-uswds
) to add inputs for the trip date, start time, and start weather.
Step 1: Add Date Picker Input
We need a way for the user to select the date of their fishing trip. We'll use the DatePicker
component from @trussworks/react-uswds
.
1.1: Import the DatePicker Component
First, open src/pages/StartTrip.jsx
and update the import statement from @trussworks/react-uswds
to include DatePicker
. Your existing import will be modified as follows:
import {
Button,
DatePicker,
Form,
FormGroup,
Label,
} from "@trussworks/react-uswds";
import Layout from "../components/Layout";
Notice that we're importing a Layout
component. This is a custom component that handles the common page structure for all our trip logging pages. It provides:
- Grid Container: Wraps content with consistent padding and responsive width
- Step Indicator: Shows the user's progress through the 4-step trip logging process
- Consistent Structure: Ensures all pages have the same layout without duplicating code
Each page uses this component with a currentStep
prop (e.g., <Layout currentStep="Start Trip">
) to highlight the appropriate step in the indicator. This approach keeps our code DRY (Don't Repeat Yourself) and makes maintenance easier.
Understanding the USWDS Components:
Before we start building the form, let's understand what each of these imported components does:
Form
: Main container for form elements with submission handling and stylingFormGroup
: Wrapper that groups related form elements (label, input, hints, errors) for proper spacing and accessibility. Each input field needs its own FormGroup wrapper.Label
: Creates accessible form labels with support for required indicatorsDatePicker
: Calendar interface for date selection with formatting and validation
These components work together to create accessible, well-structured forms that follow USWDS design patterns and accessibility guidelines.
1.2: Add the DatePicker to the Form
Add the FormGroup
, Label
, and DatePicker
input within the Form
component:
<Form onSubmit={handleSubmit} large className="margin-top-3">
<FormGroup>
<Label
htmlFor="tripDate"
hint=" mm/dd/yyyy"
className="input-date-label"
requiredMarker
>
Date
</Label>
<DatePicker
id="tripDate"
name="tripDate"
defaultValue={tripData.tripDate}
onChange={handleDateChange}
aria-describedby="trip-date-hint"
required
/>
<span id="trip-date-hint" className="usa-sr-only">
Please enter or select the date of your fishing trip.
</span>
</FormGroup>
</Form>
React State Management Explanation
Let's look at two key aspects of how React manages state with this form:
-
Initial State with
defaultValue
:src/pages/StartTrip.jsxdefaultValue={tripData.tripDate}
- The component initializes form data using React's useState hook:
src/pages/StartTrip.jsxconst [tripData, setTripData] = useState({
tripDate: undefined,
startWeather: undefined,
startTime: undefined,
});- This binds the DatePicker's initial value to the component's state
- When the form first renders, it displays any existing data if editing a trip, or empty if creating a new trip
- To learn more about React state management, visit React's Managing State documentation
-
Updating State with
onChange
:src/pages/StartTrip.jsxonChange = { handleDateChange };
- The
onChange
attribute is a required prop for controlled inputs. It is called whenever the input's value changes (on every keystroke or selection). Without this handler, React would revert the input to its original value after each change. - When a user selects a date, the
handleDateChange
function is called
src/pages/StartTrip.jsxconst handleDateChange = (date) => {
setTripData((prevData) => ({ ...prevData, tripDate: date || "" }));
};- It preserves other form field values by spreading the previous state
- To learn more about controlled inputs, visit Input documentation
- The
Step 2: Add Time Picker Input
Next, we'll add an input for the trip's start time using the TimePicker
component.
2.1: Import the TimePicker Component
Update the import statement from @trussworks/react-uswds
to include TimePicker
. Your existing import will be modified as follows:
import {
Button,
DatePicker,
Form,
FormGroup,
Label,
TimePicker,
} from "@trussworks/react-uswds";
2.2: Add the TimePicker to the Form
Let's add the TimePicker
input below the DatePicker
form group:
<span id="trip-date-hint" className="usa-sr-only">
Please enter or select the date of your fishing trip.
</span>
</FormGroup>
<FormGroup>
<Label
htmlFor="startTime"
className="input-time-label"
requiredMarker
>
Time
</Label>
<TimePicker
id="startTime"
name="startTime"
defaultValue={tripData.startTime}
onChange={handleTimeChange}
minTime="00:00"
maxTime="23:45"
step={15}
aria-describedby="start-time-hint"
required
/>
<span id="start-time-hint" className="usa-sr-only">
Please enter or select the time you started fishing.
</span>
</FormGroup>
</Form>
</Layout>
Explanation:
- Similar to the
DatePicker
, we useFormGroup
andLabel
for structure and accessibility defaultValue={tripData.startTime}
binds the input to thestartTime
field in our stateonChange={handleTimeChange}
calls our specific handler function to update the stateminTime
,maxTime
, andstep={15}
configure the available time options (from 00:00 to 23:45 in 15-minute increments)
Step 3: Add Weather Select Input
Finally, we need a dropdown menu for the user to select the weather conditions at the start of the trip. We'll use the Select
component.
3.1: Import the Select Component
Update the import statement from @trussworks/react-uswds
to include Select
. Your existing import will be modified as follows:
import {
Button,
DatePicker,
Form,
FormGroup,
Label,
Select,
TimePicker,
} from "@trussworks/react-uswds";
3.2: Add the Select to the Form
Add the following code below the TimePicker
form group:
<span id="start-time-hint" className="usa-sr-only">
Please enter or select the time you started fishing.
</span>
</FormGroup>
<FormGroup>
<Label
htmlFor="startWeather"
requiredMarker
>
Weather
</Label>
<Select
id="startWeather"
name="startWeather"
value={tripData.startWeather}
onChange={handleInputChange}
aria-describedby="start-weather-hint"
required
>
<option value="">-Select-</option>
<option value="Sunny">Sunny</option>
<option value="Cloudy">Cloudy</option>
<option value="Rainy">Rainy</option>
</Select>
<span id="start-weather-hint" className="usa-sr-only">
Please select the weather conditions at the start of your fishing trip.
</span>
</FormGroup>
</Form>
</Layout>
Explanation:
value={tripData.startWeather}
binds the selected option to thestartWeather
field in the state.onChange={handleInputChange}
uses the standard input handler (already present) because theSelect
component behaves like a standard HTML select element in this regard.- We define the available weather options using standard HTML
<option>
tags within theSelect
component. The first option has an emptyvalue
to represent the default, unselected state.
The complete Form will look like this:
<Form onSubmit={handleSubmit} large className="margin-top-3">
<FormGroup>
<Label
htmlFor="tripDate"
hint=" mm/dd/yyyy"
className="input-date-label"
requiredMarker
>
Date
</Label>
<DatePicker
id="tripDate"
name="tripDate"
defaultValue={tripData.tripDate}
onChange={handleDateChange}
aria-describedby="trip-date-hint"
required
/>
<span id="trip-date-hint" className="usa-sr-only">
Please enter or select the date of your fishing trip.
</span>
</FormGroup>
<FormGroup>
<Label
htmlFor="startTime"
className="input-time-label"
requiredMarker
>
Time
</Label>
<TimePicker
id="startTime"
name="startTime"
defaultValue={tripData.startTime}
onChange={handleTimeChange}
minTime="00:00"
maxTime="23:45"
step={15}
aria-describedby="start-time-hint"
required
/>
<span id="start-time-hint" className="usa-sr-only">
Please enter or select the time you started fishing.
</span>
</FormGroup>
<FormGroup>
<Label
htmlFor="startWeather"
requiredMarker
>
Weather
</Label>
<Select
id="startWeather"
name="startWeather"
value={tripData.startWeather}
onChange={handleInputChange}
aria-describedby="start-weather-hint"
required
>
<option value="">-Select-</option>
<option value="Sunny">Sunny</option>
<option value="Cloudy">Cloudy</option>
<option value="Rainy">Rainy</option>
</Select>
<span id="start-weather-hint" className="usa-sr-only">
Please select the weather conditions at the start of your fishing trip.
</span>
</FormGroup>
</Form>
Notice how each input field gets its own FormGroup
wrapper. This is the standard USWDS pattern that ensures proper spacing between fields and groups all related elements (label, input, and hints) together.
Step 4: Testing Form Data Collection
Before we learn how to save data to storage in the next lesson, let's first see what our form data looks like when submitted. We'll modify the form submission to display the collected data in the browser console.
4.1: Update the form submission handler
Locate the handleSubmit
function. Replace the existing submission logic with the following code that will log the form data to the console:
const handleSubmit = async (e) => {
e.preventDefault();
console.log("Trip Data:", {
tripDate: tripData.tripDate,
startWeather: tripData.startWeather,
startTime: tripData.startTime,
status: "in-progress",
step: 2,
});
};
Explanation:
- We gather all the form data (
tripDate
,startWeather
,startTime
) into a single object - We add metadata fields like
status: "in-progress"
andstep: 2
that will be useful for tracking form progress console.log()
outputs this data structure to the browser's developer console so we can inspect it
4.2: Test the form submission
Now let's test our form to see the data structure:
- Fill out all the form fields (Date, Start Time, Start Weather)
- Click the "Next" button
- Open your browser's developer console (F12 or right-click → Inspect → Console tab)
- You should see the form data logged to the console
This shows you exactly what data structure we'll be saving to IndexedDB in the next lesson. You can see how React has collected all your form inputs into a clean, structured object.
Conclusion
You have now successfully implemented the user interface for the first step of the trip logging form! You've added USWDS DatePicker
, TimePicker
, and Select
components to capture the trip's start date, start time, and start weather. These inputs are connected to the component's state, ready to be saved. The development server is running with HMR, making it easy to see your changes as you make them.
In the next lesson, we will implement the logic to save the data entered in this form using RADFish's offline storage capabilities (IndexedDB) and navigate the user to the next step.