Add the FeedbackButton component.

This commit is contained in:
Bauke 2022-03-16 23:47:04 +01:00
parent c78d088d00
commit dfeaa5db22
Signed by: Bauke
GPG Key ID: C1C0F29952BCF558
5 changed files with 131 additions and 0 deletions

View File

@ -0,0 +1,68 @@
import {html} from 'htm/preact';
import {Component, VNode} from 'preact';
/**
* Component properties for {@linkcode FeedbackButton}.
*/
export type FeedbackButtonProps = {
click: (event: MouseEvent) => unknown;
extraAttributes: Record<string, string>;
feedbackText: string;
text: string;
timeout: number;
};
/**
* Component state for {@linkcode FeedbackButton}.
*/
export type FeedbackButtonState = {
currentText: string;
timeoutHandle: number | undefined;
};
/**
* A {@linkcode https://developer.mozilla.org/docs/Web/HTML/Element/button <button>}
* element wrapper that changes its text for a given time after being clicked.
*/
export class FeedbackButton extends Component<
FeedbackButtonProps,
FeedbackButtonState
> {
constructor(props: FeedbackButtonProps) {
super(props);
this.state = {
currentText: this.props.text,
timeoutHandle: undefined,
};
}
click = (event: MouseEvent) => {
this.props.click(event);
let {timeoutHandle} = this.state;
if (timeoutHandle !== undefined) {
window.clearTimeout(timeoutHandle);
}
timeoutHandle = window.setTimeout(() => {
this.setState({
currentText: this.props.text,
timeoutHandle: undefined,
});
}, this.props.timeout);
this.setState({
currentText: this.props.feedbackText,
timeoutHandle,
});
};
render(): VNode {
return html`
<button ...${this.props.extraAttributes} onclick=${this.click}>
${this.state.currentText}
</button>
`;
}
}

View File

@ -1,5 +1,6 @@
// Button Exports // Button Exports
export * from './buttons/confirm-button.js'; export * from './buttons/confirm-button.js';
export * from './buttons/feedback-button.js';
// Link Exports // Link Exports
export * from './links/privacy-link.js'; export * from './links/privacy-link.js';

View File

@ -0,0 +1,43 @@
import {GlobalRegistrator} from '@happy-dom/global-registrator';
import test from 'ava';
import {html} from 'htm/preact';
import {render} from 'preact';
import {FeedbackButton, FeedbackButtonProps} from '../../source/gram.js';
import {sleep} from '../utilities.js';
test.before(() => {
GlobalRegistrator.register();
});
test('FeedbackButton', async (t) => {
t.plan(5);
const props: FeedbackButtonProps = {
click: (event) => t.true(event !== undefined),
extraAttributes: {
id: 'feedback-button',
},
feedbackText: 'Feedback Example',
text: 'Example',
timeout: 1000,
};
render(html`<${FeedbackButton} ...${props} />`, document);
const buttonElement =
document.querySelector<HTMLButtonElement>('#feedback-button')!;
t.snapshot(buttonElement.outerHTML, 'Default state');
buttonElement.click();
// Wait for Preact to do its stuff.
await sleep();
t.snapshot(buttonElement.outerHTML, 'Feedback state');
buttonElement.click();
await sleep(props.timeout);
t.snapshot(buttonElement.outerHTML, 'Back to default state');
});

View File

@ -0,0 +1,19 @@
# Snapshot report for `tests/buttons/feedback-button.test.ts`
The actual snapshot is saved in `feedback-button.test.ts.snap`.
Generated by [AVA](https://avajs.dev).
## FeedbackButton
> Default state
'<button id="feedback-button">Example</button>'
> Feedback state
'<button id="feedback-button">Feedback Example</button>'
> Back to default state
'<button id="feedback-button">Example</button>'

Binary file not shown.