Add tabs example

This commit is contained in:
Steve Kinney
2024-10-01 18:01:10 -05:00
parent 75e9bb7ca7
commit ad6dc3b145
8 changed files with 407 additions and 103 deletions

View File

@@ -23,7 +23,10 @@
"vitest": "^2.1.1"
},
"dependencies": {
"@sveltejs/vite-plugin-svelte": "^3.1.2",
"@testing-library/svelte": "^5.2.2",
"lit": "^3.2.0",
"svelte": "^4.2.19",
"uuid": "^10.0.0"
}
}

View 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;
};

View 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');
});
});

View File

@@ -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');

View 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>

View 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');
});
});

View File

@@ -1,8 +1,11 @@
import { defineConfig } from 'vitest/config';
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
plugins: [svelte()],
test: {
environment: 'happy-dom',
globals: true,
setupFiles: ['@testing-library/jest-dom/vitest'],
},
});