@cfpb/cfpb-design-system 4.1.0 → 4.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.
Files changed (83) hide show
  1. package/CHANGELOG.md +48 -1
  2. package/dist/base/index.css +1 -1
  3. package/dist/base/index.css.map +2 -2
  4. package/dist/base/index.js +1 -1
  5. package/dist/base/index.js.map +1 -1
  6. package/dist/components/cfpb-buttons/index.css +1 -1
  7. package/dist/components/cfpb-buttons/index.css.map +2 -2
  8. package/dist/components/cfpb-buttons/index.js +1 -1
  9. package/dist/components/cfpb-buttons/index.js.map +1 -1
  10. package/dist/components/cfpb-expandables/index.css +1 -1
  11. package/dist/components/cfpb-expandables/index.css.map +2 -2
  12. package/dist/components/cfpb-expandables/index.js.map +1 -1
  13. package/dist/components/cfpb-forms/index.css +1 -1
  14. package/dist/components/cfpb-forms/index.css.map +2 -2
  15. package/dist/components/cfpb-forms/index.js +1 -1
  16. package/dist/components/cfpb-forms/index.js.map +1 -1
  17. package/dist/components/cfpb-icons/index.css +1 -1
  18. package/dist/components/cfpb-icons/index.css.map +2 -2
  19. package/dist/components/cfpb-icons/index.js +1 -1
  20. package/dist/components/cfpb-icons/index.js.map +1 -1
  21. package/dist/components/cfpb-layout/index.css +1 -1
  22. package/dist/components/cfpb-layout/index.css.map +2 -2
  23. package/dist/components/cfpb-layout/index.js +1 -1
  24. package/dist/components/cfpb-layout/index.js.map +1 -1
  25. package/dist/components/cfpb-notifications/index.css +1 -1
  26. package/dist/components/cfpb-notifications/index.css.map +2 -2
  27. package/dist/components/cfpb-notifications/index.js +1 -1
  28. package/dist/components/cfpb-notifications/index.js.map +1 -1
  29. package/dist/components/cfpb-tooltips/index.css +1 -1
  30. package/dist/components/cfpb-tooltips/index.css.map +2 -2
  31. package/dist/components/cfpb-tooltips/index.js.map +1 -1
  32. package/dist/components/cfpb-typography/index.css +1 -1
  33. package/dist/components/cfpb-typography/index.css.map +2 -2
  34. package/dist/components/cfpb-typography/index.js +1 -1
  35. package/dist/components/cfpb-typography/index.js.map +1 -1
  36. package/dist/elements/cfpb-button/index.js +13 -4
  37. package/dist/elements/cfpb-button/index.js.map +4 -4
  38. package/dist/elements/cfpb-file-upload/index.js +4 -4
  39. package/dist/elements/cfpb-file-upload/index.js.map +4 -4
  40. package/dist/elements/cfpb-icon-text/index.js +29 -0
  41. package/dist/elements/cfpb-icon-text/index.js.map +7 -0
  42. package/dist/elements/cfpb-label/index.js.map +1 -1
  43. package/dist/elements/cfpb-multiselect/index.js +2 -2
  44. package/dist/elements/cfpb-multiselect/index.js.map +2 -2
  45. package/dist/elements/cfpb-pagination/index.js +32 -0
  46. package/dist/elements/cfpb-pagination/index.js.map +7 -0
  47. package/dist/elements/cfpb-tag-filter/index.js.map +1 -1
  48. package/dist/elements/cfpb-tag-topic/index.js +3 -3
  49. package/dist/elements/cfpb-tag-topic/index.js.map +2 -2
  50. package/dist/elements/cfpb-utilities/index.js +2 -0
  51. package/dist/elements/cfpb-utilities/index.js.map +7 -0
  52. package/dist/elements/index.js +7 -6
  53. package/dist/elements/index.js.map +4 -4
  54. package/dist/index.css +1 -1
  55. package/dist/index.css.map +2 -2
  56. package/dist/index.js +7 -6
  57. package/dist/index.js.map +4 -4
  58. package/package.json +2 -2
  59. package/src/base/base.scss +14 -26
  60. package/src/components/cfpb-buttons/button.scss +4 -2
  61. package/src/components/cfpb-forms/tag.scss +3 -0
  62. package/src/components/cfpb-icons/icon.scss +1 -1
  63. package/src/components/cfpb-layout/card.scss +8 -11
  64. package/src/components/cfpb-pagination/vars.scss +0 -4
  65. package/src/components/cfpb-typography/link.scss +4 -2
  66. package/src/components/cfpb-typography/mixins.scss +9 -3
  67. package/src/elements/cfpb-button/cfpb-button.component.scss +15 -0
  68. package/src/elements/cfpb-button/index.js +52 -27
  69. package/src/elements/cfpb-icon-text/cfpb-icon-text.component.scss +60 -0
  70. package/src/elements/cfpb-icon-text/index.js +150 -0
  71. package/src/elements/cfpb-label/index.js +4 -3
  72. package/src/elements/cfpb-pagination/cfpb-pagination.component.scss +72 -0
  73. package/src/elements/cfpb-pagination/index.js +211 -0
  74. package/src/elements/cfpb-tag-filter/index.js +1 -0
  75. package/src/elements/cfpb-tag-topic/cfpb-tag-topic.component.scss +2 -0
  76. package/src/elements/cfpb-tag-topic/index.js +1 -0
  77. package/src/elements/cfpb-utilities/i18n-service.js +128 -0
  78. package/src/elements/cfpb-utilities/i18n-service.spec.js +156 -0
  79. package/src/elements/cfpb-utilities/index.js +7 -0
  80. package/src/elements/cfpb-utilities/media-query-service.js +102 -0
  81. package/src/elements/cfpb-utilities/media-query-service.spec.js +126 -0
  82. package/src/elements/index.js +1 -0
  83. package/src/utilities/utilities.scss +8 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfpb/cfpb-design-system",
