@cfpb/cfpb-design-system 4.2.3 → 4.3.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 (147) hide show
  1. package/CHANGELOG.md +178 -1
  2. package/dist/base/index.css +1 -1
  3. package/dist/base/index.css.map +2 -2
  4. package/dist/base/index.js.map +1 -1
  5. package/dist/components/cfpb-buttons/index.css +1 -1
  6. package/dist/components/cfpb-buttons/index.css.map +2 -2
  7. package/dist/components/cfpb-buttons/index.js.map +1 -1
  8. package/dist/components/cfpb-expandables/index.css.map +1 -1
  9. package/dist/components/cfpb-expandables/index.js +1 -1
  10. package/dist/components/cfpb-expandables/index.js.map +3 -3
  11. package/dist/components/cfpb-forms/index.css +1 -1
  12. package/dist/components/cfpb-forms/index.css.map +2 -2
  13. package/dist/components/cfpb-forms/index.js +1 -1
  14. package/dist/components/cfpb-forms/index.js.map +2 -2
  15. package/dist/components/cfpb-layout/index.css +1 -1
  16. package/dist/components/cfpb-layout/index.css.map +1 -1
  17. package/dist/components/cfpb-notifications/index.css.map +1 -1
  18. package/dist/components/cfpb-pagination/index.css.map +1 -1
  19. package/dist/components/cfpb-tables/index.css.map +1 -1
  20. package/dist/components/cfpb-typography/index.css +1 -1
  21. package/dist/components/cfpb-typography/index.css.map +2 -2
  22. package/dist/components/cfpb-typography/index.js.map +1 -1
  23. package/dist/elements/cfpb-button/index.js +4 -4
  24. package/dist/elements/cfpb-button/index.js.map +3 -3
  25. package/dist/elements/cfpb-checkbox-icon/index.js +29 -0
  26. package/dist/elements/{cfpb-checkbox → cfpb-checkbox-icon}/index.js.map +4 -4
  27. package/dist/elements/cfpb-expandable/index.css +2 -0
  28. package/dist/elements/cfpb-expandable/index.css.map +7 -0
  29. package/dist/elements/cfpb-expandable/index.js +33 -0
  30. package/dist/elements/cfpb-expandable/index.js.map +7 -0
  31. package/dist/elements/cfpb-file-upload/index.js +4 -4
  32. package/dist/elements/cfpb-file-upload/index.js.map +3 -3
  33. package/dist/elements/cfpb-form-alert/index.js +32 -0
  34. package/dist/elements/cfpb-form-alert/index.js.map +7 -0
  35. package/dist/elements/cfpb-form-choice/index.js +12 -3
  36. package/dist/elements/cfpb-form-choice/index.js.map +4 -4
  37. package/dist/elements/cfpb-form-search/index.js +41 -0
  38. package/dist/elements/cfpb-form-search/index.js.map +7 -0
  39. package/dist/elements/cfpb-form-search-input/index.js +41 -0
  40. package/dist/elements/cfpb-form-search-input/index.js.map +7 -0
  41. package/dist/elements/cfpb-icon-text/index.js +3 -3
  42. package/dist/elements/cfpb-icon-text/index.js.map +3 -3
  43. package/dist/elements/cfpb-label/index.js +3 -3
  44. package/dist/elements/cfpb-label/index.js.map +2 -2
  45. package/dist/elements/cfpb-list/index.js +39 -0
  46. package/dist/elements/cfpb-list/index.js.map +7 -0
  47. package/dist/elements/cfpb-list-item/index.js +39 -0
  48. package/dist/elements/cfpb-list-item/index.js.map +7 -0
  49. package/dist/elements/cfpb-multiselect/index.js +13 -4
  50. package/dist/elements/cfpb-multiselect/index.js.map +4 -4
  51. package/dist/elements/cfpb-pagination/index.js +3 -3
  52. package/dist/elements/cfpb-pagination/index.js.map +2 -2
  53. package/dist/elements/cfpb-select/index.css +2 -0
  54. package/dist/elements/cfpb-select/index.css.map +7 -0
  55. package/dist/elements/cfpb-select/index.js +42 -0
  56. package/dist/elements/cfpb-select/index.js.map +7 -0
  57. package/dist/elements/cfpb-select-list/index.js +39 -0
  58. package/dist/elements/cfpb-select-list/index.js.map +7 -0
  59. package/dist/elements/cfpb-tag-filter/index.js +3 -3
  60. package/dist/elements/cfpb-tag-filter/index.js.map +3 -3
  61. package/dist/elements/cfpb-tag-group/index.js +3 -3
  62. package/dist/elements/cfpb-tag-group/index.js.map +4 -4
  63. package/dist/elements/cfpb-tag-topic/index.js +4 -4
  64. package/dist/elements/cfpb-tag-topic/index.js.map +2 -2
  65. package/dist/elements/index.css +2 -0
  66. package/dist/elements/index.css.map +7 -0
  67. package/dist/elements/index.js +7 -6
  68. package/dist/elements/index.js.map +4 -4
  69. package/dist/index.css +1 -1
  70. package/dist/index.css.map +2 -2
  71. package/dist/index.js +7 -6
  72. package/dist/index.js.map +4 -4
  73. package/dist/utilities/index.css.map +1 -1
  74. package/dist/utilities/index.js +1 -1
  75. package/dist/utilities/index.js.map +3 -3
  76. package/package.json +1 -1
  77. package/src/base/base.scss +1 -1
  78. package/src/components/cfpb-buttons/button-link.scss +0 -1
  79. package/src/components/cfpb-expandables/expandable.js +3 -0
  80. package/src/components/cfpb-forms/multiselect.js +1 -1
  81. package/src/components/cfpb-typography/mixins.scss +3 -0
  82. package/src/elements/abstracts/custom-props.css +123 -0
  83. package/src/elements/abstracts/grid-mixins.scss +83 -0
  84. package/src/elements/abstracts/heading-mixins.scss +346 -0
  85. package/src/elements/abstracts/index.scss +7 -0
  86. package/src/elements/abstracts/media-queries.scss +35 -0
  87. package/src/elements/abstracts/sizing-vars.scss +65 -0
  88. package/src/elements/abstracts/vars-breakpoints.scss +16 -0
  89. package/src/elements/abstracts/vars.css +79 -0
  90. package/src/elements/base/base.scss +375 -0
  91. package/src/elements/base/font.scss +27 -0
  92. package/src/elements/base/index.scss +3 -0
  93. package/src/elements/base/normalize.scss +290 -0
  94. package/src/elements/cfpb-button/cfpb-button-group.scss +10 -0
  95. package/src/elements/cfpb-button/cfpb-button-link.scss +96 -0
  96. package/src/elements/cfpb-button/cfpb-button.component.scss +11 -4
  97. package/src/elements/cfpb-button/cfpb-button.scss +222 -0
  98. package/src/elements/cfpb-button/index.js +28 -29
  99. package/src/elements/cfpb-button/vars.css +30 -0
  100. package/src/elements/cfpb-checkbox-icon/cfpb-checkbox-icon.component.scss +88 -0
  101. package/src/elements/cfpb-checkbox-icon/index.js +104 -0
  102. package/src/elements/cfpb-expandable/cfpb-expandable.component.scss +218 -0
  103. package/src/elements/cfpb-expandable/index.js +127 -0
  104. package/src/elements/cfpb-file-upload/cfpb-file-upload.component.scss +2 -2
  105. package/src/elements/cfpb-file-upload/index.js +16 -18
  106. package/src/elements/cfpb-form-alert/cfpb-form-alert.component.scss +36 -0
  107. package/src/elements/cfpb-form-alert/index.js +55 -0
  108. package/src/elements/cfpb-form-choice/cfpb-form-choice.component.scss +42 -81
  109. package/src/elements/cfpb-form-choice/index.js +58 -18
  110. package/src/elements/cfpb-form-search/cfpb-form-search.component.scss +54 -0
  111. package/src/elements/cfpb-form-search/index.js +194 -0
  112. package/src/elements/cfpb-form-search-input/cfpb-form-search-input.component.scss +217 -0
  113. package/src/elements/cfpb-form-search-input/index.js +136 -0
  114. package/src/elements/cfpb-icon-text/cfpb-icon-text.component.scss +32 -39
  115. package/src/elements/cfpb-icon-text/index.js +32 -104
  116. package/src/elements/cfpb-label/cfpb-label.component.scss +2 -2
  117. package/src/elements/cfpb-label/index.js +6 -9
  118. package/src/elements/cfpb-list/cfpb-list.component.scss +23 -0
  119. package/src/elements/cfpb-list/index.js +357 -0
  120. package/src/elements/cfpb-list/index.spec.js +169 -0
  121. package/src/elements/cfpb-list-item/cfpb-list-item.component.scss +69 -0
  122. package/src/elements/cfpb-list-item/index.js +215 -0
  123. package/src/elements/cfpb-pagination/cfpb-pagination.component.scss +2 -7
  124. package/src/elements/cfpb-pagination/index.js +6 -8
  125. package/src/elements/cfpb-select/cfpb-select.component.scss +241 -0
  126. package/src/elements/cfpb-select/index.js +381 -0
  127. package/src/elements/cfpb-tag-filter/cfpb-tag-filter.component.scss +6 -3
  128. package/src/elements/cfpb-tag-filter/index.js +15 -7
  129. package/src/elements/cfpb-tag-group/cfpb-tag-group.component.scss +2 -2
  130. package/src/elements/cfpb-tag-group/index.js +53 -6
  131. package/src/elements/cfpb-tag-topic/index.js +5 -7
  132. package/src/elements/cfpb-utilities/parse-child-data.js +50 -0
  133. package/src/elements/cfpb-utilities/parse-child-data.spec.js +56 -0
  134. package/src/elements/cfpb-utilities/search-service.js +46 -0
  135. package/src/elements/cfpb-utilities/search-service.spec.js +138 -0
  136. package/src/elements/cfpb-utilities/transition/transition.scss +98 -0
  137. package/src/elements/index.js +7 -1
  138. package/src/index.scss +11 -0
  139. package/src/tokens/abstracts/custom-props.json +1642 -0
  140. package/src/tokens/abstracts/vars.json +1319 -0
  141. package/src/tokens/cfpb-button/vars.json +436 -0
  142. package/src/utilities/transition/max-height-transition.js +74 -0
  143. package/dist/elements/cfpb-checkbox/index.js +0 -29
  144. package/src/elements/cfpb-multiselect/cfpb-multiselect.component.scss +0 -225
  145. package/src/elements/cfpb-multiselect/index.js +0 -444
  146. package/src/elements/cfpb-multiselect/multiselect-model.js +0 -288
  147. package/src/elements/cfpb-multiselect/multiselect-model.spec.js +0 -236
