@cfpb/cfpb-design-system 4.2.4 → 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 (124) hide show
  1. package/CHANGELOG.md +166 -1
  2. package/dist/components/cfpb-expandables/index.js +1 -1
  3. package/dist/components/cfpb-expandables/index.js.map +3 -3
  4. package/dist/components/cfpb-forms/index.js +1 -1
  5. package/dist/components/cfpb-forms/index.js.map +2 -2
  6. package/dist/elements/cfpb-button/index.js +4 -4
  7. package/dist/elements/cfpb-button/index.js.map +3 -3
  8. package/dist/elements/cfpb-checkbox-icon/index.js +29 -0
  9. package/dist/elements/{cfpb-checkbox → cfpb-checkbox-icon}/index.js.map +4 -4
  10. package/dist/elements/cfpb-expandable/index.css +2 -0
  11. package/dist/elements/cfpb-expandable/index.css.map +7 -0
  12. package/dist/elements/cfpb-expandable/index.js +33 -0
  13. package/dist/elements/cfpb-expandable/index.js.map +7 -0
  14. package/dist/elements/cfpb-file-upload/index.js +4 -4
  15. package/dist/elements/cfpb-file-upload/index.js.map +3 -3
  16. package/dist/elements/cfpb-form-alert/index.js +32 -0
  17. package/dist/elements/cfpb-form-alert/index.js.map +7 -0
  18. package/dist/elements/cfpb-form-choice/index.js +12 -3
  19. package/dist/elements/cfpb-form-choice/index.js.map +4 -4
  20. package/dist/elements/cfpb-form-search/index.js +41 -0
  21. package/dist/elements/cfpb-form-search/index.js.map +7 -0
  22. package/dist/elements/cfpb-form-search-input/index.js +41 -0
  23. package/dist/elements/cfpb-form-search-input/index.js.map +7 -0
  24. package/dist/elements/cfpb-icon-text/index.js +3 -3
  25. package/dist/elements/cfpb-icon-text/index.js.map +3 -3
  26. package/dist/elements/cfpb-label/index.js +3 -3
  27. package/dist/elements/cfpb-label/index.js.map +2 -2
  28. package/dist/elements/cfpb-list/index.js +39 -0
  29. package/dist/elements/cfpb-list/index.js.map +7 -0
  30. package/dist/elements/cfpb-list-item/index.js +39 -0
  31. package/dist/elements/cfpb-list-item/index.js.map +7 -0
  32. package/dist/elements/cfpb-multiselect/index.js +13 -4
  33. package/dist/elements/cfpb-multiselect/index.js.map +4 -4
  34. package/dist/elements/cfpb-pagination/index.js +3 -3
  35. package/dist/elements/cfpb-pagination/index.js.map +2 -2
  36. package/dist/elements/cfpb-select/index.css +2 -0
  37. package/dist/elements/cfpb-select/index.css.map +7 -0
  38. package/dist/elements/cfpb-select/index.js +42 -0
  39. package/dist/elements/cfpb-select/index.js.map +7 -0
  40. package/dist/elements/cfpb-select-list/index.js +39 -0
  41. package/dist/elements/cfpb-select-list/index.js.map +7 -0
  42. package/dist/elements/cfpb-tag-filter/index.js +3 -3
  43. package/dist/elements/cfpb-tag-filter/index.js.map +3 -3
  44. package/dist/elements/cfpb-tag-group/index.js +3 -3
  45. package/dist/elements/cfpb-tag-group/index.js.map +4 -4
  46. package/dist/elements/cfpb-tag-topic/index.js +4 -4
  47. package/dist/elements/cfpb-tag-topic/index.js.map +1 -1
  48. package/dist/elements/index.css +2 -0
  49. package/dist/elements/index.css.map +7 -0
  50. package/dist/elements/index.js +7 -6
  51. package/dist/elements/index.js.map +4 -4
  52. package/dist/index.js +7 -6
  53. package/dist/index.js.map +4 -4
  54. package/dist/utilities/index.js +1 -1
  55. package/dist/utilities/index.js.map +3 -3
  56. package/package.json +1 -1
  57. package/src/components/cfpb-expandables/expandable.js +3 -0
  58. package/src/components/cfpb-forms/multiselect.js +1 -1
  59. package/src/elements/abstracts/custom-props.css +123 -0
  60. package/src/elements/abstracts/grid-mixins.scss +83 -0
  61. package/src/elements/abstracts/heading-mixins.scss +346 -0
  62. package/src/elements/abstracts/index.scss +7 -0
  63. package/src/elements/abstracts/media-queries.scss +35 -0
  64. package/src/elements/abstracts/sizing-vars.scss +65 -0
  65. package/src/elements/abstracts/vars-breakpoints.scss +16 -0
  66. package/src/elements/abstracts/vars.css +79 -0
  67. package/src/elements/base/base.scss +375 -0
  68. package/src/elements/base/font.scss +27 -0
  69. package/src/elements/base/index.scss +3 -0
  70. package/src/elements/base/normalize.scss +290 -0
  71. package/src/elements/cfpb-button/cfpb-button-group.scss +10 -0
  72. package/src/elements/cfpb-button/cfpb-button-link.scss +96 -0
  73. package/src/elements/cfpb-button/cfpb-button.component.scss +11 -4
  74. package/src/elements/cfpb-button/cfpb-button.scss +222 -0
  75. package/src/elements/cfpb-button/index.js +28 -29
  76. package/src/elements/cfpb-button/vars.css +30 -0
  77. package/src/elements/cfpb-checkbox-icon/cfpb-checkbox-icon.component.scss +88 -0
  78. package/src/elements/cfpb-checkbox-icon/index.js +104 -0
  79. package/src/elements/cfpb-expandable/cfpb-expandable.component.scss +218 -0
  80. package/src/elements/cfpb-expandable/index.js +127 -0
  81. package/src/elements/cfpb-file-upload/cfpb-file-upload.component.scss +2 -2
  82. package/src/elements/cfpb-file-upload/index.js +16 -18
  83. package/src/elements/cfpb-form-alert/cfpb-form-alert.component.scss +36 -0
  84. package/src/elements/cfpb-form-alert/index.js +55 -0
  85. package/src/elements/cfpb-form-choice/cfpb-form-choice.component.scss +42 -81
  86. package/src/elements/cfpb-form-choice/index.js +58 -18
  87. package/src/elements/cfpb-form-search/cfpb-form-search.component.scss +54 -0
  88. package/src/elements/cfpb-form-search/index.js +194 -0
  89. package/src/elements/cfpb-form-search-input/cfpb-form-search-input.component.scss +217 -0
  90. package/src/elements/cfpb-form-search-input/index.js +136 -0
  91. package/src/elements/cfpb-icon-text/cfpb-icon-text.component.scss +32 -39
  92. package/src/elements/cfpb-icon-text/index.js +32 -104
  93. package/src/elements/cfpb-label/cfpb-label.component.scss +2 -2
  94. package/src/elements/cfpb-label/index.js +6 -9
  95. package/src/elements/cfpb-list/cfpb-list.component.scss +23 -0
  96. package/src/elements/cfpb-list/index.js +357 -0
  97. package/src/elements/cfpb-list/index.spec.js +169 -0
  98. package/src/elements/cfpb-list-item/cfpb-list-item.component.scss +69 -0
  99. package/src/elements/cfpb-list-item/index.js +215 -0
  100. package/src/elements/cfpb-pagination/cfpb-pagination.component.scss +2 -7
  101. package/src/elements/cfpb-pagination/index.js +6 -8
  102. package/src/elements/cfpb-select/cfpb-select.component.scss +241 -0
  103. package/src/elements/cfpb-select/index.js +381 -0
  104. package/src/elements/cfpb-tag-filter/cfpb-tag-filter.component.scss +6 -3
  105. package/src/elements/cfpb-tag-filter/index.js +15 -7
  106. package/src/elements/cfpb-tag-group/cfpb-tag-group.component.scss +2 -2
  107. package/src/elements/cfpb-tag-group/index.js +53 -6
  108. package/src/elements/cfpb-tag-topic/index.js +5 -7
  109. package/src/elements/cfpb-utilities/parse-child-data.js +50 -0
  110. package/src/elements/cfpb-utilities/parse-child-data.spec.js +56 -0
  111. package/src/elements/cfpb-utilities/search-service.js +46 -0
  112. package/src/elements/cfpb-utilities/search-service.spec.js +138 -0
  113. package/src/elements/cfpb-utilities/transition/transition.scss +98 -0
  114. package/src/elements/index.js +7 -1
  115. package/src/index.scss +11 -0
  116. package/src/tokens/abstracts/custom-props.json +1642 -0
  117. package/src/tokens/abstracts/vars.json +1319 -0
  118. package/src/tokens/cfpb-button/vars.json +436 -0
  119. package/src/utilities/transition/max-height-transition.js +74 -0
  120. package/dist/elements/cfpb-checkbox/index.js +0 -29
  121. package/src/elements/cfpb-multiselect/cfpb-multiselect.component.scss +0 -225
  122. package/src/elements/cfpb-multiselect/index.js +0 -444
  123. package/src/elements/cfpb-multiselect/multiselect-model.js +0 -288
  124. package/src/elements/cfpb-multiselect/multiselect-model.spec.js +0 -236
