@campminder/components 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +198 -0
  2. package/dist/index.d.ts +16 -0
  3. package/dist/index.js +11 -0
  4. package/dist/menu-tree.upstream.test.d.ts +1 -0
  5. package/dist/property-B5JHHBCF.js +603 -0
  6. package/dist/side-nav/MegaMenu.d.ts +50 -0
  7. package/dist/side-nav/MegaMenu.styles.d.ts +33 -0
  8. package/dist/side-nav/SideNav.d.ts +89 -0
  9. package/dist/side-nav/SideNav.styles.d.ts +42 -0
  10. package/dist/side-nav/Utils/helpers.d.ts +57 -0
  11. package/dist/side-nav/assets/MegaMenuIcons/AccountsGrayIcon.d.ts +2 -0
  12. package/dist/side-nav/assets/MegaMenuIcons/AdminGrayIcon.d.ts +2 -0
  13. package/dist/side-nav/assets/MegaMenuIcons/AlumniGrayIcon.d.ts +2 -0
  14. package/dist/side-nav/assets/MegaMenuIcons/CampInTouchGrayIcon.d.ts +2 -0
  15. package/dist/side-nav/assets/MegaMenuIcons/CamperGrayIcon.d.ts +2 -0
  16. package/dist/side-nav/assets/MegaMenuIcons/CommunicationGrayIcon.d.ts +2 -0
  17. package/dist/side-nav/assets/MegaMenuIcons/DefaultMMIcon.d.ts +2 -0
  18. package/dist/side-nav/assets/MegaMenuIcons/FinancialGrayIcon.d.ts +2 -0
  19. package/dist/side-nav/assets/MegaMenuIcons/MedicalGrayIcon.d.ts +2 -0
  20. package/dist/side-nav/assets/MegaMenuIcons/OpenInNewIcon.d.ts +2 -0
  21. package/dist/side-nav/assets/MegaMenuIcons/ReportsGrayIcon.d.ts +2 -0
  22. package/dist/side-nav/assets/MegaMenuIcons/SchedulingGrayIcon.d.ts +2 -0
  23. package/dist/side-nav/assets/MegaMenuIcons/StaffGrayIcon.d.ts +2 -0
  24. package/dist/side-nav/assets/MegaMenuIcons/TravelGrayIcon.d.ts +2 -0
  25. package/dist/side-nav/assets/MegaMenuIcons/index.d.ts +16 -0
  26. package/dist/side-nav/assets/SideNavIcons/AccountsIcon.d.ts +2 -0
  27. package/dist/side-nav/assets/SideNavIcons/AdminIcon.d.ts +2 -0
  28. package/dist/side-nav/assets/SideNavIcons/AlumniIcon.d.ts +2 -0
  29. package/dist/side-nav/assets/SideNavIcons/CampInTouch.d.ts +2 -0
  30. package/dist/side-nav/assets/SideNavIcons/CamperIcon.d.ts +2 -0
  31. package/dist/side-nav/assets/SideNavIcons/ChevronRightIcon.d.ts +2 -0
  32. package/dist/side-nav/assets/SideNavIcons/CloseIcon.d.ts +2 -0
  33. package/dist/side-nav/assets/SideNavIcons/CommunicationIcon.d.ts +2 -0
  34. package/dist/side-nav/assets/SideNavIcons/DefaultNavIcon.d.ts +2 -0
  35. package/dist/side-nav/assets/SideNavIcons/FinancialIcon.d.ts +2 -0
  36. package/dist/side-nav/assets/SideNavIcons/HomeIcon.d.ts +2 -0
  37. package/dist/side-nav/assets/SideNavIcons/MedicalIcon.d.ts +2 -0
  38. package/dist/side-nav/assets/SideNavIcons/MenuCollapseIcon.d.ts +2 -0
  39. package/dist/side-nav/assets/SideNavIcons/MenuIcon.d.ts +10 -0
  40. package/dist/side-nav/assets/SideNavIcons/ReportsIcon.d.ts +2 -0
  41. package/dist/side-nav/assets/SideNavIcons/SchedulingIcon.d.ts +2 -0
  42. package/dist/side-nav/assets/SideNavIcons/StaffIcon.d.ts +2 -0
  43. package/dist/side-nav/assets/SideNavIcons/TravelIcon.d.ts +2 -0
  44. package/dist/side-nav/assets/SideNavIcons/index.d.ts +20 -0
  45. package/dist/side-nav/icon-map.d.ts +17 -0
  46. package/dist/side-nav/index.d.ts +5 -0
  47. package/dist/side-nav/mega-menu-icon-map.d.ts +20 -0
  48. package/dist/side-nav/mega-menu-layout.d.ts +9 -0
  49. package/dist/side-nav/mega-menu.test.d.ts +1 -0
  50. package/dist/side-nav/side-nav.test.d.ts +1 -0
  51. package/dist/side-nav.d.ts +1 -0
  52. package/dist/side-nav.js +1198 -0
  53. package/dist/state-Ns9O6R8g.js +12 -0
  54. package/dist/super-user-banner/SuperUserBanner.d.ts +43 -0
  55. package/dist/super-user-banner/SuperUserBanner.styles.d.ts +21 -0
  56. package/dist/super-user-banner/index.d.ts +2 -0
  57. package/dist/super-user-banner/super-user-banner.test.d.ts +1 -0
  58. package/dist/super-user-banner.d.ts +1 -0
  59. package/dist/super-user-banner.js +178 -0
  60. package/dist/top-bar/TopBar.d.ts +39 -0
  61. package/dist/top-bar/TopBar.styles.d.ts +1 -0
  62. package/dist/top-bar/Utils/helpers.d.ts +3 -0
  63. package/dist/top-bar/Utils/safe-url.d.ts +56 -0
  64. package/dist/top-bar/Utils/safe-url.test.d.ts +1 -0
  65. package/dist/top-bar/Utils/types.d.ts +50 -0
  66. package/dist/top-bar/assets/TopBarIcons/CheckIcon.d.ts +2 -0
  67. package/dist/top-bar/assets/TopBarIcons/CrossIcon.d.ts +2 -0
  68. package/dist/top-bar/assets/TopBarIcons/DropDownIcon.d.ts +2 -0
  69. package/dist/top-bar/assets/TopBarIcons/InfoIcon.d.ts +2 -0
  70. package/dist/top-bar/assets/TopBarIcons/OpenInNewIcon.d.ts +2 -0
  71. package/dist/top-bar/assets/TopBarIcons/index.d.ts +7 -0
  72. package/dist/top-bar/index.d.ts +3 -0
  73. package/dist/top-bar/top-bar.test.d.ts +1 -0
  74. package/dist/top-bar.d.ts +1 -0
  75. package/dist/top-bar.js +615 -0
  76. package/package.json +52 -0
