Add tabs example
This commit is contained in:
43
examples/element-factory/src/login-form.js
Normal file
43
examples/element-factory/src/login-form.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import { html, render } from 'lit';
|
||||
|
||||
/**
|
||||
* @typedef {Object} LoginFormProperties
|
||||
* @property {string} action;
|
||||
* @property {'get' | 'post' | 'dialog'} method;
|
||||
* @property {(event: Event) => void} onSubmit;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Renders a login form.
|
||||
* @param {LoginFormProperties} parameters
|
||||
* @returns {HTMLDivElement}
|
||||
*/
|
||||
export const createLoginForm = ({
|
||||
action = '/login',
|
||||
method = 'post',
|
||||
onSubmit = () => {},
|
||||
} = {}) => {
|
||||
const template = html`
|
||||
<form
|
||||
action="${action}"
|
||||
method="${method}"
|
||||
@submit="${onSubmit}"
|
||||
aria-label="Login"
|
||||
>
|
||||
<label>
|
||||
<span>Email</span>
|
||||
<input type="email" name="email" placeholder="Email" />
|
||||
</label>
|
||||
<label>
|
||||
<span>Password</span>
|
||||
<input type="password" name="password" placeholder="Password" />
|
||||
</label>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
`;
|
||||
|
||||
const container = document.createElement('div');
|
||||
render(template, container);
|
||||
|
||||
return container;
|
||||
};
|
||||
28
examples/element-factory/src/login-form.test.js
Normal file
28
examples/element-factory/src/login-form.test.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import { screen } from '@testing-library/dom';
|
||||
import { createLoginForm } from './login-form';
|
||||
|
||||
describe('LoginForm', async () => {
|
||||
it('should render a login form', async () => {
|
||||
document.body.replaceChildren(createLoginForm());
|
||||
|
||||
const form = screen.getByRole('form', { name: /login/i });
|
||||
|
||||
expect(form).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render a login form with a custom action', async () => {
|
||||
document.body.replaceChildren(createLoginForm({ action: '/custom' }));
|
||||
|
||||
const form = screen.getByRole('form', { name: /login/i });
|
||||
|
||||
expect(form).toHaveAttribute('action', '/custom');
|
||||
});
|
||||
|
||||
it('should render a login form with a custom method', async () => {
|
||||
document.body.replaceChildren(createLoginForm({ method: 'get' }));
|
||||
|
||||
const form = screen.getByRole('form', { name: /login/i });
|
||||
|
||||
expect(form).toHaveAttribute('method', 'get');
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,9 @@
|
||||
import { screen } from '@testing-library/dom';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { createSecretInput } from './secret-input.js';
|
||||
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
|
||||
import { createSecretInput } from './secret-input.js';
|
||||
|
||||
describe('createSecretInput', async () => {
|
||||
beforeEach(() => {
|
||||
vi.spyOn(localStorage, 'getItem').mockReturnValue('test secret');
|
||||
|
||||
53
examples/element-factory/src/tabs.svelte
Normal file
53
examples/element-factory/src/tabs.svelte
Normal file
@@ -0,0 +1,53 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let tabs = [];
|
||||
|
||||
let tabbedContent = null;
|
||||
|
||||
const selectTab = (event) => {
|
||||
const tab = event.target;
|
||||
const panel = tabbedContent.querySelector(
|
||||
`#${tab.getAttribute('aria-controls')}`,
|
||||
);
|
||||
|
||||
tabbedContent.querySelectorAll('[role="tab"]').forEach((t) => {
|
||||
t.setAttribute('aria-selected', 'false');
|
||||
t.setAttribute('tabindex', '-1');
|
||||
});
|
||||
|
||||
tab.setAttribute('aria-selected', 'true');
|
||||
tab.setAttribute('tabindex', '0');
|
||||
|
||||
tabbedContent.querySelectorAll('[role="tabpanel"]').forEach((p) => {
|
||||
p.hidden = true;
|
||||
});
|
||||
|
||||
panel.hidden = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<section bind:this={tabbedContent}>
|
||||
<div role="tablist">
|
||||
{#each tabs as tab, i}
|
||||
{@const tabId = tab.id || i}
|
||||
<button
|
||||
role="tab"
|
||||
id="tab-{tabId}"
|
||||
aria-controls="panel-{tabId}"
|
||||
aria-selected="false"
|
||||
tabindex="-1"
|
||||
on:click={selectTab}
|
||||
>
|
||||
{tab.title}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#each tabs as tab, i}
|
||||
{@const tabId = tab.id || i}
|
||||
<div role="tabpanel" id="panel-{tabId}" hidden>
|
||||
<p>{tab.content}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</section>
|
||||
43
examples/element-factory/src/tabs.test.js
Normal file
43
examples/element-factory/src/tabs.test.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import { render, screen } from '@testing-library/svelte';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import Tabs from './tabs.svelte';
|
||||
|
||||
describe('Tabs', () => {
|
||||
beforeEach(() => {
|
||||
render(Tabs, {
|
||||
tabs: [
|
||||
{
|
||||
label: 'Venue',
|
||||
content: 'This year, we will be at this awesome venue',
|
||||
},
|
||||
{ label: 'Lineup', content: 'Check out our exciting lineup!' },
|
||||
{ label: 'Tickets', content: 'Buy tickets today!' },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should render three tabs', async () => {
|
||||
const tabs = screen.getAllByRole('tab');
|
||||
expect(tabs).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('should switch tabs', async () => {
|
||||
const tabs = screen.getAllByRole('tab');
|
||||
const secondTab = tabs[1];
|
||||
|
||||
await userEvent.click(secondTab);
|
||||
|
||||
expect(secondTab).toHaveAttribute('aria-selected', 'true');
|
||||
});
|
||||
|
||||
it('should render the content of the selected tab', async () => {
|
||||
const tabs = screen.getAllByRole('tab');
|
||||
const secondTab = tabs[1];
|
||||
|
||||
await userEvent.click(secondTab);
|
||||
|
||||
const content = screen.getByRole('tabpanel', { hidden: false });
|
||||
expect(content).toHaveTextContent('lineup');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user