SaltyCrane Blog — Notes on JavaScript and web development

Examples of CSS text-orientation and writing-mode

Shared CSS

CSS
.ex { border: 1px solid #ccc; height: 120px; padding: 5px; width: 120px; }

Examples of permutations of writing-mode and text-orientation

horizontal-tb, mixed
Some example text
HTML
<div class="ex" id="example-0a"> Some example text </div>
CSS
#example-0a { writing-mode: horizontal-tb; text-orientation: mixed; }
horizontal-tb, upright
Some example text
HTML
<div class="ex" id="example-0b"> Some example text </div>
CSS
#example-0b { writing-mode: horizontal-tb; text-orientation: upright; }
vertical-rl, mixed
Some example text
HTML
<div class="ex" id="example-1a"> Some example text </div>
CSS
#example-1a { writing-mode: vertical-rl; text-orientation: mixed; }
vertical-rl, upright
Some example text
HTML
<div class="ex" id="example-1b"> Some example text </div>
CSS
#example-1b { writing-mode: vertical-rl; text-orientation: upright; }
vertical-lr, mixed
Some example text
HTML
<div class="ex" id="example-2a"> Some example text </div>
CSS
#example-2a { writing-mode: vertical-lr; text-orientation: mixed; }
vertical-lr, upright
Some example text
HTML
<div class="ex" id="example-2b"> Some example text </div>
CSS
#example-2b { writing-mode: vertical-lr; text-orientation: upright; }

How to download pull request metadata using the GitHub GraphQL API

This is a TypeScript Node.js script to download GitHub pull request information (title, body, comments, etc.) using the GitHub GraphQL API. The data is saved in a JSON file.

The GitHub repo is here: download-github-prs.

Create a GitHub personal access token

Create a GitHub personal access token as described here (no checkboxes need to be selected for public repos): https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token This is used to access the GitHub GraphQL API.

Intall libraries

  • Create a directory and cd into it
    $ mkdir -p /tmp/my-prs
    $ cd /tmp/my-prs 
    
  • Create package.json file:
    {
      "scripts": {
        "download": "ts-node index.ts"
      },
      "dependencies": {
        "node-fetch": "^2.6.1"
      },
      "devDependencies": {
        "@types/node-fetch": "^2.5.7",
        "ts-node": "^9.0.0",
        "typescript": "^4.1.2"
      }
    }
    
  • Install
    $ npm install 
    
  • Create script

    Creat a file, /tmp/my-prs/index.ts, replacing XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX with the GitHub personal access token described above.

    import * as fs from "fs";
    import fetch from "node-fetch";
    
    const GITHUB_TOKEN = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
    const GITHUB_GRAPHQL_API_URL = "https://api.github.com/graphql";
    const OUTPUT_DIR = "/tmp/my-prs";
    const REPO_OWNER = "facebookexperimental";
    const REPO_NAME = "Recoil";
    
    fetchPullRequest(1);
    
    /**
     * fetchPullRequest
     */
    async function fetchPullRequest(prNumber: number) {
      const reactionFragment = `
        content
        user {
          login
        }
      `;
      const userContentEditFragment = `
        createdAt
        deletedAt
        deletedBy {
          login
        }
        diff
        editedAt
        editor {
          login
        }
        updatedAt
      `;
      const commentFragment = `
        author {
          login
        }
        body
        createdAt
        reactions(first: 100) {
          nodes {
            ${reactionFragment}
          }
        }
        userContentEdits(first: 100) {
          nodes {
            ${userContentEditFragment}
          }
        }
      `;
      const query = `
      query {
        repository(owner: "${REPO_OWNER}", name: "${REPO_NAME}") {
          nameWithOwner
          pullRequest(number: ${prNumber}) {
            author { login }
            baseRefName
            baseRefOid
            body
            closedAt
            comments(first: 100) {
              nodes {
                ${commentFragment}
              }
            }
            commits(first: 250) {
              nodes {
                commit {
                  oid
                }
              }
            }
            createdAt
            files(first: 100) {
              nodes { path }
            }
            headRefName
            headRefOid
            mergeCommit { oid }
            merged
            mergedAt
            mergedBy { login }
            number
            publishedAt
            reactions(first: 10) {
              nodes {
                ${reactionFragment}
              }
            }
            reviews(first: 10) {
              nodes {
                author { login }
                body
                comments(first: 10) {
                  nodes {
                    ${commentFragment}
                  }
                }
                commit {
                  oid
                }
                createdAt
                editor { login }
                publishedAt
                reactions(first: 10) {
                  nodes {
                    ${reactionFragment}
                  }
                }
                resourcePath
                submittedAt
                updatedAt
                userContentEdits(first: 10) {
                  nodes {
                    ${userContentEditFragment}
                  }
                }
              }
            }
            state
            title
            updatedAt
            userContentEdits(first: 10) {
              nodes {
                ${userContentEditFragment}
              }
            }
          }
        }
      }
      `;
    
      // make graphql query and strigify the response
      const resp = await fetchQuery(query);
      const respStr = JSON.stringify(resp, null, 2);
    
      // save json file
      const filepath = [
        `${OUTPUT_DIR}/`,
        `${REPO_NAME}-pr-${String(prNumber).padStart(4, "0")}.json`,
      ].join("");
      console.log(`Saving ${filepath}...`);
      fs.writeFileSync(filepath, respStr);
    }
    
    /**
     * fetchQuery
     */
    function fetchQuery(query: string, variables: Record<string, any> = {}) {
      return fetch(GITHUB_GRAPHQL_API_URL, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `bearer ${GITHUB_TOKEN}`,
        },
        body: JSON.stringify({
          query,
          variables,
        }),
      }).then((response) => {
        return response.json();
      });
    }
    

    Run it

    $ npm run download 
    

    Output

    This produces the following JSON file

    $ cat /tmp/my-prs/Recoil-pr-0001.json
    {
      "data": {
        "repository": {
          "nameWithOwner": "facebookexperimental/Recoil",
          "pullRequest": {
            "author": {
              "login": "facebook-github-bot"
            },
            "baseRefName": "master",
            "baseRefOid": "40e870caadc159a87e81be291ff641410ab32e8f",
            "body": "This is pull request was created automatically because we noticed your project was missing a Contributing file.\n\nCONTRIBUTING files explain how a developer can contribute to the project - which you should actively encourage.\n\nThis PR was crafted with love by Facebook's Open Source Team.",
            "closedAt": "2020-05-13T04:12:15Z",
            "comments": {
              "nodes": [
                {
                  "author": {
                    "login": "davidmccabe"
                  },
                  "body": "Already added this manually.",
                  "createdAt": "2020-05-13T04:12:15Z",
                  "reactions": {
                    "nodes": []
                  },
                  "userContentEdits": {
                    "nodes": []
                  }
                }
              ]
            },
            "commits": {
              "nodes": [
                {
                  "commit": {
                    "oid": "96f91679540362fa96a6c92611a8ef5621447b42"
                  }
                }
              ]
            },
            "createdAt": "2020-05-06T22:31:01Z",
            "files": {
              "nodes": [
                {
                  "path": "CONTRIBUTING.md"
                }
              ]
            },
            "headRefName": "automated_fixup_contributing_file_exists",
            "headRefOid": "96f91679540362fa96a6c92611a8ef5621447b42",
            "mergeCommit": null,
            "merged": false,
            "mergedAt": null,
            "mergedBy": null,
            "number": 1,
            "publishedAt": "2020-05-06T22:31:01Z",
            "reactions": {
              "nodes": []
            },
            "reviews": {
              "nodes": []
            },
            "state": "CLOSED",
            "title": "Adding Contributing file",
            "updatedAt": "2020-10-07T20:23:05Z",
            "userContentEdits": {
              "nodes": []
            }
          }
        }
      }
    }
    

