Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce dynamic props via resolveProps API #187

Merged
merged 11 commits into from
Oct 26, 2023
Merged

Introduce dynamic props via resolveProps API #187

merged 11 commits into from
Oct 26, 2023

Conversation

chrisvxd
Copy link
Member

@chrisvxd chrisvxd commented Oct 25, 2023

This PR that introduces new APIs for expanding the capability of adaptors

  • The resolveProp config API allows users to modify props dynamically without writing them to the Puck data model on a per-component basis.
  • The resolveData util function allows users to apply their dynamic props on the server or client before rendering their page with the <Render> component
  • The mapProp adaptor API to enable the user to return change the shape of the data returned by the adaptor before applying it as a prop

Additionally:

  • It removes the _data API for adaptors in favour of resolveProp. This is a breaking change.
  • It makes the types stricter for Fields (considered a Fix).

resolveProps Usage

The Hero component in the demo app contains an example

const config:Config = {
  HeadingBlock: {
    fields: {
      text: {
        type: "text"
      }
    },
    resolveProps: async (props) => {
      // modify props. May include async operations.
      return { props: { ...props, text: "Hello, world" }, locked: { text: true } }
    },
    render: ({ text }) => {
      return <h1>{text}</h1>
    }
  }
}

Pairing it with an adaptor

const quotesAdaptor = {
  name: "Quotes API",
  fetchList: async () => {
    const quotesResponse = await fetch("/api/quotes");
    
    return quotesResponse.json();
  }
};

const config:Config = {
  HeadingBlock: {
    fields: {
      quote: {
        type: "external",
        adaptor: quotesAdaptor
      },
      text: {
        type: "text"
      }
    },
    resolveProps: async (props) => {
      if (props.quote?.id) {
        const quote = await fetch(`/api/quotes/${props.quote.id}`);
        
        return { props: { text: quote.json().content }, locked: { text: true } }
      }
      
      return { props, locked: { text: false } };
    },
    render: ({ text }) => {
      return <h1>{text}</h1>
    }
  }
}

resolveData usage

This should be used by the user to resolve data before rendering their component. It should ideally be used server-side, but can also be used client side in a useEffect hook.

export const MyRSCPage =  async () => {
  const data = await getPage(); // user's database query
  const resolvedData = await resolveData(data);

  return <Render data={data} />
}

mapProp usage

const quotesAdaptor = {
  name: "Quotes API",
  fetchList: async () => {
    const quotesResponse = await fetch("/api/quotes");
    const data =  quotesResponse.json();

    return { id: data.id, title: data };
  },
  // Called _after_ the user selects an item from the adaptor
  mapProp: (prop => ({ id: prop.id }))
};

const config:Config = {
  HeadingBlock: {
    fields: {
      quote: {
        type: "external",
        adaptor: quotesAdaptor
      },
      text: {
        type: "text"
      }
    },
    render: ({ text }) => {
      return <h1>{text}</h1>
    }
  }
}

Tasks

  • add tests

@vercel
Copy link

vercel bot commented Oct 25, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
puck-demo ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 26, 2023 9:58am

Copy link
Member

@monospaced monospaced left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of questions, approving for expediency

README.md Outdated
- **type** (`"external"`)
- **adaptor** (`Adaptor`): Content adaptor responsible for fetching data to show in the table
- **name** (`string`): The human-readable name of the adaptor
- **fetchList** (`(adaptorParams: object) => object`): Fetch a list of content and return an array
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could Adaptors fetch an object instead of a list?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They could fetch an object but this method needs to return an array to populate the table

Copy link
Member

@monospaced monospaced Oct 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if "a list of content" is correct in that case? Maybe Fetch content and return an array? I could be missing the point here 😊

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated to Fetch content from a third-party API and return an array for clarity

@@ -5,7 +5,7 @@ import {
useCallback,
useState,
} from "react";
import { Config, Data } from "../../types/Config";
import { Config, Data, MappedItem } from "../../types/Config";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imported but not used?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed

@chrisvxd chrisvxd merged commit b28404f into main Oct 26, 2023
2 checks passed
@chrisvxd chrisvxd deleted the better-adaptors branch October 26, 2023 09:58
@chrisvxd chrisvxd restored the better-adaptors branch November 2, 2023 18:52
@chrisvxd chrisvxd deleted the better-adaptors branch November 2, 2023 23:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants