Skip to content

Instantly share code, notes, and snippets.

View donaldpipowitch's full-sized 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")
// @ts-check
/** @type {import("eslint").Rule.RuleModule} */
const rule = {
meta: {
docs: {
description: '"@storybook/jest" should not be used inside "./tests".',
},
},
create(context) {
@donaldpipowitch
donaldpipowitch / use-match-media.tsx
Last active February 10, 2023 17:48
useMatchMedia - a React hook for matchMedia / media queries
import { useState, useEffect } from 'react';
// pass a query like `(min-width: 768px)`
export function useMatchMedia(query: string) {
const [matches, setMatches] = useState(() => matchMedia(query).matches);
useEffect(() => {
const mediaQueryList = matchMedia(query);
const onChange = (event: MediaQueryListEvent) => setMatches(event.matches);
@donaldpipowitch
donaldpipowitch / README.md
Last active January 26, 2023 10:14
Create a codemod with Prettier and Babel

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 / README.md
Last active November 24, 2022 07:41
Storybook Addon

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

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

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 June 13, 2022 04:24
React - Error Boundary strategy

One cool feature of React which isn't highlighted that often are "error boundaries".

Error boundaries can catch errors which are thrown inside your component during a lifecycle. You can use them in very fine-granular levels wrapping very small components (but I rarely see this) or you can wrap your whole app and show some fallback content, if an error happens. But you can also wrap something in between those extrem ranges and add a proper reset. Also it's not a hidden secret how to do that I haven't see a lot of people talking about that. So here is a small example which use react-router-dom to do that. You can see the complete example here.

Let's imagine you have the following app:

import * as React from 'react';
import { render } from 'react-dom';
import { Link, BrowserRouter, Switch, Route, Redirect } from 'react-router-dom';
@donaldpipowitch
donaldpipowitch / README.md
Last active April 2, 2022 01:35
Example for a codemod which can run on a TypeScript code base

Install @codemod/cli globally:

$ npm install -g @codemod/cli
# or
$ yarn global add @codemod/cli

This package works out of the box with most code bases, because it comes bundled with @babel/preset-env and @babel/preset-typescript. If you need other presets or plugins for parsing your source code you can use a custom Babel config as well. Note that the codemod will not apply the transformations from these presets and plugins - they are only used for parsing. Therefor you keep your TypeScript types in your source code for example. Formatting will be kept the same as much as possible.

@donaldpipowitch
donaldpipowitch / use-timeout.ts
Created October 6, 2021 07:53
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 / get-nice-upper-bound.ts
Created November 13, 2020 07:05
Nice upper bound (e.g. for charts, ticks, scales)
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