diff --git a/packages/web-components/src/components/va-checkbox-group/test/va-checkbox-group.e2e.ts b/packages/web-components/src/components/va-checkbox-group/test/va-checkbox-group.e2e.ts index 3b6d99835..1096ee58f 100644 --- a/packages/web-components/src/components/va-checkbox-group/test/va-checkbox-group.e2e.ts +++ b/packages/web-components/src/components/va-checkbox-group/test/va-checkbox-group.e2e.ts @@ -86,7 +86,7 @@ describe('va-checkbox-group', () => { `); const analyticsSpy = await page.spyOnEvent('component-library-analytics'); - const checkboxEl = await page.find('va-checkbox >>> .usa-checkbox__label'); + const checkboxEl = await page.find('va-checkbox >>> .va-checkbox__label'); await checkboxEl.click(); expect(analyticsSpy).toHaveReceivedEventDetail({ @@ -109,7 +109,7 @@ describe('va-checkbox-group', () => { `); const analyticsSpy = await page.spyOnEvent('component-library-analytics'); - const inputEl = await page.find('va-checkbox >>> .usa-checkbox__label'); + const inputEl = await page.find('va-checkbox >>> .va-checkbox__label'); await inputEl.click(); expect(analyticsSpy).not.toHaveReceivedEvent(); }); diff --git a/packages/web-components/src/components/va-checkbox/test/va-checkbox.e2e.ts b/packages/web-components/src/components/va-checkbox/test/va-checkbox.e2e.ts index ae0c28ca1..26f21cee2 100644 --- a/packages/web-components/src/components/va-checkbox/test/va-checkbox.e2e.ts +++ b/packages/web-components/src/components/va-checkbox/test/va-checkbox.e2e.ts @@ -22,12 +22,10 @@ describe('va-checkbox', () => { it('has tile class added', async () => { const page = await newE2EPage(); - await page.setContent( - '', - ); + await page.setContent(''); - const input = await page.find('va-checkbox >>> .usa-checkbox__input'); - expect(input).toHaveClass("usa-checkbox__input--tile"); + const input = await page.find('va-checkbox >>> .va-checkbox__container'); + expect(input).toHaveClass('va-checkbox__container--tile'); }); it('renders with aria-invalid set to false by default', async () => { @@ -53,7 +51,9 @@ describe('va-checkbox', () => { const element = await page.find('va-checkbox >>> #checkbox-error-message'); const input = await page.find('va-checkbox >>> input'); expect(input.getAttribute('aria-invalid')).toEqual('true'); - expect(input.getAttribute('aria-describedby')).toEqual('checkbox-error-message'); + expect(input.getAttribute('aria-describedby')).toEqual( + 'checkbox-error-message', + ); expect(element.textContent).toContain('Something went horribly wrong'); }); @@ -95,7 +95,7 @@ describe('va-checkbox', () => { const inputEl = await page.find('va-checkbox >>> input'); const descriptionDiv = await page.find('va-checkbox >>> div#description'); expect(element).toEqualText('This is a description!'); - expect(descriptionDiv).not.toBeNull();; + expect(descriptionDiv).not.toBeNull(); // should still add the description aria-describedby with one empty slot expect(inputEl.getAttribute('aria-describedby')).toEqual('description'); }); @@ -133,7 +133,9 @@ describe('va-checkbox', () => { await page.setContent(''); const inputEl = await page.find('va-checkbox >>> input'); - expect(inputEl.getAttribute('aria-describedby')).toContain('checkbox-error-message'); + expect(inputEl.getAttribute('aria-describedby')).toContain( + 'checkbox-error-message', + ); }); it('passes an aXe check', async () => { @@ -156,7 +158,9 @@ describe('va-checkbox', () => { // Render the error message text const inputEl = await page.find('va-checkbox >>> input'); - expect(inputEl.getAttribute('aria-describedby')).toEqual('input-message checkbox-error-message description'); + expect(inputEl.getAttribute('aria-describedby')).toEqual( + 'input-message checkbox-error-message description', + ); }); it('fires an analytics event when enableAnalytics is true', async () => { @@ -165,7 +169,7 @@ describe('va-checkbox', () => { '', ); const analyticsSpy = await page.spyOnEvent('component-library-analytics'); - const checkboxEl = await page.find('va-checkbox >>> .usa-checkbox__label'); + const checkboxEl = await page.find('va-checkbox >>> .va-checkbox__label'); await checkboxEl.click(); expect(analyticsSpy).toHaveReceivedEventDetail({ @@ -189,7 +193,7 @@ describe('va-checkbox', () => { >> .usa-checkbox__label'); + const checkboxEl = await page.find('va-checkbox >>> .va-checkbox__label'); await checkboxEl.click(); expect(analyticsSpy).toHaveReceivedEventDetail({ @@ -211,7 +215,7 @@ describe('va-checkbox', () => { '', ); const analyticsSpy = await page.spyOnEvent('component-library-analytics'); - const checkboxEl = await page.find('va-checkbox >>> .usa-checkbox__label'); + const checkboxEl = await page.find('va-checkbox >>> .va-checkbox__label'); await checkboxEl.click(); expect(analyticsSpy).not.toHaveReceivedEvent(); @@ -223,7 +227,7 @@ describe('va-checkbox', () => { '', ); const changeSpy = await page.spyOnEvent('vaChange'); - const checkboxEl = await page.find('va-checkbox >>> .usa-checkbox__label'); + const checkboxEl = await page.find('va-checkbox >>> .va-checkbox__label'); await checkboxEl.click(); expect(changeSpy).toHaveReceivedEventDetail({ checked: true }); @@ -235,7 +239,7 @@ describe('va-checkbox', () => { '', ); const blurSpy = await page.spyOnEvent('blur'); - const checkboxEl = await page.find('va-checkbox >>> .usa-checkbox__label'); + const checkboxEl = await page.find('va-checkbox >>> .va-checkbox__label'); await checkboxEl.click(); // Focus on the element await checkboxEl.press('Tab'); // Blur the element @@ -250,7 +254,9 @@ describe('va-checkbox', () => { const checkboxEl = await page.find('va-checkbox >>> input'); expect(await checkboxEl.getProperty('checked')).toBeTruthy(); - const checkboxLabelEl = await page.find('va-checkbox >>> .usa-checkbox__label'); + const checkboxLabelEl = await page.find( + 'va-checkbox >>> .va-checkbox__label', + ); await checkboxLabelEl.click(); expect(await checkboxEl.getProperty('checked')).toBeFalsy(); @@ -272,7 +278,7 @@ describe('va-checkbox', () => { await page.setContent( '', ); - const checkboxEl = await page.find('va-checkbox >>> label'); + const checkboxEl = await page.find('va-checkbox >>> input'); expect(checkboxEl).toEqualAttribute('aria-checked', 'mixed'); }); diff --git a/packages/web-components/src/components/va-checkbox/va-checkbox.scss b/packages/web-components/src/components/va-checkbox/va-checkbox.scss index cc02df250..2d5ed63eb 100644 --- a/packages/web-components/src/components/va-checkbox/va-checkbox.scss +++ b/packages/web-components/src/components/va-checkbox/va-checkbox.scss @@ -8,6 +8,7 @@ @use 'usa-hint/src/styles/usa-hint'; @use 'usa-error-message/src/styles/usa-error-message'; +@import '../../mixins/focusable.css'; @import '../../mixins/uswds-error-border.scss'; :host { @@ -15,11 +16,57 @@ max-width: 480px; } -.usa-checkbox { +.va-checkbox__container { background: transparent; + display: flex; + margin: 0.5rem 0; } -.usa-checkbox__input:focus + [class*='__label']::before { - outline: 2px solid var(--vads-color-action-focus-on-light); - outline-offset: 4px; +.va-checkbox__container--tile { + border-radius: 0.25rem; + background-color: white; + border: 2px solid #c9c9c9; + color: #1b1b1b; + padding: 0.75rem 0 0.75rem 0.5rem; +} + +.va-checkbox__input { + height: 20px; + width: 20px; + min-width: 20px; + background: white; + border: 2px solid #1b1b1b; + border-radius: 2px; + appearance: none; + -webkit-appearance: none; + margin: 0.064rem 0.5rem 0 0; + opacity: unset; + position: initial; + + &:checked { + background: #005ea2; + background-image: url("data:image/svg+xml,%3C%3Fxml version%3D%221.0%22 encoding%3D%22UTF-8%22%3F%3E%3Csvg xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 width%3D%2265%22 height%3D%2250%22 viewBox%3D%220 0 65 50%22%3E%3Ctitle%3Ecorrect8%3C%2Ftitle%3E%3Cpath fill%3D%22%23FFF%22 fill-rule%3D%22evenodd%22 d%3D%22M63.268 7.063l-5.616-5.61C56.882.685 55.946.3 54.845.3s-2.038.385-2.808 1.155L24.951 28.552 12.81 16.385c-.77-.77-1.707-1.155-2.808-1.155-1.1 0-2.037.385-2.807 1.154l-5.616 5.61C.81 22.764.425 23.7.425 24.8s.385 2.035 1.155 2.805l14.947 14.93 5.616 5.61c.77.77 1.706 1.154 2.807 1.154s2.038-.384 2.808-1.154l5.616-5.61 29.894-29.86c.77-.77 1.157-1.707 1.157-2.805 0-1.101-.385-2.036-1.156-2.805l-.001-.002z%22%2F%3E%3C%2Fsvg%3E"), linear-gradient(transparent, transparent); + background-position: center center; + background-size: 0.75rem auto; + background-repeat: no-repeat; + border: none; + } + + &[data-indeterminate]:not([data-indeterminate=false]) { + background: #005ea2; + background-image: url("data:image/svg+xml,%3C%3Fxml version%3D%221.0%22 encoding%3D%22utf-8%22%3F%3E%3C!-- Generator%3A Adobe Illustrator 26.3.1%2C SVG Export Plug-In . SVG Version%3A 6.00 Build 0) --%3E%3Csvg version%3D%221.1%22 id%3D%22Layer_1%22 xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22 x%3D%220px%22 y%3D%220px%22%09 viewBox%3D%220 0 64 64%22 style%3D%22enable-background%3Anew 0 0 64 64%3B%22 xml%3Aspace%3D%22preserve%22%3E%3Cstyle type%3D%22text%2Fcss%22%3E%09.st0%7Bfill-rule%3Aevenodd%3Bclip-rule%3Aevenodd%3Bfill%3A%23FFFFFF%3B%7D%3C%2Fstyle%3E%3Cpath class%3D%22st0%22 d%3D%22M2.9%2C35.9c0%2C1.1%2C0.4%2C2%2C1.2%2C2.8c0.8%2C0.8%2C1.7%2C1.2%2C2.8%2C1.2h7.9h42.3c1.1%2C0%2C2-0.4%2C2.8-1.2s1.2-1.7%2C1.2-2.8l0%2C0V28%09c0-1.1-0.4-2-1.2-2.8S58.2%2C24%2C57.1%2C24H6.9c-1.1%2C0-2%2C0.4-2.8%2C1.2S2.9%2C26.9%2C2.9%2C28V35.9z%22%2F%3E%3C%2Fsvg%3E"), linear-gradient(transparent, transparent); + background-position: center center; + background-size: 0.75rem auto; + background-repeat: no-repeat; + border: none; + } +} + +.va-checkbox__label { + margin: 0; + max-width: 480px; + padding-left: 0; + box-sizing: border-box; + font-size: 1.06rem; + line-height: 1.3; } \ No newline at end of file diff --git a/packages/web-components/src/components/va-checkbox/va-checkbox.tsx b/packages/web-components/src/components/va-checkbox/va-checkbox.tsx index 68ab744ad..cbf9a3dfd 100644 --- a/packages/web-components/src/components/va-checkbox/va-checkbox.tsx +++ b/packages/web-components/src/components/va-checkbox/va-checkbox.tsx @@ -209,21 +209,23 @@ export class VaCheckbox { !description && this.el.querySelectorAll('[slot="description"]:not(:empty)').length > 0; - const inputClass = classnames({ - 'usa-checkbox__input': true, - 'usa-checkbox__input--tile': tile, + const containerClass = classnames('va-checkbox__container', { + 'va-checkbox__container--tile': tile, }); const descriptionClass = classnames({ 'usa-legend': true, - 'usa-label--error': error + 'usa-label--error': error, }); - const ariaDescribedbyIds = [ - messageAriaDescribedby ? 'input-message' : '', - error ? 'checkbox-error-message' : '', - description || hasDescriptionSlot ? 'description' : '', - // Return null so we don't add the attribute if we have an empty string - ].filter(Boolean).join(' ').trim() || null; - const ariaChecked = checked ? 'true' : 'false'; + const ariaDescribedbyIds = + [ + messageAriaDescribedby ? 'input-message' : '', + error ? 'checkbox-error-message' : '', + description || hasDescriptionSlot ? 'description' : '', + ] + .filter(Boolean) + .join(' ') + // Return null so we don't add the attribute if we have an empty string + .trim() || null; return ( @@ -247,9 +249,9 @@ export class VaCheckbox { )} - + {label} {required && ( diff --git a/packages/web-components/src/components/va-radio-option/test/va-radio-option.e2e.ts b/packages/web-components/src/components/va-radio-option/test/va-radio-option.e2e.ts index c3ececbbc..63d92131b 100644 --- a/packages/web-components/src/components/va-radio-option/test/va-radio-option.e2e.ts +++ b/packages/web-components/src/components/va-radio-option/test/va-radio-option.e2e.ts @@ -17,8 +17,8 @@ describe('va-radio-option', () => { expect(element).toEqualHtml(` - - + + Yes - Any Veteran @@ -73,4 +73,11 @@ describe('va-radio-option', () => { expect(description.getAttribute('data-dd-action-name')).toEqual('description'); }); + it('institutes the tile class if the tile prop is provided', async () => { + const page = await newE2EPage(); + await page.setContent(''); + + const element = await page.find('va-radio-option .usa-radio'); + expect(element).toHaveClass('va-radio-option__container--tile'); + }); }); diff --git a/packages/web-components/src/components/va-radio-option/va-radio-option.scss b/packages/web-components/src/components/va-radio-option/va-radio-option.scss index 9af13f13e..8708e6122 100644 --- a/packages/web-components/src/components/va-radio-option/va-radio-option.scss +++ b/packages/web-components/src/components/va-radio-option/va-radio-option.scss @@ -3,43 +3,84 @@ @use 'uswds-core/src/styles/mixins/helpers/checkbox-and-radio-colors'; @use 'usa-radio/src/styles/usa-radio'; -va-radio-option label { - max-width: 480px; - box-sizing: border-box; -} - .usa-radio { background: transparent; + display: flex; + margin: 0.5rem 0; +} + +.va-radio-option__container--tile { + border-radius: 0.25rem; + background-color: white; + border: 2px solid #c9c9c9; + color: #1b1b1b; + padding: 0.75rem 0 0.75rem 0.5rem; } -va-radio-option { +:host { display: block; margin-top: 12px; } -input[type=radio].usa-radio__input { - margin-left: unset; +input[type="radio"].va-radio-option__input { + height: 20px; + width: 20px; + min-width: 20px; + border: 2px solid #1b1b1b; + border-radius: 50%; + appearance: none; + -webkit-appearance: none; + margin: 0.064rem 0.5rem 0 0; opacity: unset; - right: auto; - left: -999em; + position: initial; +} + +input[type="radio"].va-radio-option__input:checked { + background: #005ea2; + border: none; + box-shadow: 0 0 0 2px #005ea2, inset 0 0 0 2px white; + height: 20px; + width: 20px; + min-width: 20px; } -va-radio-option input:focus { +input[type="radio"].va-radio-option__input:checked:focus { + outline-offset: 4px; +} + +.usa-radio__label { + display: flex; + flex-direction: column; + margin: 0; + max-width: 480px; + padding-left: 0; + box-sizing: border-box; +} + +:host input:focus { outline: none !important; } -// Formation override -va-radio-option label.usa-radio__label::before { - box-shadow: rgb(27, 27, 27) 0px 0px 0px 2px; - height: 20px; - width: 20px; - margin-left: 2px; +// Formation overrides +input[type="radio"] + label { + margin-bottom: 0; } -va-radio-option .usa-radio__input:focus + [class*='__label']::before { - outline: 2px solid var(--vads-color-action-focus-on-light); +input[type="radio"] + label:before { + border-radius: 0; + box-shadow: none; + height: 0; + line-height: 0; + margin-left: 0; + margin-right: 0; + width: 0; +} + +input[type="radio"]:checked + label:before { + outline: none; + box-shadow: none; } -va-radio-option input[disabled='true']:focus + label.usa-radio__label::before { +input[type="radio"]:focus + label::before { outline: none; } \ No newline at end of file diff --git a/packages/web-components/src/components/va-radio-option/va-radio-option.tsx b/packages/web-components/src/components/va-radio-option/va-radio-option.tsx index 7e6f88863..fc6f94b4f 100644 --- a/packages/web-components/src/components/va-radio-option/va-radio-option.tsx +++ b/packages/web-components/src/components/va-radio-option/va-radio-option.tsx @@ -72,30 +72,22 @@ export class VaRadioOption { render() { const { checked, name, value, label, disabled, tile, description } = this; const id = this.el.id || name + value; - const ariaChecked = checked ? 'true' : 'false'; - const inputClass = classnames({ - 'usa-radio__input': true, - 'usa-radio__input--tile': tile, + const containerClass = classnames('usa-radio', { + 'va-radio-option__container--tile': tile, }); return ( - + this.handleChange()}> this.handleChange()} id={id + 'input'} /> - + {label} {description && ( { expect(await options[0].getProperty('checked')).toBeTruthy(); expect(await options[1].getProperty('checked')).toBeFalsy(); - //without specifying center of element with this offset the click has no effect - await options[1].click({ - offset: { x: 0, y: 0 } - }); + await options[1].click(); expect(await options[0].getProperty('checked')).toBeFalsy(); expect(await options[1].getProperty('checked')).toBeTruthy(); diff --git a/packages/web-components/src/components/va-statement-of-truth/test/va-statement-of-truth.e2e.ts b/packages/web-components/src/components/va-statement-of-truth/test/va-statement-of-truth.e2e.ts index 997d600e7..6bd1ffc6d 100644 --- a/packages/web-components/src/components/va-statement-of-truth/test/va-statement-of-truth.e2e.ts +++ b/packages/web-components/src/components/va-statement-of-truth/test/va-statement-of-truth.e2e.ts @@ -62,7 +62,9 @@ describe('va-statement-of-truth', () => { const page = await newE2EPage(); await page.setContent(''); const vaCheckboxChangeSpy = await page.spyOnEvent('vaCheckboxChange'); - const checkboxEl = await page.find('va-statement-of-truth >>> va-checkbox >>> label.usa-checkbox__label'); + const checkboxEl = await page.find( + 'va-statement-of-truth >>> va-checkbox >>> label', + ); await checkboxEl.click(); expect(vaCheckboxChangeSpy).toHaveReceivedEvent(); }); @@ -76,7 +78,9 @@ describe('va-statement-of-truth', () => { it('sets an input aria described by message', async () => { const page = await newE2EPage(); - await page.setContent(''); + await page.setContent( + '', + ); const span = await page.$('pierce/span#input-message'); const text = await page.evaluate(element => element.textContent, span); expect(text).toContain('testing one two three'); @@ -84,28 +88,40 @@ describe('va-statement-of-truth', () => { it('permits prefilling the form', async () => { const page = await newE2EPage(); - await page.setContent(''); - - const value = await page.$eval('va-statement-of-truth >>> va-text-input >>> input', (comp: HTMLInputElement) => comp.value); + await page.setContent( + '', + ); + + const value = await page.$eval( + 'va-statement-of-truth >>> va-text-input >>> input', + (comp: HTMLInputElement) => comp.value, + ); expect(value).toBe('John Doe'); - const checked = await page.$eval('va-statement-of-truth >>> va-checkbox >>> input', (comp: HTMLInputElement) => comp.checked); + const checked = await page.$eval( + 'va-statement-of-truth >>> va-checkbox >>> input', + (comp: HTMLInputElement) => comp.checked, + ); expect(checked).toBeTruthy(); }); it('adds custom label to va-text-input', async () => { const page = await newE2EPage(); await page.setContent(''); - const labelEl = await page.$('pierce/label.usa-label'); - const text = await page.evaluate(element => element.textContent, labelEl); - expect(text).toContain('test label'); + const textInputEl = await page.find( + 'va-statement-of-truth >>> va-text-input', + ); + const textInputLabel = await textInputEl.getProperty('label'); + expect(textInputLabel).toBe('test label'); }); it('adds custom label to va-checkbox', async () => { const page = await newE2EPage(); - await page.setContent(''); - const labelEl = await page.$('pierce/label.usa-checkbox__label'); - const text = await page.evaluate(element => element.textContent, labelEl); - expect(text).toContain('test label'); + await page.setContent( + '', + ); + const checkboxEl = await page.find('va-statement-of-truth >>> va-checkbox'); + const checkboxLabel = await checkboxEl.getProperty('label'); + expect(checkboxLabel).toBe('test label'); }); })