Notes on Fabric 2 and Python 3

Fabric 2 is a Python package used for running commands on remote machines via SSH. Fabric 2 supports Python 3 and is a rewrite of the Fabric I used years ago. Here are my notes on using Fabric 2 and Python 3.

Set up SSH config and SSH agent

  • Create or edit your ~/.ssh/config file to contain your remote host parameters
    Host myhost
        User myusername
        HostName myhost.com
        IdentityFile ~/.ssh/id_rsa
    
  • Add your private key to your SSH agent
    $ ssh-add ~/.ssh/id_rsa
    

Create a project, create a virtualenv, and install fabric2

$ mkdir -p /tmp/my-project
$ cd /tmp/my-project
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install fabric2

Create a fabfile.py script

Create a file /tmp/my-project/fabfile.py with the following contents. Note: "myhost" is the same name used in ~/.ssh/config described above.

from fabric2 import task

hosts = ["myhost"]

@task(hosts=hosts)
def mytask(c):
    print("Starting mytask...")
    with c.cd("/var"):
        c.run("ls -l")
    print("Done.")

Run the fabric script

In /tmp/my-project, with the virtualenv activated, run the fabric task to list the contents of /var on the remote host.

$ fab2 mytask 

Output:

Starting mytask...

total 48
drwxr-xr-x  2 root root   4096 backups
drwxr-xr-x  9 root root   4096 cache
drwxrwxrwt  2 root root   4096 crash
drwxr-xr-x 38 root root   4096 lib
drwxrwsr-x  2 root root   4096 local
drwxrwxrwt  2 root root   4096 lock
drwxrwxr-x 14 root root   4096 log
drwxrwsr-x  2 root root   4096 mail
drwxr-xr-x  2 root root   4096 opt
drwxr-xr-x  5 root root   4096 spool
drwxrwxrwt  2 root root   4096 tmp
drwxr-xr-x  3 root root   4096 www

Done.

See also / References

How height is set in CSS

Here are some CSS experiments to test when elements adjust to the height of their container and when they adjust to the height of their content. The script to generate the experiments is on github and the results are shown below. See also my companion page on CSS width.

By default, elements adjust to height of their container when they are
By default, elements adjust to the height of their content when they are
Some elements can be made to adjust to the height of their container by
Some elements can be made to adjust to the height of their content by
Some miscellaneous cases:
  • elements in flex column containers with flex-grow set expand to the height of their content for tall content
  • for elements in grid containers, setting height to 100% does NOT limit their height by the height of their container
  • for elements in grid containers, setting overflow limits an element's height to the height of its container for tall content
  • for elements in grid containers, setting overflow has no effect when align-items is set
  • for elements in table cell containers, setting height to 100% does NOT expand an element's height to the height of the table cell container if the height of the table cell container is not explicitly set

See also

Block containers

Block container - elements in block containers adjust to the height of their content by default
#container-1a
#example-1a
#content-1a
HTML
<div id="container-1a"> <div id="example-1a"> <div id="content-1a" /> </div> </div>
CSS
#container-1a { display: block; /* default */ height: 400px; } #example-1a {} #content-1a { height: 80px; }
Block container, tall content - elements in block containers adjust to the height of their content even when the content is taller than the container
#container-1b
#example-1b
#content-1b
HTML
<div id="container-1b"> <div id="example-1b"> <div id="content-1b" /> </div> </div>
CSS
#container-1b { display: block; /* default */ height: 400px; } #example-1b {} #content-1b { height: 420px; }
Block container, height 100% - elements in block containers expand to the height of their container if height is set to 100%
#container-1c
#example-1c
#content-1c
HTML
<div id="container-1c"> <div id="example-1c"> <div id="content-1c" /> </div> </div>
CSS
#container-1c { display: block; /* default */ height: 400px; } #example-1c { height: 100%; } #content-1c { height: 80px; }
Block container, tall content, height 100% - elements in block containers contract to the height of their container if height is set to 100%
#container-1d
#example-1d
#content-1d
HTML
<div id="container-1d"> <div id="example-1d"> <div id="content-1d" /> </div> </div>
CSS
#container-1d { display: block; /* default */ height: 400px; } #example-1d { height: 100%; } #content-1d { height: 420px; }

Flex row containers

Flex row container - elements in flex row containers expand to the height of their container by default
#container-3a
#example-3a
#content-3a
HTML
<div id="container-3a"> <div id="example-3a"> <div id="content-3a" /> </div> </div>
CSS
#container-3a { align-items: normal; /* default - behaves as stretch in this case */ display: flex; flex-direction: row; /* default */ height: 400px; } #example-3a {} #content-3a { height: 80px; }
Flex row container, tall content - elements in flex row containers contract to the height of their container by default
#container-3b
#example-3b
#content-3b
HTML
<div id="container-3b"> <div id="example-3b"> <div id="content-3b" /> </div> </div>
CSS
#container-3b { align-items: normal; /* default - behaves as stretch in this case */ display: flex; flex-direction: row; /* default */ height: 400px; } #example-3b {} #content-3b { height: 420px; }
Flex row container, align-items set - elements in flex row containers adjust to the height of their content if align-items is set to something other than stretch
#container-3c
#example-3c
#content-3c
HTML
<div id="container-3c"> <div id="example-3c"> <div id="content-3c" /> </div> </div>
CSS
#container-3c { align-items: flex-start; display: flex; flex-direction: row; /* default */ height: 400px; } #example-3c {} #content-3c { height: 80px; }
Flex row container, align-items set, tall content - elements in flex row containers adjust to the height of their content if align-items is set to something other than stretch
#container-3d
#example-3d
#content-3d
HTML
<div id="container-3d"> <div id="example-3d"> <div id="content-3d" /> </div> </div>
CSS
#container-3d { align-items: flex-start; display: flex; flex-direction: row; /* default */ height: 400px; } #example-3d {} #content-3d { height: 420px; }

Flex column containers

Flex column container - elements in flex columns containers adjust to the height of the content by default
#container-4a
#example-4a
#content-4a
HTML
<div id="container-4a"> <div id="example-4a"> <div id="content-4a" /> </div> </div>
CSS
#container-4a { display: flex; flex-direction: column; justify-content: normal; /* default */ height: 400px; } #example-4a {} #content-4a { height: 80px; }
Flex column container, tall content - elements in flex columns containers adjust to the height of their content even for tall content
#container-4b
#example-4b
#content-4b
HTML
<div id="container-4b"> <div id="example-4b"> <div id="content-4b" /> </div> </div>
CSS
#container-4b { display: flex; flex-direction: column; justify-content: normal; /* default */ height: 400px; } #example-4b {} #content-4b { height: 420px; }
Flex column container, height: 100% - elements in flex column containers expand to the height of their container if height is set to 100%
#container-4c
#example-4c
#content-4c
HTML
<div id="container-4c"> <div id="example-4c"> <div id="content-4c" /> </div> </div>
CSS
#container-4c { display: flex; flex-direction: column; justify-content: normal; /* default */ height: 400px; } #example-4c { height: 100%; } #content-4c { height: 80px; }
Flex column container, height: 100%, tall content - elements in flex column containers contract to the height of their container if height is set to 100%
#container-4d
#example-4d
#content-4d
HTML
<div id="container-4d"> <div id="example-4d"> <div id="content-4d" /> </div> </div>
CSS
#container-4d { display: flex; flex-direction: column; justify-content: normal; /* default */ height: 400px; } #example-4d { height: 100%; } #content-4d { height: 420px; }
Flex column container, flex-grow - elements in flex column containers expand to the height of their container when flex-grow is set to 1
#container-4e
#example-4e
#content-4e
HTML
<div id="container-4e"> <div id="example-4e"> <div id="content-4e" /> </div> </div>
CSS
#container-4e { display: flex; flex-direction: column; justify-content: normal; /* default */ height: 400px; } #example-4e { flex-grow: 1; } #content-4e { height: 80px; }
Flex column container, flex-grow, tall content - elements in flex column containers expand to the height of their content when flex-grow is set to 1
#container-4f
#example-4f
#content-4f
HTML
<div id="container-4f"> <div id="example-4f"> <div id="content-4f" /> </div> </div>
CSS
#container-4f { display: flex; flex-direction: column; justify-content: normal; /* default */ height: 400px; } #example-4f { flex-grow: 1; } #content-4f { height: 420px; }

Grid containers

