Skip to content
This repository has been archived by the owner on Jan 10, 2024. It is now read-only.

Latest commit

 

History

History
325 lines (225 loc) · 14.5 KB

README.md

File metadata and controls

325 lines (225 loc) · 14.5 KB

GitHub Blocks Template

📣 Use this repository as a starter template if you're a GitHub user interested in building your own custom Blocks!

This project contains React components written in Typescript and bundled with Vite. Read on to learn how to build Blocks with it!

Or watch the video:

Blocks.tutorial.mp4

What are blocks?

Blocks allow you to extend GitHub's interface in some pretty powerful ways! It could be as simple as a custom renderer for files or folders in your repository, and it can be as flexible as a full interface for editing content.

You can view any repository in the experimental Blocks interface at blocks.githubnext.com.

🚨 Blocks is currently a technical preview! It may have bugs or issues, and should not be used for anything mission-critical.

Check out some blocks examples to get a better sense for what you can do with blocks, and see how some are authored.

Under the hood

A Block is a React component; it receives a fixed set of props and returns JSX. The Blocks web application which knows how to find and render Blocks provides props describing the content to render, and also hooks that a Block can use to update content or metadata, or call the GitHub API.

There are two kinds of of Blocks: File Blocks and Folder Blocks. Their API is mostly the same, except that File Blocks receive file content and Folder Blocks receive folder content.

How are blocks developed and shared?

You develop blocks in regular GitHub repos. You can click here to use this repository as a template to develop a new block!

💖 There's no deploy step needed! Just push your block to a GitHub repo in order to use it, anywhere.

Once developed, you can use a block on any repository by picking it from the block picker:

Screenshot showing the blocks interface with the block picker open and the mouse cursor hovering over the Markdown block

If your block is not in the dropdown list, you can paste the URL of the repo containing your block into the search field at the top of the block picker.

A block can be used by anyone that can see the repository where that block is developed. If the repo is private, it is usable by any collaborator that has read permissions (or better) on that repo.

Blocks API

These are the props that are supplied to every block when it is mounted. These are fixed: you can't change the props, and you can't control the information passed in each prop as they are automatically determined based on the underlying repo and content.

Props received by all blocks

interface CommonBlockProps {
  // metadata from `blocks.config.json`
  block: {
    id: string;
    type: string;
    title: string;
    description: string;
    entry: string;
    matches?: string[];
  };

  // context about the repo, file / folder, and version
  context: {
    path: string;
    file: string; // for File Blocks
    folder: string; // for Folder Blocks
    repo: string;
    owner: string;
    sha: string;
  };

  // arbitrary metadata for Blocks to store configuration etc.
  metadata: any;
  onUpdateMetadata: (newMetadata: any) => void;

  // callback to call any GET endpoint in the GitHub API:
  // https://docs.github.com/en/rest/overview/endpoints-available-for-github-apps
  // e.g. `/repos/{owner}/{repo}/contributors`
  onRequestGitHubData: (
    path: string,
    params: Record<string, any>
  ) => Promise<any>;

  onNavigateToPath: (path: string) => void;

  // returns a list of repos containing blocks
  onRequestBlocksRepos: (params?: {
    path?: string; // restrict to blocks that match the path (see `matches` in `blocks.config.json`)
    searchTerm?: string; // restrict to blocks matching the search term
    repoUrl?: string; // restrict to blocks in the repo
    type?: "file" | "folder"; // restrict to blocks of this type
  }) => Promise<BlocksRepo[]>;

  // a React component that renders a block viewing a file or folder
  BlockComponent: ({
    block,
    context,
  }: {
    // owner, repo, and id identifying the block
    block: { owner: string; repo: string; id: string };

    // optional context specifying the file or folder to view;
    // omitted context or omitted fields default to the current file or folder
    context?:
      owner?: string;
      repo?: string;
      path?: string;
      sha?: string;
    };
  }) => JSX.Element;
}

Props received by file blocks

interface FileBlockProps {
  // the file content
  content: string;
  // callback to update the file content
  onUpdateContent: (newContent: string) => void;

  // the original file content
  originalContent: string;

  // whether or not the user can edit the content
  isEditable: boolean;
}

Props received by folder blocks

interface FolderBlockProps {
  // flat list of files and folders in the tree
  tree: {
    path?: string;
    mode?: string;
    type?: string;
    sha?: string;
    size?: number;
    url?: string;
  }[];
}

Metadata

Blocks can store freeform metadata about themselves in the repository where they are used (as opposed to the repo containing the code that powers the block). Metadata is unique per file/folder per Block, and committed to the .github/blocks/ folder.

Often, metadata is used to store configuration data, so that you can customize how a block works by default for a specific file or folder in your repository.

To store or update metadata, call the onUpdateMetadata hook which is supplied to your block as a prop. The user will be prompted to accept the change and commit the new metadata to the repository.

When you next view the specific content with that block, the metadata prop will contain the committed metadata.

Updating content

Blocks are not just for viewing content! They can also offer custom interfaces for editing content.