@@ -0,0 +1,46 @@
1
+ export class SearchService {
2
+ constructor(items = [], options = {}) {
3
+ this.items = items;
4
+
5
+ // Specify object keys to search in.
6
+ this.fields = options.fields || null;
7
+ }
8
+
9
+ setItems(items) {
10
+ this.items = items;
11
+ }
12
+
13
+ search(query) {
14
+ if (!query || query.trim() === '') return this.items;
15
+
16
+ const lowerQuery = query.toLowerCase();
17
+
18
+ const result = this.items.filter((item) => {
19
+ if (typeof item === 'string') {
20
+ return item.toLocaleLowerCase().includes(lowerQuery);
21
+ }
22
+
23
+ if (typeof item === 'object' && this.fields) {
24
+ return this.fields.some((field) => {
25
+ const value = item[field];
26
+ return (
27
+ typeof value === 'string' &&
28
+ value.toLowerCase().includes(lowerQuery)
29
+ );
30
+ });
31
+ }
32
+
33
+ // Fallback: check all string values.
34
+ const fallback = Object.values(item).some((value) => {
35
+ const match =
36
+ typeof value === 'string'
37
+ ? value.toLowerCase().includes(lowerQuery)
38
+ : false;
39
+ return match;
40
+ });
41
+ return fallback;
42
+ });
43
+
44
+ return result;
45
+ }
46
+ }
@@ -0,0 +1,138 @@
1
+ import { SearchService } from './search-service.js';
2
+
3
+ describe('SearchService', () => {
4
+ //
5
+ // ---------------------------------------------------------------------------
6
+ // BASIC STRING SEARCH TESTS
7
+ // ---------------------------------------------------------------------------
8
+ //
9
+ describe('basic behavior', () => {
10
+ let service;
11
+ let items;
12
+
13
+ beforeEach(() => {
14
+ items = ['Pawpaw', 'Elderberry', 'Persimmon'];
15
+ service = new SearchService(items);
16
+ });
17
+
18
+ it('returns all items when query is empty', () => {
19
+ const result = service.search('');
20
+ expect(result).toEqual(items);
21
+ });
22
+
23
+ it('is case-insensitive', () => {
24
+ const result = service.search('elderberry');
25
+ expect(result).toEqual(['Elderberry']);
26
+ });
27
+
28
+ it('matches partial substrings', () => {
29
+ const result = service.search('aw');
30
+ expect(result).toEqual(['Pawpaw']);
31
+ });
32
+
33
+ it('returns empty array if no matches', () => {
34
+ const result = service.search('xyz');
35
+ expect(result).toEqual([]);
36
+ });
37
+
38
+ it('updates the internal list with setItems()', () => {
39
+ const newItems = ['Possum', 'Moose'];
40
+ service.setItems(newItems);
41
+ const result = service.search('moose');
42
+ expect(result).toEqual(['Moose']);
43
+ });
44
+ });
45
+
46
+ //
47
+ // ---------------------------------------------------------------------------
48
+ // OBJECT-BASED SEARCH TESTS
49
+ // ---------------------------------------------------------------------------
50
+ //
51
+ describe('object-based search', () => {
52
+ let service;
53
+ let items;
54
+
55
+ beforeEach(() => {
56
+ items = [
57
+ {
58
+ name: 'Pawpaw',
59
+ description: 'Largest fruit native to North America',
60
+ },
61
+ { name: 'Elderberry', description: 'Must be cooked before eaten.' },
62
+ { name: 'Persimmon', description: 'Trees are dioecious.' },
63
+ ];
64
+ });
65
+
66
+ it('searches specific fields when provided', () => {
67
+ service = new SearchService(items, { fields: ['name'] });
68
+ const result = service.search('pawpaw');
69
+ expect(result).toEqual([
70
+ {
71
+ name: 'Pawpaw',
72
+ description: 'Largest fruit native to North America',
73
+ },
74
+ ]);
75
+ });
76
+
77
+ it('searches across multiple fields', () => {
78
+ service = new SearchService(items, { fields: ['name', 'description'] });
79
+ const result = service.search('America');
80
+ expect(result).toEqual([
81
+ {
82
+ name: 'Pawpaw',
83
+ description: 'Largest fruit native to North America',
84
+ },
85
+ ]);
86
+ });
87
+
88
+ it('searches all string values if no fields specified', () => {
89
+ service = new SearchService(items);
90
+ const result = service.search('dioecious');
91
+ expect(result).toEqual([
92
+ { name: 'Persimmon', description: 'Trees are dioecious.' },
93
+ ]);
94
+ });
95
+
96
+ it('ignores non-string fields', () => {
97
+ const complexItems = [
98
+ { name: 'Grape', count: 5 },
99
+ { name: 'Mango', count: 10 },
100
+ ];
101
+
102
+ service = new SearchService(complexItems);
103
+ const result = service.search('10');
104
+
105
+ // Numbers are ignored.
106
+ expect(result).toEqual([]);
107
+ });
108
+ });
109
+
110
+ //
111
+ // ---------------------------------------------------------------------------
112
+ // EDGE CASES & ROBUSTNESS
113
+ // ---------------------------------------------------------------------------
114
+ //
115
+ describe('edge cases', () => {
116
+ let service;
117
+ beforeEach(() => {
118
+ service = new SearchService();
119
+ });
120
+
121
+ it('handles null or undefined queries gracefully', () => {
122
+ service.setItems(['Bottle cap']);
123
+ expect(service.search(null)).toEqual(['Bottle cap']);
124
+ expect(service.search(undefined)).toEqual(['Bottle cap']);
125
+ });
126
+
127
+ it('returns an empty array when items is empty', () => {
128
+ const result = service.search('bottle');
129
+ expect(result).toEqual([]);
130
+ });
131
+
132
+ it('ignores objects without string properties', () => {
133
+ service.setItems([{ id: 1 }, { id: 2 }]);
134
+ const result = service.search('1');
135
+ expect(result).toEqual([]);
136
+ });
137
+ });
138
+ });
@@ -0,0 +1,98 @@
1
+ /* ==========================================================================
2
+ Utility classes for transitions.
3
+
4
+ Adds transitions utilty classes for transform, opacity,
5
+ and for the removing the transition duration.
6
+ ========================================================================== */
7
+
8
+ .u-no-animation {
9
+ transition-duration: 0s !important;
10
+ }
11
+
12
+ //
13
+ // Utility classes for moving an element using transform translate values.
14
+ //
15
+
16
+ .u-move-transition {
17
+ transition: transform 0.25s ease-out;
18
+ }
19
+
20
+ .u-move-to-origin {
21
+ transform: translate3d(0, 0, 0);
22
+ }
23
+
24
+ .u-move-left {
25
+ transform: translate3d(-100%, 0, 0);
26
+ }
27
+
28
+ // TODO: Look into adding a mixin for movement multiples.
29
+ .u-move-left-2x {
30
+ transform: translate3d(-200%, 0, 0);
31
+ }
32
+
33
+ .u-move-left-3x {
34
+ transform: translate3d(-300%, 0, 0);
35
+ }
36
+
37
+ .u-move-right {
38
+ transform: translate3d(100%, 0, 0);
39
+ }
40
+
41
+ .u-move-up {
42
+ transform: translate3d(0, -100%, 0);
43
+ }
44
+
45
+ //
46
+ // Utility classes for setting an element's opacity.
47
+ //
48
+
49
+ .u-alpha-transition {
50
+ transition: opacity 0.25s linear;
51
+ }
52
+
53
+ .u-alpha-100 {
54
+ opacity: 1;
55
+ }
56
+
57
+ .u-alpha-0 {
58
+ opacity: 0;
59
+ }
60
+
61
+ //
62
+ // Utility classes for setting an element's height.
63
+ //
64
+
65
+ .u-max-height-transition {
66
+ overflow: hidden;
67
+ contain: paint;
68
+
69
+ // Duration is set here, but it is actually overridden in the JavaScript.
70
+ transition: max-height 0.2s ease-out;
71
+ }
72
+
73
+ /*
74
+ .u-max-height-default {
75
+ This class is kept for documentation completeness.
76
+ The actual max-height is set in the JavaScript,
77
+ so that we know what the actual height of the content is for easing purposes.
78
+ }
79
+
80
+ .u-max-height-dynamic {
81
+ This class is kept for documentation completeness.
82
+ The actual max-height is set in the JavaScript,
83
+ so that we know what the actual height of the content is for easing purposes.
84
+ }
85
+ */
86
+
87
+ .u-max-height-zero {
88
+ max-height: 0 !important;
89
+ }
90
+
91
+ .u-max-height-summary {
92
+ /* The value set here should show 4 lines of text at our standard 16px
93
+ base font size. The calculation comes from the following:
94
+ 88px = 16 * 5.5em.
95
+ 5.5em = base-line-height (22px) * 4 / base-font-size (16px)
96
+ */
97
+ max-height: 88px !important;
98
+ }
@@ -4,11 +4,17 @@
4
4
 
