@gsa-tts/graymatter-ui 0.2.0 → 0.2.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/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "@gsa-tts/graymatter-ui",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
8
  "exports": {
9
9
  ".": "./index.ts",
10
+ "./graymatter-ui.js": "./dist/graymatter-ui.js",
11
+ "./graymatter-ui.umd.cjs": "./dist/graymatter-ui.umd.cjs",
10
12
  "./stores/navigationStore": "./src/stores/navigationStore.ts",
11
13
  "./stores/subNavReadyStore": "./src/stores/subNavReadyStore.ts",
12
14
  "./assets/*": "./assets/*",
@@ -62,7 +64,7 @@
62
64
  "@uswds/uswds": "3.12.0",
63
65
  "astro": "^5.11.0",
64
66
  "slugify": "^1.6.6",
65
- "@gsa-tts/graymatter-style-tokens": "0.2.0"
67
+ "@gsa-tts/graymatter-style-tokens": "0.2.1"
66
68
  },
67
69
  "scripts": {
68
70
  "build": "gulp update && vite build",
@@ -21,7 +21,7 @@
21
21
  import { getAppUrls } from '../utils/getAppUrls';
22
22
  const urls = getAppUrls();
23
23
 
24
- const { ssrSelectedItem = undefined, onNavToggle = undefined, onNavItemClick = undefined, onProfileClick = undefined, children } = $props();
24
+ const { ssrSelectedItem = undefined, onNavToggle = undefined, onNavItemClick = undefined, onProfileClick = undefined, children, apiUrl, chatUrl, consoleUrl, discoverUrl } = $props();
25
25
 
26
26
  let hydrated = false;
27
27
  let navContainer: HTMLDivElement;
@@ -189,16 +189,18 @@
189
189
  onclick={toggleNav}
190
190
  onmouseenter={() => (menuButtonHovered = true)}
191
191
  onmouseleave={() => (menuButtonHovered = false)}
192
- aria-label="Open navigation menu"
192
+ onfocus={() => (menuButtonHovered = true)}
193
+ onblur={() => (menuButtonHovered = false)}
194
+ aria-label={sideNavExpanded() ? 'Close navigation menu' : 'Open navigation menu'}
195
+ aria-expanded={sideNavExpanded() ? 'true' : 'false'}
193
196
  >
194
- <span class="sr-only">Open navigation menu</span>
195
197
  <div class="icon-container">
196
198
  <div class="icon-wrapper">
197
199
  <MenuIcon
198
- isHovered={menuBtnHovered()}
199
- isDisabled={menuBtnDisabled()}
200
- isOpen={menuBtnOpen()}
201
- />
200
+ isHovered={menuBtnHovered()}
201
+ isDisabled={menuBtnDisabled()}
202
+ isOpen={menuBtnOpen()}
203
+ />
202
204
  </div>
203
205
  </div>
204
206
  </button>
@@ -206,16 +208,16 @@
206
208
 
207
209
  <div class="nav-items" part="nav-items">
208
210
  <a
209
- href={urls.discover}
211
+ href={discoverUrl || urls.discover}
210
212
  class="nav-item discover-item"
211
213
  class:selected={isNavSelected('discover')}
212
214
  class:hovered={isNavHovered('discover')}
213
- role="button"
214
- tabindex="0"
215
215
  onclick={() => handleNavItemClick('discover')}
216
216
  onkeydown={e => handleKeyDown(e, 'discover')}
217
217
  onmouseenter={() => (hoveredItem = 'discover')}
218
218
  onmouseleave={() => (hoveredItem = null)}
219
+ aria-current={isNavSelected('discover') ? 'page' : undefined}
220
+ part="discover-item"
219
221
  >
220
222
  <div class="icon-container">
221
223
  <div class="icon-wrapper">
@@ -225,16 +227,16 @@
225
227
  <span class="nav-text">Discover</span>
226
228
  </a>
227
229
  <a
228
- href={urls.chat}
230
+ href={chatUrl || urls.chat}
229
231
  class="nav-item chat-item"
230
232
  class:selected={isNavSelected('chat')}
231
233
  class:hovered={isNavHovered('chat')}
232
- role="button"
233
- tabindex="0"
234
234
  onclick={() => handleNavItemClick('chat')}
235
235
  onkeydown={e => handleKeyDown(e, 'chat')}
236
236
  onmouseenter={() => (hoveredItem = 'chat')}
237
237
  onmouseleave={() => (hoveredItem = null)}
238
+ aria-current={isNavSelected('chat') ? 'page' : undefined}
239
+ part="chat-item"
238
240
  >
239
241
  <div class="icon-container">
240
242
  <div class="icon-wrapper">
@@ -245,16 +247,16 @@
245
247
  </a>
246
248
 
247
249
  <a
248
- href={urls.console}
250
+ href={consoleUrl || urls.console}
249
251
  class="nav-item console-item"
250
252
  class:selected={isNavSelected('console')}
251
253
  class:hovered={isNavHovered('console')}
252
- role="button"
253
- tabindex="0"
254
254
  onclick={() => handleNavItemClick('console')}
255
255
  onkeydown={e => handleKeyDown(e, 'console')}
256
256
  onmouseenter={() => (hoveredItem = 'console')}
257
257
  onmouseleave={() => (hoveredItem = null)}
258
+ aria-current={isNavSelected('console') ? 'page' : undefined}
259
+ part="console-item"
258
260
  >
259
261
  <div class="icon-container">
260
262
  <div class="icon-wrapper">
@@ -265,16 +267,16 @@
265
267
  </a>
266
268
 
267
269
  <a
268
- href={urls.api}
270
+ href={apiUrl || urls.api}
269
271
  class="nav-item api-item"
270
272
  class:selected={isNavSelected('api')}
271
273
  class:hovered={isNavHovered('api')}
272
- role="button"
273
- tabindex="0"
274
274
  onclick={() => handleNavItemClick('api')}
275
275
  onkeydown={e => handleKeyDown(e, 'api')}
276
276
  onmouseenter={() => (hoveredItem = 'api')}
277
277
  onmouseleave={() => (hoveredItem = null)}
278
+ aria-current={isNavSelected('api') ? 'page' : undefined}
279
+ part="api-item"
278
280
  >
279
281
  <div class="icon-container">
280
282
  <div class="icon-wrapper">
@@ -342,18 +344,6 @@
342
344
  height: var(--ai-size-72);
343
345
  }