package/README.md ADDED
@@ -0,0 +1,198 @@
1
+ # @campminder/components
2
+
3
+ The Campminder chrome: left nav, top bar, super-user banner. Lit web components,
4
+ composition-agnostic, rendering only what they are handed.
5
+
6
+ ## Why one package and not three
7
+
8
+ The lineage verdict on
9
+ [#25854](https://dev.azure.com/CampMinderLLC/CampMinder/_workitems/edit/25854) is explicit
10
+ that side-nav and top-bar are **"one shell, split across two packages by history, not by
11
+ design."** Porting them into three separate packages here would have carried that accident
12
+ forward permanently — three version numbers, three release decisions, and a consumer
13
+ installing three things to get one shell. Merging published packages later is a breaking
14
+ change for everyone; merging them now, before anything publishes, costs nothing.
15
+
16
+ Subpath entries keep the granularity that actually mattered:
17
+
18
+ ```ts
19
+ import '@campminder/components/side-nav'
20
+ import '@campminder/components/top-bar'
21
+ ```
22
+
23
+ A consumer that only wants the left nav does not pull the top bar's code.
24
+
25
+ ### Dependencies are added by the floor that imports them
26
+
27
+ This package declares `lit` and nothing else. `axios`, `@carbon/web-components` and
28
+ `@material/web` were carried over from the `core-js` manifests during the scaffold and
29
+ removed 2026-09-02: nothing in this package imported them — **and nothing in `core-js`'s
30
+ `apps/side-nav`, `apps/top-bar` or `apps/super-user-banner` imports them either**, so they
31
+ were dead weight upstream before they were dead weight here. They sat in `dependencies`,
32
+ not `devDependencies`, so every consumer would have installed two whole design systems to
33
+ render a menu.
34
+
35
+ If the Floor 7 top-bar port or the Floor 8 banner port needs one, the floor that writes
36
+ the `import` adds it back. Do not pre-declare a dependency for a port that has not
37
+ happened — an unused dependency in a published package is a cost paid by every consumer
38
+ for nothing.
39
+
40
+ ## Port sources
41
+
42
+ | Entry | Port from | ADO floor |
43
+ | ------------------- | ------------------------------------------------------ | ---------- |
44
+ | `side-nav` | `core-js/apps/side-nav` (`@campminder/side-nav` 1.5.0) | Floors 3–4 |
45
+ | `top-bar` | `core-js/apps/top-bar` (`@campminder/top-bar` 0.0.111) | Floor 7 |
46
+ | `super-user-banner` | `core-js/apps/super-user-banner` (0.8.6) | Floor 8 |
47
+
48
+ `core-js` keeps building its copies for its existing consumers. This is a copy, not a
49
+ move — Rule 2 of the verdict.
50
+
51
+ ## Strip on the way in
52
+
53
+ The port is the cheapest moment: there are no consumers here yet, so nothing needs to
54
+ stay backwards-compatible. This is choosing what not to copy, not refactoring live code.
55
+
56
+ **side-nav**
57
+
58
+ - **`BodyPaddingController`** — injects `html body { padding-left: 168px !important; }`
59
+ into `document.head`. The shell's grid owns that space now.
60
+ - **`initSearchIntegration()`** and its `setTimeout(setupSearchBarListener, 100)` re-arm —
61
+ polls the DOM forever in any composition without search.
62
+ - **`document.querySelector('search-bar')`** at `SideNav.ts:129` and `:477`.
63
+ - **`SIDENAV_STORAGE_KEY = 'isSideNavCollapsed'`** — a global key two shells on one origin
64
+ would share. The shell namespaces it per portal.
65
+ - **`src/data/data.ts`** — the base64 fixture. The tree is a property.
66
+
67
+ **top-bar** — ported Floor 7, 2026-09-08. This list is now a record, not a forecast.
68
+
69
+ - **The entire CDN script loader.** `loadWebComponents()`, `getCdnUrl()`,
70
+ `WEB_COMPONENTS_PATH`, `BASE_DOMAIN`, `FALLBACK_CDN_HOST`, the `searchReady` state and
71
+ the `webComponentsUrl` property. It appended a `<script type="module">` to
72
+ `document.head` pointed at a hardcoded `cdn.*.campminder.com` host, purely so that
73
+ `search-bar` would register itself. **The plan did not know about this** — it named the
74
+ registry probe and the render, not the script injection, which is the more serious
75
+ breach of the one rule. Making search a slot removed the whole apparatus.
76
+ - **`customElements.get('search-bar')`** at `TopBar.ts:58`, the `<search-bar>` render at
77
+ `:333`, and the `enableSearchWidget` property. Search is a named
78
+ **`<slot name="search">`**, rendered unconditionally; a shell that wants it fills it,
79
+ and nothing polls for it. `search-bar` is therefore **not ported** in Phase 1.
80
+ - **All search-expansion state** — `searchExpanded`, `handleSearchExpanded`,
81
+ `handleSearchClose`, `renderSearchCloseButton()`, and two
82
+ `document.dispatchEvent(new CustomEvent('search-expanded'))` calls that existed so
83
+ side-nav could hear them. Phase 1 ships no search, so the state could never become
84
+ true. **Owed when search is slotted in:** upstream collapsed the page-info block and
85
+ the user dropdown while search was expanded on mobile. Reintroduce that as something
86
+ the slotted element drives, not as a document-level broadcast.
87
+ - **All positioning.** `topOffsetPx` and `leftOffsetPx`, the `isSideNavCollapsed` state,
88
+ the `calculatedLeftOffset` getter, `syncWithSideNavState()`, `initSideNavIntegration()`,
89
+ `handleSideNavToggle`, and the import of `shared/navigation-constants.ts`
90
+ (`SIDENAV_ANIMATION`, `SIDENAV_DIMENSIONS`, `SIDENAV_STORAGE_KEY`,
91
+ `SIDENAV_TOGGLE_EVENT`). `LAYOUT` in `@campminder/campminder-shell` is the single
92
+ source and the shell's grid applies it. The rendered `<section>` carries no `style`
93
+ attribute at all. Reading side-nav's `localStorage` key was querying for a sibling.
94
+ - **All JS device detection.** `Utils/device-css-classes.ts` and
95
+ `Utils/mobile-detection.ts` are not here. Responsiveness is the media queries that were
96
+ already in `styles.ts` alongside the class-driven duplicates. See
97
+ `TopBar.styles.ts`'s comment on the page-title breakpoint for the one behaviour-neutral
98
+ simplification this exposed.
99
+ - **`data/data.ts`** — the base64 fixture, and `index.css` — page-level `:root` and `body`
100
+ styling, which is a host concern.
101
+ - **`assets/Icons/CloseIcon.ts`** — copied with the other five, then removed: its only
102
+ consumer was the search close button. side-nav carries its own `CloseIcon`, so nothing
103
+ lost it. The remaining five live in `assets/TopBarIcons/` behind a barrel, named
104
+ `XSvgIcon`, matching side-nav's `assets/SideNavIcons/` rather than upstream's
105
+ unprefixed `assets/Icons/` with one lowercase export.
106
+ - Carbon and Material: **nothing to strip.** Verified 2026-09-08 that no file under
107
+ `core-js/apps/top-bar/src` imports `@carbon/web-components` or `@material/web`. The
108
+ standing note that retiring them is separate-and-optional still holds for the estate,
109
+ but this port added neither, so this package still declares `lit` and nothing else.
110
+ - **`@import url(fonts.googleapis.com/...)`** — missed by this floor and stripped on
111
+ Floor 8, matching what side-nav had already done on Floor 4. See the banner section
112
+ below for the measurement.
113
+
114
+ - **Seven upstream security patterns, refused rather than copied** — `link.url` in `href`
115
+ and in `window.open()`; `popup_properties` passed to `window.open()` verbatim, and then
116
+ its UI-suppressing features; `target="_blank"` with no `rel`, and the popup path with no
117
+ `noopener`; `feature.value` through lit's `unsafeHTML`; and `icon_url` / `profile_url` /
118
+ `logo_url` reaching `<img src>` unguarded. **Why each one is refused, and the trust
119
+ boundary that makes them live rather than theoretical, is stated once — in
120
+ [`Utils/safe-url.ts`](src/top-bar/Utils/safe-url.ts)**, with 46 unit tests beside it.
121
+ Floor 8 was expected to inherit the question and did not — see below.
122
+
123
+ **Old → new names**, since this element is consumed by tag name:
124
+
125
+ | Upstream | Here |
126
+ | ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
127
+ | `<top-bar>` | `<cm-top-bar>` |
128
+ | `data` (base64 attribute) | unchanged — still `data`, still base64-decoded |
129
+ | `user` | **gone** — its only consumer was `<search-bar>`'s config, which the host now supplies to whatever it slots |
130
+ | `top-offset-px`, `left-offset-px`, `enable-search-widget`, `web-components-url` | **gone** |
131
+ | — | `<slot name="search">` |
132
+
133
+ **super-user-banner**
134
+
135
+ Renaming the element and its attributes **is** allowed. It is emitted as raw markup from
136
+ `ReactAppController.cs:163`, so WebForms consumes it with no package edge — but that
137
+ constrains the Phase 2 _cutover_, not this port: per Rule 2 this is a copy-forward and
138
+ `core-js` keeps building the copy that page actually loads. Ruled 2026-09-02, reversing
139
+ the ban that used to sit here.
140
+
141
+ - **`position: fixed; top: 0; left: 0; width: 100%`**, and **the `ResizeObserver`** that
142
+ compared `scrollWidth` against `window.innerWidth` to toggle an `overflowed` class
143
+ (plus the shadow-root `id` that existed only for its `getElementById`). Both went
144
+ because the shell owns layout. **Why each one is refused, and the latent defect the
145
+ observer was carrying, is stated once — in
146
+ [`SuperUserBanner.styles.ts`](src/super-user-banner/SuperUserBanner.styles.ts)**, which
147
+ is where the replacement lives. Worth knowing at this level: the fixed positioning is
148
+ why `ReactAppController` has to emit a `hasSuperUserBanner` class and prepend container
149
+ markup at all, so deleting four declarations here deletes the reason for two host-side
150
+ workarounds in Phase 2.
151
+ - **`@import url(fonts.googleapis.com/…)`** and **`index.css`** (page-level `:root` and
152
+ `body` styling, a host concern). The `@import` was also still in
153
+ [`TopBar.styles.ts`](src/top-bar/TopBar.styles.ts) — side-nav dropped it on Floor 4,
154
+ Floor 7 missed it, Floor 8 caught it. That file carries the reasoning and the
155
+ measurement.
156
+
157
+ **No URL guard was needed, and that is a finding.** `top-bar`'s
158
+ [`Utils/safe-url.ts`](src/top-bar/Utils/safe-url.ts) was left under `top-bar/` on the
159
+ argument that Floor 8 would be the second caller and would show the right shape. It is
160
+ not: all three of the banner's values are database-sourced and cross the same untrusted
161
+ boundary, but none is a URL and none is markup, so lit escaping text is the entire
162
+ defence. A test asserts the component renders no `a`, `img`, `iframe`, `source` or
163
+ `script` at all. Promoting the guards out of `top-bar/` therefore still has exactly one
164
+ caller and still is not justified.
165
+
166
+ **It did need two fixes of a different kind, both found in review.** A banner whose job
167
+ is to make impersonation and environment unmistakable can fail at that job without any
168
+ injection being involved:
169
+
170
+ - Upstream's badge **failed open** — anything that was not exactly `staging` got the calm
171
+ `local` badge, including the literal `production` and `undefined` that the host's
172
+ `GetEnvironmentName()` really returns. A super-user impersonating in production saw the
173
+ same badge as a laptop. There are now three badges and the unrecognised case is loud;
174
+ `environmentBadgeClass` in
175
+ [`SuperUserBanner.ts`](src/super-user-banner/SuperUserBanner.ts) carries the reasoning.
176
+ - The port had **introduced** a scroll container (`overflow-x: auto`) and hidden its
177
+ scrollbar, with the badge as the last flex child — so a merely long client name pushed
178
+ the badge out of view with no affordance, on a section with no `tabindex`. Upstream had
179
+ no `overflow` rule at all, so this one was a regression, not an inheritance. The names
180
+ now truncate and the scrollbar is no longer suppressed.
181
+
182
+ **Old → new names**, since this element is consumed by tag name:
183
+
184
+ | Upstream | Here |
185
+ | ---------------------------------------------------- | ------------------------------------------------ |
186
+ | `<super-user-banner>` | `<cm-super-user-banner>` |
187
+ | `superUser=` (parsed as `superuser`) | `super-user` |
188
+ | `activeEnvironment=` (parsed as `activeenvironment`) | `active-environment` |
189
+ | `client=` | unchanged — `client` |
190
+ | `id="super-user-banner"` in the shadow root | **gone** — existed only for the `ResizeObserver` |
191
+
192
+ The old spellings are ignored, not aliased, and a test asserts that — so the cutover
193
+ fails loudly rather than silently rendering an empty banner.
194
+
195
+ ## The rule
196
+
197
+ > Components render themselves; the shell owns layout. No component may touch
198
+ > `document.body`, `document.head`, or query for a sibling.
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The menu-tree structural types are deliberately NOT re-exported here.
3
+ *
4
+ * They lived in this package's `menu-tree.ts` until 2026-09-08 (Floor 5B), when the model
5
+ * moved to `nav` and became `@campminder/menu-tree` on public npm — ADR **D4** assigns the
6
+ * menu data model to the `nav` Domain System, and **D5** wanted the schema published rather
7
+ * than copied. This package now *consumes* those types the way any other host does; it does
8
+ * not own them and does not republish them.
9
+ *
10
+ * Import them from `@campminder/menu-tree` directly. Re-exporting them here would keep alive
11
+ * exactly the framing D4 removed — that the rendering package owns the model — and would give
12
+ * consumers two import paths for one type.
13
+ */
14
+ export * from './side-nav';
15
+ export * from './top-bar';
16
+ export * from './super-user-banner';
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ import { MegaMenu as o, SideNav as a, hasIconFor as n, hasMegaMenuIconFor as p } from "./side-nav.js";
2
+ import { TopBar as f } from "./top-bar.js";
3
+ import { SuperUserBanner as s } from "./super-user-banner.js";
4
+ export {
5
+ o as MegaMenu,
6
+ a as SideNav,
7
+ s as SuperUserBanner,
8
+ f as TopBar,
9
+ n as hasIconFor,
10
+ p as hasMegaMenuIconFor
11
+ };
@@ -0,0 +1 @@
1
+ export {};