Frontend Weekly 2024-02-23
Deno 1.41、Remix 2.7.0、React Strict DOM、htmz、Safetestについて紹介する。
- # Deno 1.41
- # Remix 2.7.0
- # React Strict DOM
- # htmz
- # Safetest
Deno 1.41
2024年2月22日にDeno 1.41がリリースされました。deno compileで生成されるバイナリがかなり小さくなりました。
Remix 2.7.0
2024年2月20日にRemix 2.7.0がリリースされました。SPAモードが安定版になった他、Remix ViteがCloudflare Pagesをサポート、Viteとの互換性の改善などが行われています。
https://remix.run/blog/remix-vite-stable
React Strict DOM
MetaがReact Strict DOM(RSD)を公開しました。これはWebおよびネイティブ用のスタイル付きReactコンポーネントの開発の改善および標準化を目的としたReact DOMとStyleXの実験的な統合とのことです。
https://github.com/facebook/react-strict-dom
React Native for WebはReact NativeのAPIをWeb上で補完しようするものでしたが、RSDはWeb APIをReact Nativeに組み込んでいこうとする試みです。
既にかなりの機能が実装されています。
https://github.com/facebook/react-strict-dom/blob/main/packages/react-strict-dom/COMPATIBILITY.md
import { css, html } from "react-strict-dom";
const styles = css.create({
container: { borderTopWidth: 1 },
h1: { padding: 10, backgroundColor: "#eee" },
content: { padding: 10 },
});
export default function Example(props) {
const { title, children } = props;
return (
<html.div style={styles.container}>
<html.h1 style={styles.h1}>{title}</html.h1>
<html.div style={styles.content}>{children}</html.div>
</html.div>
);
}
htmz
htmzは標準のHTMLを利用してページの部分更新を行う数行のスニペットです。
<script>
function htmz(frame) {
setTimeout(() =>
document
.querySelector(frame.contentWindow.location.hash || null)
?.replaceWith(...frame.contentDocument.body.children)
);
}
</script>
<iframe hidden name="htmz" onload="window.htmz(this)"></iframe>
実用的かはともかく、かなり面白いアイデアだと思います。リンクをクリックされると、iframeに対応したコンテンツが描画され、そのコンテンツをhashのidが付いた要素の中身と置換する仕組みになっています。
<base target="htmz" />
<div role="tablist">
<a class="tab" href="dog.html#my-tab-panel">Dog</a>
<a class="tab" href="cat.html#my-tab-panel">Cat</a>
<a class="tab" href="horse.html#my-tab-panel">Horse</a>
</div>
<div id="my-tab-panel" role="tabpanel"></div>
<iframe hidden name="htmz" onload="window.htmz(this)"></iframe>
Safetest
Safetestは、Netflixの開発したPlaywrightやVitest/Jestを組みわせたテストライブラリです。
https://github.com/kolodny/safetest?tab=readme-ov-file
従来のフロントエンドのテスト手法である単体/統合テストやE2Eテストの欠点を、両方を組み合わせることで補完することを目的としています。アプリケーションの起動時にテスト用のフックをインジェクトする仕組みのため、ブラウザとテストコンテキスト間の双方向通信や、PlaywrightやJest/Vitestの機能へのアクセスも可能になっています。
テストの記述方法は、見慣れたものです。
// Header.tsx
export const Header = ({ admin }: { admin: boolean }) => (
<div className="header">
<div className="header-title">The App</div>
<div className="header-user">
<div className="header-user-name">admin</div>
{admin && <div className="header-user-admin">admin</div>}
<div className="header-user-logout">Logout</div>
</div>
</div>
);
// Header.safetest.tsx
import { describe, it, expect } from 'safetest/jest';
import { render } from 'safetest/react';
import { Header } from './Header';
describe('Header', () => {
it('can render a regular header', async () => {
const { page } = await render(<Header />);
await expect(page.locator('text=Logout')).toBeVisible();
await expect(page.locator('text=admin')).not.toBeVisible();
expect(await page.screenshot()).toMatchImageSnapshot();
});
it('can render an admin header', async () => {
const { page } = await render(<Header admin={true} />);
await expect(page.locator('text=Logout')).toBeVisible();
await expect(page.locator('text=admin')).toBeVisible();
expect(await page.screenshot()).toMatchImageSnapshot();
});
});
なお、実験的ですがPlaywrightにもComponent単位でのテストを可能にする似たようなことが出来る機能が入っています。