Grid container - elements in grid containers expand to the height of the container by default
#container-5a
#example-5a
#content-5a
HTML
<div id="container-5a"> <div id="example-5a"> <div id="content-5a" /> </div> </div>
CSS
#container-5a { align-items: normal; /* default - behaves like stretch in this case */ display: grid; height: 400px; } #example-5a {} #content-5a { height: 80px; }
Grid container, tall content - elements with tall content in grid containers expand to the height of the content
#container-5b
#example-5b
#content-5b
HTML
<div id="container-5b"> <div id="example-5b"> <div id="content-5b" /> </div> </div>
CSS
#container-5b { align-items: normal; /* default - behaves like stretch in this case */ display: grid; height: 400px; } #example-5b {} #content-5b { height: 420px; }
Grid container, tall content, height 100% - surprisingly, setting height to 100% does not contract an element's height to the height of its container. The element adjusts to the height of the content.
#container-5c
#example-5c
#content-5c
HTML
<div id="container-5c"> <div id="example-5c"> <div id="content-5c" /> </div> </div>
CSS
#container-5c { align-items: normal; /* default - behaves like stretch in this case */ display: grid; height: 400px; } #example-5c { height: 100%; } #content-5c { height: 420px; }
Grid container, tall content, overflow - elements with tall content in grid containers adjust to the height of the container if overflow is set to something other than visible
#container-5d
#example-5d
#content-5d
HTML
<div id="container-5d"> <div id="example-5d"> <div id="content-5d" /> </div> </div>
CSS
#container-5d { align-items: normal; /* default - behaves like stretch in this case */ display: grid; height: 400px; } #example-5d { overflow: auto; } #content-5d { height: 420px; }
Grid container, align-items - if a grid container sets align-items to something other than stretch (the default), then the element will adjust to the height of the content MDN docs on align-items.
#container-5e
#example-5e
#content-5e
HTML
<div id="container-5e"> <div id="example-5e"> <div id="content-5e" /> </div> </div>
CSS
#container-5e { align-items: start; display: grid; height: 400px; } #example-5e {} #content-5e { height: 80px; }
Grid container, align-items, tall content - elements with tall content in grid containers expand to the height of the content
#container-5f
#example-5f
#content-5f
HTML
<div id="container-5f"> <div id="example-5f"> <div id="content-5f" /> </div> </div>
CSS
#container-5f { align-items: start; display: grid; height: 400px; } #example-5f {} #content-5f { height: 420px; }
Grid container, align-items, tall content, overflow - elements with tall content in grid containers expand to the height of the content even if overflow is set
#container-5g
#example-5g
#content-5g
HTML
<div id="container-5g"> <div id="example-5g"> <div id="content-5g" /> </div> </div>
CSS
#container-5g { align-items: start; display: grid; height: 400px; } #example-5g { overflow: auto; } #content-5g { height: 420px; }

Absolute positioning

Absolutely positioned -
#container-6a
#example-6a
#content-6a
HTML
<div id="container-6a"> <div id="example-6a"> <div id="content-6a" /> </div> </div>
CSS
#container-6a { display: block; /* default */ position: relative; height: 400px; } #example-6a { position: absolute; } #content-6a { height: 80px; }
Absolutely positioned, tall content -
#container-6b
#example-6b
#content-6b
HTML
<div id="container-6b"> <div id="example-6b"> <div id="content-6b" /> </div> </div>
CSS
#container-6b { display: block; /* default */ position: relative; height: 400px; } #example-6b { position: absolute; } #content-6b { height: 420px; }
Absolutely positioned, height 100% -
#container-6c
#example-6c
#content-6c
HTML
<div id="container-6c"> <div id="example-6c"> <div id="content-6c" /> </div> </div>
CSS
#container-6c { display: block; /* default */ position: relative; height: 400px; } #example-6c { height: 100%; position: absolute; } #content-6c { height: 80px; }
Absolutely positioned, height 100%, tall content -
#container-6d
#example-6d
#content-6d
HTML
<div id="container-6d"> <div id="example-6d"> <div id="content-6d" /> </div> </div>
CSS
#container-6d { display: block; /* default */ position: relative; height: 400px; } #example-6d { height: 100%; position: absolute; } #content-6d { height: 420px; }

Floated elements

Floated elements - elements that set float adjust to the height of their content. MDN docs on float.
#container-7a
#example-7a
#content-7a
HTML
<div id="container-7a"> <div id="example-7a"> <div id="content-7a" /> </div> </div>
CSS
#container-7a { display: block; /* default */ height: 400px; } #example-7a { float: left; } #content-7a { height: 80px; }
Floated elements, tall content - elements that set float adjust to the height of their content
#container-7b
#example-7b
#content-7b
HTML
<div id="container-7b"> <div id="example-7b"> <div id="content-7b" /> </div> </div>
CSS
#container-7b { display: block; /* default */ height: 400px; } #example-7b { float: left; } #content-7b { height: 420px; }
Floated elements, height 100% -
#container-7c
#example-7c
#content-7c
HTML
<div id="container-7c"> <div id="example-7c"> <div id="content-7c" /> </div> </div>
CSS
#container-7c { display: block; /* default */ height: 400px; } #example-7c { float: left; height: 100%; } #content-7c { height: 80px; }
Floated elements, height 100%, tall content -
#container-7d
#example-7d
#content-7d
HTML
<div id="container-7d"> <div id="example-7d"> <div id="content-7d" /> </div> </div>
CSS
#container-7d { display: block; /* default */ height: 400px; } #example-7d { float: left; height: 100%; } #content-7d { height: 420px; }

Table cell containers

Table cell container - elements in table cell containers (td and th) adjust to the height of their content by default
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
#td-container-8a
#example-8a
#content-8a
HTML
<div id="td-container-8a"> <div id="example-8a"> <div id="content-8a" /> </div> </div>
CSS
#td-container-8a { /* td element */ vertical-align: top; } #example-8a {} #content-8a { height: 80px; }
Table cell container, height 100%, explicit table cell height - elements in table cell containers (td and th) expand to the height of their container if height is set to 100% and the table cell container has an explicit height set
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
#td-container-8b
#example-8b
#content-8b
HTML
<div id="td-container-8b"> <div id="example-8b"> <div id="content-8b" /> </div> </div>
CSS
#td-container-8b { /* td element */ vertical-align: top; height: 400px; } #example-8b { height: 100%; } #content-8b { height: 80px; }
Table cell container, height 100%, table cell height not set - surprisingly, setting height to 100% does not expand an element's height to the height of a table cell container (td and th) if a height for the table cell is not explicitly set. The element adjusts to the height of the content. Maybe the fifth bullet in the CSSWG's "Incomplete List of Mistakes in CSS" explains this: Percentage heights should be calculated against fill-available rather than being undefined in auto situations.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
#td-container-8c
#example-8c
#content-8c
HTML
<div id="td-container-8c"> <div id="example-8c"> <div id="content-8c" /> </div> </div>
CSS
#td-container-8c { /* td element */ vertical-align: top; } #example-8c { height: 100%; } #content-8c { height: 80px; }

Graphene and Relay example GraphQL pagination API and UI

beginning screenshot middle screenshot

This describes an example GraphQL API using Relay connections to power a discretely paged Amazon-like pagination UI. Connections are designed for infinite-scroll UIs, however Artsy Engineering developed a solution to use them with discretely paged UIs as described in their article: Effortless Pagination with GraphQL and Relay? Really!

I created a github repo with an implementation of the Artsy pagination API in Graphene, Django, and Python and a corresponding web app using Relay, React, and TypeScript.

For a vanilla Graphene Relay pagination API for an infinite scroll UI, see my basic example.

Graphene GraphQL backend

The Artsy pagination API augments the Relay Connection type with an additional pageCursors field that contains metadata used to implement the pagination UI. I ported their TypeScript code in artsy/metaphysics and relay-cursor-paging to Python using Graphene and Django.

Data is stored in a PostgreSQL database running in Docker. It is seeded with fish fixture data from ACNHAPI. Django is configured to log SQL queries to the console and show that SQL queries use COUNT, LIMIT, and OFFSET. Example:

SELECT COUNT(*) AS "__count" FROM "fishes_fish";
SELECT "fishes_fish"."id", "fishes_fish"."description", "fishes_fish"."icon_url", "fishes_fish"."name", "fishes_fish"."price" FROM "fishes_fish" ORDER BY "fishes_fish"."name" ASC LIMIT 5 OFFSET 5;
  • Install Python 3.8

  • Install packages, set up database, and run dev server

    $ git clone https://github.com/saltycrane/graphene-relay-pagination-example.git
    $ cd graphene-relay-pagination-example
    $ cd graphene-api
    $ python3 -m venv venv
    $ source venv/bin/activate
    $ pip install -r requirements.txt
    $ docker-compose up -d
    $ ./bin/resetdb
    $ ./manage.py migrate
    $ ./manage.py loaddata fishes
    $ ./manage.py runserver
    
  • Go to http://127.0.0.1:8000/graphql/ in the browser

  • Run the following query:

    {
      allFishes(first: 5, orderBy: "name") {
        pageCursors {
          previous {
            cursor
          }
          first {
            cursor
            page
          }
          around {
            cursor
            isCurrent
            page
          }
          last {
            cursor
            page
          }
          next {
            cursor
          }
        }
        edges {
          cursor
          node {
            name
          }
        }
      }
    }
    GraphiQL query screenshot
    graphiql screenshot
Generate a GraphQL schema
$ ./manage.py graphql_schema --schema pagination_ex_api.schema.schema --out ../schema.graphql

React Relay Next.js frontend

In addition to Relay, React, and TypeScript, the frontend UI uses relay-hooks, Next.js, and reactstrap. It takes advantage of Relay fragments and Next.js routing to store pagination state. Server-side rendering (SSR) is disabled because it's difficult to set up and isn't important for this example.

  • Install Node.js 14

  • Install packages and run dev server

    $ cd react-relay-webapp
    $ npm install
    $ npm run devserver
    
  • Go to http://127.0.0.1:3000 in the browser

My backend codeMy frontend codeExternal TypeScript codeExternal Python code

How to generate static HTML using React, TypeScript, and Node.js

React is used to build web applications that run JavaScript in a user's browser (client side rendering). It can also be used from a Node.js script to generate static HTML (static rendering). I used this technique to generate some CSS width experiments and a TypeScript Next.js cheat sheet. The example below shows how to use React with TypeScript and Node.js to generate static HTML. I also made an example project on github.

Install Node.js

  • $ brew install node

Set up project

  • Create a project directory and cd into it
    $ mkdir my-project
    $ cd my-project
  • Create a package.json file:
    {
      "scripts": {
        "render": "tsc && node dist/render.js"
      },
      "dependencies": {
        "@types/node": "^14.0.4",
        "@types/prettier": "^2.0.0",
        "@types/react": "^16.9.35",
        "@types/react-dom": "^16.9.8",
        "prettier": "^2.0.5",
        "react": "^16.13.1",
        "react-dom": "^16.13.1",
        "typescript": "^3.9.3"
      }
    }
  • Install React, TypeScript, and other packages
    $ npm install
  • Create a tsconfig.json file to configure TypeScript
    {
      "compilerOptions": {
        "baseUrl": ".",
        "esModuleInterop": true,
        "jsx": "react",
        "lib": ["dom", "es2019"],
        "outDir": "dist",
        "paths": {
          "*": ["src/*", "src/@types/*"]
        }
      },
      "include": ["src/*.tsx"]
    }

Create a script to generate a static HTML file

  • Create a directory, src and a file src/render.tsx:
    import * as fs from "fs";
    import prettier from "prettier";
    import React from "react";
    import ReactDOMServer from "react-dom/server";
    
    render();
    
    function render() {
      let html = ReactDOMServer.renderToStaticMarkup(<HelloWorldPage />);
      let htmlWDoc = "<!DOCTYPE html>" + html;
      let prettyHtml = prettier.format(htmlWDoc, { parser: "html" });
      let outputFile = "./output.html";
      fs.writeFileSync(outputFile, prettyHtml);
      console.log(`Wrote ${outputFile}`);
    }
    
    function HelloWorldPage() {
      return (
        <html lang="en">
          <head>
            <meta charSet="utf-8" />
            <title>Hello world</title>
          </head>
          <body>
            <h1>Hello world</h1>
          </body>
        </html>
      );
    }

