API: recipes
Scripts that put the data of this site to work in the documentation, the tests and the CI of a plugin.
Every recipe reads the JSON of the site; none of them needs a key or a build of this repository. Copy, adjust the identifiers and run them in your own build.
Link every component a plugin uses
A plugin’s README or wiki can name the components it uses and link each one to its page, in the language of the reader.
const root = 'https://moodle-design.didactika.org';
const index = await (await fetch(root + '/api/index.json')).json();
const used = ['bootstrap/buttons', 'bootstrap/tables', 'bootstrap/modals', 'patterns/working-list'];
const lines = used.map((id) => {
const page = index.pages.find((p) => p.id === id);
if (!page) { throw new Error('No such page: ' + id); }
return `- [${page.title.en}](${page.url.en}?version=v53): ${page.summary.en}`;
});
console.log(lines.join('\n'));
Check that a template uses what the system publishes
Compare the classes of a plugin’s template with those of the example, so a redraw is caught in a review.
import {readFile} from 'node:fs/promises';
const buttons = await (await fetch(root + '/api/components/buttons.json')).json();
const classes = (html) => new Set([...html.matchAll(/class="([^"]+)"/g)].flatMap((m) => m[1].split(/\s+/)));
const expected = classes(buttons.examples.find((e) => e.id === 'buttons/group').bootstrap5.html);
const actual = classes(await readFile('templates/toolbar.mustache', 'utf8'));
const missing = [...expected].filter((c) => !actual.has(c));
console.log(missing.length ? `Not in the template: ${missing.join(', ')}` : 'Same classes');
Make a CI fail when an example the plugin copied has changed
Store the markup you copied and compare it on every build:
const current = (await (await fetch(root + '/api/components/tables.json')).json())
.examples.find((e) => e.id === 'tables/selectable').bootstrap5.html;
const copied = await readFile('tests/fixtures/tables-selectable.html', 'utf8');
if (current.trim() !== copied.trim()) {
console.warn('The example tables/selectable changed upstream: review your template.');
process.exitCode = 1;
}
Use a warning instead of a failure while the plugin is being built, and a failure once it is released.
Draw the icons of a plugin in its documentation
const {icons} = await (await fetch(root + '/api/icons.json')).json();
const ids = ['t/add', 't/edit', 't/delete'];
const table = ids.map((id) => `| \`${id}\` | ${icons[id].v53} | ${icons[id].v41} |`);
console.log('| Id | Moodle 5.x | Moodle 4.1 |\n|---|---|---|\n' + table.join('\n'));
Test a screen against every Moodle version
The stylesheets of the three versions are served scoped under .ds-v53, .ds-v45 and .ds-v41. In a visual test, load the one of the version you test and put the markup inside an element with that class and with ds-v-scope:
<link rel="stylesheet" href="https://moodle-design.didactika.org/moodle/v45/boost.css">
<div class="ds-v45 ds-v-scope">…the markup of your screen…</div>
Open the page in the state of a ticket
A bug report about a component on Moodle 4.5 is a link, not a screenshot to describe:
https://moodle-design.didactika.org/en/bootstrap/tables/?version=v45#sortable-headers
Keep it working
- Check
schema === 1before reading; the number changes only when a field is removed or changes meaning. - Identifiers of pages and examples do not change once published; titles and summaries may.
- The data is rebuilt with the site: cache it for a day, not forever.
UI