npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-tinacms-contentful-bm

v0.1.9

Published

This package provides helpers for setting up TinaCMS to use the Contentful API, with Contentful authentication.

Downloads

6

Readme

react-tinacms-contentful

This package provides helpers for setting up TinaCMS to use the Contentful API, with Contentful authentication.

Installation

npm install --save react-tinacms-contentful

or

yarn add react-tinacms-contentful

Getting Started

Register the ContentfulClient

We will want to use the contentfulClient to load/save our content using the Contentful API. Let's add it as an API plugin.

import { TinaCMS } from 'tinacms';
import { contentfulClient } from 'react-tinacms-contentful';

const REPO_FULL_NAME = process.env.REPO_FULL_NAME; // e.g: tinacms/tinacms.org

const redirectUrl = process.env.CONTENTFUL_AUTH_REDIRECT_URL; //https://localhost:3000/contentful/authorizing

const cms = new TinaCMS({
  apis: {
    contentful: new ContentfulClient({
      clientId: process.env.CONTENTFUL_CLIENT_ID,
      redirectUrl: redirectUrl,
      space: process.env.CONTENTFUL_SPACE_ID,
      proxy: '/api/proxy',
    }),
  },
  // ... any other tina config
});

Managing "edit-mode" state

Add the root TinacmsContentfulProvider component to our main layout. We will supply it with handlers for authenticating and entering/exiting edit-mode. In this case, we will hit our /api server functions.

// YourLayout.ts
import { TinacmsContentfulProvider } from 'react-tinacms-contentful';

const enterEditMode = () => {
  return fetch(`/api/preview`).then(() => {
    window.location.href = window.location.pathname; //refresh in edit-mode
  });
};
const exitEditMode = () => {
  return fetch(`/api/reset-preview`).then(() => {
    window.location.reload();
  });
};

const YourLayout = ({ editMode, error, children }) => {
  return (
    <TinaContentfulProvider
      editMode={pageProps.preview}
      enterEditMode={enterEditMode}
      exitEditMode={exitEditMode}
    >
      {children}
    </TinaContentfulProvider>
  );
};

Auth Redirects

We will also need a few Contentful Specific pages to redirect the user to while authenticating with Contentful

//pages/contentful/authorizing.tsx
// Our Contentful app redirects back to this page with auth token
import { useContentfulAuthRedirect } from 'react-tinacms-contentful';

export default function Authorizing() {
  // Let the main app know, that we receieved an auth token from the Contentful redirect
  useContentfulAuthRedirect();
  return <h2>Authorizing with Contentful, Please wait...</h2>;
}

Entering / Exiting "edit-mode"

We will need a way to enter/exit mode from our site. Let's create an "Edit Link" button. Ours will take isEditing as a parameter.

If you are using Next.js's preview-mode for the editing environment, this isEditing value might get sent from your getStaticProps function.

//...EditLink.tsx
import { useContentfulEditing } from 'react-tinacms-contentful';

export interface EditLinkProps {
  isEditing: boolean;
}

export const EditLink = ({ isEditing }: EditLinkProps) => {
  const contentful = useContentfulEditing();

  return (
    <button
      onClick={isEditing ? contentful.exitEditMode : contentful.enterEditMode}
    >
      {isEditing ? 'Exit Edit Mode' : 'Edit This Site'}
    </button>
  );
};

Contentful Oauth App:

In Contentful, you will need to create an OAuth app through Contentful's settings.

Once you have done so, you can use the Client ID value to create the ContentfulClient.

NextJS implementation:

TODO - this should eventually be moved to its own package

This section provides helpers for managing the github auth token for requests, as well as providing helpers for loading content from the Github API.

Getting Started