3
- "version": "4.1.0",
3
+ "version": "4.2.1",
4
4
  "description": "CFPB's UI framework",
5
5
  "exports": {
6
6
  ".": "./src/index.js",
@@ -28,7 +28,7 @@
28
28
  "gitHead": "d9b9862ef0a34a0ca6f4835347ac7f202ed50e3e",
29
29
  "type": "module",
30
30
  "dependencies": {
31
- "@fontsource-variable/source-sans-3": "5.2.8",
31
+ "@fontsource-variable/source-sans-3": "5.2.9",
32
32
  "tippy.js": "6.3.7"
33
33
  }
34
34
  }
@@ -153,60 +153,48 @@ li {
153
153
  //
154
154
 
155
155
  a {
156
- border-width: 0;
157
- border-style: dotted;
158
- border-color: $link-underline;
159
156
  color: $link-text;
160
- text-decoration: none;
157
+
158
+ text-decoration-color: $link-underline;
159
+ text-decoration-line: underline;
160
+ text-decoration-thickness: 1px;
161
+ text-decoration-style: dotted;
162
+ text-underline-offset: 4.5px;
161
163
 
162
164
  // Note: The class definitions below are only for use in
163
165
  // demonstrating link states. Do not use in production.
164
166
 
165
167
  &:visited,
166
168
  &.visited {
167
- border-color: $link-underline-visited;
169
+ text-decoration-color: $link-underline-visited;
168
170
  color: $link-text-visited;
169
171
  }
170
172
 
171
173
  &:hover,
172
174
  &.hover {
173
- border-style: solid;
174
- border-color: $link-underline-hover;
175
+ text-decoration-style: solid;
176
+ text-decoration-color: $link-underline-hover;
175
177
  color: $link-text-hover;
176
178
  }
177
179
 
178
180
  &:focus,
179
181
  &.focus {
180
- border-style: solid;
182
+ text-decoration-style: solid;
181
183
  outline: thin dotted;
182
184
  outline-offset: 1px;
183
185
  }
184
186
 
185
187
  &:active,
186
188
  &.active {
187
- border-style: solid;
188
- border-color: $link-underline-active;
189
+ text-decoration-style: solid;
190
+ text-decoration-color: $link-underline-active;
189
191
  color: $link-text-active;
190
192
  }
191
193
  }
192
194
 
193
- //
194
- // Underlined links
195
- //
196
-
197
- p,
198
- li,
199
- dd {
200
- // Restrict bottom borders to inline text links ...
201
-
202
- a {
203
- border-bottom-width: 1px;
204
- }
205
- }
206
-
207
195
  nav a {
208
- // ... unless they're part of a nav list
209
- border-bottom-width: 0;
196
+ // Don't show underlines if they're part of a nav list.
197
+ text-decoration-line: none;
210
198
  }
211
199
 
212
200
  //
@@ -141,7 +141,8 @@ input.a-btn::-moz-focus-inner {
141
141
  //
142
142
 
143
143
  &--disabled,
144
- &[disabled] {
144
+ &[disabled],
145
+ &[aria-disabled='true'] {
145
146
  &,
146
147
  &:link,
147
148
  &:visited,
@@ -205,7 +206,8 @@ input.a-btn::-moz-focus-inner {
205
206
  }
206
207
 
207
208
  &--disabled:has(svg)::before,
208
- &[disabled]:has(svg)::before {
209
+ &[disabled]:has(svg)::before,
210
+ &[aria-disabled='true']:has(svg)::before {
209
211
  border-color: $btn-disabled-divider !important;
210
212
  }
211
213
 
@@ -50,6 +50,8 @@ a.a-tag-filter {
50
50
  }
51
51
 
52
52
  a.a-tag-filter {
53
+ text-decoration: none;
54
+
53
55
  // Colors for :link, :visited, :hover, :focus, :active.
54
56
  @include u-link-colors(
55
57
  var(--black),
@@ -101,6 +103,7 @@ a.a-tag-filter {
101
103
  a.a-tag-topic:hover,
102
104
  a.a-tag-topic:focus,
103
105
  a.a-tag-topic:active {
106
+ text-decoration: none;
104
107
  border-bottom: none;
105
108
  outline-offset: 1px;
106
109
 
@@ -7,7 +7,7 @@
7
7
 
8
8
  .cf-icon-svg {
9
9
  height: $cf-icon-height;
10
- vertical-align: text-top;
10
+ vertical-align: middle;
11
11
  fill: currentcolor;
12
12
 
13
13
  &--updating,
@@ -9,17 +9,14 @@
9
9
  @mixin u-link-card-colors($c, $v, $h, $f, $a) {
10
10
  .m-card__footer > span {
11
11
  display: inline;
12
- border-width: 0;
13
- border-bottom-width: 1px;
14
- border-color: $c;
15
- border-style: dotted;
12
+ text-decoration-color: $c;
13
+ text-decoration-style: dotted;
16
14
  font-weight: 500;
17
15
  color: $c;
18
- text-decoration: none;
19
16
  }
20
17
 
21
18
  & > a:visited .m-card__footer > span {
22
- border-color: $v;
19
+ text-decoration-color: $v;
23
20
  color: $v;
24
21
  }
25
22
 
@@ -28,19 +25,19 @@
28
25
  // instead of on the link. This differs from the visited, focus,
29
26
  // and active states, which add styles onto the link.
30
27
  &:hover .m-card__footer > span {
31
- border-style: solid;
32
- border-color: $h;
28
+ text-decoration-style: solid;
29
+ text-decoration-color: $h;
33
30
  color: $h;
34
31
  }
35
32
 
36
33
  & > a:focus .m-card__footer > span {
37
- border-color: $f;
34
+ text-decoration-color: $f;
38
35
  color: $f;
39
36
  }
40
37
 
41
38
  & > a:active .m-card__footer > span {
42
- border-color: $a;
43
- border-style: solid;
39
+ text-decoration-color: $a;
40
+ text-decoration-style: solid;
44
41
  color: $a;
45
42
  }
46
43
  }
@@ -11,7 +11,3 @@
11
11
 
12
12
  $pagination-text: var(--gray);
13
13
  $pagination-bg: var(--gray-5);
14
-
15
- // Sizing variables
16
-
17
- $pagination-btn-min-width-px: 130px;
@@ -3,10 +3,12 @@
3
3
 
4
4
  .a-link {
5
5
  border-bottom-width: 0;
6
+ text-decoration-line: none;
6
7
 
7
8
  .a-link__text {
8
- border-bottom-width: 1px;
9
- border-bottom-style: inherit;
9
+ text-decoration-line: underline;
10
+ text-decoration-style: dotted;
11
+ text-decoration-thickness: 1px;
10
12
 
11
13
  // See https://github.com/cfpb/consumerfinance.gov/pull/8252
12
14
  overflow-wrap: break-word;
@@ -52,7 +52,7 @@
52
52
  }
53
53
 
54
54
  .#{$jump-link-text-class} {
55
- border-bottom-width: 0;
55
+ text-decoration: none;
56
56
 
57
57
  // Arbitrary high value for flex-shrink to prevent squeezing of the icon
58
58
  // when the link text is very long.
@@ -71,15 +71,21 @@
71
71
  .#{$jump-link-class}__text {
72
72
  border-bottom-color: var(--gold-80);
73
73
  }
74
- } @else {
75
- font-weight: 500;
76
74
  }
77
75
 
78
76
  // Mobile only.
79
77
  @include respond-to-max($bp-xs-max) {
78
+ text-decoration: none;
79
+ border-top-style: dotted;
80
+ border-bottom-style: dotted;
80
81
  border-top-width: 1px;
81
82
  border-bottom-width: 1px;
82
83
 
84
+ &:hover {
85
+ border-top-style: solid;
86
+ border-bottom-style: solid;
87
+ }
88
+
83
89
  // We create a faux focus rectangle in the ::after pseudoelement to better
84
90
  // control the positioning of the focus rectangle, which would overwise
85
91
  // overlap the top border of the jumplink when it appears in a group.
@@ -1,6 +1,7 @@
1
1
  @use '@cfpb/cfpb-design-system/src/base' as *;
2
2
  @use '@cfpb/cfpb-design-system/src/abstracts' as *;
3
3
  @use '@cfpb/cfpb-design-system/src/components/cfpb-buttons/button' as *;
4
+ @use '@cfpb/cfpb-design-system/src/components/cfpb-buttons/button-link' as *;
4
5
 
5
6
  :host {
6
7
  // This prevents the child button from having an empty gap after the button.
@@ -14,3 +15,17 @@
14
15
  width: 100%;
15
16
  }
16
17
  }
18
+
19
+ :host([flush-left]) {
20
+ [role='button'] {
21
+ border-top-left-radius: 0;
22
+ border-bottom-left-radius: 0;
23
+ }
24
+ }
25
+
26
+ :host([flush-right]) {
27
+ [role='button'] {
28
+ border-top-right-radius: 0;
29
+ border-bottom-right-radius: 0;
30
+ }
31
+ }
@@ -1,6 +1,8 @@
1
1
  import { html, LitElement, css, unsafeCSS } from 'lit';
2
2
  import { classMap } from 'lit/directives/class-map.js';
3
+ import { ref, createRef } from 'lit/directives/ref.js';
3
4
  import styles from './cfpb-button.component.scss';
5
+ import { CfpbIconText } from '../cfpb-icon-text';
4
6
 
5
7
  // The variants are different color themes of the button.
6
8
  const VALID_VARIANTS = ['primary', 'secondary', 'warning'];
@@ -19,15 +21,19 @@ export class CfpbButton extends LitElement {
19
21
  `;
20
22
 
21
23
  /**
24
+ * @property {string} type - The button type: button, submit, or reset.
22
25
  * @property {string} href - The URL to link to (makes the button a link).
23
- * @property {boolean} disabled - Whether to stack the tags vertically.
26
+ * @property {boolean} disabled - Whether the button is disabled or not.
24
27
  * @property {string} variant - The button variant: secondary and warning.
25
28
  * @property {boolean} fullOnMobile - Whether to be width 100% on mobile.
26
- * @property {string} type - The button type: button, submit, or reset.
29
+ * @property {boolean} flushLeft - Whether button is not rounded on left.
30
+ * @property {boolean} flushRight - Whether button is not rounded on right.
31
+ * @property {boolean} styleAsLink - Style the button as a link.
27
32
  * @returns {object} The map of properties.
28
33
  */
29
34
  static get properties() {
30
35
  return {
36
+ type: { type: String },
31
37
  href: { type: String },
32
38
  disabled: { type: Boolean, reflect: true },
33
39
  variant: { type: String },
@@ -36,47 +42,55 @@ export class CfpbButton extends LitElement {
36
42
  attribute: 'full-on-mobile',
37
43
  reflect: true,
38
44
  },
39
- type: { type: String },
45
+ flushLeft: {
46
+ type: Boolean,
47
+ attribute: 'flush-left',
48
+ reflect: true,
49
+ },
50
+ flushRight: {
51
+ type: Boolean,
52
+ attribute: 'flush-right',
53
+ reflect: true,
54
+ },
55
+ styleAsLink: {
56
+ type: Boolean,
57
+ attribute: 'style-as-link',
58
+ reflect: true,
59
+ },
40
60
  };
41
61
  }
42
62
 
63
+ // DOM references.
64
+ #iconTextDom = createRef();
65
+
43
66
  constructor() {
44
67
  super();
45
- this.disabled = false;
68
+ this.type = 'button';
46
69
  this.variant = 'primary';
70
+ this.disabled = false;
47
71
  this.fullOnMobile = false;
48
- this.type = 'button';
72
+ this.styleAsLink = false;
49
73
  }
50
74
 
51
75
  /**
52
- * Hide any icon in the slot.
76
+ * @returns {boolean} True if it has an icon, false otherwise.
53
77
  */
54
- hideIcon() {
55
- const icon = this.#findIconInSlot();
56
- if (icon) icon.style.display = 'none';
78
+ hasIcon() {
79
+ return this.#iconTextDom.value?.hasIcon();
57
80
  }
58
81
 
59
82
  /**
60
- * Show any icon in the slot, if it was hidden.
83
+ * Hide any icon in the slot.
61
84
  */
62
- showIcon() {
63
- const icon = this.#findIconInSlot();
64
- if (icon) icon.style.display = '';
85
+ hideIcon() {
86
+ this.#iconTextDom.value?.hideIcon();
65
87
  }
66
88
 
67
89
  /**
68
- * Find the icon SVG in the slot.
69
- * @returns {Node} The icon SVG node.
90
+ * Show any icon in the slot, if it was hidden.
70
91
  */
71
- #findIconInSlot() {
72
- const slot = this.shadowRoot.querySelector('slot');
73
- const nodes = slot.assignedNodes({ flatten: true });
74
-
75
- for (const node of nodes) {
76
- if (node.tagName.toLowerCase() === 'svg') {
77
- return node;
78
- }
79
- }
92
+ showIcon() {
93
+ this.#iconTextDom.value?.showIcon();
80
94
  }
81
95
 
82
96
  /**
@@ -103,9 +117,18 @@ export class CfpbButton extends LitElement {
103
117
  return {
104
118
  'a-btn': true,
105
119
  [`a-btn--${this.#validVariant}`]: this.#validVariant !== 'primary',
120
+ [`a-btn--link`]: this.styleAsLink === true,
106
121
  };
107
122
  }
108
123
 
124
+ #renderTextAndIcon() {
125
+ return html`
126
+ <cfpb-icon-text ${ref(this.#iconTextDom)} ?disabled=${this.disabled}>
127
+ <slot></slot>
128
+ </cfpb-icon-text>
129
+ `;
130
+ }
131
+
109
132
  render() {
110
133
  const classes = classMap(this.#btnClass);
111
134
 
@@ -119,8 +142,8 @@ export class CfpbButton extends LitElement {
119
142
  aria-disabled=${String(this.disabled)}
120
143
  tabindex=${this.disabled ? -1 : 0}
121
144
  >
122
- <slot></slot
123
- ></a>
145
+ ${this.#renderTextAndIcon()}
146
+ </a>
124
147
  `;
125
148
  }
126
149
 
@@ -131,12 +154,14 @@ export class CfpbButton extends LitElement {
131
154
  ?disabled=${this.disabled}
132
155
  type=${this.#validType}
133
156
  >
134
- <slot></slot>
157
+ ${this.#renderTextAndIcon()}
135
158
  </button>
136
159
  `;
137
160
  }
138
161
 
139
162
  static init() {
163
+ CfpbIconText.init();
164
+
140
165
  window.customElements.get('cfpb-button') ||
141
166
  window.customElements.define('cfpb-button', CfpbButton);
142
167
  }
@@ -0,0 +1,60 @@
1
+ @use 'sass:math';
2
+ @use '@cfpb/cfpb-design-system/src/abstracts' as *;
3
+
4
+ @mixin u-btn-divider() {
5
+ content: '';
6
+ border-left: 1px solid var(--icon-text-divider);
7
+ order: 2;
8
+ place-self: normal;
9
+ }
10
+
11
+ :host {
12
+ // Theme variables.
13
+ --icon-text-divider: var(--pacific-60);
14
+
15
+ div {
16
+ // This prevents the child button from having an empty gap after the button.
17
+ display: flex;
18
+ width: fit-content;
19
+ align-items: center;
20
+
21
+ // Hide SVG by default.
22
+ & ::slotted(svg) {
23
+ display: none;
24
+ }
25
+
26
+ &.u-has-icon {
27
+ gap: math.div(10px, $btn-font-size) + rem;
28
+ & slot::before {
29
+ @include u-btn-divider;
30
+ }
31
+
32
+ // Show SVG.
33
+ & ::slotted(svg) {
34
+ display: initial;
35
+ }
36
+ }
37
+
38
+ &.u-has-icon--left {
39
+ & ::slotted(svg) {
40
+ order: 1;
41
+ }
42
+ & ::slotted(span) {
43
+ order: 3;
44
+ }
45
+ }
46
+
47
+ &.u-has-icon--right {
48
+ & ::slotted(svg) {
49
+ order: 3;
50
+ }
51
+ & ::slotted(span) {
52
+ order: 1;
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ :host([disabled]) {
59
+ --icon-text-divider: var(--gray-60);
60
+ }
@@ -0,0 +1,150 @@
1
+ import { html, LitElement, css, unsafeCSS } from 'lit';
2
+ import styles from './cfpb-icon-text.component.scss';
3
+
4
+ /**
5
+ *
6
+ * @element cfpb-icon-text
7
+ * @slot - The main content for the text and icon.
8
+ */
9
+ export class CfpbIconText extends LitElement {
10
+ static styles = css`
11
+ ${unsafeCSS(styles)}
12
+ `;
13
+
14
+ /**
15
+ * @property {boolean} disabled - Apply disabled styles or not.
16
+ * @returns {object} The map of properties.
17
+ */
18
+ static get properties() {
19
+ return {
20
+ disabled: { type: Boolean, reflect: true },
21
+ };
22
+ }
23
+
24
+ // DOM references.
25
+ #svgObserver;
26
+ #iconClasses;
27
+
28
+ constructor() {
29
+ super();
30
+ this.#iconClasses = '';
31
+ }
32
+
33
+ connectedCallback() {
34
+ super.connectedCallback();
35
+
36
+ this.#svgObserver = new MutationObserver(() => {
37
+ this.#processLightDom();
38
+ });
39
+
40
+ this.#svgObserver.observe(this, {
41
+ childList: true,
42
+ subtree: false,
43
+ });
44
+ }
45
+
46
+ disconnectedCallback() {
47
+ super.disconnectedCallback();
48
+ if (this.#svgObserver) {
49
+ this.#svgObserver.disconnect();
50
+ this.#svgObserver = null;
51
+ }
52
+ }
53
+
54
+ #processLightDom() {
55
+ const div = this.shadowRoot.querySelector('div');
56
+ const slot = this.shadowRoot.querySelector('slot');
57
+ const nodes = slot.assignedNodes({ flatten: true });
58
+
59
+ let svgEl = null;
60
+ let spanEl = null;
61
+
62
+ for (const node of nodes) {
63
+ if (
64
+ node.nodeType === Node.TEXT_NODE &&
65
+ node.textContent.trim().length > 0
66
+ ) {
67
+ const span = document.createElement('span');
68
+ span.textContent = node.textContent;
69
+ node.replaceWith(span);
70
+ if (!spanEl) spanEl = span;
71
+ } else if (node.nodeType === Node.ELEMENT_NODE) {
72
+ const tag = node.tagName.toLowerCase();
73
+ if (tag === 'svg' && !svgEl) {
74
+ svgEl = node;
75
+ } else if (tag === 'span' && !spanEl) {
76
+ spanEl = node;
77
+ }
78
+ }
79
+ }
80
+
81
+ if (svgEl && spanEl) {
82
+ div.classList.add('u-has-icon');
83
+ if (
84
+ svgEl.compareDocumentPosition(spanEl) & Node.DOCUMENT_POSITION_FOLLOWING
85
+ ) {
86
+ div.classList.add('u-has-icon--left');
87
+ } else {
88
+ div.classList.add('u-has-icon--right');
89
+ }
90
+ }
91
+ }
92
+
93
+ firstUpdated() {
94
+ this.#processLightDom();
95
+ }
96
+
97
+ /**
98
+ * Hide any icon in the slot.
99
+ */
100
+ hideIcon() {
101
+ const icon = this.#findIconInSlot();
102
+ const div = this.shadowRoot.querySelector('div');
103
+ if (icon) {
104
+ this.#iconClasses = div.className;
105
+ div.className = '';
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Show any icon in the slot, if it was hidden.
111
+ */
112
+ showIcon() {
113
+ const icon = this.#findIconInSlot();
114
+ const div = this.shadowRoot.querySelector('div');
115
+ if (icon) div.className = this.#iconClasses;
116
+ }
117
+
118
+ /**
119
+ * @returns {boolean} True if it has an icon, false otherwise.
120
+ */
121
+ hasIcon() {
122
+ const icon = this.#findIconInSlot();
123
+ if (icon) return true;
124
+ return false;
125
+ }
126
+
127
+ /**
128
+ * Find the icon SVG in the slot.
129
+ * @returns {Node} The icon SVG node.
130
+ */
131
+ #findIconInSlot() {
132
+ const slot = this.shadowRoot.querySelector('slot');
133
+ const nodes = slot.assignedNodes({ flatten: true });
134
+
135
+ for (const node of nodes) {
136
+ if (node.tagName && node.tagName.toLowerCase() === 'svg') {
137
+ return node;
138
+ }
139
+ }
140
+ }
141
+
142
+ render() {
143
+ return html`<div><slot></slot></div>`;
144
+ }
145
+
146
+ static init() {
147
+ window.customElements.get('cfpb-icon-text') ||
148
+ window.customElements.define('cfpb-icon-text', CfpbIconText);
149
+ }
150
+ }
@@ -4,8 +4,9 @@ import { ifDefined } from 'lit/directives/if-defined.js';
4
4
 
5
5
  /**
6
6
  *
7
- * @element cfpb-multiselect.
8
- * @slot - The main content for the upload button.
7
+ * @element cfpb-label.
8
+ * @slot label - The content for the label text.
9
+ * @slot helper - The content for the label helper text.
9
10
  */
10
11
  export class CfpbLabel extends LitElement {
11
12
  static styles = css`
@@ -14,7 +15,7 @@ export class CfpbLabel extends LitElement {
14
15
 
15
16
  /**
16
17
  * @property {string} for - Associate the label with an ID elsewhere.
17
- * @property {string} type - Associate the label with an ID elsewhere.
18
+ * @property {boolean} block - Whether this has block or inline helper text.
18
19
  * @returns {object} The map of properties.
19
20
  */
20
21
  static get properties() {