Run the script and view the output

  • Run the script
    $ npm run render
  • Open the output.html file in the browser
    $ open output.html

How width is set in CSS

In CSS, I'm often confused about when an element adjusts to the width of its container and when it adjusts to the width of its content. I made some experiments to test the CSS behavior in various conditions. The script to generate the experiments is on github and the results are shown below. See also my companion page on CSS height.

By default, elements adjust to the width of their container when they are
By default, elements adjust to width of their content when they are
Some elements can be made to adjust to the width of their container by
Some elements can be made to adjust to the width of their content by
For some cases, setting overflow limits an element's width to the width of its container for wide content:
Some miscellaneous cases:

See also

Block containers

Block containers - block elements in block containers adjust to the width of their container by default
#container-1a
#example-1a
#content-1a
HTML
<div id="container-1a"> <div id="example-1a"> <div id="content-1a" /> </div> </div>
CSS
#container-1a { display: block; /* default */ width: 400px; } #example-1a { display: block; /* default */ } #content-1a { width: 120px; }
#container-1b
#example-1b
#content-1b
HTML
<div id="container-1b"> <div id="example-1b"> <div id="content-1b" /> </div> </div>
CSS
#container-1b { display: block; /* default */ width: 400px; } #example-1b { display: block; /* default */ } #content-1b { width: 420px; }
Block containers, fit-content - block elements in block containers adjust to the width of their container by default, but can use fit-content to adjust to the width of their content. MDN docs on width. NOTE: fit-content is not supported by IE.
#container-1c
#example-1c
#content-1c
HTML
<div id="container-1c"> <div id="example-1c"> <div id="content-1c" /> </div> </div>
CSS
#container-1c { display: block; /* default */ width: 400px; } #example-1c { display: block; /* default */ width: fit-content; } #content-1c { width: 120px; }
#container-1d
#example-1d
#content-1d
HTML
<div id="container-1d"> <div id="example-1d"> <div id="content-1d" /> </div> </div>
CSS
#container-1d { display: block; /* default */ width: 400px; } #example-1d { display: block; /* default */ width: fit-content; } #content-1d { width: 420px; }

Inline elements

Inline elements - inline elements adjust to the width of their content by default
#container-2a
#example-2a
#content-2a
HTML
<div id="container-2a"> <div id="example-2a"> <div id="content-2a" /> </div> </div>
CSS
#container-2a { display: block; /* default */ width: 400px; } #example-2a { display: inline-block; } #content-2a { width: 120px; }
#container-2b
#example-2b
#content-2b
HTML
<div id="container-2b"> <div id="example-2b"> <div id="content-2b" /> </div> </div>
CSS
#container-2b { display: block; /* default */ width: 400px; } #example-2b { display: inline-block; } #content-2b { width: 420px; }
Inline elements, width 100% - inline elements adjust to the width of their container if width is set to 100%
#container-2c
#example-2c
#content-2c
HTML
<div id="container-2c"> <div id="example-2c"> <div id="content-2c" /> </div> </div>
CSS
#container-2c { display: block; /* default */ width: 400px; } #example-2c { display: inline-block; width: 100%; } #content-2c { width: 120px; }
#container-2d
#example-2d
#content-2d
HTML
<div id="container-2d"> <div id="example-2d"> <div id="content-2d" /> </div> </div>
CSS
#container-2d { display: block; /* default */ width: 400px; } #example-2d { display: inline-block; width: 100%; } #content-2d { width: 420px; }

Flex row containers

Flex row container - elements in flex row containers adjust to the width of their content by default
#container-3a
#example-3a
#content-3a
HTML
<div id="container-3a"> <div id="example-3a"> <div id="content-3a" /> </div> </div>
CSS
#container-3a { display: flex; flex-direction: row; /* default */ justify-content: normal; /* default - behaves as flex-start in this case */ width: 400px; } #example-3a { display: block; /* default */ } #content-3a { width: 120px; }
#container-3b
#example-3b
#content-3b
HTML
<div id="container-3b"> <div id="example-3b"> <div id="content-3b" /> </div> </div>
CSS
#container-3b { display: flex; flex-direction: row; /* default */ justify-content: normal; /* default - behaves as flex-start in this case */ width: 400px; } #example-3b { display: block; /* default */ } #content-3b { width: 420px; }
Flex row container, width 100% - elements in flex row containers adjust to the width of their content by default. Setting width to 100%causes them to adjust to the width of their container.
#container-3c
#example-3c
#content-3c
HTML
<div id="container-3c"> <div id="example-3c"> <div id="content-3c" /> </div> </div>
CSS
#container-3c { display: flex; flex-direction: row; /* default */ justify-content: normal; /* default - behaves as flex-start in this case */ width: 400px; } #example-3c { display: block; /* default */ width: 100%; } #content-3c { width: 120px; }
#container-3d
#example-3d
#content-3d
HTML
<div id="container-3d"> <div id="example-3d"> <div id="content-3d" /> </div> </div>
CSS
#container-3d { display: flex; flex-direction: row; /* default */ justify-content: normal; /* default - behaves as flex-start in this case */ width: 400px; } #example-3d { display: block; /* default */ width: 100%; } #content-3d { width: 420px; }
Flex row container, flex-grow - elements in flex row containers adjust to the width of their content by default. Setting flex-grow to 1 causes them to expand to the width of their container if their content is narrower than their container. MDN docs on flex-grow.
#container-3e
#example-3e
#content-3e
HTML
<div id="container-3e"> <div id="example-3e"> <div id="content-3e" /> </div> </div>
CSS
#container-3e { display: flex; flex-direction: row; /* default */ justify-content: normal; /* default - behaves as flex-start in this case */ width: 400px; } #example-3e { display: block; /* default */ flex-grow: 1; } #content-3e { width: 120px; }
If content is wider than the container, the element expands to the width of their content.
#container-3f
#example-3f
#content-3f
HTML
<div id="container-3f"> <div id="example-3f"> <div id="content-3f" /> </div> </div>
CSS
#container-3f { display: flex; flex-direction: row; /* default */ justify-content: normal; /* default - behaves as flex-start in this case */ width: 400px; } #example-3f { display: block; /* default */ flex-grow: 1; } #content-3f { width: 420px; }
Flex row container, flex-grow, overflow - setting overflow to something other than visible limits the width of the element to the width of its container.
#container-3g
#example-3g
#content-3g
HTML
<div id="container-3g"> <div id="example-3g"> <div id="content-3g" /> </div> </div>
CSS
#container-3g { display: flex; flex-direction: row; /* default */ justify-content: normal; /* default - behaves as flex-start in this case */ width: 400px; } #example-3g { display: block; /* default */ flex-grow: 1; overflow: auto; } #content-3g { width: 420px; }

