@bloom-housing/ui-components 6.0.0 → 6.0.1-alpha.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.
Files changed (79) hide show
  1. package/CHANGELOG.md +236 -7
  2. package/index.ts +1 -0
  3. package/package.json +3 -5
  4. package/src/actions/Button.tsx +1 -0
  5. package/src/actions/LinkButton.tsx +1 -0
  6. package/src/blocks/FormCard.scss +1 -0
  7. package/src/blocks/HousingCounselor.tsx +8 -3
  8. package/src/blocks/ImageCard.tsx +24 -13
  9. package/src/blocks/MediaCard.docs.mdx +37 -0
  10. package/src/blocks/MediaCard.scss +10 -11
  11. package/src/blocks/MediaCard.tsx +4 -4
  12. package/src/blocks/StandardCard.tsx +1 -1
  13. package/src/blocks/StatusItem.tsx +17 -6
  14. package/src/forms/DOBField.tsx +20 -8
  15. package/src/forms/DateField.tsx +16 -7
  16. package/src/forms/Dropzone.scss +7 -0
  17. package/src/forms/Dropzone.tsx +18 -5
  18. package/src/forms/Field.tsx +5 -0
  19. package/src/forms/FieldGroup.tsx +14 -3
  20. package/src/forms/HouseholdMemberForm.tsx +4 -1
  21. package/src/forms/HouseholdSizeField.tsx +16 -6
  22. package/src/forms/TimeField.tsx +15 -6
  23. package/src/global/custom_counter.scss +1 -1
  24. package/src/global/forms.scss +38 -5
  25. package/src/global/headers.scss +1 -1
  26. package/src/global/markdown.scss +2 -2
  27. package/src/global/vendor/ag_grid.scss +43 -9
  28. package/src/headers/Hero.tsx +8 -1
  29. package/src/headers/PageHeader.scss +3 -2
  30. package/src/headers/PageHeader.tsx +5 -1
  31. package/src/headers/SiteHeader.tsx +11 -4
  32. package/src/helpers/formOptions.tsx +4 -1
  33. package/src/helpers/formatYesNoLabel.ts +8 -6
  34. package/src/locales/es.json +1 -1
  35. package/src/locales/general.json +9 -4
  36. package/src/locales/tl.json +1 -1
  37. package/src/locales/vi.json +1 -1
  38. package/src/locales/zh.json +1 -1
  39. package/src/navigation/Breadcrumbs.scss +1 -0
  40. package/src/navigation/Breadcrumbs.tsx +1 -1
  41. package/src/navigation/FooterNav.tsx +5 -1
  42. package/src/navigation/LanguageNav.tsx +1 -1
  43. package/src/navigation/ProgressNav.docs.mdx +47 -0
  44. package/src/navigation/ProgressNav.scss +101 -56
  45. package/src/navigation/ProgressNav.tsx +45 -15
  46. package/src/navigation/SideNav.scss +56 -0
  47. package/src/navigation/SideNav.tsx +2 -2
  48. package/src/navigation/TabNav.scss +1 -1
  49. package/src/navigation/TabNav.tsx +1 -1
  50. package/src/navigation/Tabs.scss +25 -6
  51. package/src/notifications/AlertBox.docs.mdx +41 -0
  52. package/src/notifications/AlertBox.scss +78 -41
  53. package/src/notifications/AlertBox.tsx +20 -14
  54. package/src/notifications/SiteAlert.tsx +3 -0
  55. package/src/notifications/StatusMessage.tsx +8 -2
  56. package/src/notifications/alertTypes.ts +1 -0
  57. package/src/overlays/Modal.scss +3 -1
  58. package/src/page_components/ApplicationTimeline.scss +6 -6
  59. package/src/page_components/ApplicationTimeline.tsx +17 -7
  60. package/src/page_components/NavigationHeader.tsx +10 -10
  61. package/src/page_components/forgot-password/FormForgotPassword.tsx +1 -1
  62. package/src/page_components/listing/AdditionalFees.tsx +1 -1
  63. package/src/page_components/listing/ListingCard.scss +4 -0
  64. package/src/page_components/listing/ListingCard.tsx +18 -3
  65. package/src/page_components/listing/listing_sidebar/Contact.tsx +2 -2
  66. package/src/page_components/listing/listing_sidebar/GetApplication.tsx +31 -16
  67. package/src/page_components/listing/listing_sidebar/ListingUpdated.tsx +5 -1
  68. package/src/page_components/listing/listing_sidebar/OrDivider.tsx +4 -2
  69. package/src/page_components/listing/listing_sidebar/ReferralApplication.tsx +7 -4
  70. package/src/page_components/listing/listing_sidebar/events/DownloadLotteryResults.tsx +6 -1
  71. package/src/page_components/sign-in/FormSignIn.tsx +1 -1
  72. package/src/page_components/sign-in/FormSignInErrorBox.tsx +1 -1
  73. package/src/sections/InfoCardGrid.scss +1 -1
  74. package/src/sections/InfoCardGrid.tsx +4 -1
  75. package/src/sections/ListSection.tsx +1 -1
  76. package/src/tables/AgTable.tsx +56 -26
  77. package/src/tables/StandardTable.tsx +19 -7
  78. package/src/text/Tag.scss +7 -0
  79. package/src/text/Tag.tsx +2 -0
