Skip to content

Commit

Permalink
fix(button): submit button when in form (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
skhamvon authored Apr 27, 2023
1 parent c130868 commit 52a6f55
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,14 @@ export interface OdsButtonBehavior {
* @see OdsButtonController.validateAttributes
*/
beforeRender(): void;
/**
* Called after a click is done on the component.
* @see OdsButtonController.handleClick
*/
handleClick(event: MouseEvent): void;
/**
* Called after a keypress is done on the component.
* @see OdsButtonController.handleKey
*/
handleKey(event: KeyboardEvent): void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,38 @@ export class OdsButtonController extends OdsComponentController<OdsButton> {
attribute: this.component.variant
});
}

/**
* Handle Click and KeyPress Event on the button
*/
handleClick(event: MouseEvent): void {
this.logger.log('Click on osds-button');
this.submitForm(event);
}

handleKey(event: KeyboardEvent): void {
if(event.key === " " || event.key === "Enter") {
this.logger.log('Key on osds-button');
this.submitForm(event);
}
}

/**
* Checking if the button is in a form to add the form submit behaviour on it
*/
private submitForm(event: MouseEvent | KeyboardEvent) {
if (this.component.type && this.component.type === "submit") {
const form = (event.target as HTMLElement).closest("form");
if (form) {
event.preventDefault();

const fakeButton = document.createElement('button');
fakeButton.type = 'submit';
fakeButton.style.display = 'none';
form.appendChild(fakeButton);
fakeButton.click();
fakeButton.remove();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ export class OdsButtonMock implements OdsButton<OdsButtonMethods, OdsButtonEvent
controller: OdsButtonController = jest.fn() as unknown as OdsButtonController;

beforeRender = jest.fn();
handleClick = jest.fn();
handleKey = jest.fn();
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,37 @@ describe('e2e:osds-button', () => {
let slotContent: E2EElement;
let linkElement: E2EElement;
let buttonElement: E2EElement;
let checkSubmit;

async function setup({ attributes = {}, html = `` }: { attributes?: Partial<OdsButtonAttributes>, html?: string } = {}) {
afterEach(() => {
jest.clearAllMocks();
});

async function setup({ attributes = {}, html = ``, inForm }: { attributes?: Partial<OdsButtonAttributes>, html?: string, inForm?: boolean } = {}) {
const minimalAttributes: OdsButtonAttributes = OdsCreateAttributes(attributes, odsButtonBaseAttributes);
const stringAttributes = OdsComponentAttributes2StringAttributes<OdsButtonAttributes>(minimalAttributes, odsButtonDefaultAttributes);

let content = '';
const button = `<osds-button ${OdsStringAttributes2Str(stringAttributes)}>
${html}
</osds-button>`;

page = await newE2EPage();

if (inForm) {
content = `
<form onsubmit="onSubmit()">
${button}
</form>
`
} else {
content = button;
}

checkSubmit = jest.fn();
await page.exposeFunction('onSubmit', checkSubmit);

await page.setContent(content);
await page.setContent(`
<osds-button ${OdsStringAttributes2Str(stringAttributes)}>
${html}
Expand Down Expand Up @@ -168,6 +193,27 @@ describe('e2e:osds-button', () => {
expect(await el.getProperty('size')).toBe(OdsButtonSize.md);
});
});

describe('form', () => {
beforeEach(async () => {
await setup({ attributes: { type: 'submit' }, html: `submit`, inForm: true });
});

it('should submit a form with click when type is submit', async () => {
await el.click();
expect(checkSubmit).toHaveBeenCalledTimes(1);
});

it('should submit a form with Enter key when type is submit', async () => {
await el.press("Enter");
expect(checkSubmit).toHaveBeenCalledTimes(1);
});

it('should submit a form with Space key when type is submit', async () => {
await el.press(" ");
expect(checkSubmit).toHaveBeenCalledTimes(1);
});
});
});


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ describe('spec:osds-button', () => {
let instance: OsdsButton;
let controller: OdsButtonController;

afterEach(() => {
jest.clearAllMocks();
});

async function setup({ attributes= {} , html = `` }: { attributes?: Partial<OdsButtonAttributes>, html?: string } = {}) {
const minimalAttributes: OdsButtonAttributes = OdsCreateAttributes(attributes, odsButtonBaseAttributes);
const stringAttributes = OdsComponentAttributes2StringAttributes<OdsButtonAttributes>(minimalAttributes, odsButtonDefaultAttributes);
Expand Down Expand Up @@ -215,5 +219,28 @@ describe('spec:osds-button', () => {
expect(controller.validateAttributes).toHaveBeenCalledWith();
expect(controller.validateAttributes).toHaveBeenCalledTimes(1);
});

it('should call handleClick of controller', async () => {
const click = new MouseEvent('click');
await setup({});
instance.handleClick(click);

page.root.click()

expect(controller.handleClick).toHaveBeenCalledTimes(2);
expect(controller.handleClick).toHaveBeenCalledWith(click);
});

it('should call handleKey of controller', async () => {
await setup({});
const key = new KeyboardEvent("keyup", { key : "Enter" });
instance.handleKey(key);

page.root.dispatchEvent(key);
await page.waitForChanges();

expect(controller.handleKey).toHaveBeenCalledTimes(2);
expect(controller.handleKey).toHaveBeenCalledWith(key);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Element, Host, Prop, h } from '@stencil/core';
import { Component, Element, Host, Prop, h, Listen } from '@stencil/core';
import {
OdsButton,
OdsButtonController,
Expand Down Expand Up @@ -63,6 +63,16 @@ export class OsdsButton implements OdsButton<OdsStencilMethods<OdsButtonMethods>
/** @see OdsButtonAttributes.variant */
@Prop({ reflect: true }) public variant?: OdsButtonVariant = odsButtonDefaultAttributes.variant;

@Listen('keyup')
handleKey(event: KeyboardEvent) {
this.controller.handleKey(event);
}

@Listen('click')
handleClick(event: MouseEvent) {
this.controller.handleClick(event);
}

/** @see OdsButtonBehavior.beforeRender */
beforeRender(): void {
this.controller.validateAttributes();
Expand Down
15 changes: 15 additions & 0 deletions packages/stencil/components/button/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,22 @@
<osds-button square disabled>Square</osds-button>
<osds-button square size='l' disabled>Square large</osds-button>

<p>In Form</p>
<form>
<div>
<label for="text">Enter text: </label>
<input type="text" name="text" id="text" required>
</div>

<osds-button type='submit' size='sm'>submit</osds-button>
</form>

<script>
document.querySelector('form').addEventListener('submit', (e) => {
e.preventDefault();
console.log('form submitted');
})

document.addEventListener('odsInitialized', ({detail}) => {
const instance = detail.instance;
instance.logging(true);
Expand Down

0 comments on commit 52a6f55

Please sign in to comment.