5
5
  // TODO: aggregate and export the component subdirectories automatically.
6
6
  export * from './cfpb-button';
7
+ export * from './cfpb-form-alert';
8
+ export * from './cfpb-expandable';
7
9
  export * from './cfpb-form-choice';
8
10
  export * from './cfpb-file-upload';
9
11
  export * from './cfpb-label';
12
+ export * from './cfpb-list';
13
+ export * from './cfpb-list-item';
14
+ export * from './cfpb-form-search';
15
+ export * from './cfpb-form-search-input';
10
16
  export * from './cfpb-tag-filter';
11
17
  export * from './cfpb-tag-topic';
12
18
  export * from './cfpb-tag-group';
13
- export * from './cfpb-multiselect';
19
+ export * from './cfpb-select';
14
20
  export * from './cfpb-pagination';
package/src/index.scss CHANGED
@@ -64,3 +64,14 @@
64
64
 
65
65
  // Utilities.
66
66
  @forward 'utilities';
67
+
68
+ // Web components.
69
+ @forward 'elements/base';
70
+ @forward 'elements/abstracts';
71
+
72
+ // cfpb-button
73
+ @forward 'elements/cfpb-button/cfpb-button';
74
+ @forward 'elements/cfpb-button/cfpb-button-group';
75
+ @forward 'elements/cfpb-button/cfpb-button-link';
76
+ @forward 'elements/cfpb-button/cfpb-button.component';
77
+ @forward 'elements/cfpb-button/vars';