npm package: @glidevvr/unit-table
Description: `@glidevvr/unit-table` is a React and TypeScript-based npm package designed to display and manage facility unit data with dynamic filtering and configuration.
Latest version: 0.12.0

## Overview

`@glidevvr/unit-table` is a React and TypeScript-based npm package designed to display and manage facility unit data with dynamic filtering and configuration.

---

## Installation

```bash
npm install @glidevvr/unit-table
```

**Peer dependencies:** React 19 and React DOM 19 are required.

### Usage

```tsx
import { UnitTable } from "@glidevvr/unit-table";
import "@glidevvr/unit-table/style.css";

<UnitTable
    apiKey="YOUR_API_KEY"
    options={{
        facilityIds: [1234],
        // ...additional options (see Configuration Options below)
    }}
/>
```

---

## Local Development

Follow these steps to run and test Unit Table locally:

1. Run `npm run start`
    - Append `:prod` or `:dev` or `:qa` to denote which se api you want to connect to. Default is prod.
2. cmd click on the url outputted.

### Prerequisites

-   Node.js version: 20.x or later
-   NPM version: 10.x or later

---

## Stack

Unit Table uses the following technologies:

-   **React**: JavaScript library for building user interfaces.
-   **TypeScript**: Provides type safety and improves code maintainability.
-   **Vite**: A fast build tool with hot module replacement.
-   **Shadcn**: Component library for consistent UI elements.
-   **Tailwind CSS**: Utility-first CSS framework for custom styling.
-   **Vitest**: Testing framework for fast and reliable tests.

---

## Branching Strategy

Unit Table follows a **Dirty Trunk** repo structure to enable fast, continuous integration.

### Trunk (Main Branch)

-   **`master`** is the trunk where all development merges.
-   Trunk may contain work-in-progress code and is not guaranteed to be in a release-ready state at all times.

### Feature Branches

-   Use short-lived branches for features or fixes:
    -   Naming convention: `feature/<feature-name>` or `bugfix/<issue-description>`.
-   Merge into `master` frequently to avoid long-lived branches and reduce conflicts.
-   Use squash merges to keep the history clean.

### Release Branches

-   Create `release/x.y.z` branches from `master` when ready to stabilize for a release.
-   Only bug fixes and release preparation changes go into the release branch.
-   After release, merge back into `master` and tag with the version.

### Hotfixes

-   Create `hotfix/x.y.z` branches from the latest tagged release.
-   Merge back into `master` and the active release branch.

---

## Managing CI/CD

Unit Table uses Bitbucket Pipelines for testing, linting, and publishing:

-   **Branch Tests**: Automated build, linting, and testing run on all branches (`feature`, `bugfix`, `release`, `hotfix`).
-   **Publishing**: npm releases are triggered manually via the `npm-publish` custom pipeline in Bitbucket.
-   **Versioning**: Uses semantic versioning:
    -   Increment minor versions on releases.
    -   Increment patch versions for hotfixes.

---

## Running Tests

-   To run tests, use: `npm test`
-   For coverage: `npm test --coverage`

---

## Contributing

1. Create a feature branch.
2. Ensure all tests pass before submitting a pull request.
3. Follow the project’s coding standards and linting rules.

---

## Configuration Options

Unit Table's behavior can be customized via the `options` object:

```Json
options: {
    facilityIds: [], // Array of SE Facility Ids REQUIRED
    tableLayout: "row", // "grid" | "row"
    buttonLayout: "combined", // "combined" | "separate"

    disableRental: false, // boolean
    disableReservation: false, // boolean
    disableSoftReservation: false, // boolean

    allowedCategories: "", // String of categories to filter by name, comma separated
    allowedFeatures: "", // String of features to filter by name, comma separated

    sort: "", // String of sort.

    vacancyNotice: true, // boolean, Enables vacancy notice display
    vacancyThreshold: 3, // number, Minimum number of units left to trigger a vacancy notice
    unavailableUnits: false, // boolean, Show unavailable units on the table

    groupBy: "", // Define how units from the api should be grouped

    rentalApp: {
        location: "dialog", // "drawer" | "external" | "dialog" Location of the rental app
        externalUrl: "", // Url for the external rental app
    },

    reservationApp: {
        location: "dialog", // "drawer" | "external" | "dialog" Location of the reservation app
        externalUrl: "", // Url for the external reservation app
    },

    softReservationApp: {
        location: "drawer", // "drawer" | "external" | "dialog" Location of the reservation app
        externalUrl: "", // Url for the external soft reservation app
    },
    additionalUnitSourceInfo: "", // Comma separated list of vendor source data keys. Mainly for use in the external app urls.

    showAppliedPromoPrice: true, // boolean, Displays price with promotions applied

    language: {
        grid: {
            strikeThroughRate: "Starting at"; // String | Text (Starting at) for strike through
            webRate: "Web Rate"; // String | Text for 'Web Rate'
            promoRate: "Promo Rate"; // String | Text for "Promo Rate"
        },
        action: {
            reserveButton: "Reserve"; // String | Text for "Reserve" action button
            rentButton: "Rent"; // String | Text for "Rent" action button
            selectButton: "Select"; // String | Text for "Select" action button. Select typically will open a journey for either VBP or Rent/Reserve/Quote selection.
            quoteButton: "Get a Quote"; // String | Text for "Quote" action button
        }
    },
    valueBasedPricingSettings: {
        tierA: {
            label: "Standard", // String | Title of the Tier A
            description: "", // String | Description of the Tier A
        },
        tierB: {
            label: "Best Value", // String | Title of the Tier B
            description: "", // String | Description of the Tier B
        },
        tierC: {
            label: "Deluxe", // String | Title of the Tier C
            description: "", // String | Description of the Tier C
        },
        tierOrder: ["tierA", "tierB", "tierC"], // Array of Strings | Order from left to right that the tiers should appear in the modal.
    }
}
```

### External Rental App / External Reservation App / External Soft Reservation App

You can pass in a url and if you need values mapped to the url pass them in using the dot notation and the app will "translate".

```
unitTableOptions: {
                    facilityIds: [6011],
                    rentalApp: {
                        location: "external",
                        externalUrl: "https://compass-rental.ssm-erp.com/home/{{facility.vendor_id}}/{{unitGroup.representative_unit.source.UnitTypeId}}/{{unitGroup.representative_unit.vendor_id}}/en/{{unitGroup.rates.promo_rates.0.vendor_id}}/null"
                    },
                    additionalUnitSourceInfo: "UnitTypeId"
                }
```

If you are accessing `additionalUnitSourceInfo` then you just need to do `unitGroup.representative_unit.source.{key goes here}`. No need to do any array indexing, the translate process accounts for the additional source key value format.

---

## Data Sources

The **Unit Table** uses the **SE API Wrapper** package to interface with the Storage Essentials Api. This also means it makes use of **RTK Query** to take and manage the received data.

The **Unit Table** hits the following endpoints for its data:

-   getUnitGroups
-   getFacilitiesV2

## Changelog

### [0.12.0] - 2026-04-07

#### Added
- Layout 2 grid card view with responsive column layout (1/2/3 columns by breakpoint)
- Grid promo expansion as full-width sibling card with animated expand/collapse transitions
- "Close" button replaces Rent/Reserve/Select when promo panel is expanded (Layout 2 multi-promo)
- `closeButton` language option for customizing the Close button label
- Smooth scroll-into-view for grid promo expansion in modals and overflow-x:hidden containers

#### Changed
- Single action buttons (Rent-only, Reserve-only, Select) now render in the Reserve (bottom) position on grid cards for consistent alignment
- Scroll-into-view uses ease-in-out-cubic easing and detects nearest scrollable ancestor instead of always targeting window

#### Removed
- X icon overlay on expanded Rent/Reserve/Select buttons (replaced by Close button)

### [0.11.0] - 2026-04-06

#### Update
- Update the Cirrus version to latest:1.11.2

### [0.10.0] - 2026-03-30

#### Added
- Layout 2 multi-promo expandable rows with radio button promo selection
- "More Amenities" tooltip for Layout 2 units with 3+ features
- "SALE" ribbon for multi-promo units in Layout 2 (replaces Select dropdown)
- VBP Select button now shows for VBP units even in Separate button layout
- "Select Now" button in expanded promo row for VBP units (opens VBP modal)
- Accessibility: aria-expanded, aria-controls, role="radiogroup" on expand/collapse elements

#### Fixed
- Reserve button aria-controls now always references the promo panel when expandable

### [0.9.0] - 2026-03-25
- Add support for layout 2 and pill filters

### [0.8.1] - 2026-03-25

#### Fixed
- Pass unit id correctly in VBP tiers to Reservation funnel

### [0.8.0] - 2026-03-24

#### Added
- environment option for api calls