@@ -24,41 +24,40 @@ export class CfpbButton extends LitElement {
24
24
  * @property {string} type - The button type: button, submit, or reset.
25
25
  * @property {string} href - The URL to link to (makes the button a link).
26
26
  * @property {boolean} disabled - Whether the button is disabled or not.
27
- * @property {string} variant - The button variant: secondary and warning.
27
+ * @property {string} variant
28
+ * The button variant: primary, secondary, or warning.
28
29
  * @property {boolean} fullOnMobile - Whether to be width 100% on mobile.
29
30
  * @property {boolean} flushLeft - Whether button is not rounded on left.
30
31
  * @property {boolean} flushRight - Whether button is not rounded on right.
31
32
  * @property {boolean} styleAsLink - Style the button as a link.
32
33
  * @returns {object} The map of properties.
33
34
  */
34
- static get properties() {
35
- return {
36
- type: { type: String },
37
- href: { type: String },
38
- disabled: { type: Boolean, reflect: true },
39
- variant: { type: String },
40
- fullOnMobile: {
41
- type: Boolean,
42
- attribute: 'full-on-mobile',
43
- reflect: true,
44
- },
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
- },
60
- };
61
- }
35
+ static properties = {
36
+ type: { type: String },
37
+ href: { type: String },
38
+ disabled: { type: Boolean, reflect: true },
39
+ variant: { type: String },
40
+ fullOnMobile: {
41
+ type: Boolean,
42
+ attribute: 'full-on-mobile',
43
+ reflect: true,
44
+ },
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
+ },
60
+ };
62
61
 
