---
id: CMP-CRM-ENT-001
title: Address Editor
product_area: crm
module: entities
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
purpose: Helps staff update a contact's postal addresses, identify their uses, choose a preferred address and maintain seasonal or organization-linked entries.
reuse: Serves the Addresses collection in Create Contact and Edit Contact through the shared form engine. The documented contract concerns contact collections; reuse for organization addresses or a single address field is not established.
supports: [CAP-CRM-ENT-005, CAP-CRM-ENT-006]
realises: [CON-CRM-ENT-001]
props:
  - name: value
    type: list of address entries (type, address type, lines, city, state, postal code, county, country, coordinates, season start and end, seasonal preferred, preferred, link state)
    required: true
    default: none
    meaning: Supplies the draft addresses; ownership of the list remains with the calling form.
  - name: onChange
    type: event handler
    required: true
    default: none
    meaning: Receives the replacement list whenever a committed row changes, is added or is removed.
  - name: getError
    type: function from a field identifier to a message
    required: true
    default: none
    meaning: Looks up a card's validation message by its field identifier; the card shows an error border and the message below its content.
  - name: types
    type: list of the workspace's address types
    required: false
    default: the shipped types and no custom ones until the types are read
    meaning: Provides the configured contact address types. Active types can be selected; held inactive custom types remain available on their own row, and held standard types remain in the standard choices.
  - name: links
    type: row link controls
    required: false
    default: none
    meaning: Supplies row source states and callbacks for linking, changing a source, copying a source value and retaining a disconnected value.
  - name: addressLookup
    type: boolean
    required: false
    default: true
    meaning: Requests address lookup when available; false limits the address control to manual entry.
  - name: showGeocoded
    type: boolean
    required: false
    default: false
    meaning: Enables the Geocoded badge on a saved, nonblank owned row; the component does not display coordinate values.
  - name: linkedWork
    type: linked information
    required: false
    default: none
    meaning: Supplies the older work-address mirror state, organization name and unlink callback; the form-engine adapter uses row link controls instead.
events:
  - name: change
    when: A committed address row changes, including its type, preference or season, or a row is added or removed.
    payload: The replacement address collection for the calling form.
states:
  - state: empty
    experience: An invitation appears above an open blank row using the next proposed type. The first row needs no Add button, but it does not receive an autofocus request.
  - state: collapsed row
    experience: A closed card presents the type and formatted address with its preferred-star state. Edit expands an owned row; Remove takes the row out of the draft.
  - state: open row
    experience: An expanded card places its type beside the address control. Seasonal rows also offer start and end month/day selectors and a mailing preference for the season.
  - state: linked row
    experience: The source marker identifies the organization or explains an unresolved source. Row link controls keep the value closed to editing but leave Remove enabled; the older work-mirror path disables Remove until unlinked.
  - state: validation error
    experience: An error border and alert message identify a validation problem. Empty and seasonal rows have disabled preferred-star controls with explanatory tooltips.
accessibility: Address action names include the type; the preferred star exposes its pressed state and disabled reason, and decorative icons are hidden from assistive technology. Field labels and alert messages are present, but a complete keyboard and screen-reader audit is not recorded. Added rows request input focus; opening an existing row does not.
design_system: [card, button, badge, checkbox, select, address input with lookup, preferred star]
depends_on: []
implementation: CMI-CRM-ENT-001
acceptance:
  - id: AC-CMP-CRM-ENT-001-01
    proved_by: []
    statement: An empty collection presents an expanded blank row without an Add button. Entering a nonblank address commits the first row and leaves it expanded.
    level: unit
  - id: AC-CMP-CRM-ENT-001-02
    proved_by: []
    statement: The expanded row places its type selector beside the address control; an inactive custom type remains selectable only on the row that holds it.
    level: unit
  - id: AC-CMP-CRM-ENT-001-03
    proved_by: []
    statement: Selecting Seasonal clears the ordinary preferred flag, and a seasonal row has no enabled ordinary preferred-star action.
    level: unit
  - id: AC-CMP-CRM-ENT-001-04
    proved_by: []
    statement: A row using the current source controls can be removed directly from the draft. A work row using the older mirror state disables Remove and explains that it must first be unlinked.
    level: unit
validation: [BR-CAP-CRM-ENT-005-07, BR-CAP-CRM-ENT-006-03, BR-CAP-CRM-ENT-006-04]
controls: [TC-DAT-001, TC-PRV-001]
evidence:
  - 'react-console at baf6dae3: components/contact-editor/collections/address-list-editor.tsx; components/contact-editor/collections/blank-row.ts, contact-point-choices.ts; components/contact-editor/types.ts, constants.ts, logic.ts; components/address/address-input.tsx; components/affiliations/linked-source.tsx; components/forms/contacts/collection-components.tsx (the form-engine adapter); components/contact-editor/__tests__/blank-rows.test.tsx'
narrative:
  status: verified
  review: NARRATIVE-REVIEW-2026-10-23
