@bloom-housing/ui-components 6.0.1-alpha.0 → 6.0.1-alpha.2

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/CHANGELOG.md CHANGED
@@ -3,6 +3,56 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [6.0.1-alpha.2](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@6.0.1-alpha.1...@bloom-housing/ui-components@6.0.1-alpha.2) (2022-09-29)
7
+
8
+
9
+ * 2938/card component (#3043) ([7a4a148](https://github.com/bloom-housing/bloom/commit/7a4a14891b4c8057f27a9f95e87a00d1d7a58be8)), closes [#3043](https://github.com/bloom-housing/bloom/issues/3043)
10
+
11
+
12
+ ### BREAKING CHANGES
13
+
14
+ * Renamed exported interfaces in ListingCard so as to not conflict with Card and other components
15
+
16
+ * Fix code style issues with Prettier
17
+
18
+ * feat: split HeadingGroup out to its own separate component
19
+
20
+ * fix: simplify stories and styling for Card and HeadingGroup
21
+
22
+ * remove: prototype "slotted" card component
23
+
24
+ * test: fix the incorrect component name in Card
25
+
26
+ Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
27
+
28
+
29
+
30
+
31
+
32
+ ## [6.0.1-alpha.1](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@6.0.1-alpha.0...@bloom-housing/ui-components@6.0.1-alpha.1) (2022-09-28)
33
+
34
+
35
+ ### Features
36
+
37
+ * add duplicates v2 feature ([11cd2b3](https://github.com/bloom-housing/bloom/commit/11cd2b3b4d39c69e10ca1c9a8c2c7199b4a08b6b)), closes [#3006](https://github.com/bloom-housing/bloom/issues/3006) [#2961](https://github.com/bloom-housing/bloom/issues/2961) [#3020](https://github.com/bloom-housing/bloom/issues/3020) [#3093](https://github.com/bloom-housing/bloom/issues/3093) [#2974](https://github.com/bloom-housing/bloom/issues/2974) [#2909](https://github.com/bloom-housing/bloom/issues/2909) [#2958](https://github.com/bloom-housing/bloom/issues/2958) [#2904](https://github.com/bloom-housing/bloom/issues/2904) [#2987](https://github.com/bloom-housing/bloom/issues/2987) [#2990](https://github.com/bloom-housing/bloom/issues/2990) [#2989](https://github.com/bloom-housing/bloom/issues/2989) [#2991](https://github.com/bloom-housing/bloom/issues/2991) [#2985](https://github.com/bloom-housing/bloom/issues/2985) [#2994](https://github.com/bloom-housing/bloom/issues/2994) [#2995](https://github.com/bloom-housing/bloom/issues/2995) [#2999](https://github.com/bloom-housing/bloom/issues/2999) [#2950](https://github.com/bloom-housing/bloom/issues/2950) [#2968](https://github.com/bloom-housing/bloom/issues/2968) [#2784](https://github.com/bloom-housing/bloom/issues/2784) [#2988](https://github.com/bloom-housing/bloom/issues/2988) [#3016](https://github.com/bloom-housing/bloom/issues/3016) [#3018](https://github.com/bloom-housing/bloom/issues/3018) [#3017](https://github.com/bloom-housing/bloom/issues/3017) [#3005](https://github.com/bloom-housing/bloom/issues/3005) [#3012](https://github.com/bloom-housing/bloom/issues/3012) [#3014](https://github.com/bloom-housing/bloom/issues/3014) [#3000](https://github.com/bloom-housing/bloom/issues/3000) [#3021](https://github.com/bloom-housing/bloom/issues/3021) [#3027](https://github.com/bloom-housing/bloom/issues/3027) [#3036](https://github.com/bloom-housing/bloom/issues/3036) [#3023](https://github.com/bloom-housing/bloom/issues/3023) [#3040](https://github.com/bloom-housing/bloom/issues/3040) [#3054](https://github.com/bloom-housing/bloom/issues/3054) [#3050](https://github.com/bloom-housing/bloom/issues/3050) [#483](https://github.com/bloom-housing/bloom/issues/483) [#3073](https://github.com/bloom-housing/bloom/issues/3073) [#3070](https://github.com/bloom-housing/bloom/issues/3070) [#3041](https://github.com/bloom-housing/bloom/issues/3041) [#3061](https://github.com/bloom-housing/bloom/issues/3061) [#3077](https://github.com/bloom-housing/bloom/issues/3077) [#3063](https://github.com/bloom-housing/bloom/issues/3063) [#3084](https://github.com/bloom-housing/bloom/issues/3084) [#3088](https://github.com/bloom-housing/bloom/issues/3088) [#2892](https://github.com/bloom-housing/bloom/issues/2892) [#3006](https://github.com/bloom-housing/bloom/issues/3006) [#2961](https://github.com/bloom-housing/bloom/issues/2961) [#3020](https://github.com/bloom-housing/bloom/issues/3020) [#3086](https://github.com/bloom-housing/bloom/issues/3086) [#3102](https://github.com/bloom-housing/bloom/issues/3102) [#3101](https://github.com/bloom-housing/bloom/issues/3101) [#3104](https://github.com/bloom-housing/bloom/issues/3104) [#3105](https://github.com/bloom-housing/bloom/issues/3105)
38
+
39
+
40
+ ### BREAKING CHANGES
41
+
42
+ * The preference and program entities have been merged into a single entity called MultiselectQuestion
43
+
44
+ * chore(release): version
45
+
46
+ - @bloom-housing/backend-core@5.1.1-alpha.8
47
+ - @bloom-housing/shared-helpers@5.1.1-alpha.18
48
+ - @bloom-housing/partners@5.1.1-alpha.20
49
+ - @bloom-housing/public@5.1.1-alpha.18
50
+ - @bloom-housing/ui-components@5.1.1-alpha.11
51
+
52
+
53
+
54
+
55
+
6
56
  ## [6.0.1-alpha.0](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.1.1-alpha.35...@bloom-housing/ui-components@6.0.1-alpha.0) (2022-09-28)
7
57
 
8
58
  **Note:** Version bump only for package @bloom-housing/ui-components
package/index.ts CHANGED
@@ -10,6 +10,7 @@ export * from "./src/actions/Video"
10
10
  /* Blocks */
11
11
  export * from "./src/blocks/ActionBlock"
12
12
  export * from "./src/blocks/StatusItem"
13
+ export * from "./src/blocks/Card"
13
14
  export * from "./src/blocks/DashBlock"
14
15
  export * from "./src/blocks/DashBlocks"
15
16
  export * from "./src/blocks/FormCard"
@@ -55,6 +56,7 @@ export * from "./src/headers/PageHeader"
55
56
  export * from "./src/headers/SiteHeader"
56
57
  export * from "./src/headers/StepHeader"
57
58
  export * from "./src/headers/Heading"
59
+ export * from "./src/headers/HeadingGroup"
58
60
 
59
61
  /* Helpers */
60
62
  export * from "./src/helpers/capitalize"
@@ -89,6 +91,7 @@ export * from "./src/navigation/ProgressNav"
89
91
  export * from "./src/navigation/TabNav"
90
92
  export * from "./src/navigation/Tabs"
91
93
  export * from "./src/navigation/Breadcrumbs"
94
+ export * from "./src/navigation/SideNav"
92
95
 
93
96
  /* Notifications */
94
97
  export * from "./src/notifications/AlertBox"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloom-housing/ui-components",
3
- "version": "6.0.1-alpha.0",
3
+ "version": "6.0.1-alpha.2",
4
4
  "author": "Sean Albert <sean.albert@exygy.com>",
5
5
  "description": "Shared user interface components for Bloom affordable housing system",
6
6
  "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared/ui-components",
@@ -110,5 +110,5 @@
110
110
  "ts-jest": "^26.4.1",
111
111
  "typesafe-actions": "^5.1.0"
112
112
  },
113
- "gitHead": "2cfc84763d17fad62ed2675f85a046e05bdf91e2"
113
+ "gitHead": "2a2bd0fb61d7643c31d4a200b96a78d401bd0590"
114
114
  }
@@ -0,0 +1,60 @@
1
+ import { Canvas, Story, ArgsTable } from "@storybook/addon-docs"
2
+ import { Swatch } from "../prototypes/Swatch"
3
+ import { Card } from "./Card"
4
+
5
+ # Card
6
+
7
+ <Canvas>
8
+ <Story id="blocks-card--text-content" />
9
+ </Canvas>
10
+
11
+ The 2nd-generation Card component is intended to serve as a flexible content container with a header, optional content sections, and a footer. Over time our intention is to "port" some other card types and content blocks in the system to use Card as their underlying structure.
12
+
13
+ Styles within the Card node tree are intended to set up sensible defaults without requiring the consumer to configure bespoke styling as they author a card. For example, rule lines will be drawn automatically between the various subcomponents within a card, and margin/padding will be set according to top-level variables.
14
+
15
+ You can author a Card using subcomponents simply by referencing them via a single Card import. For example, `<Card.Header>`, `<Card.Footer>` etc. In the case where you want to create a new type of card, you can import the subcomponents directly as `<CardHeader>`, `<CardFooter>`, etc. and place them in any container. It's still recommended you include the `.card` class on your custom container so you inherit a base set of CSS properties.
16
+
17
+ Note that the `<Card.Footer>` subcomponent doesn't inherently add rules or padding so that you can use other components with self-contained styling such as `<ButtonGroup>`. However, you can add `<Card.Section>` within `<Card.Footer>` to add rules and padding.
18
+
19
+ The `<Card.Header>` subcomponent will typically be paired with a child `<HeadingGroup>` for a title + subtitle grouping, but that's not a requirement.
20
+
21
+ You can browse through the various stories for different examples of how you'd pattern a card with various subcomponents. Many of the visual aspects of a Card can be customized using the CSS variables listed below. For example, this "Detroit style" example:
22
+
23
+ <Canvas>
24
+ <Story id="blocks-card--detroit-style" />
25
+ </Canvas>
26
+
27
+ ## Card Properties
28
+
29
+ <ArgsTable of={Card} />
30
+
31
+ ## Card.Header Properties
32
+
33
+ <ArgsTable of={Card.Header} />
34
+
35
+ ## Card.Section Properties
36
+
37
+ <ArgsTable of={Card.Section} />
38
+
39
+ ## Card.Footer Properties
40
+
41
+ <ArgsTable of={Card.Footer} />
42
+
43
+ ## Theming Variables
44
+
45
+ You can apply CSS variables to the `.card` selector to customize the appearance of both the component and various subcomponents.
46
+
47
+ | Name | Type | Description | Default |
48
+ | --------------------------------- | -------------------------------------------- | ---------------------------------------------------------------------------- | ------------------------ |
49
+ | `--background-color` | <Swatch colorVar="--bloom-color-white" /> | Background color of the card | `--bloom-color-white` |
50
+ | `--border-radius` | Size | Border radius of the card | `--bloom-rounded-lg` |
51
+ | `--border-width` | Size | Border width of the card (and rules by default) | `--bloom-border-1` |
52
+ | `--border-color` | <Swatch colorVar="--bloom-color-gray-450" /> | Border color of the card (and rules by default) | `--bloom-color-gray-450` |
53
+ | `--rule-width` | Size | Width of the inset rules | `--border-width` |
54
+ | `--rule-color` | <Swatch colorVar="--bloom-color-gray-450" /> | Color of the inset rules | `--border-color` |
55
+ | `--content-padding-block` | Size | The space above and below content sections | `--bloom-s8` |
56
+ | `--content-margin-inline-desktop` | Size | The space to the left and right of content sections for desktop-size screens | `--bloom-s16` |
57
+ | `--content-margin-inline-mobile` | Size | The space to the left and right of content sections for mobile-size screens | `--bloom-s4` |
58
+ | `--header-padding-block` | Size | The space above and below the header | `--bloom-s8` |
59
+ | `--header-margin-inline-desktop` | Size | The space to the left and right of the header for desktop-size screens | `--bloom-s16` |
60
+ | `--header-margin-inline-mobile` | Size | The space to the left and right of the header for mobile-size screens | `--bloom-s4` |
@@ -0,0 +1,65 @@
1
+ .card {
2
+ /* Component Variables */
3
+ --background-color: var(--bloom-color-white);
4
+ --border-radius: var(--bloom-rounded-lg);
5
+ --border-width: var(--bloom-border-1);
6
+ --border-color: var(--bloom-color-gray-450);
7
+ --rule-width: var(--border-width);
8
+ --rule-color: var(--border-color);
9
+
10
+ --content-padding-block: var(--bloom-s8);
11
+ --content-margin-inline-desktop: var(--bloom-s16);
12
+ --content-margin-inline-mobile: var(--bloom-s4);
13
+ --content-margin-inline: var(--content-margin-inline-desktop);
14
+
15
+ --header-padding-block: var(--bloom-s8) var(--bloom-s3);
16
+ --header-margin-inline-desktop: var(--content-margin-inline-desktop);
17
+ --header-margin-inline-mobile: var(--content-margin-inline-mobile);
18
+ --header-margin-inline: var(--header-margin-inline-desktop);
19
+
20
+ @media (max-width: $screen-sm) {
21
+ --content-margin-inline: var(--content-margin-inline-mobile);
22
+ --header-margin-inline: var(--header-margin-inline-mobile);
23
+ }
24
+
25
+ /* Default Styles */
26
+ background-color: var(--background-color);
27
+ border: var(--border-width) solid var(--border-color);
28
+ overflow: hidden;
29
+ border-radius: var(--border-radius);
30
+ display: flex;
31
+ flex-direction: column;
32
+ }
33
+
34
+ .card__header {
35
+ padding-block: var(--header-padding-block);
36
+ margin-inline: var(--header-margin-inline);
37
+ border-bottom: var(--rule-width) solid var(--rule-color);
38
+
39
+ &.is-flex {
40
+ display: flex;
41
+ justify-content: space-between;
42
+ align-items: center;
43
+ gap: var(--bloom-s7);
44
+ }
45
+ }
46
+
47
+ .card__header + .card__footer {
48
+ margin-block-start: var(--content-padding-block);
49
+ }
50
+
51
+ .card > *:nth-child(n + 3),
52
+ .card__footer > .card__section {
53
+ border-top: var(--rule-width) solid var(--rule-color);
54
+ }
55
+
56
+ .card__section {
57
+ padding-block: var(--content-padding-block);
58
+ margin-inline: var(--content-margin-inline);
59
+ }
60
+
61
+ /* Using the double-class specificity hack, proceed with caution: */
62
+ .card__footer.card__footer,
63
+ .card__footer.card__footer > :not(.card__section) + .card__section {
64
+ border-top: none;
65
+ }
@@ -0,0 +1,74 @@
1
+ import React from "react"
2
+ import "./Card.scss"
3
+
4
+ export interface CardHeaderProps {
5
+ /** An additional element(s) you can add to the side of the main child element(s) */
6
+ suffix?: React.ReactNode
7
+ /** Additional class name */
8
+ className?: string
9
+ children: React.ReactNode
10
+ }
11
+
12
+ const CardHeader = (props: CardHeaderProps) => {
13
+ const classNames = ["card__header"]
14
+ if (props.suffix) classNames.push("is-flex")
15
+ if (props.className) classNames.push(props.className)
16
+
17
+ return (
18
+ <header className={classNames.join(" ")}>
19
+ {props.children}
20
+ {props.suffix}
21
+ </header>
22
+ )
23
+ }
24
+
25
+ export interface CardSectionProps {
26
+ /**
27
+ * Whether to center the text within the section
28
+ * @default false
29
+ */
30
+ centered?: boolean
31
+ /** Additional class name */
32
+ className?: string
33
+ children: React.ReactNode
34
+ }
35
+
36
+ const CardSection = (props: CardSectionProps) => {
37
+ const classNames = ["card__section"]
38
+ if (props.centered) classNames.push("text-center")
39
+ if (props.className) classNames.push(props.className)
40
+
41
+ return <div className={classNames.join(" ")}>{props.children}</div>
42
+ }
43
+
44
+ export interface CardFooterProps {
45
+ /** Additional class name */
46
+ className?: string
47
+ children: React.ReactNode
48
+ }
49
+
50
+ const CardFooter = (props: CardFooterProps) => {
51
+ const classNames = ["card__footer"]
52
+ if (props.className) classNames.push(props.className)
53
+
54
+ return <footer className={classNames.join(" ")}>{props.children}</footer>
55
+ }
56
+
57
+ export interface CardProps {
58
+ /** Additional class name */
59
+ className?: string
60
+ children: React.ReactNode
61
+ }
62
+
63
+ const Card = (props: CardProps) => {
64
+ const classNames = ["card"]
65
+ if (props.className) classNames.push(props.className)
66
+
67
+ return <article className={classNames.join(" ")}>{props.children}</article>
68
+ }
69
+
70
+ Card.Header = CardHeader
71
+ Card.Section = CardSection
72
+ Card.Footer = CardFooter
73
+
74
+ export { Card as default, Card, CardHeader, CardSection, CardFooter }
@@ -11,6 +11,12 @@
11
11
  )
