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

HMR condition argument and widget player fix #4794

Merged
merged 7 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@
### Fixes

* Extra bundle detection when using external build module works properly now.
* Widget players are now properly invoked when they arrive later in the page load process.
* Fix permission grid tooltip display.

### Adds

* It's possible now to target the HMR build when registering via `template.append` and `template.prepend`. Use `when: 'hmr:public'` or `when: 'hmr:apos'` that will be evaluated against the current asset `options.hmr` configuration.

## 4.9.0 (2024-10-31)

### Adds
Expand Down
24 changes: 16 additions & 8 deletions modules/@apostrophecms/template/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -864,7 +864,7 @@ module.exports = {
// apos.template.prepend({
// component: 'module-name:async-component-name',
// where: 'head',
// when: 'hmr, // or e.g. ['hmr', 'dev'], logical AND
// when: 'hmr', // or e.g. ['hmr', 'dev'], logical AND
// bundler: 'vite',
// });
// OR
Expand Down Expand Up @@ -909,7 +909,7 @@ module.exports = {
// apos.template.append({
// component: 'module-name:async-component-name',
// where: 'head',
// when: 'hmr, // or e.g. ['hmr', 'dev'], logical AND
// when: 'hmr', // or e.g. ['hmr', 'dev'], logical AND
// bundler: 'vite',
// });
// OR
Expand Down Expand Up @@ -938,6 +938,8 @@ module.exports = {
// - `when`: (optional) string or array of strings, the conditions to be met to insert the component.
// When an array, a logical AND is applied. One match against the injected `when` data is required.
// Currently supported values are `hmr`, `dev`, `prod`. See `getInjectConditionHandlers()` for more info.
// The `when` value can include an argument separated by `:`. E.g. `hmr:apos`, `hmr:public`. If the condition
// handler does not support arguments, it's ignored.
// - `bundler`: (optional) string, the alias of the currently registered asset external build module.
// The bundler condition is not parth of the actual inject data. It's evaluated just on the registration
// data.
Expand Down Expand Up @@ -975,7 +977,7 @@ module.exports = {
conditions.when = conditions.when ? [ conditions.when ] : [];
}
// Both sides `when` should match
if (data.when && !conditions.when.includes(data.when)) {
if (data.when && !conditions.when.map(s => s.split(':')[0]).includes(data.when)) {
return;
}
if (!data.when && conditions.when.length) {
Expand All @@ -990,11 +992,12 @@ module.exports = {
// `when` being an object same as the schema `if`, supporting
// the same logical operators. But it's too much for now.
const conditionMet = when.every(val => {
if (!handlers[val]) {
const [ fn, arg ] = val.split(':');
if (!handlers[fn]) {
self.apos.util.error(`Invalid inject condition: ${when}`);
return false;
}
return handlers[when](data);
return handlers[fn](arg, data);
});

if (bundler) {
Expand All @@ -1020,11 +1023,16 @@ module.exports = {
// Simple conditions handling for `when` injects. It can be extended to support
// custom conditions in the future - registered by modules similar to
// `helpers`.
// Every condition function receives the nunjucks `data` (`when`, `where`, etc)
// object as an argument.
// Every condition function receives an argument if available and the nunjucks
// data object. For example `when: hmr:apos` will call `hmr('apos', data)`.
// The function should return a boolean.
getInjectConditionHandlers() {
return {
hmr() {
hmr(kind) {
if (kind) {
return self.apos.asset.hasHMR() &&
self.apos.asset.getBuildOptions().devServer === kind;
}
return self.apos.asset.hasHMR();
},
dev() {
Expand Down
29 changes: 21 additions & 8 deletions modules/@apostrophecms/util/ui/src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,20 @@ export default () => {
// THAT ONE WIDGET and NO OTHER. Don't worry about finding the
// others, we will do that for you and we guarantee only one call per widget.

apos.util.widgetPlayers = {};
const widgetPlayersConfig = {
list: {},
initialized: false
};
apos.util.widgetPlayers = new Proxy(widgetPlayersConfig.list, {
set(target, prop, value) {
target[prop] = value;
// run the player if we missed the initial run
if (widgetPlayersConfig.initialized) {
apos.util.runPlayers();
}
return true;
}
});

// Run the given function whenever the DOM has new changes that
// may require attention. The passed function will be
Expand Down Expand Up @@ -183,25 +196,24 @@ export default () => {
// Your player is guaranteed to run only once per widget. Hint:
// DON'T try to find all the widgets. DO just enhance `el`.
// This is a computer science principle known as "separation of concerns."

apos.util.runPlayers = function(el) {
apos.util.runPlayers = function (el) {
const players = apos.util.widgetPlayers;
const playerList = Object.keys(players);

for (let i = 0; i < playerList.length; i++) {
const playerOpts = players[playerList[i]];
const playerEls = (el || document).querySelectorAll(playerOpts.selector);

playerEls.forEach(function (el) {
if (el.aposWidgetPlayed) {
playerEls.forEach(function (playerEl) {
if (playerEl.aposWidgetPlayed) {
return;
}
// Use an actual property, not a DOM attribute or
// "data" prefix property, to avoid the problem of
// elements cloned from innerHTML appearing to have
// been played too
el.aposWidgetPlayed = true;
playerOpts.player(el);
playerEl.aposWidgetPlayed = true;
playerOpts.player(playerEl);
});
}
};
Expand All @@ -210,7 +222,8 @@ export default () => {
// when the page is partially refreshed by the editor.

if (!apos.bus) {
apos.util.onReadyAndRefresh(function() {
apos.util.onReady(function () {
widgetPlayersConfig.initialized = true;
apos.util.runPlayers();
});
}
Expand Down
Loading