Flex column containers

Flex column container - elements in flex column containers adjust to the width of their container by default
#container-4a
#example-4a
#content-4a
HTML
<div id="container-4a"> <div id="example-4a"> <div id="content-4a" /> </div> </div>
CSS
#container-4a { display: flex; align-items: normal; /* default - behaves like stretch in this case */ flex-direction: column; width: 400px; } #example-4a { display: block; /* default */ } #content-4a { width: 120px; }
#container-4b
#example-4b
#content-4b
HTML
<div id="container-4b"> <div id="example-4b"> <div id="content-4b" /> </div> </div>
CSS
#container-4b { display: flex; align-items: normal; /* default - behaves like stretch in this case */ flex-direction: column; width: 400px; } #example-4b { display: block; /* default */ } #content-4b { width: 420px; }
Flex column container, align-items - elements in a flex column container adjust to the width of the container by default, but setting align-items to something other than stretch (e.g. flex-start), causes the element to adjust to the width of its content. MDN docs on align-items.
#container-4c
#example-4c
#content-4c
HTML
<div id="container-4c"> <div id="example-4c"> <div id="content-4c" /> </div> </div>
CSS
#container-4c { display: flex; align-items: flex-start; flex-direction: column; width: 400px; } #example-4c { display: block; /* default */ } #content-4c { width: 120px; }
#container-4d
#example-4d
#content-4d
HTML
<div id="container-4d"> <div id="example-4d"> <div id="content-4d" /> </div> </div>
CSS
#container-4d { display: flex; align-items: flex-start; flex-direction: column; width: 400px; } #example-4d { display: block; /* default */ } #content-4d { width: 420px; }

Grid containers

Grid container - elements in a grid container expand to the width of their container if their content is narrower than their container.
#container-5a
#example-5a
#content-5a
HTML
<div id="container-5a"> <div id="example-5a"> <div id="content-5a" /> </div> </div>
CSS
#container-5a { display: grid; justify-items: normal; /* default - behaves like stretch in this case */ width: 400px; } #example-5a { display: block; /* default */ } #content-5a { width: 120px; }
If the content is wider than the container, the element expands to the width of their content instead.
#container-5b
#example-5b
#content-5b
HTML
<div id="container-5b"> <div id="example-5b"> <div id="content-5b" /> </div> </div>
CSS
#container-5b { display: grid; justify-items: normal; /* default - behaves like stretch in this case */ width: 400px; } #example-5b { display: block; /* default */ } #content-5b { width: 420px; }
Grid container, width 100% - elements in a grid container expand to the width of their content for wide content even if width is set to 100%
#container-5c
#example-5c
#content-5c
HTML
<div id="container-5c"> <div id="example-5c"> <div id="content-5c" /> </div> </div>
CSS
#container-5c { display: grid; justify-items: normal; /* default - behaves like stretch in this case */ width: 400px; } #example-5c { display: block; /* default */ width: 100%; } #content-5c { width: 420px; }
Grid container, overflow - if content is wider than the continer, elements in a grid container adjust to the width of the container if overflow is set to something other than visible
#container-5d
#example-5d
#content-5d
HTML
<div id="container-5d"> <div id="example-5d"> <div id="content-5d" /> </div> </div>
CSS
#container-5d { display: grid; justify-items: normal; /* default - behaves like stretch in this case */ width: 400px; } #example-5d { display: block; /* default */ overflow: auto; } #content-5d { width: 420px; }
Grid container, justify-items - elements in grid containers with justify-items set to something other than stretch adjust to the width of their content. MDN docs on justify-items.
#container-5e
#example-5e
#content-5e
HTML
<div id="container-5e"> <div id="example-5e"> <div id="content-5e" /> </div> </div>
CSS
#container-5e { display: grid; justify-items: start; width: 400px; } #example-5e { display: block; /* default */ } #content-5e { width: 120px; }
#container-5f
#example-5f
#content-5f
HTML
<div id="container-5f"> <div id="example-5f"> <div id="content-5f" /> </div> </div>
CSS
#container-5f { display: grid; justify-items: start; width: 400px; } #example-5f { display: block; /* default */ } #content-5f { width: 420px; }
Grid container, justify-items, overflow - setting overflow has no effect when justify-items is set
#container-5g
#example-5g
#content-5g
HTML
<div id="container-5g"> <div id="example-5g"> <div id="content-5g" /> </div> </div>
CSS
#container-5g { display: grid; justify-items: start; width: 400px; } #example-5g { display: block; /* default */ overflow: auto; } #content-5g { width: 420px; }

Absolute positioning