12
12
  );
13
13
 
14
+ --ag-selected-row-background-color: var(--bloom-color-primary-light);
15
+
16
+ a {
17
+ color: var(--bloom-color-primary-dark);
18
+ }
19
+
14
20
  .ag-row {
15
21
  height: ag-param(row-height);
16
22
  }
@@ -72,6 +78,15 @@
72
78
  @apply border-b-0;
73
79
  }
74
80
 
81
+ .ag-pinned-right-header,
82
+ .ag-cell.ag-cell-first-right-pinned:not(.ag-cell-range-left):not(.ag-cell-range-single-cell) {
83
+ @apply border-gray-450;
84
+ @apply border-r-0;
85
+ @apply border-t-0;
86
+ @apply border-l-4;
87
+ @apply border-b-0;
88
+ }
89
+
75
90
  .ag-row {
76
91
  @apply border-t-0;
77
92
  @apply border-l-0;
@@ -85,29 +100,48 @@
85
100
 
86
101
  .ag-body-viewport {
87
102
  ::-webkit-scrollbar {
88
- // -webkit-appearance: none;
89
103
  height: 8px;
90
- @apply bg-gray-600;
91
104
  }
92
105
 
93
106
  ::-webkit-scrollbar-thumb {
94
107
  border-radius: 8px;
95
- @apply bg-gray-600;
108
+ @apply bg-gray-550;
96
109
  }
110
+ }
97
111
 
98
- ::-webkit-scrollbar-track {
99
- @apply bg-gray-100;
100
- -webkit-box-shadow: inset 0 0 1px rgba(0, 0, 0, 0.7);
101
- box-shadow: inset 0 0 1px rgba(0, 0, 0, 0.7);
102
- }
112
+ .ag-body-horizontal-scroll {
113
+ border-bottom: 1px solid var(--bloom-color-gray-500);
114
+ border-radius: var(--bloom-rounded-md);
103
115
  }
104
116
 
105
117
  .ag-root-wrapper {
106
118
  @apply border-b-0;
107
119
  @apply rounded-t-md;
108
- @apply rounded-b-none;
120
+ @apply rounded-b-md;
109
121
  overflow: visible;
110
122
  }
123
+
124
+ .ag-layout-auto-height {
125
+ .ag-center-cols-container,
126
+ .ag-center-cols-clipper {
127
+ --table-min-height: 124px;
128
+ min-height: var(--table-min-height);
129
+ }
130
+ }
131
+
132
+ .ag-ltr {
133
+ .ag-selection-checkbox {
134
+ margin-right: var(--bloom-s6);
135
+ }
136
+ }
137
+
138
+ .ag-horizontal-right-spacer:not(.ag-scroller-corner) {
139
+ border: none;
140
+ }
141
+
142
+ .ag-horizontal-left-spacer:not(.ag-scroller-corner) {
143
+ border: none;
144
+ }
111
145
  }
