Thursday, November 17, 2022
HomeWeb DevelopmentMaking a Settings UI for a Customized WordPress Block | CSS-Tips

Making a Settings UI for a Customized WordPress Block | CSS-Tips


To this point, we’ve lined the way to work with information from an exterior API in a customized WordPress block. We walked via the method of fetching that information to be used on the entrance finish of a WordPress website, and the way to render it instantly within the WordPress Block Editor when inserting the block in content material. This time, we’re going to bridge these two articles by hooking into the block editor’s management panel to create a settings UI for the block we made.

Working With Exterior APIs in WordPress Blocks

You realize the management panel I’m referring to, proper? It’s that panel on the appropriate that incorporates publish and block settings within the block editor.

WordPress block editor with the right control panel open containing the settings UI for a Paragraph block.

See that purple highlighted space? That’s the management panel. A Paragraph block is at the moment chosen and the settings for it are displayed within the panel. We will change types, colour, typography… quite a few issues!

Effectively, that’s precisely what we’re doing this time round. We’re going to create the controls for the settings of the Soccer Rankings block we labored on within the final two articles. Final time, we made a button in our block that fetches the exterior information for the soccer rankings. We already knew the URL and endpoints we would have liked. However what if we wish to fetch rating for a distinct nation? Or possibly a distinct league? How about information from a distinct season?

We’d like type controls to try this. We may make use of interactive React parts — like React-Choose — to flick thru the assorted API choices which might be out there to parse that information. However there’s no want for that since WordPress ships with a bunch of core parts that we hook proper into!

The documentation for these parts — known as InspectorControls — is getting higher within the WordPress Block Editor Handbook. That’ll get even higher over time, however in the meantime, we even have the WordPress Gutenberg Storybook and WordPress Gutenberg Parts websites for extra assist.

The API structure

Earlier than we hook into something, it’s a good suggestion to map out what it’s we’d like within the first place. I’ve mapped out the construction of the RapidAPI information we’re fetching so we all know what’s out there to us:

Flow chart connecting the API endpoints for the custom WordPress block data that is fetched.
Credit score: API-Soccer

Seasons and nations are two top-level endpoints that map to a leagues endpoint. From there, we have now the remainder of the info we’re already utilizing to populate the rankings desk. So, what we wish to do is create settings within the WordPress Block Editor that filter the info by Season, Nation, and League, then go that filtered information into the rankings desk. That offers us the power to drop the block in any WordPress web page or publish and show variations of the info within the block.

With a purpose to get the standings, we have to first get the leagues. And with the intention to get the leagues, we first have to get the nations and/or the seasons. You may view the assorted endpoints within the RapidAPI dashboard.

Full screen for the Rapid API dashboard that visualizes the API data.

There are totally different combos of information that we are able to use to populate the rankings, and also you may need a desire for which information you need. For the sake of this text, we’re going to create the next choices within the block settings panel:

  • Select Nation
  • Select League
  • Select Season

Then we’ll have a button to submit these choices and fetch the related information and go them into the rankings desk.

Load and retailer a listing of nations

We will’t choose which nation we would like information for if we don’t have a listing of nations to select from. So, our first job is to seize a listing of nations from RapidAPI.

The perfect factor is to fetch the listing of nations when the block is definitely used within the web page or publish content material. There’s no have to fetch something if the block isn’t in use. The strategy is similar to what we did in the primary article, the distinction being that we’re utilizing a distinct API endpoint and totally different attributes to retailer the listing of returned nations. There are different WordPress methods to fetch information, like api-fetch, however that‘s exterior the scope of what we’re doing right here.

We will both embody the nation listing manually after copying it from the API information, or we may use a separate API or library to populate the nations. However the API we’re utilizing already has a listing of nations, so I might simply use considered one of its endpoints. Let’s be certain the preliminary nation listing masses when the block is inserted into the web page or publish content material within the block editor:

// edit.js
const [countriesList, setCountriesList] = useState(null);

useEffect(() => {
  let countryOptions = {
    methodology: "GET",
    headers: {
      "X-RapidAPI-Key": "Your Speedy API key",
      "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",
    },
  };
  fetch("https://api-football-v1.p.rapidapi.com/v3/nations", countryOptions)
    .then( (response) => response.json() )
    .then( (response) => {
      let countriesArray = { ...response };
      console.log("International locations listing", countriesArray.response);
      setCountriesList(countriesArray.response);
    })
  .catch((err) => console.error(err));
}, []);