Any functions in the pages/api directory are are mapped to /api/* endpoints.

preview

Helper for creating a preview server function, to kick off NextJS preview move with your auth token

export const previewHandler = (req, res) => {
  const previewData = {
    contentful_auth_token: req.cookies["contentful_auth_token"],
  };
  res.setPreviewData(previewData);
  res.status(200).end();
};

export default previewHandler;

proxy

Proxies requests to Contentful API, attaching the appropriate access token in the process

const axios = require("axios");

const proxy = (req: any, res: any) => {
  const { headers, ...data } = JSON.parse(req.body);

  const authToken = data.url.includes("api.contentful.com")
    ? process.env.CONTENTFUL_MANAGEMENT_ACCESS_TOKEN
    : process.env.CONTENTFUL_PREVIEW_API_TOKEN;
  axios({
    ...data,
    headers: {
      ...headers,
      Authorization: "Bearer " + authToken,
    },
  })
    .then((resp: any) => {
      res.status(resp.status).json(resp.data);
    })
    .catch((err: any) => {
      res.status(err.response.status).json(err.response.data);
    });
};

export default proxy

Note that these CONTENTFUL_MANAGEMENT_ACCESS_TOKEN & CONTENTFUL_PREVIEW_API_TOKEN will need to be generated separetly through the Contentful UI.

Form implementation

If your site uses links, we recommend using the ContentfulClient's fetchFullEntry function. It automatically resolves the nested links and attaches a version number.

import { setCachedFormData, } from "@tinacms/react-tinacms-contentful";

  const loadInitialValues = async () => {
    const entry = await cms.api.contentful.fetchFullEntry(page.sys.id);

    setCachedFormData(id, {
      version: entry.sys.version,
    });

    return entry.fields;
  }

You can see that the file's version gets saved to local storage. We'll use that value below when we save the content.

  const formConfig = {
    id: page.fields.slug,
    label: page.fields.title,
    loadInitialValues,
    onSubmit: async (values) => {

      //Let's map the data back to the format that the Management API expects
      const localizedValues = mapLocalizedValues(values, "en-US");

      cms.api.contentful
        .save(id, getCachedFormData(id).version, contentType, localizedValues)
        .then(function (response) {
          return response.json();
        })
        .then((data) => {
          setCachedFormData(id, {
            version: data.sys.version,
          });
        });
    },
    fields: [
      // ...
    ],
  };

  const [pageData, form] = useForm(formConfig);

  usePlugin(form);

There are a few Tina fields available within this package.

They can be registered with your cms instance like so:

// _app.js

import { BlocksFieldPlugin } from "@tinacms/react-tinacms-contentful";

// ...
  const cms = React.useMemo(() => new TinaCMS(tinaConfig), []);
  cms.fields.add({
    name: "contentful-linked-field",
    Component: ContentfulLinkedSelectField,
  });
  cms.fields.add(BlocksFieldPlugin);
// ...

LinkedBlocks

Similar to the blocks field defined within Tina, except this field stores a list of references instead of defining the content inline.

Your field registration might look like:

      {
        name: "my-blocks-field",
        label: "My Blocks Fields",
        component: "linked-blocks",
        templates: blocks,
        getTemplateId: (block) => block.sys.contentType.sys.id,
      },

getTemplateId: function to return the block identifier, from the block object.

ContentfulLinkedSelectField

TODO: ContentfulLinkedSelectField docs

Development

TSDX Bootstrap

This project was bootstrapped with TSDX.

Local Development

yarn build

Bundles the package to the dist folder. The package is optimized and bundled with Rollup into multiple formats (CommonJS, UMD, and ES Module).

Linking for local development

If you are want to test local changes to react-tinacms-contentful, we recommend using webpack aliases rather than using npm/yarn link.

On a NextJS site, your next.config.js may look like:

const path = require("path");

const tinaWebpackHelpers = require("@tinacms/webpack-helpers");

module.exports = {
  webpack: (config, { dev }) => {
    if (dev) {
      config.resolve.alias["@tinacms/react-tinacms-contentful"] = path.resolve(
        "../react-tinacms-contentful"
      );
      config.resolve.alias["react-beautiful-dnd"] = path.resolve(
        "./node_modules/react-beautiful-dnd"
      );

      tinaWebpackHelpers.aliasTinaDev(config, "../tinacms");
    }

    return config;
  },
  // ...
};