344
346
 
345
- .sr-only {
346
- position: absolute;
347
- width: 1px;
348
- height: 1px;
349
- padding: 0;
350
- margin: -1px;
351
- overflow: hidden;
352
- clip: rect(0, 0, 0, 0);
353
- border: 0;
354
- white-space: nowrap;
355
- }
356
-
357
347
  .user-profile-container,
358
348
  .user-profile-icon {
359
349
  display: flex;
@@ -6,7 +6,16 @@
6
6
  import DesktopSideNav from './DesktopSideNav.svelte';
7
7
 
8
8
  // Props for the web component
9
- let { ssrSelectedItem, onNavToggle, onNavItemClick, onProfileClick } = $props();
9
+ let {
10
+ ssrSelectedItem,
11
+ onNavToggle,
12
+ onNavItemClick,
13
+ onProfileClick,
14
+ apiUrl,
15
+ chatUrl,
16
+ consoleUrl,
17
+ discoverUrl
18
+ } = $props();
10
19
  </script>
11
20
 
12
21
  <DesktopSideNav
@@ -14,6 +23,10 @@
14
23
  {onNavToggle}
15
24
  {onNavItemClick}
16
25
  {onProfileClick}
26
+ {apiUrl}
27
+ {chatUrl}
28
+ {consoleUrl}
29
+ {discoverUrl}
17
30
  >
18
31
  <slot />
19
32
  </DesktopSideNav>
@@ -6,16 +6,23 @@
6
6
  import ConsoleIcon from './icons/ConsoleIcon.svelte';
7
7
  import ApiIcon from './icons/ApiIcon.svelte';
8
8
  import { getAppUrls } from '../utils/getAppUrls';
9
+ import slugify from "slugify";
9
10
 
10
- export let onNavItemClick: ((data: { item: NavigationItem; expanded: boolean }) => void) | undefined = undefined;
11
+ let {onNavItemClick, apiUrl, chatUrl, consoleUrl, discoverUrl}: {
12
+ onNavItemClick?: (data: { item: NavigationItem; expanded: boolean }) => void,
13
+ apiUrl?: string,
14
+ chatUrl?: string,
15
+ consoleUrl?: string,
16
+ discoverUrl?: string,
17
+ } = $props();
11
18
 
12
19
  const urls = getAppUrls();
13
20
 
14
21
  const navItems = [
15
- { id: 'discover', label: 'Discover', icon: DiscoverIcon, href: urls.discover },
16
- { id: 'chat', label: 'Chat', icon: ChatIcon, href: urls.chat },
17
- { id: 'console', label: 'Console', icon: ConsoleIcon, href: urls.console },
18
- { id: 'api', label: 'API', icon: ApiIcon, href: urls.api }
22
+ { id: 'discover', label: 'Discover', icon: DiscoverIcon, href: discoverUrl || urls.discover },
23
+ { id: 'chat', label: 'Chat', icon: ChatIcon, href: chatUrl || urls.chat },
24
+ { id: 'console', label: 'Console', icon: ConsoleIcon, href: consoleUrl || urls.console },
25
+ { id: 'api', label: 'API', icon: ApiIcon, href: apiUrl || urls.api }
19
26
  ] as const;
20
27
 
21
28
  function handleNavItemClick(item: NavigationItem) {
@@ -84,12 +91,11 @@
84
91
  href={navItem.href}
85
92
  class="nav-item"
86
93
  class:active={$selectedItem === navItem.id}
87
- on:click={() => handleNavItemClick(navItem.id)}
88
- part="nav-item"
94
+ onclick={() => handleNavItemClick(navItem.id)}
95
+ part={`${slugify(navItem.id)}-item`}
89
96
  >
90
97
  <div class="icon">
91
- <svelte:component
92
- this={navItem.icon}
98
+ <navItem.icon
93
99
  isSelected={$selectedItem === navItem.id}
94
100
  />
95
101
  </div>
@@ -6,9 +6,13 @@
6
6
  import MobileBottomNav from './MobileBottomNav.svelte';
7
7
 
8
8
  // Props for the web component
9
- let { onNavItemClick } = $props();
9
+ let { onNavItemClick, apiUrl, chatUrl, consoleUrl, discoverUrl } = $props();
10
10
  </script>
11
11
 
12
12
  <MobileBottomNav
13
13
  {onNavItemClick}
14
+ {apiUrl}
15
+ {chatUrl}
16
+ {consoleUrl}
17
+ {discoverUrl}
14
18
  />
@@ -77,7 +77,7 @@
77
77
  class:disabled={$isMenuButtonDisabled}
78
78
  disabled={$isMenuButtonDisabled}
79
79
  on:click={toggleMobileNav}
80
- aria-label="Toggle navigation menu"
80
+ aria-label="Open navigation menu"
81
81
  part="menu-button"
82
82
  >
83
83
  <MenuIcon />
@@ -2,7 +2,6 @@
2
2
  import BaseLayout from '@gsa-tts/graymatter-ui/layouts/BaseLayout.astro';
3
3
  import '../styles/base/global.css';
4
4
  import { getPageTitle } from '@gsa-tts/graymatter-ui/helpers';
5
- import UsaBanner from '../components/UsaBanner.astro';
6
5
  import DesktopSideNav from '../components/DesktopSideNav.svelte';
7
6
  import DesktopHeader from '../components/DesktopHeader.svelte';
8
7
  import MobileTopNav from '../components/MobileTopNav.svelte';
@@ -17,9 +16,6 @@ const title = getPageTitle(Astro);
17
16
 
18
17
  <BaseLayout title={title} gtmID={gtmID}>
19
18
  <div class="app">
20
- <div class="banner">
21
- <UsaBanner isInverted={componentOptions.usaBanner?.theme === 'inverse'} />
22
- </div>
23
19
  <!-- Desktop Navigation -->
24
20
  <div class="layout-container">
25
21
  <nav aria-label="Main navigation">
@@ -1,7 +1,6 @@
1
1
  @layer designsystem, theme, buttons, appnav, applayout;
2
2
  @import '../../../static/uswds/styles/styles.css' layer(designsystem);
3
- @import '../../../node_modules/@gsa-tts/graymatter-style-tokens/dist/css/variables.css'
4
- layer(theme);
3
+ @import '@gsa-tts/graymatter-style-tokens/dist/css/variables.css' layer(theme);
5
4
  @import 'theme.css' layer(theme);
6
5
  @import 'typography.css' layer(theme);
7
6
  @import '../utilities/layout.css' layer(theme);
@@ -33,9 +33,9 @@
33
33
 
34
34
  .logo-fixed-container {
35
35
  position: fixed;
36
- top: var(--ai-size-40);
36
+ top: 1rem;
37
37
  left: var(--ai-size-88);
38
- z-index: 500;
38
+ z-index: 10;
39
39
  background: transparent;
40
40
  }
41
41
 
@@ -10,20 +10,17 @@ export function getAppUrls(envArg?: Record<string, string | undefined>) {
10
10
 
11
11
  const chatUrl = env.PUBLIC_CHAT_URL;
12
12
  const consoleUrl = env.PUBLIC_CONSOLE_URL;
13
- // NOTE: The Discover and API docs/pages are served as part of the Astro site app itself,
14
- // so the API link typically points to a local route (e.g., /api) within the same deployment.
15
- // We may not need to use the external FCS URLs for API here, since the content is bundled and deployed together with the site app.
16
-
17
- // const apiUrl = env.PUBLIC_API_URL;
13
+ const apiUrl = env.PUBLIC_API_URL;
14
+ const discoverUrl = env.PUBLIC_DISCOVER_URL;
18
15
 
19
16
  warnIfMissing('chat', chatUrl);
20
17
  warnIfMissing('console', consoleUrl);
21
- // warnIfMissing('api', api);
18
+ warnIfMissing('api', apiUrl);
22
19
 
23
20
  return {
24
21
  chat: chatUrl || fallback,
25
22
  console: consoleUrl || fallback,
26
- api: '/api',
27
- discover: '/discover',
23
+ api: apiUrl || '/api',
24
+ discover: discoverUrl || '/discover',
28
25
  };
29
26
  }
@@ -1,88 +0,0 @@
1
- ---
2
- import BaseLayout from '@gsa-tts/graymatter-ui/layouts/BaseLayout.astro';
3
- import '../styles/base/global.css';
4
- import { getPageTitle } from '@gsa-tts/graymatter-ui/helpers';
5
- import PlatformFooter from '../components/PlatformFooter.astro';
6
- import AgencyHeader from '../components/AgencyHeader.astro';
7
- import Logo from '@gsa-tts/graymatter-ui/components/Logo.svelte';
8
- import UsaBanner from '../components/UsaBanner.astro';
9
-
10
- const { componentOptions = {} } = Astro.props;
11
-
12
- const title = getPageTitle(Astro);
13
- ---
14
-
15
- <BaseLayout title={title} gtmID={Astro.props.gtmID}>
16
- <div class="app">
17
- <div class="banner">
18
- <UsaBanner isInverted={componentOptions.usaBanner?.theme === 'inverse'} />
19
- </div>
20
- <div class="sidebar">
21
- <slot name="sidebar" />
22
- </div>
23
- <main id="main-content">
24
- <slot />
25
- </main>
26
- </div>
27
- </BaseLayout>
28
-
29
- <style>
30
- :root {
31
- --ai-applayout-size-sidebar-spacing: var(--ai-size-8) var(--ai-size-32);
32
- --ai-applayout-sidebar-background: var(--ai-color-steel-50);
33
- --ai-applayout-sidebar-shadow: -3px 0px 5px 0px rgba(0, 0, 0, 0.07) inset;
34
- }
35
-
36
- main {
37
- padding: var(
38
- --ai-applayout-size-content-spacing,
39
- var(--ai-size-8) var(--ai-size-32)
40
- );
41
- }
42
-
43
- .banner {
44
- grid-column: 1 / -1;
45
- }
46
-
47
- .sidebar {
48
- border-right: 1px solid var(--ai-color-steel-200);
49
- background: var(--ai-applayout-sidebar-background);
50
- box-shadow: var(--ai-applayout-sidebar-shadow);
51
- padding: var(--ai-applayout-size-sidebar-spacing);
52
- }
53
-
54
- @media (--ai-size-breakpoint-tablet) {
55
- :root {
56
- --ai-applayout-size-sidebar-spacing: var(--ai-size-12) var(--ai-size-20);
57
- }
58
-
59
- body {
60
- overflow-y: hidden;
61
- height: 100%;
62
- }
63
-
64
- main {
65
- padding: var(
66
- --ai-applayout-size-content-spacing,
67
- var(--ai-size-40) var(--ai-size-32)
68
- );
69
- }
70
-
71
- .sidebar {
72
- padding: var(--ai-applayout-size-sidebar-spacing);
73
- }
74
-
75
- main,
76
- .sidebar {
77
- height: 100%;
78
- overflow-y: auto;
79
- }
80
-
81
- .app {
82
- display: grid;
83
- grid-template-columns: var(--ai-size-224) 1fr;
84
- grid-template-rows: auto 1fr;
85
- height: 100vh;
86
- }
87
- }
88
- </style>