narrative_sources:
  - section: What It Is For
    provenance: inferred
    source: react-console:components/contact-editor/collections/address-list-editor.tsx; react-console:components/forms/contacts/collection-components.tsx
    note: The labeled rationale interprets reuse of one collection component for contact forms; no Product motivation is attributed.
  - section: How It Behaves
    provenance: observed
    source: react-console:components/contact-editor/collections/address-list-editor.tsx; react-console:components/contact-editor/collections/blank-row.ts; react-console:components/contact-editor/collections/contact-point-choices.ts
  - section: Its Contract
    provenance: observed
    source: react-console:components/contact-editor/collections/address-list-editor.tsx:AddressListEditorProps; react-console:components/contact-editor/collections/blank-row.ts:useBlankRow
  - section: States
    provenance: observed
    source: react-console:components/contact-editor/collections/address-list-editor.tsx:AddressListEditor
  - section: Accessibility
    provenance: observed
    source: react-console:components/contact-editor/collections/address-list-editor.tsx:handleCardKeyDown; react-console:components/contact-editor/fields/preferred-star.tsx:PreferredStarButton
  - section: Accessibility
    provenance: unknown
    source: FND-CRM-ENT-079
    note: Source attributes and focus requests do not establish complete keyboard, assistive-technology or viewport behavior.
  - section: Rules It Enforces
    provenance: unknown
    source: FND-CRM-ENT-089
    note: Direct draft removal is observed; intended differences between row sources and the older work mirror remain unresolved.
  - section: Open Design Questions
    provenance: unknown
    source: FND-CRM-ENT-089
  - section: What It Is For
    provenance: observed
    source: 'react-console:components/contact-editor/collections/address-list-editor.tsx'
  - section: Where It Is Used
    provenance: observed
    source: 'react-console:components/forms/contacts/collection-components.tsx'
  - section: Rules It Enforces
    provenance: observed
    source: 'react-console:components/contact-editor/collections/address-list-editor.tsx; components/contact-editor/logic.ts'
used_by: [SCR-CRM-ENT-004]
---

# Address Editor

## What It Is For

Staff use the Address Editor to keep a person's mailing details together while distinguishing home, work, school, seasonal and custom uses. It presents an address collection as cards, with controls for the address value, its type and its mailing preference. A caller can also supply source information so a card identifies an organization address that the contact follows.

Create Contact and Edit Contact share this collection control through the form engine. That reuse gives their address cards the same interaction and lets the calling form retain responsibility for saving the contact. Our reading is that a collection component makes the relationship between an address and its type easier to inspect than separate unrelated fields.

## Where It Is Used

Create Contact and Edit Contact place this component in their compiled forms through the Addresses adapter. In the default form, the collection sits on Overview. The adapter supplies configured address types, source callbacks, the lookup setting and whether to show geocode badges. This verified usage concerns contact address lists; the evidence here does not establish a reusable contract for an organization's address collection or a single address field elsewhere.

## How It Behaves

Each stored address appears in a card showing its type, formatted lines and preferred-star state. Expanding an owned card places the type selector beside the address input. The address control supports lookup when available and manual fields when lookup is disabled or unavailable. With an empty collection, an expanded blank card is already present; entering a nonblank value commits it without closing it. The blank card itself does not request initial focus.

Add Address selects the next available unused type in the editor's fixed proposal order and requests focus for the new row. Seasonal cards offer month/day boundaries and a mailing preference for their season. Changing the selected month clamps an invalid day to that month's last day. The ordinary preferred star is disabled for seasonal rows.

A card with row source controls shows the organization's source marker and keeps the value closed to direct editing. Change Source and Make Independent Copy are available according to that source state. Remove remains enabled on this path. Only the older work-address mirror path disables Remove until unlinking. Where the caller enables geocode display, an eligible owned row shows a Geocoded badge, without coordinate values.

## Its Contract

The calling form supplies the address list, a change callback and a lookup for card validation messages. Committed edits return a complete replacement list. The component makes no persistence request; saving and aggregate validation remain the caller's job. An empty row that is still blank is held locally rather than added to the supplied collection.

Optional inputs provide the configured types, row source callbacks, lookup availability preference, geocode display flag and older work-mirror information. Local state tracks expanded cards and the row that should receive focus. The source-control callbacks let the caller manage source changes without making the collection component responsible for fetching organization records.

## States

An empty list has an invitation and one expanded blank card. Owned cards can be closed for reading or expanded for editing; a validation message opens an owned card and prevents its Done action from hiding the problem. The message appears beneath the card as an alert. Source-controlled cards show the linked-source presentation instead of editable value fields. Seasonal cards add their date boundaries and seasonal mailing choice to the open state.

## Rules It Enforces

The contact-point capabilities own the rules for ordinary mailing preference, seasonal selection and organization sources. The card represents those concerns through its star, seasonal controls and source marker. Choosing an ordinary preferred address clears the other ordinary preferred flags; selecting Seasonal clears that flag on the changed row.

Source-controlled values are presented without a value editor, while Remove still changes the caller's draft list. The older work mirror requires unlinking before removal. It is not established that Product intends these paths to differ; the address-editor finding records that open boundary. The calling form and service remain responsible for deciding whether the resulting contact can be saved.

## Accessibility

Edit, Collapse and Remove names identify the address type. The preferred-star button reports its pressed state and explains disabled actions through a tooltip. Decorative icons are hidden from assistive technology, while validation text uses an alert role. Escape collapses an eligible owned card and focuses the card; an open popup, a validation error or the blank first row prevents that collapse. Added rows request focus, but expanding an existing card does not automatically focus its input. These observations do not establish complete keyboard or screen-reader conformance.

## Open Design Questions

This review does not establish a product decision to reuse the contact collection editor for organization addresses. It also leaves the intended handling of direct removal of a source-controlled row unresolved. The address-editor finding records the observed distinction between row source controls and the older work mirror, without treating either as Product approval.
