Custom placement of error message in checkout ui extension

Im following the guide here in an attempt to add a custom consent form on my checkout page using checkout ui extension. My extension injects a Text field and Checkbox field. If the checkbox is not checked its correctly blocking the checkout progress but its hard to figure out how to place the error message.

buyerJourney.intercept returns an error array and each error can have a target such as “$.cart.deliveryGroups[0].deliveryAddress.countryCode” or it can be left off to show the error at the top of the form.

  1. how do I figure out all the valid targets on the checkout page - for instance if I wanted to put it on a payment field, contact field, or any other random field. Is there any documentation page that enumerates all these options or are we supposed to just guess?
  2. How do I put the error message on my own custom checkbox or text field I injected with my custom ui extension. I dont want the message to appear at the top of the form which is easy to do if I leave the target out of the response. I want the error to appear on the field that is in error.

I did come up with a very hacky workaround but would still like a “correct” answer from shopify.

Rather than returning an error from buyerJourney.intercept I just manually insert an error banner node and show/hide it myself based on the situation. I still block checkout if its not checked but the error message is all manually controlled. Im a java developer not javascript and this feels extremely hackey so dont judge me on my code below. I cant seem to figure out how to remove the banner by id - it seems like the id is always just the position in the array rather than the id Im setting so Im removing element “2”.

Styling on checkout ui extensions is atrocious (I have to insert empty view node to add padding between consent view and error message, cant change background colors, etc). I hope shopify makes styling less painful in checkout ui extensions - it was 100% customizable on checkout.liquid. Shopify has not offered a replacement now that they are taking away checkout.liquid access to plus customers.

On submit payment it will block and show the error where I want it. On checking the box it will remove the error and add back if I uncheck again. By default the error will not show unless I try to pay or I check then uncheck it.

toml - make sure you enable block_progress option:

[[extensions.targeting]]
module = "./src/Checkout.js"
target = "purchase.checkout.contact.render-after"

[extensions.capabilities]
api_access = true
network_access = true
block_progress = true

extension:

import { extension, Banner, TextField, Button, Checkbox,Form, TextBlock, Style, View } from "@shopify/ui-extensions/checkout";

export default extension("purchase.checkout.contact.render-after", (root, api) => {
  const { extension, i18n, shippingAddress, buyerJourney, capabilities } = api;

  const consentError = root.createComponent(Banner, 
    {
      status: 'critical',
      title:
        'Please read and check the consent box before proceeding.',
        id: 'consentErrorBanner'
    }  
  );

  const view = root.createComponent(View, {
    border: 'base',
    padding: 'base',
    cornerRadius: ['large'],
    background: 'subdued',
  });
  root.appendChild(view);

  var consentChecked = "";
  const consentCheckbox = root.createComponent(Checkbox, {
      checked: false,
      onChange: (checkvalue) => {
        consentChecked = checkvalue; 
        console.log("consentChecked:" + consentChecked);
        toggleConsentError(root,consentError,consentChecked);
      },
    },
    'please check to consent'
  );
  view.appendChild(consentCheckbox);
  const textConsent = root.createComponent(TextBlock, {appearance: 'warning'},
    'In order to purchase this subscription serivce you must read and consent to the legal terms. '
  );

  view.appendChild(textConsent); 
  root.appendChild(root.createComponent( View, {border: 'none', padding: 'base'}, ' ',  ));

  buyerJourney.intercept(
    ({canBlockProgress}) => {
      toggleConsentError(root,consentError,consentChecked);
      return consentChecked == true ? {behavior: 'allow'} :{behavior: 'block',reason:'cant proceed without consenting' };
    });
});

function toggleConsentError(root,consentError,consentChecked){
  if (consentChecked == true){
    if (root.children["2"] != null){
      root.children["2"].remove();
    }
  }else{
    root.append(consentError);
  }
}

Thanks. So there is no proper solution with buyerJourney.intercept method? It should be easy to change the error target location.

EDIT:
I found only this. But how to target my custom field?

https://shopify.dev/docs/api/functions/reference/cart-checkout-validation/graphql

@GeorgeHoffman the only way I know is to replace logic. For example, use perform instead error.

if (canBlockProgress && checked && !isVatValid(vat)) {
      return {
        behavior: "block",
        reason: `VAT value is not correct.`,
        perform: (result) => {
          if (result.behavior === "block") {
            setValidationError("The VAT number entered is incorrect. Please ensure it starts with a country code (uppercase), followed by 8 to 12 alphanumeric characters. Example: XX0123456789.");
          }
        },
        errors: [
          {
            message: "The VAT number entered is incorrect. Please ensure it starts with a country code (uppercase), followed by 8 to 12 alphanumeric characters. Example: XX0123456789.",
            // target: "$.cart.deliveryGroups[0].deliveryAddress"
          },
        ],
      };
    }