2.4.0 • Published 1 year ago

airview-cms v2.4.0

Weekly downloads
-
License
ISC
Repository
github
Last release
1 year ago

Airview CMS

Airview CMS is a React based client side content management system framework to allow CRUD workflow for GitHub persisted Markdown documentation.

The concept

For the end user, Airview CMS is:

  1. A WYSIWYG editing experience, which allows the creation and editing of Markdown documentation (body and frontmatter)
  2. A simplified interface to allow the persistence of markdown content using a gitflow workflow (commit, branch and checkout)

For the engineer, Airview CMS is:

  1. A flexible React CMS, it can suit the needs of any project via a provided configuration
  2. A set of React hooks, these simplify the request of CMS data from the remote repository
  3. React components for inline, real-time WYSIWYG markdown editing

Collections

Airview CMS groups markdown content into collections; a collection defines the shape of the data for a specific markdown file (its frontmatter), think of collections as a bucket of similar material. As an example, consider we we're building a coffee blog; we could define a collection for our coffee, another for coffee brew types (aeropress, filter, mocka pot etc) and maybe a collection for coffee news.

Entries

Entries are the markdown files that are assigned to a specific collection; the AirviewCMS API will break an entry into two parts: the meta, which is the markdown frontmatter and the body, which is the main body of the markdown file.

Installation

The package can be installed via npm, within your working directory run:

npm i airview-cms

Note: A requirement for the project is node.js LTS, currently greater that version 16.15.1 but less than version 17.0.0

Setup and Configuration

To begin using Airview CMS, you must wrap you application with the AirviewCMS component and pass a config file as a prop.

Mounting the AirviewCMS component

The AirviewCMS component should remain mounted for the entire lifecyle of your app; it is recommended you place this component at the root of your application tree.

Airview CMS provides the context for the various utility hooks, which can be called from any component that is a direct child of the AirviewCMS component.

Example:

import React from "react";
import { AirviewCMS } from "airview-cms";

function App() {
  return <AirviewCMS config={config}>{/* children */}</AirviewCMS>;
}

The AirviewCMS component config

The AirviewCMS component requires a configuration object, this is used to personalise the gitflow workflow and define your collections frontmatter. You may find it helpful to refer to the airview-demo config for an example of how this works.

Base Url

This setting is optional The baseUrl is the root url at which the cms api is located.

The default is /api. It should be a string value of a path relative to the site root, or a full host/port/path combination. e.g /api/cms or https://myhost.com:1234/api

Base Branch

This setting is required

The baseBranch allows the CMS to correctly read data from your GitHub repo when in non-edit mode.

The key should be a string value, set to the working branch of your GitHub repo.

Example:

const config = {
  baseBranch: "main",
};

Collections

This setting is required

The collections object allows the CMS to present the correct UI fields to a user for creation and editing of markdown frontmatter.

The key should be an object of keyed collections, set to the desired collection name. Each collection should contain two values:

  • label: A human readable string representation of the collection name, used within the CMS UI
  • fields: An array of available CMS fields, used to dynamically generate a UI to capture data for markdown frontmatter using widgets. See below for a detailed descrption
  • hidden: An optional boolean value to hide collections from the content creator (useful for binding content to single stand-alone pages, e.g an index page)

Example:

const config = {
  collections: {
    coffee: {
      label: "Coffee",
      fields: [
        {
          label: "Price",
          name: "price",
          widget: "string",
        },
        {
          label: "In Stock",
          name: "in_stock",
          defaultValue: false,
          widget: "boolean",
        },
      ],
    },
  },
};
Collection Fields

This setting is required

The collection fields key is used to define the frontmatter you want to capture from a user, if no frontmatter is required for the collection, pass an empty array.

The key should be an object containing the following values:

  • label: A human readable label for the frontmatter field
  • name: A unique value, used to set the YAML frontmatter key
  • widget: A string value equal to the required airview-cms widget for the field. See below for detailed information on available widgets.

Note: Additional key values for the field will be required based on the chosen widget, for example defaultValue when using the boolean widget

Field Widgets

A number of widgets are availble for specific data capture requirements:

String Widget

The string widget is used to capture a string input from a user (not multiline input). You can include the string widget within your collection field by passing string as the value to the widget key.

  • Name: string
  • UI: Text input
  • Data type: string
  • Options:
    • label: string - A human readable label for the UI - required
    • placeholder: string - An optional placeholder value for when the text area has no input value from a user

Example:

{
  label: "Price",
  name: "price",
  widget: "string",
  placeholder: "Enter a price for this product"
}

Boolean Widget

The boolean widget is used to capture a boolean value from a user. You can include the boolean widget within your collection field by passing boolean as the value to the widget key.

  • Name: boolean
  • UI: Checkbox input
  • Data type: boolean
  • Options:
    • label: string - A human readable label for the UI - required
    • defaultValue: boolean - Sets the default value for the UI - required

Example:

{
  label: "In Stock",
  name: "in_stock",
  defaultValue: false,
  widget: "boolean",
}

Date Widget

The date widget is used to capture a date from a user. You can include the date widget within your collection field by passing date as the value to the widget key.

  • Name: date
  • UI: Date select input
  • Data type: ISO date string
  • Options:
    • label: string - A human readable label for the UI - required
    • defaultValue: ISO date string - Sets the default value for the UI - required
    • format: string - Formats the display of the date to the user, pass a valid Day.js format string. Defaults to DD/MM/YYYY
    • minDate: ISO date string - Sets an optional minimum selectable date.
    • maxDate: ISO date string - Sets an optional maximum selectable date.

Example:

{
  label: "Available From",
  name: "available_from",
  widget: "date",
  minDate: "2022-05-01T00:00:00Z",
  maxDate: "2022-05-31T00:00:00Z",
  defaultValue: "2022-05-01T00:00:00Z",
  format: "DD/MM/YY"
}

Entry Select Widget

The entry select widget is used to select an entry from a specific collection. You can include the entry select widget within your collection field by passing entrySelect as the value to the widget key.

  • Name: entrySelect
  • UI: Select Picker UI
  • Data type: string (entryID)
  • Options:
    • label: string - A human readable label for the UI - required
    • collection: string - The specific collection key, ued to populate the select picker with the relevant entries for the passed collection - required

Example:

{
  label: "Related Coffee",
  name: "related_coffee",
  widget: "entrySelect",
  collection: "coffee"
}

CMS Context

The core functionality of the CMS is to provide data for a specific markdown document (body and frontmatter). You can choose to get a static representation of this data, or a contextual version. Static data is read only, contextual data allows both read and write.

The recommended approach to writing your app is to identify the current page your user is requesting and set the cms context equal to the entry ID and markdown file for that page. Once set, the CMS will fetch the data for the given entry/path and setup the CMS to allow editing of the frontmatter and markdown body.

Airview CMS exports a utility hook to allow you to set the CMS context, useSetCmsContext. The hook accepts an object with the properties entryId, which is collection name/entry name, and path, which is the relative path of the file to fetch. The hook will return frontmatter for the markdown file, a set of booleans relating to the fetch state of the data and an error object (if applicable).

Signature:

  • Hook Name: useSetCmsContext
  • Arguments: object - details of entry data to fetch. {entryId, path}
  • Returns: object
    • data - the frontmatter for the markdown file - object
    • isUninitialized - Request to fetch data has not started yet - boolean
    • isLoading - Request to fetch data is in flight for the first time - boolean
    • isFetching - Request to fetch data is in flight, but might have data from a previous request - boolean
    • isSuccess - Request to fetch data is complete and was a success - boolean
    • isError - Request to fetch data is complete and was unsuccessful - boolean
    • error - Error result, if applicable - unknown

Routing

AirviewCMS uses React Router v6, it is required that you use this package for your apps routing too.

AirviewCMS supports the instantiation of a route / page using a URL query parameter, the key should be branch with a value equal to the branch you want to view, for example https://my-app.com?branch=main

Additional hooks

useGetAllEntriesMeta

To request frontmatter for all markdown entries call useGetAllEntriesMeta. The hook will return an array of entries frontmatter and various status booleans.

Signature:

  • Hook Name: useGetAllEntriesMeta
  • Arguments: none
  • Returns: object
    • data - the frontmatter for all markdown entries - array of objects
    • isUninitialized - Request to fetch data has not started yet - boolean
    • isLoading - Request to fetch data is in flight for the first time - boolean
    • isFetching - Request to fetch data is in flight, but might have data from a previous request - boolean
    • isSuccess - Request to fetch data is complete and was a success - boolean
    • isError - Request to fetch data is complete and was unsuccessful - boolean
    • error - Error result, if applicable - unknown

useGetCollectionEntries

To request frontmatter for all markdown entries of a specific collection call useGetCollectionEntries, passing the collection name (key in config) as the argument. The hook will return an array of entries frontmatter and various booleans relating the the status of the data fetching process.

Signature:

  • Hook Name: useGetCollectionEntries
  • Arguments: collectionName - string
  • Returns: object
    • data - the frontmatter for all markdown entries of the passed collection - array of objects
    • isUninitialized - Request to fetch data has not started yet - boolean
    • isLoading - Request to fetch data is in flight for the first time - boolean
    • isFetching - Request to fetch data is in flight, but might have data from a previous request - boolean
    • isSuccess - Request to fetch data is complete and was a success - boolean
    • isError - Request to fetch data is complete and was unsuccessful - boolean
    • error - Error result, if applicable - unknown

useGetEntryMeta

To request frontmatter for a single entry, call useGetEntryMeta, passing the entry ID as the argument (which is collection name/entry name). The hook will return an object of frontmatter and various booleans relating the the status of the data fetching process.

