@glidevvr/unit-table
v0.14.27
Published
`@glidevvr/unit-table` is a React and TypeScript-based npm package designed to display and manage facility unit data with dynamic filtering and configuration.
Readme
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
npm install @glidevvr/unit-tablePeer dependencies: React 19 and React DOM 19 are required.
Usage
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:
- Run
npm run start- Append
:prodor:devor:qato denote which se api you want to connect to. Default is prod.
- Append
- 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)
masteris 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>orbugfix/<issue-description>.
- Naming convention:
- Merge into
masterfrequently to avoid long-lived branches and reduce conflicts. - Use squash merges to keep the history clean.
Release Branches
- Create
release/x.y.zbranches frommasterwhen ready to stabilize for a release. - Only bug fixes and release preparation changes go into the release branch.
- After release, merge back into
masterand tag with the version.
Hotfixes
- Create
hotfix/x.y.zbranches from the latest tagged release. - Merge back into
masterand 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-publishcustom 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
- Create a feature branch.
- Ensure all tests pass before submitting a pull request.
- Follow the project’s coding standards and linting rules.
Configuration Options
Unit Table's behavior can be customized via the options object:
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
inlineReserveFormEnabled: false, // boolean, Reserve opens a form in the unit row instead of navigating to reservationApp
inlineQuoteFormEnabled: false, // boolean, Get a Quote opens a form in the unit row instead of navigating to softReservationApp
allowedCategories: "", // String of categories to filter by name, comma separated
allowedFeatures: "", // String of features to filter by name, comma separated
categoryOrder: [], // Array of category slugs (Layout 2 only). Listed categories render first in this order; unlisted ones fall back to the default order. Layout 1 keeps API order.
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.14.27] - 2026-08-12
Added
- Inline Reserve and Get a Quote forms are now opt-in per action via two new options,
inlineReserveFormEnabledandinlineQuoteFormEnabled, both defaulting tofalse. When off, each action navigates to its configured external app as it did before WEB-1436/WEB-1432; when on, it expands the unit row in place. The host resolves its own organization/facility cascade, so these arrive as effective values, and a singleresolveInlineFormEnabled(options, action)seam applies them — inline is only ever available in the row-expanding layouts, never in the market layout. Row expansion itself is deliberately untouched, so the pre-existing multi-promo selector keeps working regardless of these options. (WEB-1569)
Fixed
- Reserving through the external reservation app now emits a
reservationtracking event. Only the same-page funnel branch emitted one, so every external redirect went untracked. The event is posted before the redirect, which is then handed off on the next task —window.postMessagedelivers as a queued task, and navigating within the same task discarded it before any listener ran. (WEB-1569) - The inline Get a Quote row no longer emits a second
inquiryevent when it is collapsed. The event fired ahead of the open/close branch, so a visitor opening and closing a row counted twice, roughly doubling reported inquiries on sites using the inline form. Opening is now the only trigger, matching Reserve. (WEB-1569) - Reserve from the unit-selection and checkout modals no longer fails silently. Both wire the reservation handler straight to a click, so the event arrived where a unit id was expected and survived the nullish fallback; once the redirect began emitting a tracking event,
postMessagecould not clone it and threw before navigation. Only a real unit id is accepted now, which also corrects the id passed into the external url — modal reservations previously interpolatedNaN. (WEB-1569)
[0.14.26] - 2026-08-10
Reverted
- Reverted the WEB-1558 featured unit-group change from 0.14.25. Matching featured groups on a physical-class key (size + category + climate/indoor) collided across facilities and unit types — it omitted
facility_idandunit_type_id, the fields Storage Essentials actually groups on — so one selection could highlight rows at the wrong facility or the wrong unit type. Featured matching returns to the priorrepresentative_unit.idbehavior. A stable fix requires an SE-side unit-group key and is being scoped separately. (WEB-1558)
[0.14.25] - 2026-08-06
Fixed
- Featured unit-group highlights (tab/badge) no longer detach from their group when availability or pricing changes. Matching keyed on
representative_unit.id— a physical unit SE re-elects on rent/reserve, whoseunit_type_idcan even differ across a group's Good/Better/Best tiers — so the stored reference stopped matching and the highlight disappeared. Featured groups now match on a stable physical-class key (size + category + climate + indoor/outdoor) viabuildUnitGroupKey, which survives availability and tier changes; a legacyrepresentative_unit.idfallback keeps pre-existing selections working until re-saved. (WEB-1558)
[0.14.24] - 2026-07-22
Fixed
- Advanced market table: VBP (Value-Based Pricing) price tiers now group correctly so the Good/Better/Best modal opens. The market previously fetched every facility in a single
unit-groupscall with no grouping, which split each size's tiers into separate single-tier groups (sub_groupslength 1) and stopped the tier modal from ever opening. The market now fetches oneunit-groupscall per grouping bucket — each facility grouped by its owngroupBy(viafacilityOptions), else ungrouped — and merges the results, so every facility groups on the market exactly as it does on its own facility page. Fetching stays client-side and lazy row rendering is unaffected; facility pages are unchanged. (WEB-704)
[0.14.23] - 2026-07-17
Changed
- Advanced market table now honors each facility's own unit-table button settings instead of a single global value. Rent / Reserve / Get-a-Quote visibility (
disableRental/disableReservation/disableSoftReservation) and the Rent/Reserve-vs-SelectbuttonLayoutare resolved per facility via a new optionalfacilityOptionsmap, fixing a facility with reservations disabled still showing the Reserve button on market pages.buttonLayoutis also resolved per facility across the rent/reserve journey, so the rendered button and the funnel steps always agree. Facility pages pass no per-facility map and are unchanged. (WEB-704)
[0.14.22] - 2026-07-16
Changed
- Mobile (all layouts, ≤767px): the featured-unit Badge now sits on the left edge of the image (vertically centered, overhanging), matching the Layout 2 grid position (WEB-1508). On mobile the promo banner sits across the top of the stacked card, so the previous top-right/corner placement collided with it.
[0.14.21] - 2026-07-15
Changed
- Split the Layout 2 featured-unit Badge position by display mode (WEB-1508):
- Row: top-right corner of the unit image, with the per-shape wide/tall lifts (0.14.18 behavior). The promo-banner collision is handled on the theme side by relocating the promo banner above the action buttons.
- Grid: left edge of the image, vertically centered and overhanging outward (the 0.14.19 position), to clear the promo banner that spans the top of the grid card.
[0.14.20] - 2026-07-15
Changed
- Advanced market table now hides units that would only show "Call for Availability" — click-to-call unit types, or otherwise non-transactable units at facilities whose quote type is "call" — since they can't be rented or reserved from the market page. "Get a Quote" (soft-reservation) lead units still show, and facility pages are unaffected (the filter is gated on market mode). (WEB-704)
[0.14.19] - 2026-07-15
Changed
- Featured-unit Badge on facility Layout 2 now sits on the left edge of the unit image, vertically centered and overhanging outward, instead of the top-right corner (WEB-1508). This clears the top of the card where a promo banner (e.g. "FREE 1 Month's Rent") can sit and previously collided with the badge. Layout 1 / market placement (top-right) is unchanged.
[0.14.18] - 2026-07-14
Added
- Badge treatment for featured units-table rows (WEB-1508): a colored, text-labeled promotional badge (e.g. "Flash Sale") overlaid on the unit image, as Option 2 alongside the Tab row highlight. Editors pick one of 12 shapes + any color + text in the CMS. The label auto-scales to fit each shape (per-shape placement, size, rotation, and line caps; whole-word wrapping, no truncation), text contrast is WCAG-derived, and badge rows pin to the top when the CMS pin-to-top toggle is on. Rendered top-right of the unit image on both layouts (lifted/smaller on Layout 2).
[0.14.17] - 2026-07-13
Changed
- Market template now shows the 3 cheapest units per facility (by displayed rate, any size), rows ordered cheapest-first — replacing the previous one-row-per-size selection. (WEB-704)
Performance
- Market facility cards lazy-mount their unit rows: only facilities in or near the viewport hydrate their rows (Rent/Reserve buttons, promo dropdowns, tooltips); the rest mount on scroll via an IntersectionObserver. Reduces initial hydration/DOM on large market pages (20-50+ facilities). (WEB-704)
[0.14.16] - 2026-07-10
Added
- Advanced market units table ("market" mode) for the market page: each facility renders as a card — banner image, name, address, reviews, first-3 feature pills, and a "See All Units" link — wrapping that facility's live unit rows with working Rent/Reserve journeys, promos, and urgency messaging. A matching loading skeleton reuses the facility-card layout (real header + skeleton unit rows) instead of a flat list of generic rows. New
FacilitySection,ReviewsRow,StarRating, andgroupByFacilitybuilding blocks. (WEB-704)
[0.14.15] - 2026-06-29
Fixed
- Removed the no-op back (✕) button that rendered above the inline Reserve/Get a Quote form on Layout 1 multi-promo units. Layout 1 picks the promo on the card's "Choose Your Special" dropdown rather than a separate selector step, so there was nothing to navigate back to; the Layout 2 multi-promo back button is unchanged. (WEB-1502)
[0.14.14] - 2026-06-25
Added
- Inline "Get a Quote" and "Reserve" expansion in Layout 1 grid view: either action now opens a full-width panel that pushes the cards below it down (matching Layout 2 and the nearby panel) instead of expanding inside the individual card. The panel is form-only — it submits the promo chosen in the card's "Choose Your Special" dropdown, with no separate in-expansion promo selector. (WEB-1432)
Changed
- Bumped
@glidevvr/reservation-appto 0.4.4 — each inline form instance now gets its own store, so a remounted form (close → reopen, or opening another unit) no longer inherits the previous instance's stale state. Fixes the email/SMS opt-in toggles freezing after the first mount. (WEB-1529)
Fixed
- VBP units with separate Rent/Reserve buttons now open the value-based-pricing modal (or the inline form, per the facility setting) instead of navigating straight to the reserve page. (WEB-1432)
- A reserve form taller than the viewport keeps its top in view when it expands, instead of jumping the scroll to the bottom of the form. (WEB-1432)
- Layout 1 grid cards no longer squish at mobile widths — a card in a single-column grid now spans the full width rather than using the multi-column placement. (WEB-1432)
- An open quote/reserve expansion closes automatically if its unit is filtered or sorted out of the list, so stale expansion state can't linger. (WEB-1432)
[0.14.13] - 2026-06-22
Added
- Nearby Units in grid view (previously row-only). Layout 2 grid renders the panel as a full-width push-down expansion below the card's row (matching the promo expansion); Layout 1 grid renders a full-width sibling at 2 columns and a card-anchored overlay (no card reflow) at 3-4 columns, chosen by the grid column count. New
NearbyPanel,FindNearbyButton, andGridNearbyExpansioncomponents. (WEB-407) - Reviews on the nearby facility cards, rendered from
facilityMap[id].reviewsScore/reviewsCountwhen present. (WEB-407)
Changed
- The nearby panel's facility cards and map reflow via container queries — responding to the panel's own width rather than the viewport — so cards sit beside the map only once the panel is wide enough and otherwise stack. (WEB-407)
Fixed
- "Get a Quote" on sold-out rows was a no-op (the unavailable-unit row stubbed the in-row promo-expansion state); it now opens the inline quote form. (WEB-407)
- Only one in-row expansion is open per row — opening Find Nearby closes the quote form and vice versa. (WEB-407)
- The Find Nearby close icon is absolutely positioned beside the label, so toggling it no longer widens the button or shifts the surrounding cards. (WEB-407)
[0.14.12] - 2026-06-18
Fixed
- Promo dropdown box now expands to contain its text instead of overflowing the box. (WEB-1517)
- Feature and category filter options are only shown when an available unit group has them, so amenities/categories that exist only on sold-out units no longer appear as filters. (WEB-1518)
[0.14.11] - 2026-06-15
Added
- Featured unit-group rows via the
featuredUnitsoption (max 2). Configured groups are matched locally against the loaded unit list (byrepresentative_unit.id, string-coerced) and given a "Tab" visual treatment: a colored outline, a background tint, and a vertical tab ribbon with a label, all driven by the CMS-selectedcolor. Optional host-providedcolorLight(row tint) andcolorForeground(ADA-correct text) are used when present, falling back to a CSScolor-mixand the widget's own luminance-based text color. Applied in both row (UnitItem) and grid (GridPromoExpansion) layouts; thebadgetreatment is reserved for a later task. (WEB-1502) pinFeaturedToTopoption (CMS toggle, defaults tofalse). When on, featured groups are pinned to the top via a stable partition applied after sorting, preserving the relative order within both the featured and non-featured sets. (WEB-1502)- "Featured units" filter in the filters and More Filters modals, shown only when featured groups are configured. Wired into the applied-filter count, Clear All, and Apply, backed by new
selectedFeaturedfilter state. (WEB-1502)
Changed
getTextColorForBackgroundnow uses the gamma-corrected WCAG 2.0 relative-luminance formula (mirrors the host repos'getContrastTextColor), adds an exportedgetLuminance, normalizes#abcshorthand hex, and falls back to white text for unparseable input. (WEB-1502)- Strip the client-side
featuredsort token from the Storage Essentials API sort params — it is handled client-side via pinning and is not a valid SE sort property. (WEB-1502)
[0.14.9] - 2026-06-05
Fixed
InlineReservationForm hardcoded the Reservation environment from import.meta.env.VITE_SE_ENVIRONMENT (baked into the published bundle → production), so the inline quote/reserve form POSTed to the production SE API even on staging. Read state.apiKey.environment from Redux instead — the same host-provided source the widget's own data calls use.
[0.14.8] - 2026-06-03
Added
- Configurable category ordering for Layout 2 via the
categoryOrderoption. Listed category slugs render first in the given order; unlisted categories fall back to the default order (size by area ascending, then unit-type A→Z). Layout 1 keeps the API order. (WEB-1474) - Inline Get a Quote and reserve/booking forms in the expanded Layout 2 row, hosted by the mode-aware
InlineReservationForm. Multi-promo units use a stepped flow: the promo selector advances to the in-row form (carrying the chosen promo) with a back control, while single/no-promo actions open the form directly. The selector shows a single context-aware Continue matching the inline button that opened it (Rent/Reserve/Quote/Select), with the heading prefixed by the action. Wired in both row (UnitItem) and grid (GridPromoExpansion) layouts; underlying funnel actions and tracking are unchanged. Separate-layout multi-subgroup (VBP) units still navigate to the funnel rather than reserving the representative unit inline. (WEB-1436)
Fixed
- Gate the quote/call slot on the facility query resolving instead of its loading state, so "Call for Availability" no longer flashes before "Get a Quote" on sold-out RESERVE units. Action loading skeletons are now full-width pills matching the action buttons, rendering 1 or 2 based on button layout. (WEB-1436)
Update
- update reservation-app to 0.4.2 (WEB-1436)
[0.14.7] - 2026-05-27
Fixed
- Adding the "View More" button dropdown toggle on mobile when there are more than 2 amenities
[0.14.6] - 2026-05-26
Fixed
- Isolate Redux store per
UnitTablemount so multiple unit tables on the same page no longer share journey/filter state (WEB-1481)
[0.14.5] - 2026-05-18
Update
- update reservation-app to 0.4.1 (WEB-1476)
[0.14.4] - 2026-05-11
Update
- Use
ReservationType.SOFTfor QUOTE funnel type inUnitFunnelso soft reservations are created when generating quotes (WEB-1270)
[0.14.3] - 2026-05-06
Update
- Uncomment
leadModeprop on the reservation funnel inUnitFunnelso QUOTE funnel type enables lead mode
[0.14.2] - 2026-05-04
Update
- update reservation app to 0.4.0
[0.14.1] - 2026-04-27
Update
- use @glidevvr/reservation-app instead of @glidevvr/cirrus
[0.14.0] - 2026-04-27
Fixed
- Revert combined rent/reserved VBP buttons to remain separate when Separate buttons is selected.
[0.13.0] - 2026-04-22
Fixed
- Null-safe access to
facility.settingsinJourneyContent,QuoteButton,TriggerButtons,SkeletonUnitItem, anduseTriggerButtonVisibilityto prevent runtime errors whensettingsis missing - Flaky
TriggerButtonsseparate-layout test by pinningsub_groupsto an empty array
Changed
useRentalAction.openRentalFunnelnow accepts an optionalselectedUnitIdand threads it throughredirectToExternalRentalandopenSamePageRentalFunnel, falling back to the hook'sunitIdwhen not provided
[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)
closeButtonlanguage 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
