Add tabs helper function
This commit is contained in:
parent
07b115e2b7
commit
ef82ae7bc6
22
src/back/routes/common/tabs/tabs.css
Normal file
22
src/back/routes/common/tabs/tabs.css
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
.tabs {
|
||||||
|
& > nav input[type="radio"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.tabs__tab-label {
|
||||||
|
label {
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tabs__tab {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
70
src/back/routes/common/tabs/tabs.tsx
Normal file
70
src/back/routes/common/tabs/tabs.tsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import type { FlatTemplatable } from "tempstream";
|
||||||
|
import { TempstreamJSX } from "tempstream";
|
||||||
|
|
||||||
|
const ids = (function* () {
|
||||||
|
let i = 0;
|
||||||
|
while (true) {
|
||||||
|
yield i++;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
export function tabs({
|
||||||
|
tabs,
|
||||||
|
default_tab,
|
||||||
|
tab_bar,
|
||||||
|
remember_tab = false,
|
||||||
|
active_navbar_tab_style = "",
|
||||||
|
}: {
|
||||||
|
tabs: { id: string; label?: string; content: FlatTemplatable }[];
|
||||||
|
default_tab: string;
|
||||||
|
tab_bar?: FlatTemplatable;
|
||||||
|
remember_tab?: boolean;
|
||||||
|
active_navbar_tab_style?: string;
|
||||||
|
}) {
|
||||||
|
const tab_section_id = ids.next().value;
|
||||||
|
return (
|
||||||
|
<section class={`tabs tabs__${tab_section_id}`}>
|
||||||
|
{tab_bar || (
|
||||||
|
<nav>
|
||||||
|
{tabs.map(({ id, label }) => (
|
||||||
|
<div class="tabs__tab-label">
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
id={`tabs__${tab_section_id}__tab__${id}`}
|
||||||
|
name={`tabs__${tab_section_id}`}
|
||||||
|
checked={id == default_tab}
|
||||||
|
autocomplete={remember_tab ? false : "off"}
|
||||||
|
/>
|
||||||
|
{label || id}
|
||||||
|
{
|
||||||
|
/* HTML */ `<style>
|
||||||
|
.tabs__${tab_section_id} nav label:has(input:checked) {
|
||||||
|
${active_navbar_tab_style}
|
||||||
|
}
|
||||||
|
</style>`
|
||||||
|
}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
)}
|
||||||
|
{tabs.map(({ id, content }) => {
|
||||||
|
const tab_id = `tabs__${tab_section_id}__tab__${id}`;
|
||||||
|
return (
|
||||||
|
<div class={`tabs__tab tabs__${tab_section_id}__tab ` + tab_id}>
|
||||||
|
{
|
||||||
|
/* HTML */ `<style>
|
||||||
|
body:has(#tabs__${tab_section_id}__tab__${id}:checked)
|
||||||
|
.${tab_id} {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>`
|
||||||
|
}
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
48
src/back/routes/tabs-demo.page.tsx
Normal file
48
src/back/routes/tabs-demo.page.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import type { Context } from "koa";
|
||||||
|
import { TempstreamJSX } from "tempstream";
|
||||||
|
import { Page } from "@sealcode/sealgen";
|
||||||
|
import html from "../html.js";
|
||||||
|
import { tabs } from "./common/tabs/tabs.js";
|
||||||
|
|
||||||
|
export const actionName = "TabsDemo";
|
||||||
|
|
||||||
|
export default new (class TabsDemoPage extends Page {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
async canAccess(_: Context) {
|
||||||
|
return { canAccess: true, message: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
async render(ctx: Context) {
|
||||||
|
return html(
|
||||||
|
ctx,
|
||||||
|
"TabsDemo",
|
||||||
|
<div>
|
||||||
|
{tabs({
|
||||||
|
default_tab: "tab1",
|
||||||
|
active_navbar_tab_style:
|
||||||
|
"font-weight: bold; text-decoration: underline",
|
||||||
|
tabs: [
|
||||||
|
{
|
||||||
|
id: "tab1",
|
||||||
|
label: "First tab",
|
||||||
|
content: (
|
||||||
|
<div>
|
||||||
|
<h2>This is tab 1</h2>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "tab2",
|
||||||
|
label: "Second tab",
|
||||||
|
content: (
|
||||||
|
<div>
|
||||||
|
<h2>And this is tab 2</h2>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})();
|
40
src/back/routes/tabs-demo.test.ts
Normal file
40
src/back/routes/tabs-demo.test.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { withProdApp } from "../test_utils/with-prod-app.js";
|
||||||
|
import { VERY_LONG_TEST_TIMEOUT, webhintURL } from "../test_utils/webhint.js";
|
||||||
|
import { TabsDemoURL } from "./urls.js";
|
||||||
|
import { getBrowser } from "../test_utils/browser-creator.js";
|
||||||
|
import type { Browser, BrowserContext, Page } from "@playwright/test";
|
||||||
|
|
||||||
|
describe("TabsDemo webhint", () => {
|
||||||
|
it("doesn't crash", async function () {
|
||||||
|
return withProdApp(async ({ base_url, rest_api }) => {
|
||||||
|
await rest_api.get(TabsDemoURL);
|
||||||
|
await webhintURL(base_url + TabsDemoURL);
|
||||||
|
// alternatively you can use webhintHTML for faster but less precise scans
|
||||||
|
// or for scanning responses of requests that use some form of authorization:
|
||||||
|
// const response = await rest_api.get(TabsDemoURL);
|
||||||
|
// await webhintHTML(response);
|
||||||
|
});
|
||||||
|
}).timeout(VERY_LONG_TEST_TIMEOUT);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("TabsDemo", () => {
|
||||||
|
let page: Page;
|
||||||
|
let browser: Browser;
|
||||||
|
let context: BrowserContext;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
browser = await getBrowser();
|
||||||
|
context = await browser.newContext();
|
||||||
|
page = await context.newPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await context.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("works as expected", async function () {
|
||||||
|
return withProdApp(async ({ base_url }) => {
|
||||||
|
await page.goto(base_url + TabsDemoURL);
|
||||||
|
});
|
||||||
|
}).timeout(VERY_LONG_TEST_TIMEOUT);
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user