Signature:

  • Hook Name: useGetEntryMeta
  • Arguments: entryId - string
  • Returns: object
    • data - the frontmatter for all markdown entry siblings of the passed entryID - object
    • isUninitialized - Request to fetch data has not started yet - boolean
    • isLoading - Request to fetch data is in flight for the first time - boolean
    • isFetching - Request to fetch data is in flight, but might have data from a previous request - boolean
    • isSuccess - Request to fetch data is complete and was a success - boolean
    • isError - Request to fetch data is complete and was unsuccessful - boolean
    • error - Error result, if applicable - unknown

useGetEntry

To request frontmatter and body content for a single entry, call useGetEntry, passing an object with entryId (which is collection name/entry name) and relative path to the file as the argument. The hook will return an object containing the content of the file and various booleans relating the the status of the data fetching process.

Note: This is a static representation of this data, it will not reflect CMS edits made in real time, use useSetCmsContext and the MarkdownEditor if you need this functionality.

Signature:

  • Hook Name: useGetEntry
  • Arguments: object - details of entry data to fetch. {entryId, path}
  • Returns: object
    • data - the frontmatter any body content for the passed entryID - array of objects
    • isUninitialized - Request to fetch data has not started yet - boolean
    • isLoading - Request to fetch data is in flight for the first time - boolean
    • isFetching - Request to fetch data is in flight, but might have data from a previous request - boolean
    • isSuccess - Request to fetch data is complete and was a success - boolean
    • isError - Request to fetch data is complete and was unsuccessful - boolean
    • error - Error result, if applicable - unknown

useCMSViewportOffset

Airview CMS presents a persistant toolbar as part of the UI; this topbar is positioned outside of the document flow and will require an offset in your UI to prevent content being obscured from view.

A hook is available that will return an integer value with the required toolbar offset.

Signature:

  • Hook Name: useCMSViewportOffset
  • Arguments: none
  • Returns: integer - The pixel value of the CMS topbar offset

Components

MarkdownEditor

AirviewCMS exports a MarkdownEditor component, this provides realtime editing of markdown content for the current CMS Context.

Example:

import React from "react";
import { useSetCmsContext, MarkdownEditor } from "airview-cms";

function EditableMarkdown() {
  // Set our CMS context, we don't require the return values in this example
  useSetCmsContext("coffee/house-blend");

  // MarkdownEditor component will parse the markdown file within the set context and present a WYSIWYG editing interface. Note: This will be read only for protected branches and when not in edit mode.
  return <MarkdownEditor />;
}

Contributing to the package

Please see our documentation here for guidance

Issues, bugs and feature requests

Please see our documentation here for guidance

2.4.0

1 year ago

2.2.3

1 year ago

2.3.8

1 year ago

2.3.7

1 year ago

2.3.9

1 year ago

0.1.1

1 year ago

2.3.0

1 year ago

2.3.2

1 year ago

2.3.1

1 year ago

2.3.4

1 year ago

2.3.3

1 year ago

2.3.6

1 year ago

2.3.5

1 year ago

2.3.17

1 year ago

2.3.16

1 year ago

0.2.1

1 year ago

2.3.13

1 year ago

2.3.12

1 year ago

2.3.15

1 year ago

2.3.14

1 year ago

2.3.11

1 year ago

2.3.10

1 year ago

0.2.3

1 year ago

0.2.2

1 year ago

0.0.53

1 year ago

0.1.0

1 year ago

0.0.52

1 year ago

0.0.51

1 year ago

0.0.50

1 year ago

0.0.49

2 years ago

0.0.48

2 years ago

0.0.47

2 years ago

0.0.46

2 years ago

0.0.45

2 years ago

0.0.44

2 years ago

0.0.43

2 years ago

0.0.42

2 years ago

0.0.41

2 years ago

0.0.40

2 years ago

0.0.39

2 years ago

0.0.38

2 years ago

0.0.37

2 years ago

0.0.36

2 years ago

0.0.35

2 years ago

0.0.34

2 years ago

0.0.33

2 years ago

0.0.32

2 years ago

0.0.31

2 years ago

0.0.30

2 years ago

0.0.29

2 years ago

0.0.28

2 years ago

0.0.27

2 years ago

0.0.26

2 years ago

0.0.25

2 years ago

0.0.24

2 years ago

0.0.23

2 years ago

0.0.22

2 years ago

0.0.21

2 years ago

0.0.20

2 years ago

0.0.19

2 years ago

0.0.18

2 years ago

0.0.17

2 years ago

0.0.16

2 years ago

0.0.15

2 years ago

0.0.14

2 years ago

0.0.13

2 years ago

0.0.12

2 years ago

0.0.11

2 years ago

0.0.10

2 years ago

0.0.8

2 years ago

0.0.7

2 years ago

0.0.6

2 years ago

0.0.5

2 years ago

0.0.4

2 years ago

0.0.3

2 years ago

0.0.2

2 years ago