@bloom-housing/ui-components 8.0.3 → 8.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 (32) hide show
  1. package/dist/src/actions/ExpandableText.d.ts +1 -0
  2. package/dist/src/actions/ExpandableText.js +2 -2
  3. package/dist/src/actions/ExpandableText.js.map +1 -1
  4. package/dist/src/headers/SiteHeader.d.ts +4 -1
  5. package/dist/src/headers/SiteHeader.js +43 -38
  6. package/dist/src/headers/SiteHeader.js.map +1 -1
  7. package/dist/src/headers/SiteHeader.stories.d.ts +1 -0
  8. package/dist/src/headers/SiteHeader.stories.js +2 -5
  9. package/dist/src/headers/SiteHeader.stories.js.map +1 -1
  10. package/dist/src/lists/PreferencesList.d.ts +1 -0
  11. package/dist/src/lists/PreferencesList.js +1 -1
  12. package/dist/src/lists/PreferencesList.js.map +1 -1
  13. package/dist/src/lists/PreferencesList.stories.js +5 -0
  14. package/dist/src/lists/PreferencesList.stories.js.map +1 -1
  15. package/dist/src/page_components/listing/ContentAccordion.d.ts +1 -0
  16. package/dist/src/page_components/listing/ContentAccordion.js +3 -3
  17. package/dist/src/page_components/listing/ContentAccordion.js.map +1 -1
  18. package/dist/src/page_components/listing/ContentAccordion.stories.d.ts +1 -0
  19. package/dist/src/page_components/listing/ContentAccordion.stories.js +3 -0
  20. package/dist/src/page_components/listing/ContentAccordion.stories.js.map +1 -1
  21. package/package.json +1 -1
  22. package/src/actions/ExpandableText.tsx +7 -2
  23. package/src/global/accessibility.scss +22 -0
  24. package/src/global/app-css.scss +1 -0
  25. package/src/global/print.scss +1 -2
  26. package/src/headers/SiteHeader.scss +521 -351
  27. package/src/headers/SiteHeader.stories.tsx +10 -2
  28. package/src/headers/SiteHeader.tsx +89 -47
  29. package/src/lists/PreferencesList.stories.tsx +5 -0
  30. package/src/lists/PreferencesList.tsx +2 -1
  31. package/src/page_components/listing/ContentAccordion.stories.tsx +11 -0
  32. package/src/page_components/listing/ContentAccordion.tsx +7 -6
