Skip to content

Instantly share code, notes, and snippets.

Avatar

Donald Pipowitch donaldpipowitch

View GitHub Profile
@donaldpipowitch
donaldpipowitch / disallow-storybook-jest-import.js
Created February 14, 2023 09:40
ESLint rule which disallows importing "@storybook/jest" (it shall not be used inside "./tests")
View disallow-storybook-jest-import.js
// @ts-check
/** @type {import("eslint").Rule.RuleModule} */
const rule = {
meta: {
docs: {
description: '"@storybook/jest" should not be used inside "./tests".',
},
},
create(context) {
@donaldpipowitch
donaldpipowitch / README.md
Last active November 24, 2022 07:41
Storybook Addon
View README.md

This example shows how you can easily add a custom addon with TypeScript in an existing Storybook project.

This addon renders data on the manager side of Storybook, but what will be rendered can be influenced by the preview side of Storybook. We can easily interact with the addon within any Story.

import { useCounter } from './counter-addon';

// just use this hook in your stories like every other hook
const { add } = useCounter();
add(17);
@donaldpipowitch
donaldpipowitch / README.md
Created November 3, 2022 09:15
Codemod to add missing import, if a certain JSX Element is encountered
View README.md

I have a small helper file called prettiermod.js which I use to run codemods. You need to copy'n'paste the prettiermod.js file and install the dependencies ($ pnpm add globby@^11.0.0 @babel/traverse prettier).

After this you can create new codemods which use prettiermod.js. The following codemod adds an import in case we find a specific component (add import { SuspenseLoader } from 'src/components/spinner-container'; in case <SuspenseLoader/> can be found).

Just call $ node run-codemod.js to execute the codemod.

I use AST explorer for debugging (@babel/parser with flow disabled and typescript enabled and babelv7).

@donaldpipowitch
donaldpipowitch / README.md
Last active November 3, 2022 09:08
Codemod to add an attribute to a JSX Element
View README.md

I have a small helper file called prettiermod.js which I use to run codemods. You need to copy'n'paste the prettiermod.js file and install the dependencies ($ pnpm add globby@^11.0.0 @babel/traverse prettier).

After this you can create new codemods which use prettiermod.js. The following codemod adds a property with a default value to a specific component in case it is not there (flow="column" will be added to the <Stack /> component).

Just call $ node run-codemod.js to execute the codemod.

I use AST explorer for debugging (@babel/parser with flow disabled and typescript enabled and babelv7).

@donaldpipowitch
donaldpipowitch / README.md
Last active December 28, 2021 19:39
Type-safe query params in React with a "Hook Builder"
View README.md

Everyone loves type-safe APIs nowadays, but as far as I can tell query parameters are a blind spot to most people right now.

I create a hook which uses a builder pattern a while ago, which allows you to create type-safe query parameters. It is not very polished and contains some specific use cases, that's why I hesitated to share it. But as I successfully use it for a couple of month already and (afaik) no one came up with something similar so far, I wanted to share it.

How does it look like?

import * as pp from './page-params';

type FavoriteFood = 'pizza' | 'noodles' | 'wraps' | 'hot-dogs';
@donaldpipowitch
donaldpipowitch / use-timeout.ts
Created October 6, 2021 07:53
use-timeout.ts
View use-timeout.ts
// it's like setTimeout as a hook, but with a small twist:
// you can make multiple subsequent calls and can increase or decrease
// the delay (e.g. useful to create polling like functionality)
type Options = {
delay: number;
multiplier?: number;
};
type State = {
@donaldpipowitch
donaldpipowitch / README.md
Last active August 12, 2021 13:59
Rewrite relative import paths with a codemod
View README.md

⚠️ This is for an internal project, but shared publicly as a reference for myself.

If you want to have a general introduction how to write such a codemod have a look at this Gist from me.

What to do?

Place the usage.js and prettiermod.js in the root of the project. Run:

$ pnpm add globby@^11.0.0 @babel/traverse
@donaldpipowitch
donaldpipowitch / README.md
Last active January 26, 2023 10:14
Create a codemod with Prettier and Babel
View README.md

Every now and then I'd like to apply a so called codemod to my a codebase. A codemod is a piece of code which modifies other code. Very often this is done by running some transformation on the abstract syntax tree (AST).

Whenever I need to do this I look for "What is the best way to apply a codemod on my TS code base right now?", because when I only do this every couple of month I either have forgotten about how to do it or tools have changed and my old way stopped working... or both. Surprisingly every time I search for that I found the existing tools bloated or quirky or not matching my workflow.

As I already use Babel to compile our source code I would like to create a Babel plugin which transforms my code. Sadly Babel alone is not good in preserving whitespace and formatting. Thankfully I use Prettier for that and because Prettier uses Babel internally I can create a Babel visitor to transform my source code.

The actually easiest and _most r

@donaldpipowitch
donaldpipowitch / get-nice-upper-bound.ts
Created November 13, 2020 07:05
Nice upper bound (e.g. for charts, ticks, scales)
View get-nice-upper-bound.ts
function getNiceUpperBound(value: number) {
const zeroCount = String(Math.round(value)).length - 1;
const factor = Math.pow(10, zeroCount);
return Math.ceil(value / factor) * factor;
}
const upperBound = getNiceUpperBound(3464634); // 4000000
@donaldpipowitch
donaldpipowitch / use-stable-memo.ts
Last active October 15, 2020 08:12
useStableMemo
View use-stable-memo.ts
import { DependencyList, useState, useEffect, useRef } from 'react';
// use this instead of useMemo of you need a stable value
// see https://twitter.com/0xca0a/status/1314326386555924480
export function useStableMemo<T>(factory: () => T, deps: DependencyList): T {
const [value, setValue] = useState<T>(factory);
const firstRun = useRef(true);
useEffect(
() => {