Data assertions for Fills

Description

Right now fills can only be used in the whole web and there’s no way to specify a fill that should be used only in some part, like only in the home, or only for a custom post type.

User Stories

As a Frontity theme consumer
I want to specify some data assertions for my fills
so that I can fill the slots only under certain conditions

Examples

  • Use an ad fill only in the home.
  • Use a newsletter fill only in the pages.
  • Use a comments fill only in the posts.

Possible solution

While talking with @David in the Slot and Fill FD (Slot and Fill) we thought it could work like this.

Fills can have a assertions.data property:

const state = {
  fills: {
    homeAd: {
      slot: "Below header",
      library: "Adsense",
      props: {
        adId: 123
      },
      assertions: {
        data: {
          isHome: true
        }
      }
    },
    postsAd: {
      slot: "Below header",
      library: "Adsense",
      props: {
        adId: 456
      },
      assertions: {
        data: {
          isPostType: true
        }
      }
    },
    categoriesAd: {
      slot: "Below header",
      library: "Adsense",
      props: {
        adId: 789
      },
      assertions: {
        data: {
          isTaxonomy: true,
          taxonomy: category
        }
      }
    }
  }
};

The Slot component will run those assertions against data:

const Slot = ({
  state,
  actions,
  libraries,
  data: dataProp,
  name,
  ...slotProps
}) => {
  const data = dataProp || state.source.get(state.router.link);
  const fills = Object.values(state.fills)
    .find(fill => fill.name === name)
    .sort((a, b) => a.priority > b.priority);

  return fills.map(fill => {
    const Fill = libraries.fills[fill.library];
    const show = assert(data, fill.assertions);
    return show ? <Fill {...slotProps} {...fill.props} /> : null;
  });
};

As you can see, Slot can receive a custom data object. If it doesn’t, the assertions run against the current data object.

To support more than “equal” assertions, they can be a value or an object. We can start with "equal" and "notEqual" and add more in the future.

The default will be “equal” so these are equivalent:

const assertions = {
  data: {
    isHome: true,
    isHome: {
      type: "equal",
      value: true
    }
  }
};

Using an object opens new possibilities:

const state = {
  fills: {
    allPostTypesButProducts: {
      assertions: {
        data: {
          isPostType: true,
          type: {
            type: "notEqual",
            value: "products"
          }
        }
      }
    }
  }
};

ORs could be done with arrays:

const state = {
  fills: {
    homeAndCategories: {
      assertions: {
        data: [
          {
            isHome: true
          },
          {
            isTaxonomy: true,
            taxonomy: "category"
          }
        ]
      }
    }
  }
};

This system can be used in other parts of Frontity, like the handlers of Source v2. It can also support other types of assertions, hence the assertions.data property.