@@ -9,14 +9,22 @@ export default {
9
9
  export const basic = () => (
10
10
  <SiteHeader
11
11
  homeURL={"/"}
12
- // languages={[{ label: "English", onClick: () => console.log("Clicked English"), active: true }]}
13
12
  logoSrc="/images/logo_glyph.svg"
14
- // notice="This is a preview of our new website. We're just getting started. We'd love to get your feedback."
15
13
  title="Alameda County Housing Portal"
16
14
  menuLinks={[]}
17
15
  />
18
16
  )
19
17
 
18
+ export const withSubtitle = () => (
19
+ <SiteHeader
20
+ homeURL={"/"}
21
+ logoSrc="/images/logo_glyph.svg"
22
+ title="Alameda County Housing Portal"
23
+ subtitle="Subtitle"
24
+ menuLinks={[]}
25
+ />
26
+ )
27
+
20
28
  export const withLanguage = () => (
21
29
  <SiteHeader
22
30
  homeURL={"/"}
@@ -12,6 +12,7 @@ type LogoWidth = "slim" | "base" | "medium" | "wide"
12
12
  type SiteHeaderWidth = "base" | "wide"
13
13
 
14
14
  export interface MenuLink {
15
+ className?: string
15
16
  href?: string
16
17
  iconClassName?: string
17
18
  iconSrc?: string
@@ -21,7 +22,9 @@ export interface MenuLink {
21
22
  }
22
23
 
23
24
  export interface SiteHeaderProps {
25
+ desktopMinWidth?: number
24
26
  dropdownItemClassName?: string
27
+ flattenSubMenus?: boolean
25
28
  homeURL: string
26
29
  imageOnly?: boolean
27
30
  languageNavLabel?: string
@@ -34,10 +37,10 @@ export interface SiteHeaderProps {
34
37
  menuLinks: MenuLink[]
35
38
  mobileDrawer?: boolean
36
39
  mobileText?: boolean
37
- flattenSubMenus?: boolean
38
40
  notice?: string | React.ReactNode
39
41
  noticeMobile?: boolean
40
42
  siteHeaderWidth?: SiteHeaderWidth
43
+ subtitle?: string
41
44
  title?: string
42
45
  strings?: {
43
46
  close?: string
@@ -56,7 +59,7 @@ const SiteHeader = (props: SiteHeaderProps) => {
56
59
 
57
60
  const { LinkComponent } = useContext(NavigationContext)
58
61
 
59
- const DESKTOP_MIN_WIDTH = 767 // @screen md
62
+ const DESKTOP_MIN_WIDTH = props.desktopMinWidth || 767 // @screen md
60
63
  // Enables toggling off navbar links when entering mobile
61
64
  useEffect(() => {
62
65
  if (window.innerWidth > DESKTOP_MIN_WIDTH) {
@@ -74,13 +77,13 @@ const SiteHeader = (props: SiteHeaderProps) => {
74
77
  }
75
78
  window.addEventListener("resize", updateMedia)
76
79
  return () => window.removeEventListener("resize", updateMedia)
77
- }, [])
80
+ }, [DESKTOP_MIN_WIDTH])
78
81
 
79
82
  const getLogoWidthClass = () => {
80
- if (props.logoWidth === "slim") return "navbar-logo-width-slim"
81
- if (!props.logoWidth || props.logoWidth === "base") return "navbar-logo-width-base"
82
- if (props.logoWidth === "medium") return "navbar-logo-width-med"
83
- if (props.logoWidth === "wide") return "navbar-logo-width-wide"
83
+ if (props.logoWidth === "slim") return "site-header__logo-width-slim"
84
+ if (!props.logoWidth || props.logoWidth === "base") return "site-header__logo-width-base"
85
+ if (props.logoWidth === "medium") return "site-header__logo-width-med"
86
+ if (props.logoWidth === "wide") return "site-header__logo-width-wide"
84
87
  return ""
85
88
  }
86
89
 
@@ -154,7 +157,7 @@ const SiteHeader = (props: SiteHeaderProps) => {
154
157
  }
155
158
  dropdownOptionKeyDown(event, index)
156
159
  }}
157
- data-test-id={`${option.title}`}
160
+ data-test-id={`${option.title}-${index}`}
158
161
  >
159
162
  {dropdownOptionContent(option)}
160
163
  </button>
@@ -171,9 +174,9 @@ const SiteHeader = (props: SiteHeaderProps) => {
171
174
  {menuTitle}
172
175
  <Icon size="small" symbol="arrowDown" fill={"#555555"} className={"pl-2"} />
173
176
  {activeMenus.indexOf(menuTitle) >= 0 && (
174
- <span className={"navbar-dropdown-container"}>
175
- <div className={"navbar-dropdown"}>
176
- {getDropdownOptions(subMenus, "navbar-dropdown-item", menuTitle)}
177
+ <span className={"site-header__dropdown-container"}>
178
+ <div className={"site-header__dropdown"}>
179
+ {getDropdownOptions(subMenus, "site-header__dropdown-item", menuTitle)}
177
180
  </div>
178
181
  </span>
179
182
  )}
@@ -207,7 +210,12 @@ const SiteHeader = (props: SiteHeaderProps) => {
207
210
  }}
208
211
  >
209
212
  {menuLink.title}
210
- <Icon size="small" symbol="arrowDown" fill={"#555555"} className={"pl-2"} />
213
+ <Icon
214
+ size="small"
215
+ symbol="arrowDown"
216
+ fill={"#555555"}
217
+ className={"site-header__icon-spacing"}
218
+ />
211
219
  </button>
212
220
  {activeMobileMenus.indexOf(menuLink.title) >= 0 && (
213
221
  <div className={dropdownContainerClassName}>
@@ -236,11 +244,16 @@ const SiteHeader = (props: SiteHeaderProps) => {
236
244
  // Render the mobile drawer that opens on menu press when prop mobileDrawer is set
237
245
  const getMobileDrawer = () => {
238
246
  return (
239
- <CSSTransition in={mobileDrawer} timeout={400} classNames={"drawer-transition"} unmountOnExit>
240
- <span className={`navbar-mobile-drawer-dropdown-container`}>
241
- <div className={"navbar-mobile-drawer-dropdown"}>
247
+ <CSSTransition
248
+ in={mobileDrawer}
249
+ timeout={400}
250
+ classNames={"site-header__drawer-transition"}
251
+ unmountOnExit
252
+ >
253
+ <span className={"site-header__mobile-drawer-dropdown-container"}>
254
+ <div className={"site-header__mobile-drawer-dropdown"}>
242
255
  <button
243
- className={"navbar-mobile-drawer-close-row"}
256
+ className={"site-header__mobile-drawer-close-row"}
244
257
  onClick={() => setMobileDrawer(false)}
245
258
  onKeyPress={(event) => {
246
259
  if (event.key === "Enter") {
@@ -249,12 +262,17 @@ const SiteHeader = (props: SiteHeaderProps) => {
249
262
  }}
250
263
  aria-label={props.strings?.close ?? t("t.close")}
251
264
  >
252
- <Icon size="small" symbol="arrowForward" fill={"#ffffff"} className={"pl-2"} />
265
+ <Icon
266
+ size="small"
267
+ symbol="arrowForward"
268
+ fill={"#ffffff"}
269
+ className={"site-header__icon-spacing"}
270
+ />
253
271
  </button>
254
272
  {buildMobileMenuOptions(
255
273
  props.menuLinks,
256
- "navbar-mobile-drawer-dropdown-item navbar-mobile-drawer-dropdown-item-sublink",
257
- "navbar-mobile-drawer-dropdown-item"
274
+ "site-header__mobile-drawer-dropdown-item site-header__mobile-drawer-dropdown-item-sublink",
275
+ "site-header__mobile-drawer-dropdown-item"
258
276
  )}
259
277
  </div>
260
278
  </span>
@@ -267,13 +285,12 @@ const SiteHeader = (props: SiteHeaderProps) => {
267
285
  return (
268
286
  <>
269
287
  {!props.mobileDrawer && (
270
- <span className={"navbar-mobile-dropdown-container"}>
271
- <div className={"navbar-mobile-dropdown"}>
288
+ <span className={"site-header__mobile-dropdown-container"}>
289
+ <div className={"site-header__mobile-dropdown"}>
272
290
  {buildMobileMenuOptions(
273
291
  props.menuLinks,
274
- "navbar-mobile-dropdown-item navbar-mobile-dropdown-item-sublink",
275
- "navbar-mobile-dropdown-item",
276
- "navbar-mobile-dropdown-links"
292
+ "site-header__mobile-dropdown-item site-header__mobile-dropdown-item-sublink",
293
+ "site-header__mobile-dropdown-item"
277
294
  )}
278
295
  </div>
279
296
  </span>
@@ -305,10 +322,12 @@ const SiteHeader = (props: SiteHeaderProps) => {
305
322
  if (menuLink.href) {
306
323
  return (
307
324
  <LinkComponent
308
- className={`navbar-link ${props.menuItemClassName ?? ""}`}
325
+ className={`site-header__link ${props.menuItemClassName ?? ""} ${
326
+ menuLink.className ?? ""
327
+ }`}
309
328
  href={menuLink.href}
310
329
  key={`${menuLink.title}-${index}`}
311
- data-test-id={`${menuLink.title}`}
330
+ data-test-id={`${menuLink.title}-${index}`}
312
331
  >
313
332
  {menuContent}
314
333
  </LinkComponent>
@@ -316,7 +335,9 @@ const SiteHeader = (props: SiteHeaderProps) => {
316
335
  } else {
317
336
  return (
318
337
  <button
319
- className={`navbar-link ${props.menuItemClassName ?? ""} desktop-header-button`}
338
+ className={`site-header__link ${
339
+ props.menuItemClassName ?? ""
340
+ } site-header__desktop-header-button`}
320
341
  tabIndex={0}
321
342
  onClick={() => {
322
343
  menuAction(menuLink.onClick)
@@ -335,7 +356,7 @@ const SiteHeader = (props: SiteHeaderProps) => {
335
356
  } else {
336
357
  return (
337
358
  <span
338
- className={`navbar-link navbar-dropdown-title`}
359
+ className={`site-header__link site-header__dropdown-title`}
339
360
  tabIndex={0}
340
361
  key={`${menuLink.title}-${index}`}
341
362
  onKeyPress={(event) => {
@@ -346,7 +367,7 @@ const SiteHeader = (props: SiteHeaderProps) => {
346
367
  onMouseEnter={() => changeMenuShow(menuLink.title, activeMenus, setActiveMenus)}
347
368
  onMouseLeave={() => changeMenuShow(menuLink.title, activeMenus, setActiveMenus)}
348
369
  role={"button"}
349
- data-test-id={`${menuLink.title}`}
370
+ data-test-id={`${menuLink.title}-${index}`}
350
371
  >
351
372
  {menuContent}
352
373
  </span>
@@ -371,7 +392,7 @@ const SiteHeader = (props: SiteHeaderProps) => {
371
392
  <>
372
393
  {props.mobileText ? (
373
394
  <button
374
- className={"flex flex-row items-center justify-center"}
395
+ className={"site-header__mobile-menu-text-button"}
375
396
  onClick={() => {
376
397
  mobileHeaderAction()
377
398
  }}
@@ -382,13 +403,13 @@ const SiteHeader = (props: SiteHeaderProps) => {
382
403
  }
383
404
  }}
384
405
  >
385
- <div className={"pr-2 text-sm text-primary uppercase"}>
406
+ <div className={"site-header__mobile-menu-text-button-content"}>
386
407
  {props.strings?.menu ?? t("t.menu")}
387
408
  </div>
388
409
  <Icon
389
410
  symbol={mobileMenu ? "closeSmall" : "hamburger"}
390
411
  size={"base"}
391
- className={"pr-3"}
412
+ className={"site-header__mobile-menu-icon"}
392
413
  />
393
414
  </button>
394
415
  ) : (
@@ -404,10 +425,14 @@ const SiteHeader = (props: SiteHeaderProps) => {
404
425
  }}
405
426
  icon={mobileMenu ? "closeSmall" : "hamburger"}
406
427
  iconSize="base"
407
- className={"navbar-mobile-menu-button"}
428
+ className={"site-header__mobile-menu-button"}
408
429
  unstyled
409
430
  >
410
- {mobileMenu ? props.strings?.close ?? t("t.close") : props.strings?.menu ?? t("t.menu")}
431
+ <span className={"site-header__mobile-menu-button-text"}>
432
+ {mobileMenu
433
+ ? props.strings?.close ?? t("t.close")
434
+ : props.strings?.menu ?? t("t.menu")}
435
+ </span>
411
436
  </Button>
412
437
  )}
413
438
  </>
@@ -416,21 +441,34 @@ const SiteHeader = (props: SiteHeaderProps) => {
416
441
 
417
442
  const getLogo = () => {
418
443
  return (
419
- <div className={`navbar-logo ${getLogoWidthClass()}`}>
444
+ <div className={`site-header__logo-container ${getLogoWidthClass()}`}>
420
445
  <LinkComponent
421
- className={`logo ${props.logoClass ?? ""} ${
422
- (props.logoWidth && "navbar-custom-width") ?? ""
446
+ className={`site-header__logo ${props.logoClass ?? ""} ${
447
+ (props.logoWidth && "site-header__custom-width") ?? ""
423
448
  }`}
424
449
  href={props.homeURL}
425
450
  aria-label={props.strings?.logoAriaLable ?? t("t.homePage")}
426
451
  >
427
- <div className={`logo-content ${props.imageOnly ? "navbar-image-only-container" : ""}`}>
452
+ <div
453
+ className={`site-header__logo-content ${
454
+ props.imageOnly ? "site-header__image-only-container" : ""
455
+ }`}
456
+ >
428
457
  <img
429
- className={`logo__image ${props.imageOnly ? "navbar-image-only" : ""}`}
458
+ className={`site-header__logo-image ${
459
+ props.imageOnly ? "site-header__image-only" : ""
460
+ }`}
430
461
  src={props.logoSrc}
431
462
  alt={"Site logo"}
432
463
  />
433
- {props.title && <div className="logo__title">{props.title}</div>}
464
+ {props.title && (
465
+ <div className="site-header__logo-title">
466
+ {props.title}
467
+ {props.subtitle && (
468
+ <div className="site-header__logo__subtitle">{props.subtitle}</div>
469
+ )}
470
+ </div>
471
+ )}
434
472
  </div>
435
473
  </LinkComponent>
436
474
  </div>
@@ -440,7 +478,7 @@ const SiteHeader = (props: SiteHeaderProps) => {
440
478
  return (
441
479
  <header className={"site-header"}>
442
480
  {props.mainContentId && (
443
- <a className="skip-link" href={`#${props.mainContentId}`}>
481
+ <a className="site-header__skip-link" href={`#${props.mainContentId}`}>
444
482
  {props.strings?.skipToMainContent}
445
483
  </a>
446
484
  )}
@@ -448,18 +486,22 @@ const SiteHeader = (props: SiteHeaderProps) => {
448
486
  <LanguageNav ariaLabel={props.languageNavLabel} languages={props.languages} />
449
487
  )}
450
488
 
451
- <div className={`navbar-notice ${!props.noticeMobile ? `navbar-notice-hide` : ""}`}>
452
- <div className="navbar-notice__text">{props.notice ?? ""}</div>
489
+ <div
490
+ className={`site-header__notice ${!props.noticeMobile ? `site-header__notice-hide` : ""}`}
491
+ >
492
+ <div className="site-header__notice-text">{props.notice ?? ""}</div>
453
493
  </div>
454
494
 
455
- <nav className="navbar-container" role="navigation" aria-label="main navigation">
495
+ <nav className="site-header__container" role="navigation" aria-label="main navigation">
456
496
  <div
457
- className={`navbar ${
458
- props.siteHeaderWidth === "wide" ? "navbar-width-wide" : "navbar-width-base"
497
+ className={`site-header__base ${
498
+ props.siteHeaderWidth === "wide" ? "site-header__width-wide" : "side-header__width-base"
459
499
  }`}
460
500
  >
461
501
  {getLogo()}
462
- <div className="navbar-menu">{isDesktop ? getDesktopHeader() : getMobileHeader()}</div>
502
+ <div className="site-header__navbar-menu">
503
+ {isDesktop ? getDesktopHeader() : getMobileHeader()}
504
+ </div>
463
505
  </div>
464
506
  </nav>
465
507
  {!isDesktop && mobileMenu && getMobileDropdown()}
@@ -29,6 +29,11 @@ const listingPreferences = [
29
29
  url: "http://www.google.com",
30
30
  title: "Link Title 2",
31
31
  },
32
+ {
33
+ url: "http://www.apple.com",
34
+ ariaLabel: "Link to Apple",
35
+ title: "Link Title with Aria Label",
36
+ },
32
37
  ],
33
38
  title: "Title 2",
34
39
  subtitle: "Subtitle 2",
@@ -5,6 +5,7 @@ import { locale } from "../helpers/translator"
5
5
  export interface ListPreferenceLink {
6
6
  title: string
7
7
  url: string
8
+ ariaLabel?: string
8
9
  }
9
10
 
10
11
  export interface ListPreference {
@@ -54,7 +55,7 @@ const PreferencesList = (props: PreferencesListProps) => {
54
55
  <div className="preferences-list__links">
55
56
  {preference.links.map((link: ListPreferenceLink, linkIndex: number) => (
56
57
  <span key={linkIndex}>
57
- <a href={link.url} target="_blank">
58
+ <a href={link.url} target="_blank" aria-label={link.ariaLabel}>
58
59
  {link.title}
59
60
  </a>
60
61
  </span>
@@ -25,6 +25,17 @@ export const blueThemeBasic = () => {
25
25
  )
26
26
  }
27
27
 
28
+ export const blueThemeBasicWithInitialExpanded = () => {
29
+ return (
30
+ <ContentAccordion
31
+ customBarContent={barContent()}
32
+ customExpandedContent={expandedContent()}
33
+ accordionTheme={"blue"}
34
+ initialExpanded={true}
35
+ />
36
+ )
37
+ }
38
+
28
39
  export const blueThemeBasicDisabled = () => {
29
40
  return (
30
41
  <ContentAccordion
@@ -8,6 +8,7 @@ interface ContentAccordionProps {
8
8
  disableAccordion?: boolean
9
9
  accordionTheme?: AccordionTheme
10
10
  barClass?: string
11
+ initialExpanded?: boolean
11
12
  }
12
13
 
13
14
  export type AccordionTheme = "blue" | "gray"
@@ -17,7 +18,7 @@ export type AccordionTheme = "blue" | "gray"
17
18
  * Two existing themes under our design system are available
18
19
  */
19
20
  const ContentAccordion = (props: ContentAccordionProps) => {
20
- const [accordionOpen, setAccordionOpen] = useState(false)
21
+ const [accordionOpen, setAccordionOpen] = useState(props.initialExpanded || false)
21
22
  const buttonRef = useRef<HTMLButtonElement>(null)
22
23
 
23
24
  const toggleTable = () => {
@@ -31,16 +32,16 @@ const ContentAccordion = (props: ContentAccordionProps) => {
31
32
  <div className={`mb-4`}>
32
33
  <button
33
34
  onClick={toggleTable}
34
- className={`w-full text-left ${props.disableAccordion && "cursor-default"}`}
35
+ className={`w-full text-left ${props.disableAccordion ? "cursor-default" : ""}`}
35
36
  ref={buttonRef}
36
37
  aria-expanded={accordionOpen}
37
38
  data-test-id={"content-accordion-button"}
38
39
  >
39
40
  <div
40
- className={`flex justify-between ${props.barClass} ${
41
- props.accordionTheme === "blue" && "accordion-blue-theme__bar"
42
- } ${props.accordionTheme === "gray" && "accordion-gray-theme__bar"} ${
43
- accordionOpen && "accordion-open"
41
+ className={`flex justify-between ${props.barClass ? props.barClass : ""} ${
42
+ props.accordionTheme === "blue" ? "accordion-blue-theme__bar" : ""
43
+ } ${props.accordionTheme === "gray" ? "accordion-gray-theme__bar" : ""} ${
44
+ accordionOpen ? "accordion-open" : ""
44
45
  }`}
45
46
  >
46
47
  {props.customBarContent}