@cfpb/cfpb-design-system 4.2.4 → 4.3.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.
- package/CHANGELOG.md +186 -1
- package/dist/components/cfpb-buttons/index.css +1 -1
- package/dist/components/cfpb-buttons/index.css.map +2 -2
- package/dist/components/cfpb-buttons/index.js +1 -1
- package/dist/components/cfpb-buttons/index.js.map +1 -1
- package/dist/components/cfpb-expandables/index.css +1 -1
- package/dist/components/cfpb-expandables/index.css.map +2 -2
- package/dist/components/cfpb-expandables/index.js +1 -1
- package/dist/components/cfpb-expandables/index.js.map +4 -4
- package/dist/components/cfpb-forms/index.css +1 -1
- package/dist/components/cfpb-forms/index.css.map +2 -2
- package/dist/components/cfpb-forms/index.js +1 -1
- package/dist/components/cfpb-forms/index.js.map +2 -2
- package/dist/components/cfpb-icons/index.css +1 -1
- package/dist/components/cfpb-icons/index.css.map +2 -2
- package/dist/components/cfpb-icons/index.js +1 -1
- package/dist/components/cfpb-icons/index.js.map +1 -1
- package/dist/components/cfpb-layout/index.css +1 -1
- package/dist/components/cfpb-layout/index.css.map +2 -2
- package/dist/components/cfpb-layout/index.js +1 -1
- package/dist/components/cfpb-layout/index.js.map +1 -1
- package/dist/components/cfpb-notifications/index.css +1 -1
- package/dist/components/cfpb-notifications/index.css.map +2 -2
- package/dist/components/cfpb-notifications/index.js +1 -1
- package/dist/components/cfpb-notifications/index.js.map +1 -1
- package/dist/components/cfpb-pagination/index.css +1 -1
- package/dist/components/cfpb-pagination/index.css.map +2 -2
- package/dist/components/cfpb-pagination/index.js +1 -1
- package/dist/components/cfpb-pagination/index.js.map +1 -1
- package/dist/components/cfpb-tables/index.css +1 -1
- package/dist/components/cfpb-tables/index.css.map +2 -2
- package/dist/components/cfpb-tables/index.js +1 -1
- package/dist/components/cfpb-tables/index.js.map +1 -1
- package/dist/components/cfpb-tooltips/index.css +1 -1
- package/dist/components/cfpb-tooltips/index.css.map +2 -2
- package/dist/components/cfpb-tooltips/index.js +1 -1
- package/dist/components/cfpb-tooltips/index.js.map +1 -1
- package/dist/components/cfpb-typography/index.css +1 -1
- package/dist/components/cfpb-typography/index.css.map +2 -2
- package/dist/components/cfpb-typography/index.js +1 -1
- package/dist/components/cfpb-typography/index.js.map +1 -1
- package/dist/elements/abstracts/index.js +2 -0
- package/dist/elements/abstracts/index.js.map +7 -0
- package/dist/elements/base/index.css +3 -0
- package/dist/elements/base/index.css.map +7 -0
- package/dist/elements/base/index.js +2 -0
- package/dist/elements/base/index.js.map +7 -0
- package/dist/elements/cfpb-button/index.js +4 -4
- package/dist/elements/cfpb-button/index.js.map +3 -3
- package/dist/elements/cfpb-checkbox-icon/index.js +29 -0
- package/dist/elements/{cfpb-checkbox → cfpb-checkbox-icon}/index.js.map +4 -4
- package/dist/elements/cfpb-expandable/index.css +2 -0
- package/dist/elements/cfpb-expandable/index.css.map +7 -0
- package/dist/elements/cfpb-expandable/index.js +33 -0
- package/dist/elements/cfpb-expandable/index.js.map +7 -0
- package/dist/elements/cfpb-file-upload/index.js +4 -4
- package/dist/elements/cfpb-file-upload/index.js.map +3 -3
- package/dist/elements/cfpb-form-alert/index.js +32 -0
- package/dist/elements/cfpb-form-alert/index.js.map +7 -0
- package/dist/elements/cfpb-form-choice/index.js +12 -3
- package/dist/elements/cfpb-form-choice/index.js.map +4 -4
- package/dist/elements/cfpb-form-search/index.js +41 -0
- package/dist/elements/cfpb-form-search/index.js.map +7 -0
- package/dist/elements/cfpb-form-search-input/index.js +41 -0
- package/dist/elements/cfpb-form-search-input/index.js.map +7 -0
- package/dist/elements/cfpb-icon-text/index.js +3 -3
- package/dist/elements/cfpb-icon-text/index.js.map +3 -3
- package/dist/elements/cfpb-label/index.js +3 -3
- package/dist/elements/cfpb-label/index.js.map +2 -2
- package/dist/elements/cfpb-list/index.js +39 -0
- package/dist/elements/cfpb-list/index.js.map +7 -0
- package/dist/elements/cfpb-list-item/index.js +39 -0
- package/dist/elements/cfpb-list-item/index.js.map +7 -0
- package/dist/elements/cfpb-multiselect/index.js +13 -4
- package/dist/elements/cfpb-multiselect/index.js.map +4 -4
- package/dist/elements/cfpb-pagination/index.js +3 -3
- package/dist/elements/cfpb-pagination/index.js.map +2 -2
- package/dist/elements/cfpb-select/index.css +2 -0
- package/dist/elements/cfpb-select/index.css.map +7 -0
- package/dist/elements/cfpb-select/index.js +42 -0
- package/dist/elements/cfpb-select/index.js.map +7 -0
- package/dist/elements/cfpb-select-list/index.js +39 -0
- package/dist/elements/cfpb-select-list/index.js.map +7 -0
- package/dist/elements/cfpb-tag-filter/index.js +3 -3
- package/dist/elements/cfpb-tag-filter/index.js.map +3 -3
- package/dist/elements/cfpb-tag-group/index.js +3 -3
- package/dist/elements/cfpb-tag-group/index.js.map +4 -4
- package/dist/elements/cfpb-tag-topic/index.js +4 -4
- package/dist/elements/cfpb-tag-topic/index.js.map +2 -2
- package/dist/elements/index.css +2 -0
- package/dist/elements/index.css.map +7 -0
- package/dist/elements/index.js +7 -6
- package/dist/elements/index.js.map +4 -4
- package/dist/index.css +1 -1
- package/dist/index.css.map +3 -3
- package/dist/index.js +7 -6
- package/dist/index.js.map +4 -4
- package/dist/utilities/index.css +1 -1
- package/dist/utilities/index.css.map +2 -2
- package/dist/utilities/index.js +1 -1
- package/dist/utilities/index.js.map +4 -4
- package/package.json +1 -1
- package/src/components/cfpb-buttons/button-group.scss +1 -1
- package/src/components/cfpb-buttons/button-link.scss +10 -54
- package/src/components/cfpb-buttons/button.scss +3 -3
- package/src/components/cfpb-buttons/vars.scss +1 -1
- package/src/components/cfpb-expandables/expandable-group.scss +1 -1
- package/src/components/cfpb-expandables/expandable.js +3 -0
- package/src/components/cfpb-expandables/expandable.scss +1 -1
- package/src/components/cfpb-expandables/summary.scss +1 -1
- package/src/components/cfpb-forms/form-alert.scss +1 -1
- package/src/components/cfpb-forms/form-field.scss +6 -6
- package/src/components/cfpb-forms/form.scss +1 -1
- package/src/components/cfpb-forms/label.scss +2 -2
- package/src/components/cfpb-forms/multiselect.js +1 -1
- package/src/components/cfpb-forms/multiselect.scss +1 -1
- package/src/components/cfpb-forms/range.scss +7 -7
- package/src/components/cfpb-forms/search-input.scss +1 -1
- package/src/components/cfpb-forms/select.scss +1 -1
- package/src/components/cfpb-forms/tag.scss +1 -1
- package/src/components/cfpb-forms/text-input.scss +1 -1
- package/src/components/cfpb-icons/icon.scss +1 -1
- package/src/components/cfpb-layout/card-group.scss +1 -1
- package/src/components/cfpb-layout/card.scss +1 -1
- package/src/components/cfpb-layout/email-signup.scss +1 -1
- package/src/components/cfpb-layout/featured-content-module.scss +1 -1
- package/src/components/cfpb-layout/hero.scss +1 -1
- package/src/components/cfpb-layout/layout.scss +9 -9
- package/src/components/cfpb-layout/well.scss +1 -1
- package/src/components/cfpb-notifications/banner.scss +1 -1
- package/src/components/cfpb-notifications/notification.scss +1 -1
- package/src/components/cfpb-pagination/pagination.scss +1 -1
- package/src/components/cfpb-tables/table.scss +1 -1
- package/src/components/cfpb-tooltips/tooltip.scss +1 -1
- package/src/components/cfpb-typography/date.scss +1 -1
- package/src/components/cfpb-typography/list.scss +1 -1
- package/src/components/cfpb-typography/meta-header.scss +1 -1
- package/src/components/cfpb-typography/mixins.scss +1 -1
- package/src/components/cfpb-typography/pull-quote.scss +1 -1
- package/src/components/cfpb-typography/slug-header.scss +1 -1
- package/src/components/cfpb-typography/tagline.scss +1 -1
- package/src/elements/abstracts/custom-props.css +123 -0
- package/src/{abstracts → elements/abstracts}/grid-mixins.scss +2 -1
- package/src/{abstracts → elements/abstracts}/heading-mixins.scss +1 -0
- package/src/{abstracts → elements/abstracts}/index.scss +1 -0
- package/src/{abstracts → elements/abstracts}/media-queries.scss +1 -1
- package/src/elements/abstracts/sizing-vars.scss +66 -0
- package/src/elements/abstracts/vars.css +79 -0
- package/src/{base → elements/base}/base.scss +14 -14
- package/src/elements/cfpb-button/cfpb-button-group.scss +12 -0
- package/src/elements/cfpb-button/cfpb-button-link.scss +103 -0
- package/src/elements/cfpb-button/cfpb-button.component.scss +11 -4
- package/src/elements/cfpb-button/cfpb-button.scss +218 -0
- package/src/elements/cfpb-button/index.js +44 -30
- package/src/elements/cfpb-button/vars.css +30 -0
- package/src/elements/cfpb-checkbox-icon/cfpb-checkbox-icon.component.scss +88 -0
- package/src/elements/cfpb-checkbox-icon/index.js +104 -0
- package/src/elements/cfpb-expandable/cfpb-expandable.component.scss +218 -0
- package/src/elements/cfpb-expandable/index.js +127 -0
- package/src/elements/cfpb-file-upload/cfpb-file-upload.component.scss +2 -2
- package/src/elements/cfpb-file-upload/index.js +25 -27
- package/src/elements/cfpb-form-alert/cfpb-form-alert.component.scss +36 -0
- package/src/elements/cfpb-form-alert/index.js +55 -0
- package/src/elements/cfpb-form-choice/cfpb-form-choice.component.scss +42 -81
- package/src/elements/cfpb-form-choice/index.js +58 -18
- package/src/elements/cfpb-form-search/cfpb-form-search.component.scss +54 -0
- package/src/elements/cfpb-form-search/index.js +194 -0
- package/src/elements/cfpb-form-search-input/cfpb-form-search-input.component.scss +217 -0
- package/src/elements/cfpb-form-search-input/index.js +140 -0
- package/src/elements/cfpb-icon-text/cfpb-icon-text.component.scss +33 -39
- package/src/elements/cfpb-icon-text/index.js +32 -104
- package/src/elements/cfpb-label/cfpb-label.component.scss +2 -2
- package/src/elements/cfpb-label/index.js +6 -9
- package/src/elements/cfpb-list/cfpb-list.component.scss +34 -0
- package/src/elements/cfpb-list/index.js +379 -0
- package/src/elements/cfpb-list/index.spec.js +214 -0
- package/src/elements/cfpb-list-item/cfpb-list-item.component.scss +69 -0
- package/src/elements/cfpb-list-item/index.js +215 -0
- package/src/elements/cfpb-pagination/cfpb-pagination.component.scss +2 -7
- package/src/elements/cfpb-pagination/index.js +6 -8
- package/src/elements/cfpb-select/cfpb-select.component.scss +241 -0
- package/src/elements/cfpb-select/index.js +371 -0
- package/src/elements/cfpb-select/multiple-select-event-proxy.js +88 -0
- package/src/elements/cfpb-select/single-select-event-proxy.js +47 -0
- package/src/elements/cfpb-tag-filter/cfpb-tag-filter.component.scss +6 -3
- package/src/elements/cfpb-tag-filter/index.js +15 -7
- package/src/elements/cfpb-tag-group/cfpb-tag-group.component.scss +2 -2
- package/src/elements/cfpb-tag-group/index.js +53 -6
- package/src/elements/cfpb-tag-topic/cfpb-tag-topic.component.scss +2 -2
- package/src/elements/cfpb-tag-topic/index.js +5 -7
- package/src/elements/cfpb-utilities/parse-child-data.js +50 -0
- package/src/elements/cfpb-utilities/parse-child-data.spec.js +56 -0
- package/src/elements/cfpb-utilities/search-service.js +46 -0
- package/src/elements/cfpb-utilities/search-service.spec.js +138 -0
- package/src/elements/cfpb-utilities/transition/transition.scss +98 -0
- package/src/elements/index.js +7 -1
- package/src/index.js +2 -2
- package/src/index.scss +14 -2
- package/src/tokens/abstracts/custom-props.json +1642 -0
- package/src/tokens/abstracts/vars.json +1319 -0
- package/src/tokens/cfpb-button/vars.json +436 -0
- package/src/utilities/breakpoint-state.js +1 -1
- package/src/utilities/transition/max-height-transition.js +74 -0
- package/src/utilities/utilities.scss +1 -1
- package/dist/elements/cfpb-checkbox/index.js +0 -29
- package/src/abstracts/custom-props.scss +0 -175
- package/src/abstracts/vars.scss +0 -184
- package/src/elements/cfpb-multiselect/cfpb-multiselect.component.scss +0 -225
- package/src/elements/cfpb-multiselect/index.js +0 -444
- package/src/elements/cfpb-multiselect/multiselect-model.js +0 -288
- package/src/elements/cfpb-multiselect/multiselect-model.spec.js +0 -236
- /package/src/{abstracts → elements/abstracts}/index.js +0 -0
- /package/src/{abstracts → elements/abstracts}/vars-breakpoints.js +0 -0
- /package/src/{abstracts → elements/abstracts}/vars-breakpoints.scss +0 -0
- /package/src/{base → elements/base}/font.scss +0 -0
- /package/src/{base → elements/base}/index.js +0 -0
- /package/src/{base → elements/base}/index.scss +0 -0
- /package/src/{base → elements/base}/normalize.scss +0 -0
|
@@ -17,12 +17,10 @@ export class CfpbTagTopic extends LitElement {
|
|
|
17
17
|
* Whether the preceding sibling is a jump link or not.
|
|
18
18
|
* @returns {object} The map of properties.
|
|
19
19
|
*/
|
|
20
|
-
static
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
25
|
-
}
|
|
20
|
+
static properties = {
|
|
21
|
+
href: { type: String, reflect: true },
|
|
22
|
+
siblingOfJumpLink: { type: Boolean },
|
|
23
|
+
};
|
|
26
24
|
|
|
27
25
|
/*
|
|
28
26
|
* @property {string} href - The URL to link to (makes the tag a link).
|
|
@@ -55,7 +53,7 @@ export class CfpbTagTopic extends LitElement {
|
|
|
55
53
|
? html`<span class="a-tag-topic"
|
|
56
54
|
>${bullet}<span class="a-tag-topic__text"><slot></slot></span
|
|
57
55
|
></span>`
|
|
58
|
-
: html`<a class
|
|
56
|
+
: html`<a class=${this.#tagClass} href=${this.href}
|
|
59
57
|
>${bullet}<span class="a-tag-topic__text"><slot></slot></span
|
|
60
58
|
></a>`;
|
|
61
59
|
return html`${slot}`;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize "childData" style inputs into an array.
|
|
3
|
+
* Accepts:
|
|
4
|
+
* - JS arrays
|
|
5
|
+
* - JSON strings
|
|
6
|
+
* - JSON-like strings with single quotes
|
|
7
|
+
* @param {Array | string} input
|
|
8
|
+
* @param {object} options - optional settings.
|
|
9
|
+
* @param {boolean} options.allowSingleQuotes - default true.
|
|
10
|
+
* @returns {Array|null} Parsed array/string, or null if invalid.
|
|
11
|
+
*/
|
|
12
|
+
export function parseChildData(input, options = {}) {
|
|
13
|
+
const { allowSingleQuotes = true } = options;
|
|
14
|
+
|
|
15
|
+
if (!input) return null;
|
|
16
|
+
|
|
17
|
+
// Already an array - most desirable case.
|
|
18
|
+
if (Array.isArray(input)) {
|
|
19
|
+
return input;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (typeof input !== 'string') {
|
|
23
|
+
console.error('childData must be a string or array.');
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let text = input.trim();
|
|
28
|
+
|
|
29
|
+
// String is empty after trim.
|
|
30
|
+
if (!text) return null;
|
|
31
|
+
|
|
32
|
+
// Optional conversation: single -> double quotes for HTML convenience.
|
|
33
|
+
if (allowSingleQuotes) {
|
|
34
|
+
text = text.replace(/'/g, '"');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const parsed = JSON.parse(text);
|
|
39
|
+
|
|
40
|
+
if (!Array.isArray(parsed)) {
|
|
41
|
+
console.error('childData JSON must parse to an array.');
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return parsed;
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.error('Failed to parse childData JSON:', err);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { parseChildData } from './parse-child-data.js';
|
|
2
|
+
|
|
3
|
+
describe('parseChildData', () => {
|
|
4
|
+
it('returns null for empty input', () => {
|
|
5
|
+
expect(parseChildData(null)).toBeNull();
|
|
6
|
+
expect(parseChildData(undefined)).toBeNull();
|
|
7
|
+
expect(parseChildData('')).toBeNull();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('returns the array unchanged if input is already an array', () => {
|
|
11
|
+
const arr = [1, 2, 3];
|
|
12
|
+
expect(parseChildData(arr)).toBe(arr);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('parses valid JSON string into array', () => {
|
|
16
|
+
const json = '[{"text":"a"},{"text":"b"}]';
|
|
17
|
+
const result = parseChildData(json);
|
|
18
|
+
expect(result).toEqual([{ text: 'a' }, { text: 'b' }]);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('parses JSON-like string with single quotes when allowSingleQuotes=true', () => {
|
|
22
|
+
const jsonLike = "[{'text':'a'},{'text':'b'}]";
|
|
23
|
+
const result = parseChildData(jsonLike, { allowSingleQuotes: true });
|
|
24
|
+
expect(result).toEqual([{ text: 'a' }, { text: 'b' }]);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('throws error / returns null for invalid JSON', () => {
|
|
28
|
+
const invalid = '[{text:a},{text:b}]';
|
|
29
|
+
const result = parseChildData(invalid);
|
|
30
|
+
expect(result).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('returns null when parsed JSON is not an array', () => {
|
|
34
|
+
const objString = '{"a":1,"b":2}';
|
|
35
|
+
const result = parseChildData(objString);
|
|
36
|
+
expect(result).toBeNull();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('does not convert single quotes if allowSingleQuotes=false', () => {
|
|
40
|
+
const singleQuoteStr = "[{'text':'a'}]";
|
|
41
|
+
const result = parseChildData(singleQuoteStr, { allowSingleQuotes: false });
|
|
42
|
+
expect(result).toBeNull();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('trims whitespace before parsing', () => {
|
|
46
|
+
const json = ' [ {"text":"x"} ] ';
|
|
47
|
+
const result = parseChildData(json);
|
|
48
|
+
expect(result).toEqual([{ text: 'x' }]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('works with mixed content and multiple spaces', () => {
|
|
52
|
+
const jsonLike = " [ { 'text' : 'x' } , { 'text':'y' } ] ";
|
|
53
|
+
const result = parseChildData(jsonLike, { allowSingleQuotes: true });
|
|
54
|
+
expect(result).toEqual([{ text: 'x' }, { text: 'y' }]);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -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
|
+
}
|
package/src/elements/index.js
CHANGED
|
@@ -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-
|
|
19
|
+
export * from './cfpb-select';
|
|
14
20
|
export * from './cfpb-pagination';
|
package/src/index.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Design System
|
|
3
3
|
========================================================================== */
|
|
4
4
|
|
|
5
|
-
export * from './abstracts';
|
|
5
|
+
export * from './elements/abstracts';
|
|
6
6
|
|
|
7
|
-
export * from './base';
|
|
7
|
+
export * from './elements/base';
|
|
8
8
|
|
|
9
9
|
export * from './components/cfpb-buttons';
|
|
10
10
|
export * from './components/cfpb-expandables';
|
package/src/index.scss
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
@forward 'abstracts';
|
|
2
|
-
@forward 'base';
|
|
3
1
|
|
|
4
2
|
// Buttons.
|
|
5
3
|
@forward 'components/cfpb-buttons/vars';
|
|
@@ -64,3 +62,17 @@
|
|
|
64
62
|
|
|
65
63
|
// Utilities.
|
|
66
64
|
@forward 'utilities';
|
|
65
|
+
|
|
66
|
+
// Web components.
|
|
67
|
+
@forward 'elements/base';
|
|
68
|
+
@forward 'elements/abstracts';
|
|
69
|
+
|
|
70
|
+
// cfpb-button
|
|
71
|
+
@forward 'elements/cfpb-button/cfpb-button';
|
|
72
|
+
@forward 'elements/cfpb-button/cfpb-button-group';
|
|
73
|
+
@forward 'elements/cfpb-button/cfpb-button-link';
|
|
74
|
+
@forward 'elements/cfpb-button/cfpb-button.component';
|
|
75
|
+
@forward 'elements/cfpb-button/vars';
|
|
76
|
+
|
|
77
|
+
// cfpb-icon-text
|
|
78
|
+
@forward 'elements/cfpb-icon-text/cfpb-icon-text.component';
|