112
146
 
113
147
  .data-pager {
@@ -22,10 +22,11 @@ const HeaderStyleMap = {
22
22
  const Heading = (props: HeadingProps) => {
23
23
  const priority = props.priority && props.priority >= 1 && props.priority <= 6 ? props.priority : 1
24
24
  const Tag = `h${priority}` as keyof JSX.IntrinsicElements
25
- const classNames = `${props.style && HeaderStyleMap[props.style]} ${
26
- props.className && props.className
27
- }`
28
- return <Tag className={classNames}>{props.children}</Tag>
25
+ const classNames = []
26
+ if (props.style) classNames.push(HeaderStyleMap[props.style])
27
+ if (props.className) classNames.push(props.className)
28
+
29
+ return <Tag className={classNames.join(" ")}>{props.children}</Tag>
29
30
  }
30
31
 
31
32
  export { Heading as default, Heading }
@@ -0,0 +1,25 @@
1
+ import { Canvas, Story, ArgsTable } from "@storybook/addon-docs"
2
+ import { HeadingGroup } from "./HeadingGroup"
3
+
4
+ # Heading Group
5
+
6
+ <Canvas>
7
+ <Story id="headers-heading-group--with-content" />
8
+ </Canvas>
9
+
10
+ The HeadingGroup component provides a heading tag (h1-h6) combined with a paragraph tag as a subheading in an aria-minded fashion. Good for use in Card header components and other headers.
11
+
12
+ ## Heading Group Properties
13
+
14
+ <ArgsTable of={HeadingGroup} />
15
+
16
+ ## Theming Variables
17
+
18
+ You can apply CSS variables to the `.heading-group` selector to customize the appearance of the component.
19
+
20
+ | Name | Type | Description | Default |
21
+ | --------------------- | ----- | --------------------------------------------------- | ------------ |
22
+ | `--heading-margin` | Size | Vertical space added around the top heading, if any | `0rem` |
23
+ | `--subheading-margin` | Size | The space between the heading and subheading | `--bloom-s3` |
24
+ | `--heading-color` | Color | Override to set a specific heading text color | `inherit` |
25
+ | `--subheading-color` | Color | Override to set a specific subheading text color | `inherit` |
@@ -0,0 +1,23 @@
1
+ .heading-group {
2
+ /* Component Variables */
3
+ --heading-margin: 0rem;
4
+ --subheading-margin: var(--bloom-s3);
5
+ --heading-color: inherit;
6
+ --subheading-color: inherit;
7
+
8
+ h1,
9
+ h2,
10
+ h3,
11
+ h4,
12
+ h5,
13
+ h6 {
14
+ margin-block: var(--heading-margin);
15
+ color: var(--heading-color);
16
+ }
17
+
18
+ p {
19
+ margin-block: var(--subheading-margin);
20
+ font-size: var(--bloom-font-size-base);
21
+ color: var(--subheading-color);
22
+ }
23
+ }
@@ -0,0 +1,31 @@
1
+ import React from "react"
2
+ import { Heading } from "./Heading"
3
+ import "./HeadingGroup.scss"
4
+
5
+ export interface HeadingGroupProps {
6
+ /** A string or element to display in an `h2` tag (overridable via `headingPriority`) */
7
+ heading: React.ReactNode
8
+ /** A string or element to display in an `p` tag (using `aria-roledescription="subtitle"`) */
9
+ subheading: React.ReactNode
10
+ /**
11
+ * The heading level (1 through 6)
12
+ * @default 2
13
+ */
14
+ headingPriority?: number
15
+ /** Additional class name for the whole group */
16
+ className?: string
17
+ }
18
+
19
+ const HeadingGroup = (props: HeadingGroupProps) => {
20
+ const classNames = ["heading-group"]
21
+ if (props.className) classNames.push(props.className)
22
+
23
+ return (
24
+ <hgroup className={classNames.join(" ")} role="group">
25
+ <Heading priority={props.headingPriority ?? 2}>{props.heading}</Heading>
26
+ <p aria-roledescription="subtitle">{props.subheading}</p>
27
+ </hgroup>
28
+ )
29
+ }
30
+
31
+ export { HeadingGroup as default, HeadingGroup }
@@ -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);
@@ -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
 
@@ -8,9 +8,11 @@
8
8
  --selection-parent-accent: var(--bloom-color-gray-450);
9
9
  --hover-link-color: var(--bloom-color-primary);
10
10
  --hover-background-color: var(--bloom-color-primary-lighter);
11
+ --background-color: var(--bloom-color-white);
11
12
 
12
13
  border: var(--border);
13
14
  border-radius: var(--border-radius);
15
+ background-color: var(--background-color);
14
16
 
15
17
  & > ul {
16
18
  & > li:first-child > a {
@@ -70,3 +72,57 @@
70
72
  padding-inline-start: calc(var(--current-padding-inline) + var(--current-padding-block));
71
73
  }
72
74
  }
75
+
76
+ .side-nav__horizontal {
77
+ display: flex;
78
+
79
+ ul {
80
+ width: 100%;
81
+ }
82
+
83
+ @media (min-width: $screen-sm) {
84
+ a {
85
+ &[aria-current]:not(:focus) {
86
+ box-shadow: inset 0px -3px 0px 0px var(--hover-link-color);
87
+ }
88
+
89
+ &.has-current-child:not(:focus) {
90
+ box-shadow: inset 0px 0px -3px 0px var(--selection-parent-accent);
91
+ }
92
+ }
93
+
94
+ ul {
95
+ width: auto;
96
+ display: flex;
97
+ flex-direction: row;
98
+ @media (min-width: $screen-md) {
99
+ flex-direction: row;
100
+ }
101
+ @media (max-width: $screen-sm) {
102
+ flex-direction: column;
103
+ width: 100%;
104
+ }
105
+ }
106
+
107
+ li:not(:last-child) {
108
+ border-right: var(--border);
109
+ border-bottom: 0px;
110
+ }
111
+
112
+ & > ul {
113
+ & > li:first-child > a {
114
+ border-bottom-left-radius: var(--border-radius);
115
+ border-bottom-right-radius: 0px;
116
+ }
117
+
118
+ & > li:last-child > a {
119
+ border-bottom-right-radius: var(--border-radius);
120
+ border-bottom-left-radius: 0px;
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ .side-nav__count {
127
+ margin-left: var(--bloom-s4);
128
+ }
@@ -11,8 +11,8 @@ export interface SideNavItemProps {
11
11
  }
12
12
 
13
13
  export interface SideNavProps {
14
- navItems?: SideNavItemProps[]
15
14
  className?: string
15
+ navItems?: SideNavItemProps[]
16
16
  }
17
17
 
18
18
  const ItemLabel = ({ item }: { item: SideNavItemProps }) => {
@@ -20,7 +20,7 @@ const ItemLabel = ({ item }: { item: SideNavItemProps }) => {
20
20
  return (
21
21
  <>
22
22
  <span>{item.label}</span>
23
- <span>{item.count}</span>
23
+ <span className={"side-nav__count"}>{item.count}</span>
24
24
  </>
25
25
  )
26
26
  } else {
@@ -10,6 +10,29 @@
10
10
  }
11
11
  }
12
12
 
13
+ .tabs__horizontal {
14
+ ul {
15
+ @apply flex;
16
+ @apply flex-row;
17
+ }
18
+
19
+ .tabs__tab {
20
+ &:first-of-type {
21
+ @apply border-l;
22
+ @apply rounded-tl-lg;
23
+ @apply rounded-tr-lg;
24
+ @apply rounded-tr-none;
25
+ @apply rounded-bl-lg;
26
+ }
27
+ &:last-of-type {
28
+ @apply rounded-tr-lg;
29
+ @apply rounded-br-lg;
30
+ }
31
+
32
+ border-bottom-color: var(--bloom-color-gray-450);
33
+ }
34
+ }
35
+
13
36
  .tabs__tab {
14
37
  @apply bg-white;
15
38
  @apply text-gray-800;
@@ -30,14 +53,10 @@
30
53
  @apply border-l;
31
54
  @apply rounded-tl-lg;
32
55
  @apply rounded-tr-lg;
33
- @screen md {
34
- @apply rounded-tr-none;
35
- }
56
+ @apply rounded-tr-none;
36
57
  }
37
58
  &:last-of-type {
38
- @screen md {
39
- @apply rounded-tr-lg;
40
- }
59
+ @apply rounded-tr-lg;
41
60
  }
42
61
  &:hover {
43
62
  border-bottom-color: map-get($tailwind-gray, 400);
@@ -17,12 +17,12 @@ type NavigationHeaderTabs = {
17
17
  flagsQty?: number
18
18
  listingLabel: string
19
19
  applicationsLabel: string
20
- flagsLabel: string
21
20
  }
22
21
 
23
22
  type NavigationHeaderTabsElement = {
24
23
  label: string
25
24
  path: string
25
+ activePaths: string[]
26
26
  content: React.ReactNode | undefined
27
27
  }
28
28
 
@@ -42,23 +42,23 @@ const NavigationHeader = ({
42
42
  {
43
43
  label: tabs?.listingLabel || "",
44
44
  path: `/listings/${listingId}`,
45
+ activePaths: [`/listings/${listingId}`],
45
46
  content: undefined,
46
47
  },
47
48
  {
48
49
  label: tabs?.applicationsLabel || "",
49
50
  path: `/listings/${listingId}/applications`,
51
+ activePaths: [
52
+ `/listings/${listingId}/applications`,
53
+ `/listings/${listingId}/applications/pending`,
54
+ `/listings/${listingId}/applications/pending?type=name_dob`,
55
+ `/listings/${listingId}/applications/pending?type=email`,
56
+ `/listings/${listingId}/applications/resolved`,
57
+ ],
50
58
  content: undefined,
51
59
  },
52
60
  ]
53
61
 
54
- if (process.env.showDuplicates && typeof tabs?.flagsQty === "number") {
55
- elements.push({
56
- label: tabs.flagsLabel,
57
- path: `/listings/${listingId}/flags`,
58
- content: <>{tabs.flagsQty}</>,
59
- })
60
- }
61
-
62
62
  return elements
63
63
  }, [tabs, listingId])
64
64
 
@@ -69,7 +69,7 @@ const NavigationHeader = ({
69
69
  <TabNavItem
70
70
  key={tab.path}
71
71
  tagContent={tab?.content}
72
- current={tab.path === currentPath}
72
+ current={tab.activePaths.includes(currentPath)}
73
73
  href={tab.path}
74
74
  tagSize={AppearanceSizeType.small}
75
75
  >
@@ -12,7 +12,7 @@ import { NavigationContext } from "../../config/NavigationContext"
12
12
 
13
13
  interface ListingCardTableProps extends StandardTableProps, StackedTableProps {}
14
14
 
15
- export interface CardHeader {
15
+ export interface ListingCardHeader {
16
16
  content: string | React.ReactNode
17
17
  href?: string
18
18
  customClass?: string
@@ -20,17 +20,17 @@ export interface CardHeader {
20
20
  isPillType?: boolean
21
21
  }
22
22
 
23
- export interface FooterButton {
23
+ export interface ListingFooterButton {
24
24
  href: string
25
25
  text: string
26
26
  ariaHidden?: boolean
27
27
  }
28
28
 
29
29
  export interface ListingCardContentProps {
30
- contentHeader?: CardHeader
31
- contentSubheader?: CardHeader
32
- tableHeader?: CardHeader
33
- tableSubheader?: CardHeader
30
+ contentHeader?: ListingCardHeader
31
+ contentSubheader?: ListingCardHeader
32
+ tableHeader?: ListingCardHeader
33
+ tableSubheader?: ListingCardHeader
34
34
  }
35
35
  export interface ListingCardProps {
36
36
  /** A list of tags to be rendered below the content header, a Tag component is rendered for each */
@@ -40,7 +40,7 @@ export interface ListingCardProps {
40
40
  /** An object containing fields that render optional headers above the content section's table */
41
41
  contentProps?: ListingCardContentProps
42
42
  /** A list of buttons to render in the footer of the content section */
43
- footerButtons?: FooterButton[]
43
+ footerButtons?: ListingFooterButton[]
44
44
  /** A class name applied to the footer container of the content section */
45
45
  footerContainerClass?: string
46
46
  /** Custom content rendered below the content table */
@@ -74,7 +74,7 @@ const ListingCard = (props: ListingCardProps) => {
74
74
  const { LinkComponent } = useContext(NavigationContext)
75
75
 
76
76
  const getHeader = (
77
- header: CardHeader | undefined,
77
+ header: ListingCardHeader | undefined,
78
78
  priority: number,
79
79
  styleType?: HeaderType,
80
80
  customClass?: string
@@ -1,7 +1,14 @@
1
1
  import React, { useState, useCallback, useEffect, useRef } from "react"
2
2
  import { useForm } from "react-hook-form"
3
3
  import { AgGridReact } from "ag-grid-react"
4
- import { GridOptions, ColumnState, ColumnApi, ColDef, ColGroupDef } from "ag-grid-community"
4
+ import {
5
+ GridOptions,
6
+ ColumnState,
7
+ ColumnApi,
8
+ ColDef,
9
+ ColGroupDef,
10
+ GridApi,
11
+ } from "ag-grid-community"
5
12
  import { AgPagination, AG_PER_PAGE_OPTIONS } from "./AgPagination"
6
13
  import { LoadingOverlay } from "../overlays/LoadingOverlay"
7
14
  import { Field } from "../forms/Field"
@@ -15,11 +22,12 @@ export interface ColumnOrder {
15
22
  }
16
23
 
17
24
  export interface AgTableProps {
18
- id: string
19
25
  config: AgTableConfig
20
26
  data: AgTableData
21
- pagination: AgTablePagination
27
+ id: string
28
+ pagination?: AgTablePagination
22
29
  search: AgTableSearch
30
+ selectConfig?: AgTableSelectConfig
23
31
  sort?: AgTableSort
24
32
  headerContent?: React.ReactNode
25
33
  className?: string
@@ -36,11 +44,17 @@ export interface AgTablePagination {
36
44
  setCurrentPage: React.Dispatch<React.SetStateAction<number>>
37
45
  }
38
46
 
47
+ export interface AgTableSelectConfig {
48
+ setGridApi: React.Dispatch<React.SetStateAction<GridApi | null>>
49
+ updateSelectedValues: () => void
50
+ }
51
+
39
52
  export interface AgTableConfig {
40
53
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
54
  gridComponents?: { [p: string]: any }
42
55
  columns: (ColDef | ColGroupDef)[]
43
56
  totalItemsLabel: string
57
+ rowSelection?: boolean
44
58
  }
45
59
 
46
60
  export interface AgTableData {
@@ -53,6 +67,7 @@ export interface AgTableData {
53
67
 
54
68
  export interface AgTableSearch {
55
69
  setSearch: React.Dispatch<React.SetStateAction<string>>
70
+ showSearch?: boolean
56
71
  }
57
72
 
58
73
  export interface AgTableSort {
@@ -85,14 +100,15 @@ export const useAgTable = () => {
85
100
  }
86
101
 
87
102
  const AgTable = ({
88
- id,
89
103
  className,
104
+ config: { gridComponents, columns, totalItemsLabel, rowSelection },
105
+ data,
106
+ headerContent,
107
+ id,
108
+ selectConfig,
90
109
  pagination,
91
- search: { setSearch },
110
+ search: { setSearch, showSearch = true },
92
111
  sort: { setSort } = {},
93
- headerContent,
94
- data,
95
- config: { gridComponents, columns, totalItemsLabel },
96
112
  strings,
97
113
  }: AgTableProps) => {
98
114
  // local storage key with column state
@@ -148,7 +164,7 @@ const AgTable = ({
148
164
  const debounceFilter = useRef(
149
165
  debounce((value: string) => {
150
166
  setSearch(value)
151
- pagination.setCurrentPage(1)
167
+ pagination?.setCurrentPage(1)
152
168
  }, 500)
153
169
  )
154
170
  useEffect(() => {
@@ -184,12 +200,15 @@ const AgTable = ({
184
200
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
185
201
  const onGridReady = (params: any) => {
186
202
  setGridColumnApi(params.columnApi)
203
+ if (selectConfig?.setGridApi) {
204
+ selectConfig.setGridApi(params.api)
205
+ }
187
206
  }
188
207
 
189
208
  return (
190
209
  <div className={`ag-theme-alpine ag-theme-bloom ${className || ""}`}>
191
210
  <div className="flex justify-between flex-col md:flex-row">
192
- <div className="flex flex-wrap">
211
+ <div className={`flex flex-wrap ${showSearch ? "mb-5" : "hidden"}`}>
193
212
  <div className="md:mr-5 w-full md:w-56">
194
213
  <Field
195
214
  dataTestId="ag-search-input"
@@ -200,7 +219,7 @@ const AgTable = ({
200
219
  placeholder={strings?.filter ?? t("t.filter")}
201
220
  />
202
221
  </div>
203
- <div className="w-full md:w-auto mt-2 mb-2 md:mb-0">
222
+ <div className="w-full md:w-auto mt-2 md:mt-0 mb-2 md:mb-0">
204
223
  {!validSearch && (
205
224
  <AlertBox type="notice">
206
225
  {strings?.searchError ?? t("applications.table.searchError")}
@@ -211,8 +230,7 @@ const AgTable = ({
211
230
 
212
231
  {headerContent}
213
232
  </div>
214
-
215
- <div className="applications-table mt-5">
233
+ <div className="applications-table">
216
234
  <LoadingOverlay isLoading={data.loading}>
217
235
  <div>
218
236
  <AgGridReact
@@ -227,19 +245,25 @@ const AgTable = ({
227
245
  suppressPaginationPanel={true}
228
246
  paginationPageSize={AG_PER_PAGE_OPTIONS[0]}
229
247
  suppressScrollOnNewData={true}
248
+ rowSelection={rowSelection ? "multiple" : undefined}
249
+ rowMultiSelectWithClick={rowSelection}
250
+ onRowDataChanged={selectConfig?.updateSelectedValues ?? undefined}
251
+ onFirstDataRendered={selectConfig?.updateSelectedValues ?? undefined}
230
252
  ></AgGridReact>
231
253
  </div>
232
254
  </LoadingOverlay>
233
255
 
234
- <AgPagination
235
- totalItems={data.totalItems}
236
- totalPages={data.totalPages}
237
- currentPage={pagination.currentPage}
238
- itemsPerPage={pagination.perPage}
239
- quantityLabel={totalItemsLabel}
240
- setCurrentPage={pagination.setCurrentPage}
241
- setItemsPerPage={pagination.setPerPage}
242
- />
256
+ {pagination && (
257
+ <AgPagination
258
+ totalItems={data.totalItems}
259
+ totalPages={data.totalPages}
260
+ currentPage={pagination.currentPage}
261
+ itemsPerPage={pagination.perPage}
262
+ quantityLabel={totalItemsLabel}
263
+ setCurrentPage={pagination.setCurrentPage}
264
+ setItemsPerPage={pagination.setPerPage}
265
+ />
266
+ )}
243
267
  </div>
244
268
  </div>
245
269
  )