Skip to content

Commit

Permalink
va-button: add submit functionality (#1174)
Browse files Browse the repository at this point in the history
* dispatch submit

* comment out story on click

* adds option to trigger onsubmit callback w/o sending form

* removes an alert used for debbugging

* removes commented code; removes dontSendForm and megreges functionality with submit attribute

* removed native HTML button from story

* fix to pass submit unit tests

* added a safety check, in case the button has no ancester form

* add unit tests; fix comment typos

* adds e2e tests; fix blank submit bug

* clean up comments; storybook submit now doesnt navigate away

* corrects typo

* updates components.d.ts
  • Loading branch information
rmessina1010 authored Jul 29, 2024
1 parent aed4c47 commit 47cb262
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 34 deletions.
63 changes: 58 additions & 5 deletions packages/storybook/stories/va-button-uswds.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const defaultArgs = {
'disabled': undefined,
'label': undefined,
'secondary': undefined,
'primaryAlternate': undefined,
'primary-alternate': undefined,
'submit': undefined,
'text': 'Default',
'message-aria-describedby': 'Optional description text for screen readers',
Expand All @@ -31,15 +31,15 @@ const defaultArgs = {
const Template = ({
back,
big,
'continue': _continue,
'disable-analytics': disableAnalytics,
_continue,
disableAnalytics,
disabled,
label,
secondary,
primaryAlternate,
submit,
text,
'message-aria-describedby': messageAriaDescribedby,
messageAriaDescribedby,
}) => {
return (
<va-button
Expand Down Expand Up @@ -89,7 +89,7 @@ Big.args = {
export const Continue = Template.bind(null);
Continue.args = {
...defaultArgs,
continue: true,
_continue: true,
text: undefined,
};

Expand All @@ -106,3 +106,56 @@ Disabled.args = {
disabled: true,
text: "Disabled",
};


const TemplateWithForm = ({
back,
big,
_continue,
disableAnalytics,
disabled,
label,
secondary,
primaryAlternate,
submit,
text,
messageAriaDescribedby,
onsub,
onclk,
}) => {
return (
<form onSubmit={onsub}>
<p>This is inside a form, which has an onsubmit() that displays an alert when the form is submitted</p>
<va-button
back={back}
big={big}
continue={_continue}
disable-analytics={disableAnalytics}
disabled={disabled}
label={label}
secondary={secondary}
primary-alternate={primaryAlternate}
submit={submit}
text={text}
onClick={e => onclk(e) }
message-aria-describedby={messageAriaDescribedby}
/>
</form>
);
}

export const Submitted = TemplateWithForm.bind(null);
Submitted.args = {
...defaultArgs,
onsub : (e)=>{
console.log(e.target, "I am submitted!");
alert ("form onsubmit method fired!--on form");
},
onclk : (e)=>{
console.log("called the on click method-on button", e.target);
alert( "onclick happened on button");

},
submit: 'prevent',
text: "Submit me",
};
16 changes: 8 additions & 8 deletions packages/web-components/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,9 @@ export namespace Components {
*/
"secondary"?: boolean;
/**
* If `true`, the button will submit form data when clicked.
* Having this attribute present will set the type of this button as 'submit'. The va-button element must be within a `form` element for this functionality to take place A value of: `prevent` will trigger the onsubmit callback on the form, but won't submit the form; `skip` will submit the form but not trigger the onsubmit callback; All other values will trigger the onsubmit and onclick callbacks, then submit the form; in that order.
*/
"submit"?: boolean;
"submit"?: string;
/**
* The text displayed on the button. If `continue` or `back` is true, the value of text is ignored.
*/
Expand Down Expand Up @@ -266,9 +266,9 @@ export namespace Components {
*/
"secondaryLabel"?: string;
/**
* If `true`, the primary button will submit form data when clicked.
* Having this attribute present will set the type of this button as 'submit'. The va-button element must be within a `form` element for this functionality to take place A value of: `prevent` will trigger the onsubmit callback on the form, but won't submit the form; `skip` will submit the form but not trigger the onsubmit callback; All other values will trigger the onsubmit and onclick callbacks, then submit the form; in that order.
*/
"submit"?: boolean;
"submit"?: string;
/**
* If `true`, button pair will use Update and Cancel for button text.
*/
Expand Down Expand Up @@ -2364,9 +2364,9 @@ declare namespace LocalJSX {
*/
"secondary"?: boolean;
/**
* If `true`, the button will submit form data when clicked.
* Having this attribute present will set the type of this button as 'submit'. The va-button element must be within a `form` element for this functionality to take place A value of: `prevent` will trigger the onsubmit callback on the form, but won't submit the form; `skip` will submit the form but not trigger the onsubmit callback; All other values will trigger the onsubmit and onclick callbacks, then submit the form; in that order.
*/
"submit"?: boolean;
"submit"?: string;
/**
* The text displayed on the button. If `continue` or `back` is true, the value of text is ignored.
*/
Expand Down Expand Up @@ -2421,9 +2421,9 @@ declare namespace LocalJSX {
*/
"secondaryLabel"?: string;
/**
* If `true`, the primary button will submit form data when clicked.
* Having this attribute present will set the type of this button as 'submit'. The va-button element must be within a `form` element for this functionality to take place A value of: `prevent` will trigger the onsubmit callback on the form, but won't submit the form; `skip` will submit the form but not trigger the onsubmit callback; All other values will trigger the onsubmit and onclick callbacks, then submit the form; in that order.
*/
"submit"?: boolean;
"submit"?: string;
/**
* If `true`, button pair will use Update and Cancel for button text.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Component, Event, EventEmitter, Host, h, Prop, Listen } from '@stencil/core';
import {
Component,
Event,
EventEmitter,
Host,
h,
Prop,
Listen,
} from '@stencil/core';

/**
* @componentName Button pair
Expand Down Expand Up @@ -33,9 +41,13 @@ export class VaButtonPair {
@Prop() secondaryLabel?: string;

/**
* If `true`, the primary button will submit form data when clicked.
* Having this attribute present will set the type of this button as 'submit'.
* The va-button element must be within a `form` element for this functionality to take place
* A value of: `prevent` will trigger the onsubmit callback on the form, but won't submit the form;
* `skip` will submit the form but not trigger the onsubmit callback;
* All other values will trigger the onsubmit and onclick callbacks, then submit the form; in that order.
*/
@Prop() submit?: boolean = false;
@Prop() submit?: string;

/**
* If `true`, button pair will use Update and Cancel for button text.
Expand Down Expand Up @@ -76,7 +88,7 @@ export class VaButtonPair {
componentLibraryAnalytics: EventEmitter;

/**
* Listen for the va-button GA event and capture it so
* Listen for the va-button GA event and capture it so
* that we can emit a single va-button-pair GA event that includes
* the va-button details.
*/
Expand All @@ -93,9 +105,9 @@ export class VaButtonPair {
componentName: 'va-button-pair',
action: 'click',
details: {
type: null,
label: null,
...event.detail?.details // Merging the va-button GA event details.
type: null,
label: null,
...event.detail?.details, // Merging the va-button GA event details.
},
};
this.componentLibraryAnalytics.emit(detail);
Expand Down Expand Up @@ -179,13 +191,13 @@ export class VaButtonPair {
<Host>
<ul class="usa-button-group">
<li class="usa-button-group__item">
<va-button
disable-analytics={disableAnalytics}
label={primaryLabel}
onClick={handlePrimaryClick}
text={update ? 'Update' : 'Yes'}
submit={submit}
/>
<va-button
disable-analytics={disableAnalytics}
label={primaryLabel}
onClick={handlePrimaryClick}
text={update ? 'Update' : 'Yes'}
submit={submit}
/>
</li>
<li class="usa-button-group__item">
<va-button
Expand All @@ -198,7 +210,7 @@ export class VaButtonPair {
</li>
</ul>
</Host>
)
);
} else {
return (
<Host>
Expand All @@ -224,6 +236,5 @@ export class VaButtonPair {
);
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,29 @@ describe('va-button', () => {
expect(descriptionSpan.textContent).toBe('Button description.');
});
});


it(`uswds v3 renders a default submit button variant`, async () => {
const page = await newE2EPage();
await page.setContent('<va-button submit text="Submit"></va-button>');
const element = await page.find('va-button');
expect(element).toEqualHtml(`
<va-button class="hydrated" submit="" text="Submit" uswds="">
<mock:shadow-root>
<button class="usa-button" type="submit" part="button">
Submit
</button>
</mock:shadow-root>
</va-button>
`);
});

it('submits form when clicked', async () => {
const page = await newE2EPage();
await page.setContent('<form onsubmit="e=>{e.preventDefault();}"><va-button submit text="Submit" ></va-button></form>');
const submitSpy = await page.spyOnEvent('submit');
const button = await page.find('va-button >>> button');
await button.click();
await page.waitForChanges();
expect(submitSpy).toHaveReceivedEventTimes(1);
});
38 changes: 33 additions & 5 deletions packages/web-components/src/components/va-button/va-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
h,
Listen,
Prop,
Element
Element,
} from '@stencil/core';
import classnames from 'classnames';

Expand Down Expand Up @@ -66,9 +66,13 @@ export class VaButton {
@Prop({ reflect: true }) secondary?: boolean = false;

/**
* If `true`, the button will submit form data when clicked.
* Having this attribute present will set the type of this button as 'submit'.
* The va-button element must be within a `form` element for this functionality to take place
* A value of: `prevent` will trigger the onsubmit callback on the form, but won't submit the form;
* `skip` will submit the form but not trigger the onsubmit callback;
* All other values will trigger the onsubmit and onclick callbacks, then submit the form; in that order.
*/
@Prop() submit?: boolean = false;
@Prop() submit?: string;

/**
* The text displayed on the button. If `continue` or `back` is true, the value of text is ignored.
Expand Down Expand Up @@ -124,6 +128,27 @@ export class VaButton {
return this.text;
};

private handleSubmit() {
if (this.submit === undefined) {
return;
}
const theForm = this.el.closest('form');
if (!theForm) {
return;
}
const submitEvent = new CustomEvent('submit', {
bubbles: true,
cancelable: true,
composed: true,
});
if (this.submit !== 'skip') {
theForm.dispatchEvent(submitEvent);
}
if (this.submit !== 'prevent') {
theForm.submit();
}
}

/**
* This workaround allows us to use disabled for styling and to prevent the click event from firing while improving
* the button's accessibility by allowing it to be focusable and through the use of aria-disabled.
Expand All @@ -138,6 +163,7 @@ export class VaButton {
return;
}
this.handleClick();
this.handleSubmit();
}

render() {
Expand All @@ -155,11 +181,13 @@ export class VaButton {
messageAriaDescribedby,
} = this;

const ariaDescribedbyIds = `${messageAriaDescribedby ? 'button-description' : ''}`.trim() || null;
const ariaDescribedbyIds =
`${messageAriaDescribedby ? 'button-description' : ''}`.trim() || null;

const ariaDisabled = disabled ? 'true' : undefined;
const buttonText = getButtonText();
const type = submit ? 'submit' : 'button';

const type = submit !== undefined ? 'submit' : 'button';

if (uswds) {
const buttonClass = classnames({
Expand Down

0 comments on commit 47cb262

Please sign in to comment.