@cfpb/cfpb-design-system 4.0.4 → 4.2.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 (114) hide show
  1. package/CHANGELOG.md +60 -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 +1 -1
  13. package/dist/components/cfpb-expandables/index.js.map +1 -1
  14. package/dist/components/cfpb-forms/index.css +1 -1
  15. package/dist/components/cfpb-forms/index.css.map +2 -2
  16. package/dist/components/cfpb-forms/index.js +1 -1
  17. package/dist/components/cfpb-forms/index.js.map +1 -1
  18. package/dist/components/cfpb-icons/index.css +1 -1
  19. package/dist/components/cfpb-icons/index.css.map +2 -2
  20. package/dist/components/cfpb-icons/index.js +1 -1
  21. package/dist/components/cfpb-icons/index.js.map +1 -1
  22. package/dist/components/cfpb-layout/index.css +1 -1
  23. package/dist/components/cfpb-layout/index.css.map +2 -2
  24. package/dist/components/cfpb-layout/index.js +1 -1
  25. package/dist/components/cfpb-layout/index.js.map +1 -1
  26. package/dist/components/cfpb-notifications/index.css +1 -1
  27. package/dist/components/cfpb-notifications/index.css.map +2 -2
  28. package/dist/components/cfpb-notifications/index.js +1 -1
  29. package/dist/components/cfpb-notifications/index.js.map +1 -1
  30. package/dist/components/cfpb-pagination/index.css +1 -1
  31. package/dist/components/cfpb-pagination/index.css.map +2 -2
  32. package/dist/components/cfpb-pagination/index.js +1 -1
  33. package/dist/components/cfpb-pagination/index.js.map +1 -1
  34. package/dist/components/cfpb-tables/index.css +1 -1
  35. package/dist/components/cfpb-tables/index.css.map +2 -2
  36. package/dist/components/cfpb-tables/index.js +1 -1
  37. package/dist/components/cfpb-tables/index.js.map +1 -1
  38. package/dist/components/cfpb-tooltips/index.css +1 -1
  39. package/dist/components/cfpb-tooltips/index.css.map +2 -2
  40. package/dist/components/cfpb-tooltips/index.js +1 -1
  41. package/dist/components/cfpb-tooltips/index.js.map +1 -1
  42. package/dist/components/cfpb-typography/index.css +1 -1
  43. package/dist/components/cfpb-typography/index.css.map +2 -2
  44. package/dist/components/cfpb-typography/index.js +1 -1
  45. package/dist/components/cfpb-typography/index.js.map +1 -1
  46. package/dist/elements/cfpb-button/index.js +21 -4
  47. package/dist/elements/cfpb-button/index.js.map +4 -4
  48. package/dist/elements/cfpb-file-upload/index.js +11 -4
  49. package/dist/elements/cfpb-file-upload/index.js.map +4 -4
  50. package/dist/elements/cfpb-form-choice/index.js +11 -3
  51. package/dist/elements/cfpb-form-choice/index.js.map +4 -4
  52. package/dist/elements/cfpb-icon-text/index.js +29 -0
  53. package/dist/elements/cfpb-icon-text/index.js.map +7 -0
  54. package/dist/elements/cfpb-label/index.js +36 -0
  55. package/dist/elements/cfpb-label/index.js.map +7 -0
  56. package/dist/elements/cfpb-multiselect/index.js +13 -4
  57. package/dist/elements/cfpb-multiselect/index.js.map +4 -4
  58. package/dist/elements/cfpb-pagination/index.js +32 -0
  59. package/dist/elements/cfpb-pagination/index.js.map +7 -0
  60. package/dist/elements/cfpb-tag-filter/index.js +2 -2
  61. package/dist/elements/cfpb-tag-filter/index.js.map +2 -2
  62. package/dist/elements/cfpb-tag-group/index.js +2 -2
  63. package/dist/elements/cfpb-tag-group/index.js.map +2 -2
  64. package/dist/elements/cfpb-tag-topic/index.js +3 -3
  65. package/dist/elements/cfpb-tag-topic/index.js.map +2 -2
  66. package/dist/elements/cfpb-utilities/index.js +2 -0
  67. package/dist/elements/cfpb-utilities/index.js.map +7 -0
  68. package/dist/elements/index.js +15 -5
  69. package/dist/elements/index.js.map +4 -4
  70. package/dist/index.css +1 -1
  71. package/dist/index.css.map +2 -2
  72. package/dist/index.js +15 -5
  73. package/dist/index.js.map +4 -4
  74. package/dist/utilities/index.css +1 -1
  75. package/dist/utilities/index.css.map +2 -2
  76. package/dist/utilities/index.js +1 -1
  77. package/dist/utilities/index.js.map +1 -1
  78. package/package.json +2 -2
  79. package/src/abstracts/heading-mixins.scss +6 -0
  80. package/src/abstracts/vars.scss +23 -0
  81. package/src/base/base.scss +15 -27
  82. package/src/components/cfpb-buttons/button.scss +4 -2
  83. package/src/components/cfpb-forms/tag.scss +3 -0
  84. package/src/components/cfpb-pagination/vars.scss +0 -4
  85. package/src/components/cfpb-typography/link.scss +4 -2
  86. package/src/components/cfpb-typography/mixins.scss +8 -0
  87. package/src/elements/cfpb-button/cfpb-button.component.scss +23 -0
  88. package/src/elements/cfpb-button/index.js +127 -19
  89. package/src/elements/cfpb-file-upload/index.js +1 -1
  90. package/src/elements/cfpb-form-choice/cfpb-form-choice.component.scss +6 -1
  91. package/src/elements/cfpb-form-choice/index.js +62 -29
  92. package/src/elements/cfpb-form-choice/index.spec.js +47 -0
  93. package/src/elements/cfpb-icon-text/cfpb-icon-text.component.scss +59 -0
  94. package/src/elements/cfpb-icon-text/index.js +150 -0
  95. package/src/elements/cfpb-label/cfpb-label.component.scss +36 -0
  96. package/src/elements/cfpb-label/index.js +62 -0
  97. package/src/elements/cfpb-multiselect/cfpb-multiselect.component.scss +225 -0
  98. package/src/elements/cfpb-multiselect/index.js +444 -0
  99. package/src/elements/cfpb-multiselect/multiselect-model.js +288 -0
  100. package/src/elements/cfpb-multiselect/multiselect-model.spec.js +236 -0
  101. package/src/elements/cfpb-pagination/cfpb-pagination.component.scss +72 -0
  102. package/src/elements/cfpb-pagination/index.js +211 -0
  103. package/src/elements/cfpb-tag-filter/index.js +2 -1
  104. package/src/elements/cfpb-tag-filter/index.spec.js +1 -1
  105. package/src/elements/cfpb-tag-group/index.js +2 -1
  106. package/src/elements/cfpb-tag-topic/cfpb-tag-topic.component.scss +2 -0
  107. package/src/elements/cfpb-tag-topic/index.js +7 -0
  108. package/src/elements/cfpb-utilities/i18n-service.js +128 -0
  109. package/src/elements/cfpb-utilities/i18n-service.spec.js +156 -0
  110. package/src/elements/cfpb-utilities/index.js +7 -0
  111. package/src/elements/cfpb-utilities/media-query-service.js +102 -0
  112. package/src/elements/cfpb-utilities/media-query-service.spec.js +126 -0
  113. package/src/elements/index.js +3 -0
  114. package/src/utilities/utilities.scss +8 -8