We’ve got a state variable to retailer the listing of nations. Subsequent, we’re going to import a element from the @wordpress/block-editor bundle known as InspectorControls which is the place the entire parts we have to create our settings controls are situated.

import { InspectorControls } from "@wordpress/block-editor";

The bundle’s GitHub repo does job explaining InspectorControls. In our instance, we are able to use it to manage the API information settings like Nation, League, and Season. Right here’s a preview so that you just get an concept of the UI we’re making:

The custom settings UI for the WordPress block showing the three settings options for the custom block and a blue button to fetch the data.

And as soon as these choices are made within the block settings, we use them in the block’s Edit operate:

<InspectorControls>
  { countriesList && (
    <LeagueSettings
      props={props}
      countriesList={ countriesList }
      setApiData={ setApiData }
    ></LeagueSettings>
  )}
</InspectorControls>

Right here, I’m ensuring that we’re utilizing conditional rendering in order that the operate solely masses the element after the listing of nations is loaded. If you happen to’re questioning about that LeagueSettings element, it’s a customized element I created in a separate parts subfolder within the block so we are able to have a cleaner and extra organized Edit operate as a substitute of a whole lot of strains of nation information to take care of in a single file.

File structure for the block directory showing the current file.

We will import it into the edit.js file like this:

import { LeagueSettings } from "./parts/LeagueSettings";

Subsequent, we’re passing the required props to the LeagueSettings element from the mum or dad Edit element in order that we are able to entry the state variables and attributes from the LeagueSettings little one element. We will additionally try this with different strategies just like the Context API to keep away from prop drilling, however what we have now proper now’s completely appropriate for what we’re doing.

The opposite components of the Edit operate will also be transformed into parts. For instance, the league standings code might be put inside a separate element — like possibly LeagueTable.js — after which imported similar to we imported LeagueSettings into the Edit operate.

Contained in the LeagueSettings.js file

LeagueSettings is rather like one other React element from which we are able to destructure the props from the mum or dad element. I’m going to make use of three state variables and a further leagueID state as a result of we’re going to extract the ID from the league object:

const [country, setCountry] = useState(null);
const [league, setLeague] = useState(null);
const [season, setSeason] = useState(null);
const [leagueID, setLeagueID] = useState(null);

The very first thing we’re going to do is import the PanelBody element from the @wordpress/block-editor bundle:

import { PanelBody } from "@wordpress/block-editor";

…and embody it in our return operate:

<PanelBody title="Knowledge settings" initialOpen={false}></PanelBody>

There are different panel tags and attributes — it’s simply my private desire to make use of these ones. Not one of the others are required… however have a look at all of the parts we have now out there to make a settings panel! I just like the simplicity of the PanelBody for our use case. It expands and collapses to disclose the dropdown settings for the block and that’s it.

Talking of which, we have now a option to make for these choices. We may use the SelectControl element or a ComboBoxControl, which the docs describe as “an enhanced model of a SelectControl, with the addition of with the ability to seek for choices utilizing a search enter.” That’s good for us as a result of the listing of nations may get fairly lengthy and customers will be capable to both do a search question or choose from a listing.

Right here’s an instance of how a ComboboxControl may work for our nation listing:

<ComboboxControl
  label="Select nation"
  worth={nation}
  choices={ filteredCountryOptions }
  onChange={ (worth) => handleCountryChange(worth) }
  onInputChange={ (inputValue) => {
    setFilteredCountryOptions(
      setupCountrySelect.filter((possibility) =>
        possibility.label
          .toLowerCase()
          .startsWith(inputValue.toLowerCase())
      )
    );
  }}
/>

The ComboboxControl is configurable within the sense that we are able to apply totally different sizing for the management’s label and values:

{
  worth: 'small',
  label: 'Small',
},

However our API information is just not on this syntax, so we are able to convert the countriesList array that comes from the mum or dad element when the block is included:

let setupCountrySelect;

setupCountrySelect = countriesList.map((nation) => {
  return {
    label: nation.title,
    worth: nation.title,
  };
});

When a rustic is chosen from the ComboboxControl, the nation worth modifications and we filter the info accordingly:

operate handleCountryChange(worth) {
  // Set state of the nation 
  setCountry(worth); 

  // League code from RapidAPI
  const choices = {
    methodology: "GET",
    headers: {
      "X-RapidAPI-Key": "Your RapidAPI key",
      "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",
    },
  };

  fetch(`https://api-football-v1.p.rapidapi.com/v3/leagues?nation=${worth}`, choices)
    .then((response) => response.json())
    .then((response) => {
      return response.response;
    })
    .then((leagueOptions) => {
      // Set state of the league variable
      setLeague(leagueOptions);

      // Convert it as we did for Nation choices
      setupLeagueSelect = leagueOptions.map((league) => {
        return {
          label: league.league.title,
          worth: league.league.title,
        };
      });
      setFilteredLeagueOptions(setupLeagueSelect);
    })
  .catch((err) => console.error(err));
}

