Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
da08686
Add a widget playground for manual testing and demos
ashyablok-cs Sep 15, 2026
b42b1d4
Add form mode to the playground, drop SiteGate
ashyablok-cs Sep 15, 2026
8d2c564
Fix playground failing to initialise on first load
ashyablok-cs Sep 15, 2026
5e77593
Drop the branding control from the playground form
ashyablok-cs Sep 15, 2026
3c4c225
Drop manualTrigger from the playground
ashyablok-cs Sep 15, 2026
a21e985
Order the playground catalogue Message, Form, Subscription
ashyablok-cs Sep 15, 2026
cf6829a
Add formStates to the playground form
ashyablok-cs Sep 16, 2026
5298275
Add a panel collapse toggle to the playground
ashyablok-cs Sep 16, 2026
0e47877
Move the panel toggle next to the panels, as an icon
ashyablok-cs Sep 16, 2026
5d46391
Make the panel toggle a handle on the panel seam
ashyablok-cs Sep 16, 2026
65c0556
Add an opt-in Lytics tag and audience targeting
ashyablok-cs Sep 16, 2026
87ab4b8
Correct the targeting story, and make repeat targeted renders work
ashyablok-cs Sep 16, 2026
b093ac8
Offer the account's Lytics managed audiences for targeting
ashyablok-cs Sep 16, 2026
44c1591
Stop the form jumping to the top while you type
ashyablok-cs Sep 16, 2026
6de3e44
Always show the audience exclusion field
ashyablok-cs Sep 16, 2026
a02243d
Stop the stage page's own CSS restyling the widgets
ashyablok-cs Sep 16, 2026
53b530a
Hide the audience exclusion again until a target is set
ashyablok-cs Sep 16, 2026
a968a67
Suggest the account's Lytics managed content collections
ashyablok-cs Sep 16, 2026
3d51e35
Flag the sections that need the Lytics tag
ashyablok-cs Sep 16, 2026
1292e59
Add attribute targeting to the playground
ashyablok-cs Sep 16, 2026
bae44e0
Refresh attribute suggestions when the profile arrives
ashyablok-cs Sep 16, 2026
0c48bb6
Fix the playground's targeting emitter and stage reloads
ashyablok-cs Sep 21, 2026
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
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,74 @@ The docs task will walk through every `.js` file in the examples source director

This allows us to keep our source code in one place. Changing a js file in the examples source folder will change the code snippet in the docs and update the example .html file.

### Widget playground

`playground/` is a local page for rendering any widget type and layout, for manual
QA and for demoing. Start the dev server and open it:

```sh
$ yarn run local
```

Then visit [http://localhost:8080/playground/](http://localhost:8080/playground/).

Pick any combination from the sidebar to render it, then use either mode to configure it:

- **Form** builds the config from controls covering content, buttons, placement, theme
and colours, all 14 display conditions, content recommendations and custom form
fields. Controls only appear where the option actually applies, which keeps you away
from the combinations that throw - `footerText` on a bar, a `position` on a gate, a
`pushDown` on a bar that is not top-positioned.
- **Config** is the generated JavaScript, editable by hand. It is the same shape as the
examples in `docs/docs/examples/src`, so a snippet from a bug report can be pasted in
and run as-is. Switching back to Form regenerates the config from the controls.

Two things it handles that are easy to get wrong by hand:

- It sets `window.PathforaCSS` to `/dist/pathfora.min.css` before loading the SDK. The
SDK otherwise injects the CDN stylesheet, and that production CSS wins the cascade
over your local build - so local CSS changes appear to do nothing, with no error.
- It clears pathfora's stored state before each render. `pathfora.clearAll()` only
resets in-memory trackers, so without this a submitted gate stays unlocked and
impression caps stay spent, across renders *and* across reloads. Tick **Keep stored
state** when you are deliberately testing impressions or `hideAfterAction`.

**Lytics tag** in the toolbar swaps the stubs for the real tag, against the same demo
account the published docs examples use. It is off by default so the playground stays
network-free for anyone just checking a layout. With it on:

- Audience targeting works - the **Audience** section targets a segment, matched against
the visitor's own memberships, an attribute against a field on their profile, or both.
The segment suggestions are the demo account's Lytics managed audiences, hardcoded in
`playground/fields.js` so that reading them live does not mean storing an API key; any
other slug can be typed in. An exclude subtracts from that match, which is the only
thing exclusions do: `initTargetedWidgets` filters the widgets a target already
matched, so an exclusion on its own matches nothing. The exclude field only appears
once a "show to" segment is set, for that reason.
- "everyone" is the literal `*` segment, and it is not a target at all:
`validateWidgetsObject` hoists a `*` entry into `widgets.common`, which
`initTargetedWidgets` renders before the targeting callback ever runs. So there is
nothing for an exclusion or an attribute to act on, and those controls hide while it
is selected.
- A segment and an attribute together come out as one target entry whose rule ORs
`pathfora.rules.inSegment` with the attribute rule, rather than as two entries. Two
entries each concat `[widget]`, so a visitor matching both would hand
`initializeWidgetArray` the same widget twice and it would throw on the duplicate id.
- Content recommendations call the recommendation API for real, with the `content`
default document as the fallback. The collection field suggests the account's
Lytics managed collections, hardcoded alongside the audiences in
`playground/fields.js`; any other slug can be typed in. Without the tag there is no account to call, so the
default is all you see. Either way `setupWidgetContentUnit` needs both `recommend` and
`content` set, so a default document on its own renders nothing.

The tag is configured with `publish` and `preview` disabled, which stops the demo
account's own campaigns rendering on top of the widget under test and, as a side effect,
stops the tag installing its own SDK - so the local `dist/` build stays in charge.

`SiteGate` is deliberately absent: it is deprecated, and its confirm button is dead code
because `construct-widget-actions.js` never assigns it a `widgetAction`. Use `Form` with
layout `gate` instead.

### Testing

Pathfora uses [Jasmine](https://github.com/jasmine/jasmine) as a test framework, and [Karma](https://github.com/karma-runner/karma/) to run tests. Before running tests, or commiting changes be sure to run `gulp build` instead of `gulp local`, or tests may fail due to mismatching URLs.
Expand Down
2 changes: 1 addition & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ gulp.task(
gulp.series(
'build:js',
shell.task([
'eslint --fix src/rollup/**/*.js gulpfile.js test/**/*.js docs/docs/examples/**/*.js',
'eslint --fix src/rollup/**/*.js gulpfile.js test/**/*.js docs/docs/examples/**/*.js playground/**/*.js',
])
)
);
Expand Down
Loading
Loading