63
62
  // DOM references.
64
63
  #iconTextDom = createRef();
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Do not edit directly, this file was auto-generated.
3
+ */
4
+
5
+ :host {
6
+ --btn-bg: var(--pacific);
7
+ --btn-bg-active: var(--navy);
8
+ --btn-bg-hover: var(--pacific-dark);
9
+ --btn-disabled-bg: var(--gray-20);
10
+ --btn-disabled-divider: var(--gray-60);
11
+ --btn-disabled-outline: var(--gray-20);
12
+ --btn-disabled-text: var(--gray-dark);
13
+ --btn-divider: var(--pacific-60);
14
+ --btn-secondary-bg: var(--white);
15
+ --btn-secondary-bg-active: var(--pacific-20);
16
+ --btn-secondary-bg-hover: var(--pacific-10);
17
+ --btn-secondary-border: var(--pacific);
18
+ --btn-secondary-border-active: var(--navy);
19
+ --btn-secondary-border-hover: var(--pacific-dark);
20
+ --btn-secondary-divider: var(--pacific-60);
21
+ --btn-secondary-text: var(--pacific);
22
+ --btn-secondary-text-active: var(--navy);
23
+ --btn-secondary-text-hover: var(--pacific-dark);
24
+ --btn-text: var(--white);
25
+ --btn-warning-bg: var(--red-mid-dark);
26
+ --btn-warning-bg-active: var(--gray-dark);
27
+ --btn-warning-bg-hover: var(--red-dark);
28
+ --btn-warning-divider: var(--red-60);
29
+ --btn-warning-text: var(--white);
30
+ }
@@ -0,0 +1,88 @@
1
+ @use 'sass:math';
2
+ @use '@cfpb/cfpb-design-system/src/elements/abstracts' as *;
3
+ @use '@cfpb/cfpb-design-system/src/utilities' as *;
4
+
5
+ :host {
6
+ // Theme variables.
7
+ --choice-border: var(--choice-border-default);
8
+ --choice-border-hover: var(--choice-border-hover-default);
9
+ --choice-bg: var(--choice-bg-default);
10
+ --choice-bg-selected: var(--choice-bg-selected-default);
11
+
12
+ // Private variables.
13
+ --choice-border-width-addendum: 0;
14
+
15
+ .cfpb-checkbox-icon::before {
16
+ display: inline-block;
17
+ grid-row-start: 1;
18
+ grid-row-end: 3;
19
+ border: 1px solid var(--choice-border);
20
+ outline: var(--choice-border-width-addendum) solid var(--choice-border);
21
+ height: math.div(18px, $base-font-size-px) + em;
22
+ width: math.div(18px, $base-font-size-px) + em;
23
+ margin-right: 10px;
24
+ background-color: var(--choice-bg);
25
+ content: '';
26
+ vertical-align: top;
27
+
28
+ // Offset so that the checkbox/radio fits within focused area.
29
+ position: relative;
30
+ top: 1px;
31
+ left: 1px;
32
+ }
33
+
34
+ .checked::before {
35
+ --cfpb-background-icon-svg: 'approved';
36
+ background-size: auto $cf-icon-height;
37
+ background-repeat: no-repeat;
38
+ background-position: center 0;
39
+ }
40
+
41
+ .disabled::before {
42
+ cursor: not-allowed;
43
+
44
+ --choice-border: var(--choice-border-disabled);
45
+ --choice-bg: var(--choice-bg-disabled);
46
+ --choice-bg-selected: var(--choice-bg-selected-disabled);
47
+ }
48
+
49
+ .checked.disabled::before {
50
+ // RGB values are CFPB gray (#5a5d61).
51
+ // For some reason SVG isn't accepting hex values for the fill.
52
+ --cfpb-background-icon-svg: 'approved rgb(90,93,97)';
53
+ }
54
+
55
+ .cfpb-checkbox-icon:not(.disabled, .borderless).focus::before,
56
+ .cfpb-checkbox-icon:not(.disabled, .borderless):focus::before,
57
+ .cfpb-checkbox-icon:not(.disabled, .borderless).hover::before,
58
+ .cfpb-checkbox-icon:not(.disabled, .borderless):hover::before {
59
+ cursor: pointer;
60
+ border-color: var(--choice-border-hover);
61
+ box-shadow: 0 0 0 1px var(--choice-border-hover);
62
+ outline-color: var(--choice-border-hover);
63
+ }
64
+
65
+ .validation-error,
66
+ .validation-warning,
67
+ .validation-success {
68
+ --choice-border-width-addendum: 1px;
69
+ }
70
+
71
+ .validation-error {
72
+ --choice-border: var(--choice-border-error);
73
+ }
74
+
75
+ .validation-warning {
76
+ --choice-border: var(--choice-border-warning);
77
+ }
78
+
79
+ .validation-success {
80
+ --choice-border: var(--choice-border-success);
81
+ }
82
+
83
+ .borderless::before {
84
+ border-color: transparent;
85
+ outline-color: transparent;
86
+ background-color: transparent;
87
+ }
88
+ }
@@ -0,0 +1,104 @@
1
+ import { html, LitElement, css, unsafeCSS } from 'lit';
2
+ import styles from './cfpb-checkbox-icon.component.scss';
3
+
4
+ // The validation states are error, warning, or success.
5
+ const VALID_VALIDATION = ['error', 'warning', 'success'];
6
+
7
+ /**
8
+ * @element cfpb-checkbox-icon
9
+ */
10
+ export class CfpbCheckboxIcon extends LitElement {
11
+ static styles = css`
12
+ ${unsafeCSS(styles)}
13
+ `;
14
+
15
+ #hover;
16
+ #focus;
17
+
18
+ /**
19
+ * @property {boolean} borderless - Whether the checkbox has a border or not.
20
+ * @property {boolean} checked - Whether the checkbox is checked or not.
21
+ * @property {boolean} disabled - Whether the checkbox is disabled or not.
22
+ * @property {string} validation - Validation style: error, warning, success.
23
+ * @returns {object} The map of properties.
24
+ */
25
+ static properties = {
26
+ borderless: { type: Boolean, reflect: true },
27
+ checked: { type: Boolean, reflect: true },
28
+ disabled: { type: Boolean, reflect: true },
29
+ validation: { type: String, reflect: true },
30
+ };
31
+
32
+ constructor() {
33
+ super();
34
+ this.borderless = false;
35
+ this.checked = false;
36
+ this.disabled = false;
37
+ this.validation = '';
38
+ this.#hover = false;
39
+ this.#focus = false;
40
+ }
41
+
42
+ /**
43
+ * Ensure the validation value is valid, and fall back to a default if not.
44
+ * @returns {string|undefined} A valid validation value string, or undefined.
45
+ */
46
+ get #validValidation() {
47
+ return VALID_VALIDATION.includes(this.validation)
48
+ ? this.validation
49
+ : undefined;
50
+ }
51
+
52
+ mouseover() {
53
+ if (!this.disabled) {
54
+ this.#hover = true;
55
+ this.requestUpdate();
56
+ }
57
+ }
58
+
59
+ mouseleave() {
60
+ this.#hover = false;
61
+ this.requestUpdate();
62
+ }
63
+
64
+ focus() {
65
+ if (!this.disabled) {
66
+ this.#focus = true;
67
+ this.requestUpdate();
68
+ }
69
+ }
70
+
71
+ blur() {
72
+ this.#focus = false;
73
+ this.requestUpdate();
74
+ }
75
+
76
+ #computeClassString() {
77
+ return [
78
+ 'cfpb-checkbox-icon',
79
+ this.checked && 'checked',
80
+ this.disabled && 'disabled',
81
+ this.borderless && 'borderless',
82
+ this.#validValidation && `validation-${this.#validValidation}`,
83
+ this.#hover && 'hover',
84
+ this.#focus && 'focus',
85
+ ]
86
+ .filter(Boolean)
87
+ .join(' ');
88
+ }
89
+
90
+ render() {
91
+ return html`
92
+ <div
93
+ class=${this.#computeClassString()}
94
+ ?disabled=${this.disabled}
95
+ aria-hidden="true"
96
+ ></div>
97
+ `;
98
+ }
99
+
100
+ static init() {
101
+ window.customElements.get('cfpb-checkbox-icon') ||
102
+ window.customElements.define('cfpb-checkbox-icon', CfpbCheckboxIcon);
103
+ }
104
+ }
@@ -0,0 +1,218 @@
1
+ @use 'sass:math';
2
+ @use '@cfpb/cfpb-design-system/src/abstracts' as *;
3
+ @use '@cfpb/cfpb-design-system/src/base' as *;
4
+ @use '@cfpb/cfpb-design-system/src/utilities' as *;
5
+ @use '../cfpb-utilities/transition/transition.scss' as *;
6
+
7
+ :host {
8
+ // Theme
9
+
10
+ --expandable-border: var(--gray-40);
11
+
12
+ .cf-icon-svg {
13
+ height: 1.1875em;
14
+ vertical-align: middle;
15
+ fill: currentcolor;
16
+ }
17
+
18
+ // Overide heading defaults.
19
+ ::slotted([slot='header']) {
20
+ margin-bottom: 0 !important;
21
+ color: var(--black) !important;
22
+ font-weight: 500 !important;
23
+
24
+ // h4 size.
25
+ font-size: math.div(18px, $base-font-size-px) + em !important;
26
+
27
+ // Mobile only.
28
+ @include respond-to-max($bp-xs-max) {
29
+ font-size: math.div(16px, $base-font-size-px) + em !important;
30
+ }
31
+
32
+ .o-expandable--padded {
33
+ // h2 size.
34
+ font-size: math.div(26px, $base-font-size-px) + em !important;
35
+
36
+ // Mobile only.
37
+ @include respond-to-max($bp-xs-max) {
38
+ font-size: math.div(22px, $base-font-size-px) + em !important;
39
+ }
40
+ }
41
+ }
42
+
43
+ //
44
+ // Recommended expandable pattern
45
+ //
46
+
47
+ .o-expandable {
48
+ position: relative;
49
+
50
+ //
51
+ // Header
52
+ //
53
+
54
+ &__header {
55
+ display: flex;
56
+ justify-content: space-between;
57
+ gap: 10px;
58
+ padding: math.div(10px, $base-font-size-px) + em
59
+ math.div(15px, $base-font-size-px) + em;
60
+ border: 0;
61
+ background-color: transparent;
62
+ cursor: pointer;
63
+
64
+ &:focus {
65
+ outline: 1px dotted var(--black);
66
+ outline-offset: 2px;
67
+ }
68
+
69
+ .o-expandable__cue-close,
70
+ .o-expandable__cue-open {
71
+ display: none;
72
+ }
73
+
74
+ &[aria-expanded='false'] .o-expandable__cue-open {
75
+ display: block;
76
+ }
77
+
78
+ &[aria-expanded='true'] .o-expandable__cue-close {
79
+ display: block;
80
+ }
81
+ }
82
+
83
+ // Using the button element with .o-expandable__header requires setting
84
+ // an explicit width.
85
+ button.o-expandable__header {
86
+ width: 100%;
87
+ text-align: left;
88
+ }
89
+
90
+ //
91
+ // Expandable text elements
92
+ //
93
+
94
+ &__label {
95
+ // Grow to available width.
96
+ flex-grow: 1;
97
+ }
98
+
99
+ &__icon,
100
+ &__label {
101
+ // h4 size.
102
+ font-size: math.div(18px, $base-font-size-px) + em !important;
103
+
104
+ // Mobile only.
105
+ @include respond-to-max($bp-xs-max) {
106
+ font-size: math.div(16px, $base-font-size-px) + em !important;
107
+ }
108
+ }
109
+
110
+ &__cues {
111
+ align-self: center;
112
+ color: var(--pacific);
113
+ font-size: math.div(16px, $base-font-size-px) + em;
114
+ line-height: math.div($base-line-height-px, $base-font-size-px);
115
+ }
116
+
117
+ &__content {
118
+ padding: math.div(15px, $base-font-size-px) + em;
119
+ padding-top: 0;
120
+
121
+ // The divider between __header and __content.
122
+ &::before {
123
+ content: '';
124
+ display: block;
125
+ border-top: 1px solid var(--expandable-border);
126
+ padding-top: math.div(15px, $base-font-size-px) + em;
127
+ }
128
+
129
+ &::after {
130
+ padding-bottom: math.div(15px, $base-font-size-px) + em;
131
+ width: 100%;
132
+ }
133
+ }
134
+
135
+ //
136
+ // Padded expandable modifier
137
+ //
138
+
139
+ &--padded {
140
+ .o-expandable {
141
+ &__header {
142
+ padding: math.div(25px, $base-font-size-px) + em
143
+ math.div(15px, $base-font-size-px) + em;
144
+ }
145
+
146
+ &__icon,
147
+ &__label {
148
+ // h2 size.
149
+ font-size: math.div(26px, $base-font-size-px) + em !important;
150
+
151
+ // Mobile only.
152
+ @include respond-to-max($bp-xs-max) {
153
+ font-size: math.div(22px, $base-font-size-px) + em !important;
154
+ }
155
+ }
156
+ }
157
+ }
158
+
159
+ //
160
+ // Expandable with a background color modifier
161
+ //
162
+
163
+ &--background {
164
+ background: var(--gray-5);
165
+ }
166
+
167
+ //
168
+ // Expandable with a border modifier
169
+ //
170
+
171
+ &--border {
172
+ border: 1px solid var(--expandable-border);
173
+ }
174
+
175
+ //
176
+ // Expandable groups
177
+ //
178
+
179
+ &-group {
180
+ .o-expandable {
181
+ border-bottom: 1px solid var(--expandable-border);
182
+
183
+ &:first-child {
184
+ border-top: 1px solid var(--expandable-border);
185
+ }
186
+ }
187
+ }
188
+
189
+ @media print {
190
+ // Hide the interactive expandable cues when printing
191
+ &__header[aria-expanded='true'] &__cue-close,
192
+ &__header[aria-expanded='false'] &__cue-open {
193
+ display: none;
194
+ } // Ensure all expandables are expanded when printing.
195
+ // To accommodate print stylesheets that display the raw URL after links,
196
+ // set an enormous max height to accommodate expandables that have a lot of links.
197
+ &__content[aria-expanded='false'] {
198
+ display: block;
199
+ max-height: 99999px !important;
200
+ }
201
+ }
202
+ }
203
+ }
204
+
205
+ // Used when the set language reads right-to-left
206
+ html[lang='ar'] {
207
+ :host {
208
+ .o-expandable {
209
+ &__header {
210
+ text-align: right;
211
+ }
212
+
213
+ &__cues {
214
+ text-align: left;
215
+ }
216
+ }
217
+ }
218
+ }
@@ -0,0 +1,127 @@
1
+ import { html, LitElement, css, unsafeCSS } from 'lit';
2
+ import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
3
+ import styles from './cfpb-expandable.component.scss';
4
+ import expandIcon from '../../components/cfpb-icons/icons/plus-round.svg';
5
+ import collapseIcon from '../../components/cfpb-icons/icons/minus-round.svg';
6
+ import { MaxHeightTransition } from '../../utilities/transition/max-height-transition';
7
+ import { FlyoutMenu } from '../../utilities/behavior/flyout-menu';
8
+
9
+ /**
10
+ *
11
+ * @element cfpb-button
12
+ * @slot - The main content for the button.
13
+ */
14
+ export class CfpbExpandable extends LitElement {
15
+ static styles = css`
16
+ ${unsafeCSS(styles)}
17
+ `;
18
+
19
+ #flyoutMenu;
20
+ #transition;
21
+
22
+ /**
23
+ * @property {boolean} isExpanded - Whether the expandable is expanded or not.
24
+ * @returns {object} The map of properties.
25
+ */
26
+ static get properties() {
27
+ return {
28
+ isExpanded: { type: Boolean, attribute: 'open', reflect: true },
29
+ };
30
+ }
31
+
32
+ constructor() {
33
+ super();
34
+ }
35
+
36
+ firstUpdated() {
37
+ const root = this.shadowRoot.querySelector('div');
38
+ const contentDom = root.querySelector('.o-expandable__content');
39
+
40
+ // If it's expanded we don't set an initial height,
41
+ // as it will be calculated internally.
42
+ const initialClass = this.isExpanded
43
+ ? MaxHeightTransition.CLASSES.MH_DEFAULT
44
+ : MaxHeightTransition.CLASSES.MH_ZERO;
45
+ this.#transition = new MaxHeightTransition(contentDom).init(initialClass);
46
+
47
+ this.#flyoutMenu = new FlyoutMenu(root);
48
+
49
+ this.#flyoutMenu.setTransition(
50
+ this.#transition,
51
+ this.#transition.maxHeightZero,
52
+ this.#transition.maxHeightDefault,
53
+ );
54
+
55
+ this.#flyoutMenu.init(this.isExpanded);
56
+
57
+ // Add events.
58
+ this.#flyoutMenu.addEventListener('expandbegin', () => {
59
+ this.isExpanded = true;
60
+ contentDom.classList.remove('u-hidden');
61
+ this.dispatchEvent(
62
+ new CustomEvent('expandbegin', {
63
+ detail: { target: this },
64
+ bubbles: true,
65
+ composed: true,
66
+ }),
67
+ );
68
+ });
69
+ this.#flyoutMenu.addEventListener('collapseend', () => {
70
+ this.isExpanded = false;
71
+ contentDom.classList.add('u-hidden');
72
+ });
73
+ }
74
+
75
+ updated(changedProps) {
76
+ if (changedProps.has('isExpanded')) {
77
+ const oldVal = changedProps.get('isExpanded');
78
+ const newVal = this.isExpanded;
79
+
80
+ if (newVal !== oldVal) {
81
+ if (newVal) {
82
+ this.#flyoutMenu.expand();
83
+ } else {
84
+ this.#flyoutMenu.collapse();
85
+ }
86
+ }
87
+ }
88
+ }
89
+
90
+ render() {
91
+ return html`
92
+ <div
93
+ class="o-expandable o-expandable--background o-expandable--border"
94
+ data-js-hook="behavior_flyout-menu"
95
+ >
96
+ <button
97
+ class="o-expandable__header"
98
+ title="Expand content"
99
+ data-js-hook="behavior_flyout-menu_trigger"
100
+ >
101
+ <slot name="header" class="o-expandable__label"></slot>
102
+ <span class="o-expandable__cues">
103
+ <span class="o-expandable__cue-open" role="img" aria-label="Show">
104
+ ${unsafeSVG(expandIcon)}
105
+ <span class="u-visually-hidden">Show</span>
106
+ </span>
107
+ <span class="o-expandable__cue-close" role="img" aria-label="Hide">
108
+ ${unsafeSVG(collapseIcon)}
109
+ <span class="u-visually-hidden">Hide</span>
110
+ </span>
111
+ </span>
112
+ </button>
113
+ <div
114
+ class="o-expandable__content"
115
+ data-js-hook="behavior_flyout-menu_content"
116
+ >
117
+ <slot name="content"></slot>
118
+ </div>
119
+ </div>
120
+ `;
121
+ }
122
+
123
+ static init() {
124
+ window.customElements.get('cfpb-expandable') ||
125
+ window.customElements.define('cfpb-expandable', CfpbExpandable);
126
+ }
127
+ }
@@ -1,5 +1,5 @@
1
- @use '@cfpb/cfpb-design-system/src/abstracts' as *;
2
- @use '@cfpb/cfpb-design-system/src/components/cfpb-buttons/vars' as *;
1
+ @use '@cfpb/cfpb-design-system/src/elements/abstracts' as *;
2
+ @use '@cfpb/cfpb-design-system/src/elements/cfpb-button/vars' as *;
3
3
 
4
4
  :host {
5
5
  // This prevents the child button from having an empty gap after the button.