Absolutely positioned - when position: absolute is used, the element adjusts to the width of its content. MDN docs on position: absolute.
#container-6a
#example-6a
#content-6a
HTML
<div id="container-6a"> <div id="example-6a"> <div id="content-6a" /> </div> </div>
CSS
#container-6a { display: block; /* default */ position: relative; width: 400px; } #example-6a { display: block; /* default */ position: absolute; } #content-6a { width: 120px; }
#container-6b
#example-6b
#content-6b
HTML
<div id="container-6b"> <div id="example-6b"> <div id="content-6b" /> </div> </div>
CSS
#container-6b { display: block; /* default */ position: relative; width: 400px; } #example-6b { display: block; /* default */ position: absolute; } #content-6b { width: 420px; }

Floated elements

Floated elements - elements that set float adjust to the width of their content. MDN docs on float.
#container-7a
#example-7a
#content-7a
HTML
<div id="container-7a"> <div id="example-7a"> <div id="content-7a" /> </div> </div>
CSS
#container-7a { display: block; /* default */ width: 400px; } #example-7a { display: block; /* default */ float: left; } #content-7a { width: 120px; }
#container-7b
#example-7b
#content-7b
HTML
<div id="container-7b"> <div id="example-7b"> <div id="content-7b" /> </div> </div>
CSS
#container-7b { display: block; /* default */ width: 400px; } #example-7b { display: block; /* default */ float: left; } #content-7b { width: 420px; }

COVID-19 and kids links

Data

Tools

Government and health authorities

Kids

How to remount a React component when a prop changes

To remount a component when a prop changes, use the React key attribute as described in this post on the React blog:

When a key changes, React will create a new component instance rather than update the current one.

The example below shows how the key attribute can be used. In Parent, the key attribute of <Child> is set to String(primaryExists). When primaryExists changes in the parent component, the child component unmounts and remounts allowing useState to re-initialize its state with the intial value passed in from props (!primaryExists). Play with it on CodeSandbox.

import React, { useState } from "react";

function Parent() {
  // Note: in my real code, primaryExists was derived from API data,
  // but I useState here to simplify the example
  const [primaryExists, setPrimaryExists] = useState(true);
  return (
    <div>
      <label>
        <input
          checked={primaryExists}
          onChange={() => setPrimaryExists(x => !x)}
          type="checkbox"
        />
        Primary exists
      </label>
      <Child key={String(primaryExists)} primaryExists={primaryExists} />
    </div>
  );
}

function Child({ primaryExists }) {
  const [isPrimary, setIsPrimary] = useState(!primaryExists);
  return (
    <div>
      <label>
        <input
          checked={isPrimary}
          onChange={() => setIsPrimary(x => !x)}
          type="checkbox"
        />
        Is primary
      </label>
    </div>
  );
} 

Additional information

Sebastian Markbåge, from the React core team, reinforced the usage of keys on single items:

...BUT it has given the misinterpretation that only lists need keys. You need keys on single items too. In a master/detail, the detail should have a key.

React hook to fit text in a div

This is a React hook that iteratively adjusts the font size so that text will fit in a div.

  • checks if text is overflowing by using scrollHeight and offsetHeight from https://stackoverflow.com/a/10017343/101911
  • uses binary search; makes a maximum of 5 adjustments with a resolution of 5% font size from 20-100%

The code is also in a github repo: use-fit-text

const useFitText = () => {
  const MIN_FONT_SIZE = 20;
  const MAX_FONT_SIZE = 100;
  const RESOLUTION = 5;

  const ref = useRef(null);

  const [state, setState] = useState({
    fontSize: MAX_FONT_SIZE,
    fontSizePrev: MIN_FONT_SIZE,
    fontSizeMax: MAX_FONT_SIZE,
    fontSizeMin: MIN_FONT_SIZE,
  });
  const { fontSize, fontSizeMax, fontSizeMin, fontSizePrev } = state;

  useEffect(() => {
    const isDone = Math.abs(fontSize - fontSizePrev) <= RESOLUTION;
    const isOverflow =
      !!ref.current &&
      (ref.current.scrollHeight > ref.current.offsetHeight ||
        ref.current.scrollWidth > ref.current.offsetWidth);
    const isAsc = fontSize > fontSizePrev;

    // return if the font size has been adjusted "enough" (change within RESOLUTION)
    // reduce font size by one increment if it's overflowing
    if (isDone) {
      if (isOverflow) {
        const fontSizeNew =
          fontSizePrev < fontSize
            ? fontSizePrev
            : fontSize - (fontSizePrev - fontSize);
        setState({
          fontSize: fontSizeNew,
          fontSizeMax,
          fontSizeMin,
          fontSizePrev,
        });
      }
      return;
    }

    // binary search to adjust font size
    let delta;
    let newMax = fontSizeMax;
    let newMin = fontSizeMin;
    if (isOverflow) {
      delta = isAsc ? fontSizePrev - fontSize : fontSizeMin - fontSize;
      newMax = Math.min(fontSizeMax, fontSize);
    } else {
      delta = isAsc ? fontSizeMax - fontSize : fontSizePrev - fontSize;
      newMin = Math.max(fontSizeMin, fontSize);
    }
    setState({
      fontSize: fontSize + delta / 2,
      fontSizeMax: newMax,
      fontSizeMin: newMin,
      fontSizePrev: fontSize,
    });
  }, [fontSize, fontSizeMax, fontSizeMin, fontSizePrev, ref]);

  return { fontSize: `${fontSize}%`, ref };
};

Example usage

import React from "react";
import useFitText from "use-fit-text";

const Example = () => {
  const { fontSize, ref } = useFitText();

  return (
    <div ref={ref} style={{ fontSize, height: 40, width: 100 }}>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    </div>
  );
}