There are three props which facilitate editing content in blocks:

  • originalContent: contains the original contents of the file. This is useful if you want to be able to display a diff of changes made, but not yet committed.
  • isEditable: a flag indicating whether the current user has permission to edit the file. If it is false, the block should disable editing functionality, if present.
  • onUpdateContent: a hook that can be called to update content programmatically. Users can also commit changes using the Save button in the Blocks interface next to the block picker. Whether triggered via the hook or the save button, the Blocks application will display a dialog containing a diff to the user, and ask for their permission to make a commit. Commits recorded as being authored by the user that made (and approved) the request.

Nesting

Blocks can be nested inside other blocks using BlockComponent; you can retrieve a list of available blocks using onRequestBlocksRepos. See the edit-and-preview block for an example of their use.

Caveats and callouts:

  • You can import CSS files or split your block into multiple TypeScript files, and everything will be bundled with your block.
  • You can use any third-party dependencies from NPM; just add them with yarn add and import them as usual, and they'll be bundled with your block.
  • The GitHub Primer React components are already included as a dependency
  • Your Block entry file must have the Block component as its default export so the GitHub Blocks application can find it.

Relevant repos

Blocks examples

Example blocks that we've built to showcase the API.

Blocks utility library

This template project includes a dependency on the GitHub Blocks utility library, which contains types and functions to make it easier to build Blocks. You can import them from @githubnext/blocks; see the repository page for more detail.

Setup

This repo is a template! To use it just click on the "Use this template" button on the top right to set it up for your use.

"Use this template" button

The button will take you to a screen to specify what you want to name your own repo.

"Create a new repository from blocks-template" screen

Step 1. Develop locally

git clone <repo URL>
yarn # install dependencies
yarn start # start the dev server

A development server should now be running on localhost:4000.

Step 2. View your development server within the Blocks app

When you visit localhost:4000 in your browser, you'll be redirected to the Blocks app with your locally-developed blocks embedded in it. You can see your blocks by clicking on the block picker toward the top of the page (they're shown at the top in blue with a plug icon):

Block picker

This starter project has one example folder block and one example file block. Try choosing one of the example blocks in the block picker to see it in action. When you make changes to the block code it will be hot-reloaded in the app.

If you don't see your blocks, make sure that:

  • your development server is running
  • there's a devServer query parameter in the URL pointing to your development server like this

Step 3. Build your custom Blocks with the GitHub Blocks API

To create your own custom blocks you need to do two things:

Step 3.1: Define your custom block

If you open up /blocks.config.json, you'll notice an array of block objects with the definitions for each custom block. Block objects follow this interface:

interface BlockDefinition {
  type: "file" | "folder";
  id: string;
  title: string;
  description: string;
  entry: string;
  matches?: string[];
}

You have to define these properties for your own custom Block.

From top to bottom:

  • type determines whether this block applies to files or folders.
  • id is an identifier string for this block, like "code-block". GitHub Blocks uses this to determine which block to render; it needs to be unique within your project.
  • title and description control how your block appears within the GitHub Blocks application.
  • entry is the path (relative to the project root) to the block's entry point, like "blocks/example-file-block/index.tsx".
  • matches is an array of globs describing filenames for which GitHub Blocks should show this block as an option, like ["*.json"] for a block that handles JSON files, or ["*"] for one that handles any file. (The glob syntax follows https://github.com/micromatch/picomatch)

Note that the /blocks.config.json file is not hot-reloaded, so you'll need to refresh the app to pick up changes.

Step 3.2: Code your Block

Your code goes in the blocks/ directory. Here you'll find the two example blocks as a starting point. You can modify them, rename them (don't forget to update blocks.config.json if you change the path or file names), or just delete them.

Step 4. Deploy your Blocks to production

To make your custom block accessible to the Blocks app without your development server running, there are a few steps you need to take:

Step 4.1: Commit your code and push a new tag

This template includes a GitHub Action to build a production version of your block. All you need to do to kick off the build process is commit and push your code changes, then create a new tag:

git commit -m'made an awesome block'
git push
git tag 0.9.0 # Create a new tag with your own version number
git push --tags # Push the tag to GitHub

The action runs yarn build, which can be more picky than the development server. You might want to run yarn build manually to ensure the action will succeed, or to debug an issue if the action fails.

Step 4.2: Wait for the build process to finish

Look under Actions for your repo to see that your build has finished. The latest successful build should now be accessible in the GitHub Blocks application.

From the repository settings page, make sure that your workflow has Read and write permissions or the action will fail with a 403 error.

"Workflow permissions" setting

Successful build action screen

Tagged release

Step 4.3: Test your production block in the Blocks app

Once the action has completed, visit [https://blocks.githubnext.com/] (with no devServer parameter in the URL), then paste your repo URL (https://github.com/{owner}/{repo}) into the block picker search bar. This will load the production version of your block, without using your development server.

Everything should work the same as before!

Step 4.4: Add the topic github-blocks to your repo (optional)

If you want your blocks to show up in the block picker in GitHub Blocks, you need to tag this repository with the topic github-blocks so the application can find it.

Repository tagged with "github-blocks"

This step is optional! If you aren't ready to share your block with others, don't tag the repo. Your blocks won't show up in the block picker by default, but you can still paste the repo URL (https://github.com/{owner}/{repo}) into the search box at the top of the block picker to search blocks in the repo. If your repo is private, only people with access to the repo can see and use your blocks.