@@ -1,6 +1,6 @@
1
1
  .page-header {
2
2
  /* Component Variables */
3
- --background-color: var(--bloom-color-primary-lighter);
3
+ --background-color: var(--bloom-color-gray-300);
4
4
  --border-color: var(--bloom-color-gray-450);
5
5
  --text-color: inherit;
6
6
  --text-font-family: var(--bloom-font-serif);
@@ -15,7 +15,7 @@
15
15
  border-top: var(--bloom-border-1) solid var(--border-color);
16
16
  color: var(--text-color);
17
17
 
18
- @media (min-width: $screen-sm) {
18
+ @media (min-width: $screen-md) {
19
19
  padding: var(--bloom-s10) 0;
20
20
  }
21
21
 
@@ -47,6 +47,7 @@
47
47
  .page-header__title {
48
48
  text-align: center;
49
49
  font-family: var(--text-font-family);
50
+ word-break: break-all;
50
51
 
51
52
  @media (min-width: $screen-md) {
52
53
  font-size: var(--title-font-size);
@@ -19,7 +19,11 @@ const PageHeader = (props: PageHeaderProps) => {
19
19
  return (
20
20
  <header className={classNames.join(" ")}>
21
21
  <hgroup className="page-header__group">
22
- {props?.breadcrumbs && <nav className="page-header__breadcrumbs">{props?.breadcrumbs}</nav>}
22
+ {props?.breadcrumbs && (
23
+ <nav className="page-header__breadcrumbs" aria-label={"Page Header"}>
24
+ {props?.breadcrumbs}
25
+ </nav>
26
+ )}
23
27
 
24
28
  {props.title && (
25
29
  <h1 data-test-id="page-header" className="page-header__title">
@@ -38,6 +38,11 @@ export interface SiteHeaderProps {
38
38
  noticeMobile?: boolean
39
39
  siteHeaderWidth?: SiteHeaderWidth
40
40
  title?: string
41
+ strings?: {
42
+ close?: string
43
+ logoAriaLable?: string
44
+ menu?: string
45
+ }
41
46
  }
42
47
 
43
48
  const SiteHeader = (props: SiteHeaderProps) => {
@@ -240,7 +245,7 @@ const SiteHeader = (props: SiteHeaderProps) => {
240
245
  setMobileDrawer(false)
241
246
  }
242
247
  }}
243
- aria-label={t("t.close")}
248
+ aria-label={props.strings?.close ?? t("t.close")}
244
249
  >
245
250
  <Icon size="small" symbol="arrowForward" fill={"#ffffff"} className={"pl-2"} />
246
251
  </button>
@@ -377,7 +382,9 @@ const SiteHeader = (props: SiteHeaderProps) => {
377
382
  }
378
383
  }}
379
384
  >
380
- <div className={"pr-2 text-tiny text-primary uppercase"}>{t("t.menu")}</div>
385
+ <div className={"pr-2 text-tiny text-primary uppercase"}>
386
+ {props.strings?.menu ?? t("t.menu")}
387
+ </div>
381
388
  <Icon
382
389
  symbol={mobileMenu ? "closeSmall" : "hamburger"}
383
390
  size={"base"}
@@ -400,7 +407,7 @@ const SiteHeader = (props: SiteHeaderProps) => {
400
407
  className={"navbar-mobile-menu-button"}
401
408
  unstyled
402
409
  >
403
- {mobileMenu ? t("t.close") : t("t.menu")}
410
+ {mobileMenu ? props.strings?.close ?? t("t.close") : props.strings?.menu ?? t("t.menu")}
404
411
  </Button>
405
412
  )}
406
413
  </>
@@ -415,7 +422,7 @@ const SiteHeader = (props: SiteHeaderProps) => {
415
422
  props.logoWidth && "navbar-custom-width"
416
423
  }`}
417
424
  href={props.homeURL}
418
- aria-label={t("t.homePage")}
425
+ aria-label={props.strings?.logoAriaLable ?? t("t.homePage")}
419
426
  >
420
427
  <div className={`logo-content ${props.imageOnly && "navbar-image-only-container"}`}>
421
428
  <img
@@ -5,6 +5,9 @@ import { SelectOption } from "../forms/Select"
5
5
  export interface FormOptionsProps {
6
6
  options: (string | SelectOption)[]
7
7
  keyPrefix?: string
8
+ strings?: {
9
+ selectOne?: string
10
+ }
8
11
  }
9
12
 
10
13
  export const numberOptions = (end: number, start = 1): SelectOption[] => {
@@ -21,7 +24,7 @@ export const FormOptions = (props: FormOptionsProps) => {
21
24
  if (option == "" || option["value"] == "") {
22
25
  return (
23
26
  <option value="" key="select-one">
24
- {t("t.selectOne")}
27
+ {props.strings?.selectOne ?? t("t.selectOne")}
25
28
  </option>
26
29
  )
27
30
  } else {
@@ -1,9 +1,11 @@
1
1
  import { t } from "./translator"
2
2
 
3
- export const formatYesNoLabel = (value: boolean | null) => {
4
- if (value === null || typeof value == "undefined") return t("t.n/a")
5
- if (value) return t("t.yes")
6
- if (!value) return t("t.no")
7
-
8
- return t("t.n/a")
3
+ export const formatYesNoLabel = (
4
+ value: boolean | null,
5
+ strings?: { yesString?: string; noString?: string; notApplicableString?: string }
6
+ ) => {
7
+ if (value === null || typeof value == "undefined")
8
+ return strings?.notApplicableString ?? t("t.n/a")
9
+ if (value) return strings?.yesString ?? t("t.yes")
10
+ return strings?.noString ?? t("t.no")
9
11
  }
@@ -371,7 +371,7 @@
371
371
  "authentication.forgotPassword.sendEmail": "Enviar correo electrónico",
372
372
  "authentication.forgotPassword.success": "Le hemos enviado un correo electrónico con un enlace para restablecer tu contraseña.",
373
373
  "authentication.signIn.accountHasBeenLocked": "Por razones de seguridad_ esta cuenta ha sido bloqueada.",
374
- "authentication.signIn.afterFailedAttempts": "Por razones de seguridad_ después de 5 intentos fallidos_ deberá esperar 30 minutos antes de volver a intentarlo.",
374
+ "authentication.signIn.afterFailedAttempts": "Por razones de seguridad_ después de %{count} intentos fallidos_ deberá esperar 30 minutos antes de volver a intentarlo.",
375
375
  "authentication.signIn.changeYourPassword": "Puede cambiar su contraseña",
376
376
  "authentication.signIn.enterLoginEmail": "Por favor_ escriba su correo electrónico de inicio de sesión",
377
377
  "authentication.signIn.enterLoginPassword": "Por favor_ escriba su contraseña de inicio de sesión",
@@ -363,7 +363,7 @@
363
363
  "application.review.confirmation.createAccountParagraph": "Creating an account will save your information for future applications, and you can check the status of this application anytime.",
364
364
  "application.review.confirmation.createAccountTitle": "Would you like to create an account?",
365
365
  "application.review.confirmation.doNotSubmitTitle": "Do not submit another application for this listing.",
366
- "application.review.confirmation.eligibleApplicants.FCFS": "Eligibile applicants will be contacted by on a **first come first serve** basis until vacancies are filled.",
366
+ "application.review.confirmation.eligibleApplicants.FCFS": "Eligible applicants will be contacted by on a **first come first serve** basis until vacancies are filled.",
367
367
  "application.review.confirmation.eligibleApplicants.lottery": "Eligible applicants will be contacted by the agent in lottery rank order until vacancies are filled.",
368
368
  "application.review.confirmation.eligibleApplicants.lotteryDate": "The lottery will be held on **%{lotteryDate}**.",
369
369
  "application.review.confirmation.imdone": "No thanks, I'm done.",
@@ -495,7 +495,7 @@
495
495
  "authentication.forgotPassword.sendEmail": "Send email",
496
496
  "authentication.forgotPassword.success": "We've sent you an email. You'll receive an email with a link to reset your password.",
497
497
  "authentication.signIn.accountHasBeenLocked": "For security reasons, this account has been locked.",
498
- "authentication.signIn.afterFailedAttempts": "For security reasons, after 5 failed attempts, you’ll have to wait 30 minutes before trying again.",
498
+ "authentication.signIn.afterFailedAttempts": "For security reasons, after %{count} failed attempts, you’ll have to wait 30 minutes before trying again.",
499
499
  "authentication.signIn.changeYourPassword": "You can change your password",
500
500
  "authentication.signIn.enterLoginEmail": "Please enter your login email",
501
501
  "authentication.signIn.enterLoginPassword": "Please enter your login password",
@@ -614,9 +614,12 @@
614
614
  "listings.apply.submitPaperNoDueDateNoPostMark": "%{developer} is not responsible for lost or delayed mail.",
615
615
  "listings.apply.submitPaperNoDueDatePostMark": "Applications must be received by the deadline. If sending by U.S. Mail, the application must be received by mail no later than %{postmarkReceivedByDate}. Applications received after %{postmarkReceivedByDate} via mail will not be accepted. %{developer} is not responsible for lost or delayed mail.",
616
616
  "listings.availableAndWaitlist": "Available Units & Open Waitlist",
617
- "listings.availableUnitsDescription": "Applicants will be reviewed in order until all vacancies are filled.",
617
+ "listings.availableUnitsDescription": "Applicants will be reviewed in lottery rank order until all vacancies are filled.",
618
618
  "listings.availableUnitsAndWaitlist": "Available units and waitlist",
619
619
  "listings.availableUnitsAndWaitlistDesc": "Once applicants fill all available units, additional applicants will be placed on the waitlist for <span class='t-italic'>%{number} units</span>",
620
+ "listings.vacantUnit": "Vacant Unit",
621
+ "listings.vacantUnits": "Vacant Units",
622
+ "listings.vacantUnitsAvailable": "Vacant Units Available",
620
623
  "listings.availableUnit": "Available Unit",
621
624
  "listings.availableUnits": "Available Units",
622
625
  "listings.bath": "bath",
@@ -789,7 +792,8 @@
789
792
  "pageTitle.welcomeEnglish": "Welcome",
790
793
  "pageTitle.welcomeSpanish": "Bienvenido",
791
794
  "pageTitle.welcomeVietnamese": "Tiếng Việt",
792
- "progressNav.current": "Current step: ",
795
+ "progressNav.completed": "completed",
796
+ "progressNav.notCompleted": "not completed",
793
797
  "progressNav.srHeading": "Progress",
794
798
  "region.name": "Local Region",
795
799
  "states.AK": "Alaska",
@@ -867,6 +871,7 @@
867
871
  "t.edit": "Edit",
868
872
  "t.email": "Email",
869
873
  "t.emailAddressPlaceholder": "you@myemail.com",
874
+ "t.filter": "Filter",
870
875
  "t.floor": "floor",
871
876
  "t.floors": "floors",
872
877
  "t.getDirections": "Get Directions",
@@ -324,7 +324,7 @@
324
324
  "authentication.forgotPassword.sendEmail": "Magpadala ng email",
325
325
  "authentication.forgotPassword.success": "Nagpadala kami sa iyo ng email. Makakatanggap ka ng email na may link para i-reset ang iyong password.",
326
326
  "authentication.signIn.accountHasBeenLocked": "Para sa mga kadahilanang pangseguridad_ ang account na ito isinara na.",
327
- "authentication.signIn.afterFailedAttempts": "Para sa mga kadahilanang pangseguridad_ pagkatapos ng 5 nabigong pagtatangka_ kailangan mong maghintay ng 30 minuto bago subukang muli.",
327
+ "authentication.signIn.afterFailedAttempts": "Para sa mga kadahilanang pangseguridad_ pagkatapos ng %{count} nabigong pagtatangka_ kailangan mong maghintay ng 30 minuto bago subukang muli.",
328
328
  "authentication.signIn.changeYourPassword": "Maaari mong palitan ang iyong password",
329
329
  "authentication.signIn.enterLoginEmail": "Pakilagay ang iyong email sa pag-login",
330
330
  "authentication.signIn.enterLoginPassword": "Pakilagay ang iyong password sa pag-log in",
@@ -371,7 +371,7 @@
371
371
  "authentication.forgotPassword.sendEmail": "Gửi email",
372
372
  "authentication.forgotPassword.success": "Chúng tôi đã gửi email cho quý vị. Quý vị sẽ nhận được email có liên kết để đặt lại mật khẩu.",
373
373
  "authentication.signIn.accountHasBeenLocked": "Vì lý do bảo mật_ tài khoản này đã bị khóa.",
374
- "authentication.signIn.afterFailedAttempts": "Vì lý do bảo mật_ quý vị sẽ phải chờ 30 phút trước khi thử lại sau 5 lần thử không thành công.",
374
+ "authentication.signIn.afterFailedAttempts": "Vì lý do bảo mật_ quý vị sẽ phải chờ 30 phút trước khi thử lại sau %{count} lần thử không thành công.",
375
375
  "authentication.signIn.changeYourPassword": "Quý vị có thể đổi mật khẩu",
376
376
  "authentication.signIn.enterLoginEmail": "Vui lòng nhập email đăng nhập của quý vị",
377
377
  "authentication.signIn.enterLoginPassword": "Vui lòng nhập mật khẩu đăng nhập của quý vị",
@@ -371,7 +371,7 @@
371
371
  "authentication.forgotPassword.sendEmail": "傳送電子郵件",
372
372
  "authentication.forgotPassword.success": "我們已向您傳送電子郵件。您會收到具有重設密碼連結的電子郵件。",
373
373
  "authentication.signIn.accountHasBeenLocked": "基於安全原因,此帳戶已遭到鎖定。",
374
- "authentication.signIn.afterFailedAttempts": "基於安全原因,只要失敗嘗試達 5 次,您就必須等待 30 分鐘才能再試一次。",
374
+ "authentication.signIn.afterFailedAttempts": "基於安全原因,只要失敗嘗試達 %{count} 次,您就必須等待 30 分鐘才能再試一次。",
375
375
  "authentication.signIn.changeYourPassword": "您可以變更密碼",
376
376
  "authentication.signIn.enterLoginEmail": "請輸入您的登入電子郵件",
377
377
  "authentication.signIn.enterLoginPassword": "請輸入您的登入密碼",
@@ -2,6 +2,7 @@
2
2
  ol {
3
3
  @apply flex;
4
4
  @apply items-center;
5
+ flex-wrap: wrap;
5
6
  line-height: 1.5rem;
6
7
  }
7
8
 
@@ -9,7 +9,7 @@ export interface BreadcrumbsProps {
9
9
  const BreadcrumbLink = (props: { href: string; children: React.ReactNode; current?: boolean }) => (
10
10
  <li>
11
11
  <LocalizedLink
12
- className={props.current ? "is-active" : undefined}
12
+ className={props.current ? "is-active text-gray-850" : "text-blue-700"}
13
13
  aria-current={props.current ? "page" : undefined}
14
14
  href={props.href}
15
15
  >
@@ -10,7 +10,11 @@ const FooterNav = (props: FooterNavProps) => (
10
10
  <section className="footer-sock">
11
11
  <div className="footer-sock__inner">
12
12
  <p className="footer-copyright">{props.copyright}</p>
13
- {props.children && <nav className="footer-nav">{props.children}</nav>}
13
+ {props.children && (
14
+ <nav className="footer-nav" aria-label={"Footer"}>
15
+ {props.children}
16
+ </nav>
17
+ )}
14
18
  </div>
15
19
  </section>
16
20
  )
@@ -16,7 +16,7 @@ const LanguageNav = ({ ariaLabel, languages }: LanguageNavProps) => {
16
16
  return (
17
17
  <div className="language-bar">
18
18
  <div className="language-bar__inner">
19
- <nav {...(ariaLabel ? { "aria-label": ariaLabel } : {})} className="language-nav">
19
+ <nav {...{ "aria-label": ariaLabel ?? "Language" }} className="language-nav">
20
20
  <ul className="language-nav__list">
21
21
  {languages.map((item) => (
22
22
  <li key={item.label}>
@@ -0,0 +1,47 @@
1
+ import { Canvas, Story, ArgsTable } from "@storybook/addon-docs"
2
+ import { ProgressNav } from "./ProgressNav"
3
+
4
+ # Progress Nav
5
+
6
+ The progress nav component is used to illustrate progress in a multi-step process. It displays the step labels, and uses color and layout to indicate which steps are completed, in-progress, and unstarted.
7
+
8
+ ## Dot Style
9
+
10
+ <Canvas>
11
+ <Story id="navigation-progress-nav--default" />
12
+ </Canvas>
13
+
14
+ ## Bar Style
15
+
16
+ <Canvas>
17
+ <Story id="navigation-progress-nav--bar-style" />
18
+ </Canvas>
19
+
20
+ You can apply CSS variables to the `.progrss-nav` selector to customize the appearance of the component.
21
+
22
+ | Name | Type | Description | Default |
23
+ | ----------------------------- | ----- | ------------------------------------------- | ------------------------------ |
24
+ | `--completed-step-color` | Color | The color of completed step | `--bloom-color-primary-darker` |
25
+ | `--completed-step-font-color` | Color | The color of completed step label | `--bloom-color-primary-darker` |
26
+ | `--active-step-color` | Color | The color of active step | `--bloom-color-primary` |
27
+ | `--active-step-font-color` | Color | The color of active step label | `--bloom-color-primary-darker` |
28
+ | `--future-step-color` | Color | The color of future step | `--bloom-color-gray-450` |
29
+ | `--future-step-font-color` | Color | The color of future step label | `--bloom-color-gray-750` |
30
+ | | | | |
31
+ | `--dot-size` | Size | The diameter of each dot step | `--bloom-s3` |
32
+ | `--dot-padding-left-mobile` | Size | The padding-left of each dot step on mobile | `--bloom-s2` |
33
+ | `--dot-label-padding-top` | Size | The padding-top of each dot step label | `--bloom-s4` |
34
+ | `--dot-label-padding-left` | Size | The padding-left of each dot step label | `--bloom-s1` |
35
+ | `--dot-font-size-desktop` | Size | The font size of dot step labels on desktop | `--bloom-font-size-base` |
36
+ | `--dot-font-size-mobile` | Size | The font size of dot step labels on mobile | `--bloom-font-size-2xs` |
37
+ | `--dot-line-color` | Color | The color of the dot connecting line | `--bloom-color-gray-450` |
38
+ | `--dot-active-font-weight` | Size | The font weight of active dot step label | `600` |
39
+ | `--dot-text-transform` | Size | The capitalization of dot step label | `capitalize` |
40
+ | | | | |
41
+ | `--bar-height` | Size | The height of bar step | `--bloom-s4` |
42
+ | `--bar-spacing` | Size | The spacing between each bar step | `--bloom-s0_5` |
43
+ | `--bar-label-padding-top` | Size | The padding-top of each bar step label | `--bloom-s2` |
44
+ | `--bar-label-padding-left` | Size | The padding-left of each bar step label | `--bloom-s0_5` |
45
+ | `--bar-font-size` | Size | The font size of bar step labels on desktop | `--bloom-font-size-base` |
46
+ | `--bar-active-font-weight` | Size | The font weight of active bar step label | `600` |
47
+ | `--bar-text-transform` | Size | The capitalization of dot step label | `capitalize` |
@@ -1,40 +1,54 @@
1
1
  .progress-nav {
2
- // @include clearfix;
3
- @apply list-none;
4
- @apply table;
5
- @apply m-0;
6
- @apply p-0;
7
- @apply w-full;
8
- }
2
+ --completed-step-color: var(--bloom-color-gray-850);
3
+ --completed-step-font-color: var(--bloom-color-gray-850);
4
+ --active-step-color: var(--bloom-color-primary);
5
+ --active-step-font-color: var(--bloom-color-gray-900);
6
+ --future-step-color: var(--bloom-color-gray-450);
7
+ --future-step-font-color: var(--bloom-color-gray-750);
9
8
 
10
- .progress-nav__item {
11
- @apply text-2xs;
12
- @apply px-1;
13
- @apply py-3;
14
- @apply relative;
15
- @apply text-center;
16
- @apply uppercase;
17
- @apply table-cell;
18
- @apply whitespace-nowrap;
19
- @apply float-none;
20
- max-width: 20%;
9
+ --dot-size: var(--bloom-s3);
10
+ --dot-padding-left-mobile: var(--bloom-s2);
11
+ --dot-label-padding-top: var(--bloom-s4);
12
+ --dot-label-padding-left: var(--bloom-s1);
13
+ --dot-font-size-desktop: var(--bloom-font-size-base);
14
+ --dot-font-size-mobile: var(--bloom-font-size-2xs);
15
+ --dot-line-color: var(--bloom-color-gray-450);
16
+ --dot-active-font-weight: bold;
17
+ --dot-text-transform: capitalize;
18
+
19
+ --bar-height: var(--bloom-s4);
20
+ --bar-spacing: var(--bloom-s0_5);
21
+ --bar-label-padding-top: var(--bloom-s2);
22
+ --bar-label-padding-left: var(--bloom-s0_5);
23
+ --bar-font-size: var(--bloom-font-size-base);
24
+ --bar-active-font-weight: bold;
25
+ --bar-text-transform: capitalize;
21
26
 
22
- @screen md {
23
- @apply text-sm;
24
- @apply p-0;
25
- @apply px-2;
27
+ display: table;
28
+ width: 100%;
29
+ }
30
+
31
+ .progress-nav__dot-item {
32
+ font-size: var(--dot-font-size-mobile);
33
+ padding: var(--dot-size) var(--dot-padding-left-mobile);
34
+ position: relative;
35
+ text-align: center;
36
+ text-transform: var(--dot-text-transform);
37
+ display: table-cell;
38
+ white-space: nowrap;
39
+ float: none;
40
+ @media (min-width: $screen-md) {
41
+ font-size: var(--dot-font-size-desktop);
42
+ padding: 0rem;
26
43
  }
27
44
 
28
45
  &:before {
29
- @apply absolute;
30
- @apply h-3;
31
- @apply w-3;
32
- @apply bg-lush;
33
- @apply border-solid;
34
- @apply border;
35
- @apply border-white;
36
- @apply rounded-full;
37
- @apply top-0;
46
+ position: absolute;
47
+ height: var(--dot-size);
48
+ width: var(--dot-size);
49
+ background-color: var(--completed-step-color);
50
+ border-radius: 50%;
51
+ top: 0;
38
52
  left: 50%;
39
53
  content: "";
40
54
  transform: translateX(-50%);
@@ -42,10 +56,10 @@
42
56
  }
43
57
 
44
58
  &:after {
45
- @apply absolute;
46
- @apply bg-gray-450;
47
- @apply w-full;
48
- @apply left-0;
59
+ position: absolute;
60
+ background-color: var(--dot-line-color);
61
+ width: 100%;
62
+ left: 0;
49
63
  top: 0.4rem;
50
64
  content: "";
51
65
  height: 1px;
@@ -59,45 +73,76 @@
59
73
 
60
74
  &:last-of-type {
61
75
  &:after {
62
- @apply left-auto;
76
+ left: auto;
63
77
  right: 50%;
64
78
  }
65
79
  }
80
+ .progress-nav__item-container {
81
+ padding-top: var(--dot-label-padding-top);
82
+ padding-left: var(--dot-label-padding-left);
83
+ color: var(--completed-step-font-color);
84
+ display: block;
85
+ position: relative;
86
+ pointer-events: none;
87
+ cursor: default;
88
+ z-index: 3;
89
+ }
66
90
 
67
91
  &.is-active {
68
92
  &:before {
69
- @apply h-3;
70
- @apply w-3;
71
- @apply bg-primary;
72
- @apply top-0;
93
+ background-color: var(--active-step-color);
73
94
  }
74
-
75
95
  .progress-nav__item-container {
76
- @apply text-black;
77
- @apply font-bold;
96
+ color: var(--active-step-font-color);
97
+ font-weight: var(--dot-active-font-weight);
78
98
  }
79
99
  }
80
100
 
81
101
  &.is-disabled {
82
102
  &:before {
83
- @apply h-3;
84
- @apply w-3;
85
- @apply bg-gray-450;
86
- @apply top-0;
103
+ background-color: var(--future-step-color);
87
104
  }
88
-
89
105
  .progress-nav__item-container {
90
- @apply pointer-events-none;
91
- @apply cursor-default;
106
+ color: var(--future-step-font-color);
92
107
  }
93
108
  }
109
+ }
110
+
111
+ .progress-nav__bar {
112
+ border-spacing: var(--bar-spacing);
113
+ table-layout: fixed;
114
+ }
94
115
 
116
+ .progress-nav__bar-item {
117
+ // drop the labels on mobile view
118
+ font-size: 0;
119
+ padding-top: var(--bar-label-padding-top);
120
+ padding-left: var(--bar-label-padding-left);
121
+ position: relative;
122
+ display: table-cell;
123
+ white-space: nowrap;
124
+ float: none;
125
+ max-width: 20%;
126
+ border-top-width: var(--bar-height);
127
+ border-top-color: var(--completed-step-color);
128
+ text-transform: var(--bar-text-transform);
129
+ @media (min-width: $screen-md) {
130
+ font-size: var(--bar-font-size);
131
+ }
132
+ &.is-active {
133
+ border-top-color: var(--active-step-color);
134
+ .progress-nav__item-container {
135
+ font-weight: var(--bar-active-font-weight);
136
+ color: var(--active-step-font-color);
137
+ }
138
+ }
139
+ &.is-disabled {
140
+ border-top-color: var(--future-step-color);
141
+ .progress-nav__item-container {
142
+ color: var(--future-step-font-color);
143
+ }
144
+ }
95
145
  .progress-nav__item-container {
96
- @apply pt-4;
97
- @apply pl-1;
98
- @apply text-gray-700;
99
- @apply block;
100
- @apply relative;
101
- z-index: 3;
146
+ color: var(--completed-step-font-color);
102
147
  }
103
148
  }
@@ -2,12 +2,20 @@ import React from "react"
2
2
  import "./ProgressNav.scss"
3
3
  import { t } from "../helpers/translator"
4
4
 
5
+ type ProgressNavStyle = "bar" | "dot"
6
+
5
7
  const ProgressNavItem = (props: {
6
8
  section: number
7
9
  currentPageSection: number
8
10
  completedSections: number
9
11
  label: string
10
12
  mounted: boolean
13
+ style: ProgressNavStyle
14
+ strings?: {
15
+ screenReaderCompleted?: string
16
+ screenReaderNotCompleted?: string
17
+ screenReaderTitle?: string
18
+ }
11
19
  }) => {
12
20
  let bgColor = "is-disabled"
13
21
  if (props.mounted) {
@@ -18,18 +26,32 @@ const ProgressNavItem = (props: {
18
26
  }
19
27
  }
20
28
 
21
- const srText =
22
- props.section === props.currentPageSection ? (
23
- <span className="sr-only">{t("progressNav.current")}</span>
24
- ) : (
25
- ""
26
- )
29
+ const srTextBuilder = (): string | React.ReactFragment => {
30
+ if (props.section < props.currentPageSection) {
31
+ return (
32
+ <span className="sr-only">
33
+ {props.strings?.screenReaderCompleted ?? t("progressNav.completed")}
34
+ </span>
35
+ )
36
+ } else if (props.section > props.currentPageSection) {
37
+ return (
38
+ <span className="sr-only">
39
+ {props.strings?.screenReaderNotCompleted ?? t("progressNav.notCompleted")}
40
+ </span>
41
+ )
42
+ } else {
43
+ return ""
44
+ }
45
+ }
27
46
 
28
47
  return (
29
- <li className={`progress-nav__item ${bgColor}`}>
30
- <span aria-disabled={bgColor === "is-disabled"} className={"progress-nav__item-container"}>
31
- {srText}
32
- {props.label}
48
+ <li className={`progress-nav__${props.style}-item ${bgColor}`}>
49
+ <span
50
+ aria-disabled={bgColor === "is-disabled"}
51
+ aria-current={bgColor === "is-active"}
52
+ className={"progress-nav__item-container"}
53
+ >
54
+ {props.label} {srTextBuilder()}
33
55
  </span>
34
56
  </li>
35
57
  )
@@ -40,23 +62,31 @@ const ProgressNav = (props: {
40
62
  completedSections: number
41
63
  labels: string[]
42
64
  mounted: boolean
65
+ style?: ProgressNavStyle
66
+ strings?: {
67
+ screenReaderHeading?: string
68
+ }
43
69
  }) => {
70
+ let navClasses = "progress-nav"
71
+ if (props.style === "bar") navClasses += " progress-nav__bar"
44
72
  return (
45
- <div>
46
- <h2 className="sr-only">{t("progressNav.srHeading")}</h2>
47
- <ul className={!props.mounted ? "invisible" : "progress-nav"}>
73
+ <div aria-label="progress">
74
+ <h2 className="sr-only">
75
+ {props.strings?.screenReaderHeading ?? t("progressNav.srHeading")}
76
+ </h2>
77
+ <ol className={!props.mounted ? "invisible" : navClasses}>
48
78
  {props.labels.map((label, i) => (
49
79
  <ProgressNavItem
50
80
  key={label}
51
- // Sections are 1-indexed
52
81
  section={i + 1}
53
82
  currentPageSection={props.currentPageSection}
54
83
  completedSections={props.completedSections}
55
84
  label={label}
56
85
  mounted={props.mounted}
86
+ style={props.style ?? "dot"}
57
87
  />
58
88
  ))}
59
- </ul>
89
+ </ol>
60
90
  </div>
61
91
  )
62
92
  }