@helloimjolopez-pathway/pathway-components 0.8.1 → 0.9.1

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.
package/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # Pathway Components
2
+
3
+ The Pathway Design System's UI components, for Ministry Brands products. Each one ships two ways from this one package:
4
+
5
+ - **Web components** (custom elements): one script tag, then plain HTML tags. Works in Blazor, Razor, Angular, Vue, React, jQuery or plain HTML.
6
+ - **React source** (`.jsx`): for React apps that want the components as React components.
7
+
8
+ The same components are on NuGet as [`Pathway.Components`](https://www.nuget.org/packages/Pathway.Components), at the same version.
9
+
10
+ | | |
11
+ |---|---|
12
+ | Storybook (every component, live, with its tokens) | https://potential-bassoon-voz982n.pages.github.io/storybook/ (sign in with your Ministry Brands GitHub account) |
13
+ | Source repo | https://github.com/ministry-brands-llc/jolopez-collab-pathwayds (visible to everyone in the Ministry Brands GitHub organisation) |
14
+ | Developer guide, all stacks | [`docs/consuming-from-any-stack.md`](https://github.com/ministry-brands-llc/jolopez-collab-pathwayds/blob/main/docs/consuming-from-any-stack.md) |
15
+ | Tokens package (required) | [`@helloimjolopez-pathway/pathway-tokens`](https://www.npmjs.com/package/@helloimjolopez-pathway/pathway-tokens) · NuGet [`Pathway.DesignTokens`](https://www.nuget.org/packages/Pathway.DesignTokens) |
16
+
17
+ ## What is in it
18
+
19
+ | Component | Web component | React import (`@helloimjolopez-pathway/pathway-components/…`) |
20
+ |---|---|---|
21
+ | NavShell (TopNav + SideNav + page area) | `<pathway-nav-shell>` | `…/nav-shell` → `NavShell` |
22
+ | TopNav | `<pathway-top-nav>` | `…/top-nav` → `TopNav` |
23
+ | SideNav | `<pathway-sidenav>` | `…/sidenav` → `SideNav` |
24
+ | Accounting side nav | `<pathway-accounting-sidenav>` | `…/accounting-side-nav` → `AccountingSideNav` |
25
+ | Page Template | `<pathway-page-template>` | `…/page-template` → `PageTemplate` |
26
+ | Button | `<pathway-button>` | `…/button` → `Button` |
27
+ | Badge | `<pathway-badge>` | `…/badge` → `Badge` |
28
+ | Tooltip | `<pathway-tooltip>` | `…/tooltip` → `Tooltip` |
29
+ | Toggle Button | `<pathway-toggle-button>` | `…/toggle-button` → `ToggleButton` |
30
+ | Radio | `<pathway-radio-group>` | `…/radio` → `RadioGroup` |
31
+ | BottomSheet | `<pathway-bottom-sheet>` | `…/bottom-sheet` → `BottomSheet` |
32
+ | Scroll-to-Enable | `<pathway-scroll-to-enable>` | `…/scroll-to-enable` → `ScrollToEnable` |
33
+ | Dropdown | (React only) | `…/dropdown` → `Dropdown` |
34
+
35
+ The web components are in three bundles under `dist/`:
36
+
37
+ | File | Registers |
38
+ |---|---|
39
+ | `dist/pathway-components.js` | every tag above |
40
+ | `dist/pathway-sidenav.js` | `<pathway-sidenav>` only |
41
+ | `dist/pathway-accounting-sidenav.js` | `<pathway-accounting-sidenav>` only |
42
+
43
+ Each bundle is self-contained: React is inside it, so the host page needs no React.
44
+
45
+ ## Set up a page (any stack)
46
+
47
+ Three things go on the host page, once.
48
+
49
+ ```html
50
+ <!-- 1. Tokens: every colour, size and font in the components is a CSS custom property from this file -->
51
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@helloimjolopez-pathway/pathway-tokens@12/dist/index.css">
52
+
53
+ <!-- 2. Fonts -->
54
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Red+Hat+Text:wght@300;400;500;600;700&display=swap">
55
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200">
56
+
57
+ <!-- 3. The components -->
58
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@helloimjolopez-pathway/pathway-components@0.9/dist/pathway-components.js"></script>
59
+ ```
60
+
61
+ Pin an exact version in production (`@0.9.1`, not `@0.9`).
62
+
63
+ From npm instead of the CDN:
64
+
65
+ ```bash
66
+ npm install @helloimjolopez-pathway/pathway-components @helloimjolopez-pathway/pathway-tokens
67
+ ```
68
+
69
+ ```js
70
+ import "@helloimjolopez-pathway/pathway-tokens/css";
71
+ import "@helloimjolopez-pathway/pathway-components/elements"; // registers every tag
72
+ ```
73
+
74
+ From NuGet (.NET): the files are static web assets, so a Razor or Blazor page references them as
75
+
76
+ ```html
77
+ <link rel="stylesheet" href="_content/Pathway.DesignTokens/index.css">
78
+ <script type="module" src="_content/Pathway.Components/pathway-components.js"></script>
79
+ ```
80
+
81
+ **Light and Dark, Amplify and NewCo.** Dark is `data-theme="dark"` on any element (it applies to everything inside). NewCo is `data-brand="newco"` plus the NewCo layer: `@helloimjolopez-pathway/pathway-tokens@12/dist/brands/newco/index.css` after the Amplify one.
82
+
83
+ ## The Accounting nav shell
84
+
85
+ The whole Accounting frame (TopNav without search or actions, the Accounting side nav with the Financials mark and application switcher, and the page area) is one tag:
86
+
87
+ ```html
88
+ <pathway-nav-shell app-switcher active-app-id="financials-home" active-nav-id="dashboard">
89
+ <main>Your page</main>
90
+ </pathway-nav-shell>
91
+ ```
92
+
93
+ Give it your real data as JavaScript properties, and listen for its events:
94
+
95
+ ```js
96
+ const shell = document.querySelector("pathway-nav-shell");
97
+
98
+ shell.org = { id: "org-1", name: "Grace Community Church", campus: "Knoxville", initials: "GC" };
99
+ shell.orgs = [ /* same shape, for the org switcher */ ];
100
+ shell.user = { name: "Jo Lopez", initials: "JL", email: "jo@example.org" };
101
+ // Optional: replace the built-in Accounting applications and their navs.
102
+ // shell.appSwitcher = { label, icon, home: { id, label, navItems }, apps: [ { id, label, navItems } ] };
103
+
104
+ shell.addEventListener("pathway-navigate", (e) => router.go(e.detail.id)); // a nav item was chosen
105
+ shell.addEventListener("pathway-app-select", (e) => loadApp(e.detail.id)); // an application was chosen
106
+ shell.addEventListener("pathway-org-select", (e) => switchOrg(e.detail.id));
107
+ shell.addEventListener("pathway-module-select", (e) => openModule(e.detail.id));
108
+ ```
109
+
110
+ Attributes on `<pathway-nav-shell>`:
111
+
112
+ | Attribute | What it does |
113
+ |---|---|
114
+ | `app-switcher` | Turns the side nav's application switcher on. With it on, TopNav hides search and actions unless you say otherwise |
115
+ | `active-app-id` | The current application; updated when someone picks another |
116
+ | `active-nav-id` | The current page in the side nav; updated on navigation |
117
+ | `active-module-id` | The current module in TopNav (default `home`) |
118
+ | `default-collapsed` | Start with the side nav as the 72 px rail on desktop |
119
+ | `hide-search`, `hide-actions` | Hide TopNav's search, or its action icons |
120
+
121
+ Other properties: `modules`, `navItems` or `navSections` (for a non-Accounting module). Slots: the unnamed slot is the page; `nav-header-end` and `nav-footer` add to the side nav.
122
+
123
+ Only the side nav? Use `<pathway-accounting-sidenav active-app-id="financials-home">`, with the same `appSwitcher` property and the `pathway-app-select`, `pathway-navigate` and `pathway-collapse-change` events.
124
+
125
+ ## React
126
+
127
+ ```jsx
128
+ import "@helloimjolopez-pathway/pathway-tokens/css";
129
+ import { NavShell } from "@helloimjolopez-pathway/pathway-components/nav-shell";
130
+ import { ACCOUNTING_APP_SWITCHER } from "@helloimjolopez-pathway/pathway-components/accounting-side-nav";
131
+
132
+ <NavShell appSwitcher={ACCOUNTING_APP_SWITCHER} activeNavId="dashboard"
133
+ org={org} orgs={orgs} user={user}
134
+ onNavigate={(id) => go(id)} onOrgSelect={(id) => switchOrg(id)}>
135
+ <YourPage />
136
+ </NavShell>
137
+ ```
138
+
139
+ `ACCOUNTING_APP_SWITCHER` is the built-in Accounting data (applications and their navs); pass your own object of the same shape to change it.
140
+
141
+ React and React DOM 17 or later are peer dependencies. The `.jsx` files are shipped as source, so your bundler compiles them (Vite, webpack with Babel, Next.js and esbuild all do by default).
142
+
143
+ ## Taking it into your product
144
+
145
+ - **Depend on the package and pin the version.** You get fixes by bumping it, and nothing changes under you until you do. Every release is in the repo's history and on npm.
146
+ - **What your review will find:** the web component bundles contain React, React DOM and `react-aria-components`; the components make no network calls and store nothing; the only external requests are the fonts above, which you can self-host. Licence: CC0-1.0.
147
+ - **Accessibility is built in and should still be tested in your product:** keyboard focus rings, focus returning to triggers, Esc to close, 44 px touch targets, and hidden nav rows kept out of the tab order.
148
+ - **Report a problem or ask for a change** in the repo, where the component's spec (`components/<name>/<name>-spec.md`) records how it is meant to behave.
@@ -13,7 +13,7 @@
13
13
  import React, { useState, useEffect, useCallback } from "react";
14
14
  import TopNav, { DEFAULT_MODULES } from "../top-nav/top-nav.jsx";
15
15
  import { SideNav } from "../sidenav/sidenav.jsx";
16
- import { ModuleMark } from "../module-icon/module-icon.jsx";
16
+ import { moduleHeader } from "../sidenav/accounting-side-nav.jsx";
17
17
 
18
18
  // ─── CONSTANTS ────────────────────────────────────────────────────────────────
19
19
  const TOPNAV_H = 60; // px — spec §9.3 rule 1
@@ -292,7 +292,7 @@ export function NavShell({
292
292
  hideCollapseButton={isTablet || isMobile}
293
293
  headerEnd={navHeaderEnd}
294
294
  footer={navFooter}
295
- headerStart={appSwitcher && appSwitcher.moduleMark ? <ModuleMark {...appSwitcher.moduleMark} /> : null}
295
+ headerStart={moduleHeader(appSwitcher)}
296
296
  appSwitcher={appSwitcher ? { ...appSwitcher, activeId: appActive, onSelect: handleAppSelect,
297
297
  // Mobile opens the menu as a BottomSheet (Figma 40016737:141824).
298
298
  presentation: isMobile ? "sheet" : "popover" } : null}
@@ -29,7 +29,8 @@
29
29
  */
30
30
  import React, { useEffect, useState } from "react";
31
31
  import { SideNav } from "./sidenav.jsx";
32
- import { ModuleMark } from "../module-icon/module-icon.jsx";
32
+ import { SideNavHeaderLabel } from "./sidenav.jsx";
33
+ import { ModuleIcon } from "../module-icon/module-icon.jsx";
33
34
 
34
35
  // The Accounting module's applications, each with its own side nav. NavShell's
35
36
  // `app-switcher` and <pathway-accounting-sidenav> fall back to this when no
@@ -49,7 +50,8 @@ const APP_NAV_STANDARD = ["Dashboard", "Enter", "Manage", "View", "Reports", "Mo
49
50
  export const ACCOUNTING_APP_SWITCHER = {
50
51
  label: "Financials Home",
51
52
  icon: "apps",
52
- // The module's mark in the SideNav header (Figma Module.Mark, Accounting).
53
+ // The module's label in the SideNav header: Figma SideNav.Header.Label with
54
+ // the Accounting icon chip (40016737:141726, 2026-11-12).
53
55
  moduleMark: { module: "accounting", label: "Financials" },
54
56
  // The Accounting TopNav is TopNav.Global with Search and Actions hidden
55
57
  // (Figma 40007879:18072): module switcher, org switcher and profile only.
@@ -66,6 +68,15 @@ export const ACCOUNTING_APP_SWITCHER = {
66
68
  { id: "purchasing-management", label: "Purchasing Management", navItems: accountingNav("pm", ["Dashboard", "Enter", "Manage", "Reports", "Modify", "Help"]) },
67
69
  ],
68
70
  };
71
+ // The nav header's start slot for a module: SideNav.Header.Label with the
72
+ // module's icon chip. NavShell uses the same, so the two headers never differ.
73
+ export function moduleHeader(appSwitcher) {
74
+ const m = appSwitcher && appSwitcher.moduleMark;
75
+ if (!m) return null;
76
+ return <SideNavHeaderLabel label={m.label}
77
+ icon={m.module ? <ModuleIcon module={m.module} size={14} color="mono" /> : null} />;
78
+ }
79
+
69
80
  const MOBILE_MAX = 767; // below 768, as NavShell's breakpoint
70
81
 
71
82
  function useIsMobile() {
@@ -107,7 +118,7 @@ export function AccountingSideNav({
107
118
 
108
119
  return (
109
120
  <SideNav key={current && current.id} items={items} activeId={active} onNavigate={navigate}
110
- headerStart={appSwitcher.moduleMark ? <ModuleMark {...appSwitcher.moduleMark} /> : null}
121
+ headerStart={moduleHeader(appSwitcher)}
111
122
  appSwitcher={{ ...appSwitcher, activeId: current && current.id, onSelect: selectApp, presentation: mode }}
112
123
  {...rest} />
113
124
  );
@@ -747,6 +747,38 @@ export function AppSwitcher({ label: fallbackLabel, icon = "apps", home, apps =
747
747
  );
748
748
  }
749
749
 
750
+ // ─── SideNavHeaderLabel ───────────────────────────────────────────────────────
751
+ // Figma SideNav.Header.Label (40018260:4928), read 2026-11-12 from the
752
+ // Accounting Module Nav Shell (40016737:141726). The label at the start of the
753
+ // nav header, beside the collapse control. Text only for a pillar rail; with
754
+ // `icon`, the module's icon chip sits before the name (a module rail, such as
755
+ // Accounting's Financials).
756
+ // IconChip 28, CornerRadius/Base, Fill/Static/Brand/Strong, clips
757
+ // icon the module glyph at 14, Fill/Static/Neutral/Mono
758
+ // label Heading/Local/Base/Semibold: 14 / 600 / 18 / 0.3,
759
+ // Foreground/Static/Neutral/Strong, truncates; gap Gap/XXTight
760
+ // width 160 in Figma: here it fills the row up to the collapse control
761
+ export function SideNavHeaderLabel({ label, icon = null }) {
762
+ return (
763
+ <span style={{ display: "flex", alignItems: "center", minWidth: 0, flex: 1,
764
+ gap: "var(--semantic-layout-units-gap-xxtight)" }}>
765
+ {icon && (
766
+ <span aria-hidden="true" style={{ width: 28, height: 28, flexShrink: 0, overflow: "hidden",
767
+ display: "inline-flex", alignItems: "center", justifyContent: "center",
768
+ borderRadius: "var(--semantic-layout-units-cornerradius-base)",
769
+ background: c("fill-static-brand-strong"), color: c("fill-static-neutral-mono") }}>
770
+ {icon}
771
+ </span>
772
+ )}
773
+ <span style={{ flex: 1, minWidth: 0, fontFamily: "var(--semantic-type-family-brand)",
774
+ fontWeight: "var(--semantic-type-weight-semibold)", fontSize: "var(--semantic-type-font-size-s)",
775
+ lineHeight: "var(--semantic-type-line-height-s-tight)", letterSpacing: "var(--semantic-type-letter-spacing-spacious)",
776
+ color: c("foreground-static-neutral-strong"),
777
+ whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{label}</span>
778
+ </span>
779
+ );
780
+ }
781
+
750
782
  // ─── CollapseButton ───────────────────────────────────────────────────────────
751
783
  // Redesigned in Figma (2026-05-12): now uses Slot.RowStart (Module.Mark icon)
752
784
  // + Slot.RowEnd (Action Icon). The action icon uses Foreground/Action/Secondary/Rest