@@ -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
+ }
@@ -0,0 +1,36 @@
1
+ @use 'sass:math';
2
+ @use '@cfpb/cfpb-design-system/src/abstracts' as *;
3
+
4
+ :host {
5
+ .a-label {
6
+ slot {
7
+ display: inline-block;
8
+ }
9
+
10
+ &__helper {
11
+ color: $label-helper;
12
+ font-size: math.div(16px, $base-font-size-px) + rem;
13
+ font-weight: normal;
14
+
15
+ &--block {
16
+ display: block;
17
+
18
+ // Add a gap between the label helper and label.
19
+ margin-top: math.div(10px, $size-vi) + em;
20
+ }
21
+ }
22
+
23
+ &--heading {
24
+ display: block;
25
+
26
+ margin-bottom: math.div(10px, $size-iv) + em;
27
+
28
+ @include heading-4($has-margin-bottom: false);
29
+
30
+ // Add a gap between the label helper and label heading
31
+ .a-label__helper--block {
32
+ margin-top: math.div(10px, $base-font-size-px) + rem;
33
+ }
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,62 @@
1
+ import { html, LitElement, css, unsafeCSS } from 'lit';
2
+ import styles from './cfpb-label.component.scss';
3
+ import { ifDefined } from 'lit/directives/if-defined.js';
4
+
5
+ /**
6
+ *
7
+ * @element cfpb-label.
8
+ * @slot label - The content for the label text.
9
+ * @slot helper - The content for the label helper text.
10
+ */
11
+ export class CfpbLabel extends LitElement {
12
+ static styles = css`
13
+ ${unsafeCSS(styles)}
14
+ `;
15
+
16
+ /**
17
+ * @property {string} for - Associate the label with an ID elsewhere.
18
+ * @property {boolean} block - Whether this has block or inline helper text.
19
+ * @returns {object} The map of properties.
20
+ */
21
+ static get properties() {
22
+ return {
23
+ // Other properties.
24
+ block: { type: Boolean, reflect: true },
25
+ for: { type: String },
26
+ };
27
+ }
28
+
29
+ constructor() {
30
+ super();
31
+ this.block = false;
32
+ this.for = '';
33
+ }
34
+
35
+ get #helperClass() {
36
+ let helperClass = 'a-label__helper';
37
+ if (this.block) {
38
+ helperClass += ' a-label__helper--block';
39
+ }
40
+
41
+ return helperClass;
42
+ }
43
+
44
+ render() {
45
+ return html`
46
+ <label
47
+ class="a-label a-label--heading"
48
+ for=${ifDefined(this.for && this.for.trim() ? this.for : undefined)}
49
+ >
50
+ <slot name="label"></slot>
51
+ <small class="${this.#helperClass}">
52
+ <slot name="helper"></slot>
53
+ </small>
54
+ </label>
55
+ `;
56
+ }
57
+
58
+ static init() {
59
+ window.customElements.get('cfpb-label') ||
60
+ window.customElements.define('cfpb-label', CfpbLabel);
61
+ }
62
+ }
@@ -0,0 +1,225 @@
1
+ @use 'sass:math';
2
+ @use '@cfpb/cfpb-design-system/src/base' as *;
3
+ @use '@cfpb/cfpb-design-system/src/abstracts' as *;
4
+ @use '@cfpb/cfpb-design-system/src/components/cfpb-buttons/vars' as *;
5
+
6
+ :host {
7
+ // Theme variables.
8
+ --select-input-border: var(--gray-60);
9
+ --select-input-border-hover: var(--pacific);
10
+ --select-input-border-focus: var(--pacific);
11
+ --select-input-bg: var(--white);
12
+ --select-input-text: var(--black);
13
+
14
+ // Initial and no-js state.
15
+ select.o-multiselect {
16
+ display: block;
17
+ box-sizing: border-box;
18
+ width: 100%;
19
+ padding: math.div(7px, $base-font-size-px) + em;
20
+
21
+ // Fixed height breaks the bottom border
22
+ // mid-character to indicate there's more content.
23
+ height: 5.5em;
24
+ padding-top: math.div(4px, $base-font-size-px) + em;
25
+ padding-bottom: math.div(4px, $base-font-size-px) + em;
26
+ border: 1px solid var(--select-border-default);
27
+
28
+ option {
29
+ padding: math.div(2px, $base-font-size-px) + em
30
+ math.div(6px, $base-font-size-px) + em;
31
+ }
32
+ }
33
+
34
+ .o-multiselect {
35
+ position: relative;
36
+
37
+ & header {
38
+ position: relative;
39
+
40
+ &::after {
41
+ // Arrow box width must be odd size to properly center the bg image
42
+ width: math.div($select-height, $base-font-size-px) + em;
43
+ box-sizing: border-box;
44
+ border: 1px solid var(--select-border-default);
45
+ position: absolute;
46
+ top: 0;
47
+ right: 0;
48
+ bottom: 0;
49
+ background-color: var(--select-icon-bg-default);
50
+
51
+ --cfpb-background-icon-svg: 'down';
52
+
53
+ background-size: auto $cf-icon-height;
54
+ background-repeat: no-repeat;
55
+ background-position: center center;
56
+ content: '';
57
+ pointer-events: none;
58
+ }
59
+ }
60
+
61
+ & input[type='text'] {
62
+ width: 100%;
63
+ min-height: 35px;
64
+
65
+ // Reset the browser's default styling.
66
+ appearance: none;
67
+ display: inline-block;
68
+ padding: math.div(7px, $base-font-size-px) + em;
69
+ border: 1px solid var(--select-input-border);
70
+ outline: 0 solid var(--select-input-border);
71
+ background: var(--select-input-bg);
72
+ color: var(--select-input-text);
73
+ box-sizing: border-box;
74
+
75
+ &:hover,
76
+ &.hover {
77
+ border-color: var(--select-input-border-hover);
78
+ outline: 1px solid var(--select-input-border-hover);
79
+ }
80
+
81
+ &:focus,
82
+ &.focus {
83
+ border-color: var(--select-input-border-focus);
84
+ box-shadow: 0 0 0 1px var(--select-input-border-focus);
85
+ outline: 1px dotted var(--select-input-border-focus);
86
+ outline-offset: 2px;
87
+ }
88
+ }
89
+
90
+ & fieldset {
91
+ // Resets
92
+ border-color: var(--select-border-default);
93
+ border-top: none;
94
+ margin: 0;
95
+ padding: 0;
96
+
97
+ // Styles
98
+ box-sizing: border-box;
99
+ overflow-x: hidden;
100
+ overflow-y: scroll;
101
+ position: absolute;
102
+ z-index: 10;
103
+
104
+ max-height: 0;
105
+ margin-top: -1px;
106
+ width: 100%;
107
+
108
+ transition: max-height 0.25s ease-out;
109
+ }
110
+
111
+ &.u-active {
112
+ fieldset {
113
+ margin-top: 0;
114
+
115
+ // This needs to match the value set in _bindEvents in Multiselect.js.
116
+ // See https://github.com/cfpb/design-system/blob/4d26d5af04317bcc00b4677aa866fe8d526e82e0/packages/cfpb-forms/src/organisms/Multiselect.js#L340
117
+ max-height: 140px;
118
+
119
+ border-color: var(--pacific);
120
+ border-width: 2px;
121
+ border-top: 0;
122
+ }
123
+
124
+ // Reverse arrow when search drop-down is open.
125
+ header::after {
126
+ --cfpb-background-icon-svg: 'up';
127
+ }
128
+ }
129
+
130
+ & ul {
131
+ list-style-type: none;
132
+ background-color: var(--white);
133
+ padding: 0;
134
+ padding-top: math.div(5px, $base-font-size-px) + em;
135
+ padding-bottom: math.div(5px, $base-font-size-px) + em;
136
+
137
+ li {
138
+ margin: 0;
139
+ }
140
+
141
+ li:first-child {
142
+ .a-label {
143
+ padding-top: math.div(10px, $base-font-size-px) + em;
144
+ }
145
+ }
146
+
147
+ &.u-filtered li:not(.u-filter-match) {
148
+ display: none;
149
+ }
150
+
151
+ &.u-no-results,
152
+ &.u-max-selections {
153
+ padding: math.div(10px, $base-font-size-px) + em;
154
+ li {
155
+ display: none;
156
+ }
157
+
158
+ &::after {
159
+ display: list-item;
160
+ }
161
+ }
162
+
163
+ &.u-no-results::after {
164
+ content: 'No results found';
165
+ }
166
+
167
+ &.u-max-selections {
168
+ pointer-events: none;
169
+
170
+ &::after {
171
+ content: 'Reached maximum number of selections';
172
+ }
173
+ }
174
+ }
175
+ }
176
+
177
+ .u-invisible {
178
+ visibility: hidden;
179
+ }
180
+
181
+ /* button {
182
+ // Filter tags appear in filtered contexts, often as part of multiselects.
183
+ line-height: math.div(19px, $base-font-size-px);
184
+
185
+ display: flex;
186
+ gap: math.div(10px, $btn-font-size) + rem;
187
+
188
+ border: 1px solid var(--teal);
189
+ padding: 4px 6px;
190
+ background-color: var(--teal-20);
191
+ border-radius: math.div(3px, $base-font-size-px) + rem;
192
+ color: var(--black);
193
+ text-align: left;
194
+ min-width: fit-content;
195
+
196
+ &:hover {
197
+ background-color: var(--teal-40);
198
+ cursor: pointer;
199
+ }
200
+
201
+ &:focus {
202
+ outline: 1px dotted var(--teal);
203
+ outline-offset: 1px;
204
+ }
205
+
206
+ &:active {
207
+ background-color: var(--teal-60);
208
+ }
209
+ }
210
+
211
+ svg {
212
+ pointer-events: none;
213
+
214
+ // Prevent flexbox from squishing icon when tag text is long.
215
+ flex: none;
216
+
217
+ height: 1rem;
218
+ }
219
+
220
+ // If the contents are wrapped in a label, negate the label's display.
221
+ label {
222
+ display: contents;
223
+ pointer-events: none;
224
+ } */
225
+ }