Skip to main content

CompanySwitcher

Provides a dropdown selector for B2B users (Contact type) who belong to multiple companies. Displays the currently active company name and lets the user switch between their assigned companies. Designed for use in the site header or navigation bar.

Usage

Basic usage in Header

import CompanySwitcher from '@/components/propeller/CompanySwitcher';
import { Contact, Company } from 'propeller-sdk-v2';

function Header({ user }: { user: Contact }) {
const handleCompanyChange = (company: Company) => {
console.log('Switched to:', company.name, company.companyId);
};

return (
<CompanySwitcher
user={user}
onCompanyChange={handleCompanyChange}
/>
);
}

With CompanyContext integration

The recommended pattern uses CompanyContext to persist the selected company across the app and sync it with other components (e.g., OrderList, UserDetails, AddressCard).

import CompanySwitcher from '@/components/propeller/CompanySwitcher';
import { useCompany } from '@/context/CompanyContext';
import { Contact } from 'propeller-sdk-v2';

function Header({ user }: { user: Contact }) {
const { selectedCompany, setSelectedCompany } = useCompany();

return (
<CompanySwitcher
user={user}
selectedCompanyId={selectedCompany?.companyId}
onCompanyChange={setSelectedCompany}
/>
);
}

Conditional rendering for multi-company contacts only

Only show the switcher when the user is a Contact with more than one company:

import { Contact } from 'propeller-sdk-v2';

{state.isAuthenticated && state.user && 'contactId' in state.user
&& (state.user as Contact).companies
&& ((state.user as Contact).companies!.items?.length || 0) > 1 && (
<CompanySwitcher
user={state.user as Contact}
selectedCompanyId={selectedCompany?.companyId}
onCompanyChange={setSelectedCompany}
/>
)}

With custom icon

<CompanySwitcher
user={user}
icon="building"
onCompanyChange={handleCompanyChange}
/>

Configuration

Required Props

PropTypeDescription
userContactThe authenticated B2B user. Companies are read from user.companies.items (or user.companies._items). Falls back to user.company if no companies list is available.
onCompanyChange(company: Company) => voidCallback fired when the user selects a different company. Receives the full Company object.

Optional Props

PropTypeDefaultDescription
iconstring'default-company-switch-icon'CSS icon class identifier. Applied as icon-{value} on the trigger button icon span.
selectedCompanyIdnumberundefinedExternally controlled active company ID. Use this to sync the switcher with a context provider (e.g., CompanyContext). When not provided, the component defaults to user.company.

Behavior

Active company resolution

The component determines the active company using the following priority:

  1. Internal state (activeCompanyId) -- set when the user clicks a company in the dropdown
  2. selectedCompanyId prop -- syncs with external state (e.g., CompanyContext)
  3. user.company -- the user's default company from the Propeller API

If no company can be resolved, the trigger button displays "Select company".

Company list resolution

The component reads the user's companies from user.companies.items. Due to SDK serialization behavior, it also checks user.companies._items as a fallback. If neither is available or the array is empty, it falls back to a single-item list containing user.company.

  • Open/close: Clicking the trigger button toggles the dropdown. An animated chevron rotates to indicate open/closed state.
  • Click outside: Clicking anywhere outside the component closes the dropdown.
  • Selection: Clicking a company in the list immediately updates the internal state, closes the dropdown, and fires onCompanyChange.
  • Active indicator: The currently active company is shown in bold with a checkmark icon.

Accessibility

  • The trigger button uses aria-haspopup="listbox" and aria-expanded to communicate dropdown state.
  • The dropdown list uses role="listbox" with role="option" and aria-selected on each item.
  • The trigger has aria-label="Switch company" for screen readers.

Cross-component sync via CompanyContext

When used with CompanyContext, selecting a company triggers the following chain:

  1. onCompanyChange fires with the selected Company
  2. CompanyContext.setSelectedCompany() stores it in React state and localStorage (key: selected_company)
  3. A companySwitched custom event is dispatched on window
  4. Other components listening for companySwitched (e.g., UserDetails, OrderList) react to the change and refetch data for the new company
  5. On logout, CompanyContext automatically clears the selected company

SDK Services

This component does not call any SDK services directly. It is a purely presentational component that reads company data from the user prop (a Contact object already fetched via the Propeller SDK).

The company data it consumes is typically fetched as part of the user/viewer query:

import { UserService } from 'propeller-sdk-v2';

const userService = new UserService(graphqlClient);
const viewer = await userService.getViewer({});
// viewer.companies.items contains the Company[] list
// viewer.company contains the default Company
import { Contact, Company } from 'propeller-sdk-v2';

// Contact — B2B user type
interface Contact {
contactId: number;
company: Company; // Default company
companies: {
items: Company[]; // All assigned companies
};
// ...other fields
}

// Company
interface Company {
companyId: number;
name: string;
// ...other fields
}

GraphQL Queries and Mutations

The company data consumed by this component comes from the viewer query, which returns the authenticated user's profile including their company associations:

query Viewer {
viewer {
... on Contact {
contactId
firstName
lastName
company {
companyId
name
}
companies {
items {
companyId
name
}
}
}
}
}

CSS Classes

The component uses BEM-style class names for targeted styling:

ClassElement
.company-switcherRoot container
.company-switcher__triggerTrigger button
.company-switcher__iconIcon span (also receives icon-{value})
.company-switcher__labelActive company name (truncated at 160px)
.company-switcher__chevronChevron arrow (rotates on open)
.company-switcher__dropdownDropdown list container
.company-switcher__optionIndividual company list item
.company-switcher__option-nameCompany name text within option
.company-switcher__option-checkCheckmark SVG on active option