Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Changelog

## 0.2.0

- **Model year step.** A new optional `year` select is filled from the chosen model's `years_on_record` span, newest first. Changing the make clears model and year before `onChange` fires, and changing the model refills the year before `onChange` fires. The spans come with the models list already loaded, so the year step makes no request of its own.
- New options: `year`, `placeholderYear` (default `"Year"`), `initialYear` (a server-rendered selected year works too, and form reset restores it), and `yearFallback` for models with no span: `"range"` (default, the current year back 40 years, so the field never dead-ends) or `"none"` (placeholder only, select disabled).
- `onChange` and `.value` gain `year` (a number, `null`, or `"other"` with `includeOther`) only when a year select is attached. Widgets without `year` behave exactly as in 0.1.4.
- Older API responses without `years_on_record`, and spans outside 1886 to next year, are handled the same as `null`.
- A host-supplied year outside the model's span is kept (inserted in order and selected, with one `console.warn`), never dropped.
- The widget disables the year select only for `yearFallback: "none"` with nothing to choose (with `includeOther`, Other stays choosable), and only ever undoes its own disabling.
35 changes: 30 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Car, motorcycle and truck make and model dropdown

**The make/model picker you stopped hand-maintaining.** `make-model-dropdown` turns two `<select>` elements into a dependent make → model dropdown, populated live from the [VehiclesDB API](https://vehiclesdb.com/api): **14,000+ models across 850+ makes**, six vehicle kinds — cars, motorcycles (motorbikes), mopeds, vans, trucks and buses — reconciled from the official vehicle registers of 14 countries and refreshed monthly. One `kind` option turns the same widget into a car picker, a motorcycle picker or a truck picker.
**The make/model picker you stopped hand-maintaining.** `make-model-dropdown` turns two `<select>` elements into a dependent make → model dropdown, populated live from the [VehiclesDB API](https://vehiclesdb.com/api): **14,000+ models across 850+ makes**, six vehicle kinds — cars, motorcycles (motorbikes), mopeds, vans, trucks and buses — reconciled from official national vehicle registers and refreshed monthly. One `kind` option turns the same widget into a car picker, a motorcycle picker or a truck picker.

Zero dependencies. No build step. Works with plain HTML forms, React, Vue, Rails, anything that renders a `<select>`.

Expand All @@ -14,7 +14,7 @@ Get a free publishable API key at [vehiclesdb.com](https://vehiclesdb.com/settin
<select id="make"></select>
<select id="model"></select>

<script src="https://cdn.jsdelivr.net/npm/make-model-dropdown@0.1.4"></script>
<script src="https://cdn.jsdelivr.net/npm/make-model-dropdown@0.2.0"></script>
<script>
VehiclesDropdown.attach({
key: "vdb_pk_your_key",
Expand Down Expand Up @@ -45,13 +45,16 @@ VehiclesDropdown.attach({
key: "vdb_pk_…", // required — your publishable key
make: "#make", // required — <select> element or selector
model: "#model", // optional — the dependent model <select>
year: "#year", // optional — the model-year <select> (needs `model`)

kind: "car", // only one kind: car, motorcycle, moped, van, truck, bus
country: "de", // only vehicles evidenced in a register country
order: "popular", // models by real-world registrations (default) or "alphabetical"
topMakes: 40, // only the N most-registered makes, best first (default: all)
initialMake: "audi", // preselect a make on load (slug or name)
initialModel: "a3", // preselect a model once its make's models load
initialYear: 2019, // preselect a year once its model's years fill
yearFallback: "range", // a model with no register span: "range" (default) or "none"
values: "slug", // option values: "slug" (default) or "name"
includeOther: true, // append "Other / not listed" — pickers should never dead-end

Expand All @@ -60,14 +63,36 @@ VehiclesDropdown.attach({

placeholderMake: "Make",
placeholderModel: "Model",
placeholderYear: "Year",
attribution: true, // the CC-BY credit line (keep it on — see below)

onChange: ({ make, model }) => {},
onChange: ({ make, model, year }) => {}, // `year` only when a year select is attached
onError: (error) => {}
})
```

`attach` returns an instance with `.value` (`{ make, model }`) and `.destroy()`.
`attach` returns an instance with `.value` (`{ make, model }`, plus `year` with a year select) and `.destroy()`.

### Model year

Booking and quote forms ask for the year, so the widget can fill a third `<select>`:

```js
VehiclesDropdown.attach({ key: "vdb_pk_your_key", make: "#make", model: "#model", year: "#year" })
```

Picking a model fills the year select from that model's span, newest first (most bookings are for recent cars); changing the make clears model and year together. `onChange` then reports `{ make, model, year }`, where `year` is a number, `null` when nothing is picked, or `"other"` for the `includeOther` escape hatch, exactly like make and model. Without `year`, nothing changes: the payload stays `{ make, model }`.

The years are register years, not production years: the span runs from the first to the latest year the official registers have the model on record, which can differ from the factory's own dates.

About one model in eight has no span on record (and an "Other / not listed" model never has one). `yearFallback` decides what those models get:

- `"range"` (default): the current year back to 40 years ago. The year field never dead-ends, so a customer whose model the registers cannot date can still book.
- `"none"`: no years, for forms that would rather not offer years the registers cannot back up. With `includeOther` the select stays enabled with the placeholder and Other, so that choice can still be made; without it there is nothing to choose, so the select is disabled. The widget only ever undoes its own disabling and never overrides a `disabled` your page sets.

A year the host supplies (`initialYear`, or a server-rendered selected option in an edit form) is never dropped: if it falls outside the model's span it is added to the list in its place, selected, and logged once with `console.warn`.

No extra request is made: the spans come with the model list the widget already loads.

### Logos

Expand All @@ -94,7 +119,7 @@ npm ci
npm test
```

The test suite runs the published UMD artifact in a real DOM implementation and covers dependent loading, credential-scoped caches, combobox keyboard/blur behavior, labels, callback isolation, and object-URL cleanup.
The test suite runs the published UMD artifact in a real DOM implementation and covers dependent loading, the year step and its fallbacks, credential-scoped caches, combobox keyboard/blur behavior, labels, callback isolation, and object-URL cleanup.

## License

Expand Down
3 changes: 3 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ <h1>VehiclesDropdown demo</h1>
<select id="make" name="make"></select>
<label for="model">Model</label>
<select id="model" name="model"></select>
<label for="year">Year</label>
<select id="year" name="year"></select>
</form>

<pre id="state">{}</pre>
Expand All @@ -39,6 +41,7 @@ <h1>VehiclesDropdown demo</h1>
apiBase: "http://localhost:3000/api/v1",
make: "#make",
model: "#model",
year: "#year",
search: true,
logos: true,
includeOther: true,
Expand Down
Loading
Loading