Be aware that I’m utilizing one other three state variables to deal with modifications when the nation choice modifications:

const [filteredCountryOptions, setFilteredCountryOptions] = useState(setupCountrySelect);
const [filteredLeagueOptions, setFilteredLeagueOptions] = useState(null);
const [filteredSeasonOptions, setFilteredSeasonOptions] = useState(null);

What concerning the different settings choices?

I’ll present the code that I used for the opposite settings however all it does is take regular circumstances under consideration whereas defining errors for particular circumstances. For instance, there might be errors in some nations and leagues as a result of:

  • there aren’t any standings for some leagues, and
  • some leagues have standings however they don’t seem to be in a single desk.

This isn’t a JavaScript or React tutorial, so I’ll allow you to deal with the particular circumstances for the API that you just plan to make use of:

operate handleLeagueChange(worth) {
  setLeague(worth);
  if (league) {
    const selectedLeague = league.filter((el) => {
      if (el.league.title === worth) {
        return el;
      }
    });

    if (selectedLeague) {
      setLeague(selectedLeague[0].league.title);
      setLeagueID(selectedLeague[0].league.id);
      setupSeasonSelect = selectedLeague[0].seasons.map((season) => {
        return {
          label: season.yr,
          worth: season.yr,
        };
      });
      setFilteredSeasonOptions(setupSeasonSelect);
    }
  } else {
    return;
  }
}

operate handleSeasonChange(worth) {
  setSeason(worth);
}

Submitting the settings choices

Within the final article, we made a button within the block editor that fetches contemporary information from the API. There’s no extra want for it now that we have now settings. Effectively, we do want it — simply not the place it at the moment is. As an alternative of getting it instantly within the block that’s rendered within the block editor, we’re going to maneuver it to our PanelBody element to submit the settings choices.

So, again in LeagueSettings.js:

// When countriesList is loaded, present the nation combo field
{ countriesList && (
  <ComboboxControl
    label="Select nation"
    worth={nation}
    choices={filteredCountryOptions}
    onChange={(worth) => handleCountryChange(worth)}
    onInputChange={(inputValue) => {
      setFilteredCountryOptions(
        setupCountrySelect.filter((possibility) =>
          possibility.label
            .toLowerCase()
            .startsWith(inputValue.toLowerCase())
        )
      );
    }}
  />
)}

// When filteredLeagueOptions is about via handleCountryChange, present league combobox
{ filteredLeagueOptions && (
  <ComboboxControl
    label="Select league"
    worth={league}
    choices={filteredLeagueOptions}
    onChange={(worth) => handleLeagueChange(worth)}
    onInputChange={(inputValue) => {
      setFilteredLeagueOptions(
        setupLeagueSelect.filter((possibility) =>
          possibility.label
            .toLowerCase()
            .startsWith(inputValue.toLowerCase())
        )
      );
    }}
  />
)}

// When filteredSeasonOptions is about via handleLeagueChange, present season combobox
{ filteredSeasonOptions && (
  <>
    <ComboboxControl
      label="Select season"
      worth={season}
      choices={filteredSeasonOptions}
      onChange={(worth) => handleSeasonChange(worth)}
      onInputChange={
          (inputValue) => {
            setFilteredSeasonOptions(
              setupSeasonSelect.filter((possibility) =>
              possibility.label
              .toLowerCase()
              .startsWith(inputValue.toLowerCase()
            )
          );
        }
      }
    />

    // When season is about via handleSeasonChange, present the "Fetch information" button
    {
      season && (
        <button className="fetch-data" onClick={() => getData()}>Fetch information</button>
      )
    }
    </>
  </>
)}

Right here’s the consequence!

We’re in an excellent place with our block. We will render it within the block editor and the entrance finish of the positioning. We will fetch information from an exterior API primarily based on a collection of settings we created that filters the info. It’s fairly darn purposeful!

However there’s one other factor we have now to deal with. Proper now, after we save the web page or publish that incorporates the block, the settings we chosen for the block reset. In different phrases, these choices should not saved anyplace. There’s slightly extra work to make these choices persistent. That’s the place we plan to go within the subsequent article, so keep tuned.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments