Skip to content

CompanyAddressBook

Pure presentation of the company's (shipping) addresses. It never fetches; the caller passes the resolved addresses and composes each card via renderItem — wiring the connected actions (CompanyAddressDialog, DeleteCompanyAddressButton) itself.

import { CompanyAddressBook } from "@evolve-storefront/feature-organization/company-address-book";

Renders inside OrganizationProvider.

Preview

DefaultOpen in Storybook ↗

Usage

A card is titled with the name the company gave the address, such as "Warehouse Utrecht", and with where it is when it has none.

const { data: book } = useBusinessUnitAddresses();
<CompanyAddressBook
addresses={book?.items}
renderItem={(address) => (
<AddressCard>
<AddressCardHeader>
<AddressCardTitle>{companyAddressTitle(address)}</AddressCardTitle>
</AddressCardHeader>
<AddressLines lines={formatAddressLines(address, { locale })} />
<AddressCardFooter><DeleteCompanyAddressButton address={address} /></AddressCardFooter>
</AddressCard>
)}
/>;

Props

PropTypeDescription
addresses?Address[]

The resolved company addresses (from useBusinessUnit().data.addresses.items).

renderItem?CompanyAddressBookItemRenderer<Address>

Renders each address — required for the default composition (no children). Ignored when children are supplied (pass it to CompanyAddressBookItems).

empty?ReactNode

Custom empty state for the default composition.

emptyAction?ReactNode

Shown in the default empty state (e.g. an add-address button).

searchTerm?string

The term the caller queries with. Controlled, because the query owns it: BusinessUnit.addresses is searched on the server, so this filters nothing.

onSearchTermChange?((searchTerm: string) => void)

Pass to show the search field.

defaultAddress?Address | null

The company's default delivery address (useBusinessUnit().data.defaultShippingAddress), pinned at the top and left out of the letter groups.

headingLevel?HeadingLevel

Level for the group headings. Defaults to 3.

groupBy?"name" | "location"

What the letter groups file by. Defaults to location; see CompanyAddressBookItems.

children?ReactNode

Any other props go to the root element, like on a plain HTML element.

Change the layout

Pass children to CompanyAddressBook to arrange its parts yourself. Every part is exported from @evolve-storefront/feature-organization/company-address-book.

CompanyAddressBookItemsThe company addresses in one column, the default first and the rest under letter headings by location.
PropTypeDescription
addressesAddress[]

The addresses to render. Kept an explicit prop so renderItem's type flows from an extended query's shape.

renderItemCompanyAddressBookItemRenderer<Address>

Renders each address. Required — the feature owns no card markup.

empty?ReactNode

Custom empty state. Defaults to <CompanyAddressBookEmpty />.

defaultAddress?Address | null

The company's default delivery address, pinned above the rest under a heading of its own and left out of them. From the business unit rather than the page, so it stays on top whichever page of the book is showing.

headingLevel?HeadingLevel

Level for the group headings. Defaults to 3.

groupBy?"name" | "location"

What the letter groups file by. location (the default) uses the city and street, because every address of a company carries the company's own name; name files by company or contact, as a personal address book does.

Any other props go to the root element, like on a plain HTML element.

Loading and empty states

Render these while the data loads or when there is nothing to show, so the page does not jump when it arrives.

EmptyCompanyAddressBookEmptyEmpty state for the company address book.
LoadingCompanyAddressBookSkeletonLoading state for CompanyAddressBook.

Examples

ComposedChildren replace the default grid, so the caller can put a toolbar above it.
<CompanyAddressBook>
<Button onClick={() => setAdding(true)}>{t("add-address")}</Button>
<CompanyAddressBookItems
addresses={book.items}
renderItem={renderItem}
empty={<CompanyAddressBookEmpty />}
/>
</CompanyAddressBook>;
Open in Storybook ↗
Empty
// An empty book renders the empty state, with your action in it.
<CompanyAddressBook
addresses={book?.items}
renderItem={renderItem}
emptyAction={<Button onClick={() => setAdding(true)}>{t("add-address")}</Button>}
/>;
Open in Storybook ↗
LoadingSkeleton and real view adjacent — the only cheap check that the placeholder mirrors the DOM it replaces.
const { data: book, isPending } = useBusinessUnitAddresses();
// Gate on isPending: the query waits for the session rather than settling empty.
if (isPending) return <CompanyAddressBookSkeleton />;
<CompanyAddressBook addresses={book?.items} renderItem={renderItem} />;
Open in Storybook ↗
Set Default From The MenuConnected to the mock gateway: choosing "Set as default" in an address's menu makes it the company's default delivery address, which then sits on top under a heading of its own and in no letter group.
const { data: unit } = useBusinessUnit();
const { data: book } = useBusinessUnitAddresses();
<CompanyAddressBook
addresses={book?.items}
defaultAddress={unit?.defaultShippingAddress}
renderItem={(address) => (
<AddressCard>
…
<ActionsMenu
label={t("actions")}
actions={[useSetDefaultCompanyAddressAction({ addressId: address.id, isDefault })]}
/>
</AddressCard>
)}
/>;
Open in Storybook ↗