@owlmeans/web-panel 0.1.18-rc.72 → 0.1.18-rc.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/web-panel/SKILL.md +28 -5
- package/build/auth/entrypoints.d.ts +1 -1
- package/build/auth/entrypoints.d.ts.map +1 -1
- package/build/components/login/types.d.ts +1 -1
- package/build/components/login/types.d.ts.map +1 -1
- package/build/consent/component.d.ts +4 -0
- package/build/consent/component.d.ts.map +1 -1
- package/build/consent/component.js +19 -5
- package/build/consent/component.js.map +1 -1
- package/build/consent/geo.d.ts +17 -0
- package/build/consent/geo.d.ts.map +1 -0
- package/build/consent/geo.js +19 -0
- package/build/consent/geo.js.map +1 -0
- package/build/consent/index.d.ts +4 -3
- package/build/consent/index.d.ts.map +1 -1
- package/build/consent/index.js +2 -1
- package/build/consent/index.js.map +1 -1
- package/build/consent/translate.js +1 -1
- package/build/consent/translate.js.map +1 -1
- package/build/consent/types.d.ts +20 -0
- package/build/consent/types.d.ts.map +1 -1
- package/build/entrypoints.d.ts +1 -7
- package/build/entrypoints.d.ts.map +1 -1
- package/build/types.d.ts +3 -0
- package/build/types.d.ts.map +1 -1
- package/package.json +23 -23
- package/src/components/login/types.ts +1 -1
- package/src/consent/component.tsx +30 -8
- package/src/consent/geo.ts +26 -0
- package/src/consent/index.ts +6 -2
- package/src/consent/translate.ts +1 -1
- package/src/consent/types.ts +22 -0
- package/src/types.ts +3 -0
- package/tests/consent.spec.ts +50 -0
- package/tests/harness/mount.tsx +48 -5
- package/tests/login.spec.ts +22 -0
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ but never started.
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
|
-
bun add @owlmeans/web-panel@^0.1.18-rc.
|
|
14
|
+
bun add @owlmeans/web-panel@^0.1.18-rc.73
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Peer requirements (the consuming app provides these): `react`, `react-dom`,
|
|
@@ -413,7 +413,9 @@ consumer's CSS puts dark tokens under `.dark` AND under
|
|
|
413
413
|
|
|
414
414
|
| Symbol | Kind | Purpose |
|
|
415
415
|
|---|---|---|
|
|
416
|
-
| `PanelCookieConsent`, `PanelCookiePolicy` | component | `@owlmeans/web-consent` components bound to the app's language and
|
|
416
|
+
| `PanelCookieConsent`, `PanelCookiePolicy` | component | `@owlmeans/web-consent` components bound to the app's language, translations and `cfg.cookieConsent` (`{ mode?, geo? }` — a prop wins) |
|
|
417
|
+
| `appendConsentGeoPlugin(ctx, plugin)` | function | Register the app's own country locator (`ConsentGeoPlugin`) while the context is configured; turns the geo gate on |
|
|
418
|
+
| `PanelCookieConsentConfig`, `ConsentGeoPluginAppend` | type | `cfg.cookieConsent`; the `context.consentGeo()` accessor |
|
|
417
419
|
| `PanelConsentMenuWidget` | component | The cookie-preferences control for a host menu or footer ("Cookie settings" — pass `label` and `className`) |
|
|
418
420
|
| `appendConsentWidgetService(ctx, alias?)`, `createConsentWidgetService` | function | Ref-counted presence service over a state resource |
|
|
419
421
|
| `useConsentMenuPresence()`, `useConsentWidgetPresent()` | hook | Declare that a menu shows the row; read whether one does (hides the floating button) |
|
|
@@ -496,7 +498,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
496
498
|
your project's skill store (`.agents/skills/`):
|
|
497
499
|
|
|
498
500
|
```sh
|
|
499
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
501
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.52
|
|
500
502
|
```
|
|
501
503
|
|
|
502
504
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/web-panel",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-10-
|
|
4
|
+
"version": "0.1.18-rc.73",
|
|
5
|
+
"generatedAt": "2026-10-08T21:27:54.321Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -8,7 +8,7 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/web-panel
|
|
9
9
|
|
|
10
10
|
**Layer:** Web (React)
|
|
11
|
-
**Install:** `"@owlmeans/web-panel": "^0.1.18-rc.
|
|
11
|
+
**Install:** `"@owlmeans/web-panel": "^0.1.18-rc.73"` in `dependencies`
|
|
12
12
|
|
|
13
13
|
## Key Exports
|
|
14
14
|
|
|
@@ -481,8 +481,29 @@ landmark.
|
|
|
481
481
|
## Subpath: `./consent`
|
|
482
482
|
|
|
483
483
|
`PanelCookieConsent` and `PanelCookiePolicy` — `@owlmeans/web-consent`'s components bound to this
|
|
484
|
-
app's language and translations, falling through to the package's own
|
|
485
|
-
every key the app has not overridden
|
|
484
|
+
app's language and translations, falling through to the package's own eight-language bundle for
|
|
485
|
+
every key the app has not overridden — and to the app's cookie-consent configuration. See the
|
|
486
|
+
`consent` and `web-consent` skills.
|
|
487
|
+
|
|
488
|
+
**The configuration lives in `cfg.cookieConsent`** (`PanelCookieConsentConfig`, on `AppConfig`),
|
|
489
|
+
set where the rest of the app's configuration is, before anything renders:
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
cfg.cookieConsent = { geo: { cloudflare: true } } // ask only in consent countries; mode: 'bar' default
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
- `mode` (`'bar'` | `'window'`) and `geo` (`ConsentGeoOptions`) feed both components; a prop of the
|
|
496
|
+
same name always wins. Not `cfg.consent` — "consent" also names the marketing-consent and sign-in
|
|
497
|
+
terms options.
|
|
498
|
+
- A file restored into apps of every age (a generated target's `config.ts`) writes it as
|
|
499
|
+
`Object.assign(cfg, { cookieConsent: … })`, so a tree whose framework predates the key still
|
|
500
|
+
type-checks; an older package ignores it.
|
|
501
|
+
- **A locator of the app's own** is code, not configuration: `appendConsentGeoPlugin(context,
|
|
502
|
+
plugin)` in `context.ts` registers a `ConsentGeoPlugin` (`{ alias, locate }` — resolve the country
|
|
503
|
+
or throw) ahead of Cloudflare's and installs `context.consentGeo()`. Appending one IS asking for
|
|
504
|
+
the gate: `PanelCookieConsent` turns it on (`geo: {}`) even where `cfg.cookieConsent.geo` says
|
|
505
|
+
nothing. `PanelCookiePolicy` reads the same, so the policy states the regional rule exactly when
|
|
506
|
+
the dialog applies it.
|
|
486
507
|
|
|
487
508
|
A re-export does not move Tailwind class strings, so a consumer adds a second `@source` for
|
|
488
509
|
`@owlmeans/web-consent` alongside this package's — pointing at **`src`**, for the reason spelled out
|
|
@@ -532,7 +553,8 @@ menu has already mounted) and because React 18 StrictMode double-invokes mount/c
|
|
|
532
553
|
|
|
533
554
|
| Job | Import from `@owlmeans/web-panel/consent` | Where it goes |
|
|
534
555
|
|---|---|---|
|
|
535
|
-
| The consent
|
|
556
|
+
| The consent UI — bar, window, spinner (and its floating re-open button) | `PanelCookieConsent` | Beside the router — a `PanelApp` child — once |
|
|
557
|
+
| Asking only in consent countries | `cfg.cookieConsent.geo`, or `appendConsentGeoPlugin(context, plugin)` | The app's `config.ts` / `context.ts` |
|
|
536
558
|
| The "Cookie settings" control in a footer or menu | `PanelConsentMenuWidget` (`label`, `className`, `onSelect?` — defaults to `consentStore.open('reopen')`) | Inside the host's own footer/menu |
|
|
537
559
|
| Hiding the floating button while that control is reachable | `useConsentMenuPresence()` | Called by the always-mounted component that renders the control — mounted ONLY while the control can actually be reached: a component that is merely hidden by CSS (`xl:hidden`) is still mounted and still claims presence, so the floating button vanishes with nothing else to open the dialog; render it conditionally instead (viable's header mounts its hamburger menu only below the full-row breakpoint) |
|
|
538
560
|
| Registering the presence service | `appendConsentWidgetService(context)` | The app's `context.ts` |
|
|
@@ -557,7 +579,8 @@ bound dialog with its floating button; `useConsentMenuPresence()` then has nothi
|
|
|
557
579
|
does nothing. The presence read itself (`useConsentWidgetPresent`) still needs the service — its
|
|
558
580
|
state resource is registered by it — so any other caller checks first. A dialog that read an
|
|
559
581
|
unregistered state resource threw inside render ("Resource consent-widget-presence not found") and
|
|
560
|
-
blanked the whole application. Pinned by `tests/consent.spec.ts` (`?consent=bare` / `?consent=menu
|
|
582
|
+
blanked the whole application. Pinned by `tests/consent.spec.ts` (`?consent=bare` / `?consent=menu`;
|
|
583
|
+
the configuration by `?consentCfg=window|bar`, `?modeProp=` and `?consentGeo=<CC>|fail`).
|
|
561
584
|
|
|
562
585
|
## Consumer setup — package boundary and Tailwind
|
|
563
586
|
|
|
@@ -6,5 +6,5 @@ export declare const entrypoints: (import("@owlmeans/client-entrypoint").ClientP
|
|
|
6
6
|
query: Partial<import("@owlmeans/auth").AuthToken>;
|
|
7
7
|
}, undefined>> | import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
8
8
|
query: import("@owlmeans/auth").AuthToken;
|
|
9
|
-
},
|
|
9
|
+
}, unknown>>)[];
|
|
10
10
|
//# sourceMappingURL=entrypoints.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entrypoints.d.ts","sourceRoot":"","sources":["../../src/auth/entrypoints.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW;;;;;;;;
|
|
1
|
+
{"version":3,"file":"entrypoints.d.ts","sourceRoot":"","sources":["../../src/auth/entrypoints.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW;;;;;;;;eAAuC,CAAA"}
|
|
@@ -15,7 +15,7 @@ export interface LoginCreditProps {
|
|
|
15
15
|
export interface LoginTermsProps {
|
|
16
16
|
model: LoginTermsModel;
|
|
17
17
|
translate: (key: string, defaultValue: string) => string;
|
|
18
|
-
/** The current language, for `Intl.ListFormat
|
|
18
|
+
/** The current language, for `Intl.ListFormat`, document labels and explicit translated hrefs. */
|
|
19
19
|
locale?: string;
|
|
20
20
|
className?: string;
|
|
21
21
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/login/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAExG,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,gBAAgB,EAAE,WAAW,CAAC;IAC3E,0DAA0D;IAC1D,IAAI,CAAC,EAAE,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,SAAS,CAAA;CACzD;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,gBAAgB,CAAA;IACvB,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,MAAM,CAAA;IACxD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,eAAe,CAAA;IACtB,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,MAAM,CAAA;IACxD,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/login/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAExG,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,gBAAgB,EAAE,WAAW,CAAC;IAC3E,0DAA0D;IAC1D,IAAI,CAAC,EAAE,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,SAAS,CAAA;CACzD;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,gBAAgB,CAAA;IACvB,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,MAAM,CAAA;IACxD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,eAAe,CAAA;IACtB,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,MAAM,CAAA;IACxD,kGAAkG;IAClG,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,MAAM,uBAAuB,GAAG,eAAe,CAAA;AAErD,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,kBAAkB,CAAA;IACzB,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,MAAM,CAAA;IACxD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB"}
|
|
@@ -19,5 +19,9 @@ import { type CookieConsentProps, type CookiePolicyProps } from '@owlmeans/web-c
|
|
|
19
19
|
* while the context is built, before anything renders.
|
|
20
20
|
*/
|
|
21
21
|
export declare const PanelCookieConsent: FC<CookieConsentProps>;
|
|
22
|
+
/**
|
|
23
|
+
* The cookie-policy page, bound like the dialog — and told about the geo gate the same way, so it
|
|
24
|
+
* states the regional rule exactly when the dialog applies it.
|
|
25
|
+
*/
|
|
22
26
|
export declare const PanelCookiePolicy: FC<CookiePolicyProps>;
|
|
23
27
|
//# sourceMappingURL=component.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../src/consent/component.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAC/B,OAAO,
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../src/consent/component.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAC/B,OAAO,EACgD,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EACrG,MAAM,uBAAuB,CAAA;AAqC9B;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,kBAAkB,EAAE,EAAE,CAAC,kBAAkB,CAWrD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,EAAE,CAAC,iBAAiB,CAQnD,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { CookieConsent, CookiePolicy } from '@owlmeans/web-consent';
|
|
2
|
+
import { CookieConsent, CookiePolicy, } from '@owlmeans/web-consent';
|
|
3
3
|
import { useLanguage } from '@owlmeans/client-i18n';
|
|
4
4
|
import { useContext } from '../context.js';
|
|
5
5
|
import { useConsentTranslate } from './translate.js';
|
|
@@ -12,6 +12,12 @@ const BoundCookieConsent = ({ menuPresent, ...props }) => {
|
|
|
12
12
|
return _jsx(CookieConsent, { ...props, locale: locale, translate: translate, noReopenButton: props.noReopenButton ?? menuPresent });
|
|
13
13
|
};
|
|
14
14
|
const MenuAwareCookieConsent = props => _jsx(BoundCookieConsent, { ...props, menuPresent: useConsentWidgetPresent() });
|
|
15
|
+
/**
|
|
16
|
+
* The geo gate in force: the prop, else `cfg.cookieConsent.geo`, else — when the application
|
|
17
|
+
* appended a locator of its own — the gate with nothing but that locator.
|
|
18
|
+
*/
|
|
19
|
+
const geoOf = (prop, context) => prop ?? context.cfg.cookieConsent?.geo ?? (typeof context.consentGeo === 'function' ? {} : undefined);
|
|
20
|
+
void _localeParity;
|
|
15
21
|
/**
|
|
16
22
|
* The consent dialog, bound to this application's language and translations.
|
|
17
23
|
*
|
|
@@ -32,15 +38,23 @@ const MenuAwareCookieConsent = props => _jsx(BoundCookieConsent, { ...props, men
|
|
|
32
38
|
*/
|
|
33
39
|
export const PanelCookieConsent = props => {
|
|
34
40
|
const context = useContext();
|
|
41
|
+
const mode = props.mode ?? context.cfg.cookieConsent?.mode;
|
|
42
|
+
const geo = geoOf(props.geo, context);
|
|
43
|
+
const bound = { ...props, ...(mode != null ? { mode } : {}), ...(geo != null ? { geo } : {}) };
|
|
35
44
|
return typeof context.consentWidget === 'function'
|
|
36
|
-
? _jsx(MenuAwareCookieConsent, { ...
|
|
37
|
-
: _jsx(BoundCookieConsent, { ...
|
|
45
|
+
? _jsx(MenuAwareCookieConsent, { ...bound })
|
|
46
|
+
: _jsx(BoundCookieConsent, { ...bound });
|
|
38
47
|
};
|
|
48
|
+
/**
|
|
49
|
+
* The cookie-policy page, bound like the dialog — and told about the geo gate the same way, so it
|
|
50
|
+
* states the regional rule exactly when the dialog applies it.
|
|
51
|
+
*/
|
|
39
52
|
export const PanelCookiePolicy = props => {
|
|
53
|
+
const context = useContext();
|
|
40
54
|
const [lng] = useLanguage();
|
|
41
55
|
const locale = props.locale ?? lng;
|
|
42
56
|
const translate = useConsentTranslate(locale, props.translate);
|
|
43
|
-
|
|
57
|
+
const geo = geoOf(props.geo, context);
|
|
58
|
+
return _jsx(CookiePolicy, { ...props, ...(geo != null ? { geo } : {}), locale: locale, translate: translate });
|
|
44
59
|
};
|
|
45
|
-
void _localeParity;
|
|
46
60
|
//# sourceMappingURL=component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.js","sourceRoot":"","sources":["../../src/consent/component.tsx"],"names":[],"mappings":";AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"component.js","sourceRoot":"","sources":["../../src/consent/component.tsx"],"names":[],"mappings":";AACA,OAAO,EACL,aAAa,EAAE,YAAY,GAC5B,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAGjD,MAAM,kBAAkB,GAAgC,CAAC,EAAE,WAAW,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE;IACpF,MAAM,CAAC,GAAG,CAAC,GAAG,WAAW,EAAE,CAAA;IAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,GAAG,CAAA;IAClC,MAAM,SAAS,GAAG,mBAAmB,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAA;IAE9D,OAAO,KAAC,aAAa,OACf,KAAK,EACT,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,WAAW,GACnD,CAAA;AACJ,CAAC,CAAA;AAED,MAAM,sBAAsB,GAA2B,KAAK,CAAC,EAAE,CAC7D,KAAC,kBAAkB,OAAK,KAAK,EAAE,WAAW,EAAE,uBAAuB,EAAE,GAAI,CAAA;AAE3E;;;GAGG;AACH,MAAM,KAAK,GAAG,CACZ,IAAmC,EAAE,OAAgE,EACtE,EAAE,CACjC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,OAAO,OAAO,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;AAEvG,KAAK,aAAa,CAAA;AAElB;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAA2B,KAAK,CAAC,EAAE;IAChE,MAAM,OAAO,GAAG,UAAU,EAEvB,CAAA;IACH,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAA;IAC1D,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IACrC,MAAM,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;IAE9F,OAAO,OAAO,OAAO,CAAC,aAAa,KAAK,UAAU;QAChD,CAAC,CAAC,KAAC,sBAAsB,OAAK,KAAK,GAAI;QACvC,CAAC,CAAC,KAAC,kBAAkB,OAAK,KAAK,GAAI,CAAA;AACvC,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAA0B,KAAK,CAAC,EAAE;IAC9D,MAAM,OAAO,GAAG,UAAU,EAAsE,CAAA;IAChG,MAAM,CAAC,GAAG,CAAC,GAAG,WAAW,EAAE,CAAA;IAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,GAAG,CAAA;IAClC,MAAM,SAAS,GAAG,mBAAmB,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAA;IAC9D,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IAErC,OAAO,KAAC,YAAY,OAAK,KAAK,KAAM,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAI,CAAA;AAC5G,CAAC,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ConsentGeoPlugin } from '@owlmeans/web-consent';
|
|
2
|
+
import type { ClientConfig } from '@owlmeans/client-context';
|
|
3
|
+
import type { ClientContext } from '@owlmeans/client';
|
|
4
|
+
import type { ConsentGeoPluginAppend } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Add the application's own country locator while its context is configured — beside
|
|
7
|
+
* `appendConsentWidgetService`, before anything renders.
|
|
8
|
+
*
|
|
9
|
+
* A locator is one async method that resolves the visitor's country or throws
|
|
10
|
+
* (`ConsentGeoPlugin`). It is registered into `@owlmeans/consent`'s plugin registry, ahead of the
|
|
11
|
+
* built-in Cloudflare one, and `PanelCookieConsent` turns the geo gate on for it even where
|
|
12
|
+
* `cfg.cookieConsent.geo` says nothing — appending a locator IS asking for the gate. Its result
|
|
13
|
+
* decides as Cloudflare's would: a visitor in a consent country is asked, anyone else is decided for
|
|
14
|
+
* automatically, and a throw hands over to the next locator — with none left, the visitor is asked.
|
|
15
|
+
*/
|
|
16
|
+
export declare const appendConsentGeoPlugin: <C extends ClientConfig, T extends ClientContext<C>>(ctx: T, plugin: ConsentGeoPlugin) => T & ConsentGeoPluginAppend;
|
|
17
|
+
//# sourceMappingURL=geo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geo.d.ts","sourceRoot":"","sources":["../../src/consent/geo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAClF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAExD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,GAAI,CAAC,SAAS,YAAY,EAAE,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,OAClF,CAAC,UAAU,gBAAgB,KAC/B,CAAC,GAAG,sBAON,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { consentPluginHelper } from '@owlmeans/web-consent';
|
|
2
|
+
/**
|
|
3
|
+
* Add the application's own country locator while its context is configured — beside
|
|
4
|
+
* `appendConsentWidgetService`, before anything renders.
|
|
5
|
+
*
|
|
6
|
+
* A locator is one async method that resolves the visitor's country or throws
|
|
7
|
+
* (`ConsentGeoPlugin`). It is registered into `@owlmeans/consent`'s plugin registry, ahead of the
|
|
8
|
+
* built-in Cloudflare one, and `PanelCookieConsent` turns the geo gate on for it even where
|
|
9
|
+
* `cfg.cookieConsent.geo` says nothing — appending a locator IS asking for the gate. Its result
|
|
10
|
+
* decides as Cloudflare's would: a visitor in a consent country is asked, anyone else is decided for
|
|
11
|
+
* automatically, and a throw hands over to the next locator — with none left, the visitor is asked.
|
|
12
|
+
*/
|
|
13
|
+
export const appendConsentGeoPlugin = (ctx, plugin) => {
|
|
14
|
+
const _ctx = ctx;
|
|
15
|
+
consentPluginHelper.registerConsentPlugin(plugin);
|
|
16
|
+
_ctx.consentGeo = () => plugin;
|
|
17
|
+
return _ctx;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=geo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geo.js","sourceRoot":"","sources":["../../src/consent/geo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAyB,MAAM,uBAAuB,CAAA;AAKlF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CACpC,GAAM,EAAE,MAAwB,EACJ,EAAE;IAC9B,MAAM,IAAI,GAAG,GAAiC,CAAA;IAE9C,mBAAmB,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAA;IACjD,IAAI,CAAC,UAAU,GAAG,GAAG,EAAE,CAAC,MAAM,CAAA;IAE9B,OAAO,IAAI,CAAA;AACb,CAAC,CAAA"}
|
package/build/consent/index.d.ts
CHANGED
|
@@ -3,8 +3,9 @@ export * from './menu-widget.js';
|
|
|
3
3
|
export * from './presence.js';
|
|
4
4
|
export { installConsentLanguage } from './language.js';
|
|
5
5
|
export { createConsentWidgetService, appendConsentWidgetService, CONSENT_WIDGET_STATE } from './service.js';
|
|
6
|
+
export { appendConsentGeoPlugin } from './geo.js';
|
|
6
7
|
export { CONSENT_WIDGET_SERVICE } from './consts.js';
|
|
7
|
-
export type { ConsentWidgetPresenceRecord, ConsentWidgetService, ConsentWidgetServiceAppend } from './types.js';
|
|
8
|
-
export { useConsent, useConsentCategory, consentStore, openConsent, isConsented, readConsent, writeConsent, clearConsent, DEFAULT_CONSENT_CATEGORIES, CONSENT_KEY, CONSENT_ESSENTIAL, CONSENT_ANALYTICS, CONSENT_MARKETING, CONSENT_EVENT, consentBootstrapScript, } from '@owlmeans/web-consent';
|
|
9
|
-
export type { ConsentCategory, ConsentMenuWidgetProps, ConsentOptions, ConsentRecord, ConsentSignal, ConsentState, CookieConsentProps, CookiePolicyProps, } from '@owlmeans/web-consent';
|
|
8
|
+
export type { ConsentWidgetPresenceRecord, ConsentWidgetService, ConsentWidgetServiceAppend, PanelCookieConsentConfig, ConsentGeoPluginAppend, } from './types.js';
|
|
9
|
+
export { useConsent, useConsentCategory, consentStore, openConsent, isConsented, readConsent, writeConsent, clearConsent, DEFAULT_CONSENT_CATEGORIES, CONSENT_KEY, CONSENT_ESSENTIAL, CONSENT_ANALYTICS, CONSENT_MARKETING, CONSENT_EVENT, consentBootstrapScript, CONSENT_REQUIRED_COUNTRIES, } from '@owlmeans/web-consent';
|
|
10
|
+
export type { ConsentCategory, ConsentMenuWidgetProps, ConsentOptions, ConsentRecord, ConsentSignal, ConsentState, CookieConsentProps, CookiePolicyProps, ConsentDisplayMode, ConsentGeoOptions, ConsentGeoPlugin, ConsentGeoLocation, } from '@owlmeans/web-consent';
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consent/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAC3G,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACpD,YAAY,EACV,2BAA2B,EAAE,oBAAoB,EAAE,0BAA0B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consent/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAC3G,OAAO,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACpD,YAAY,EACV,2BAA2B,EAAE,oBAAoB,EAAE,0BAA0B,EAC7E,wBAAwB,EAAE,sBAAsB,GACjD,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EACtE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,0BAA0B,EACnE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EACpE,aAAa,EACb,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EACV,eAAe,EAAE,sBAAsB,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EACrF,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,iBAAiB,EAC1F,gBAAgB,EAAE,kBAAkB,GACrC,MAAM,uBAAuB,CAAA"}
|
package/build/consent/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export * from './menu-widget.js';
|
|
|
3
3
|
export * from './presence.js';
|
|
4
4
|
export { installConsentLanguage } from './language.js';
|
|
5
5
|
export { createConsentWidgetService, appendConsentWidgetService, CONSENT_WIDGET_STATE } from './service.js';
|
|
6
|
+
export { appendConsentGeoPlugin } from './geo.js';
|
|
6
7
|
export { CONSENT_WIDGET_SERVICE } from './consts.js';
|
|
7
|
-
export { useConsent, useConsentCategory, consentStore, openConsent, isConsented, readConsent, writeConsent, clearConsent, DEFAULT_CONSENT_CATEGORIES, CONSENT_KEY, CONSENT_ESSENTIAL, CONSENT_ANALYTICS, CONSENT_MARKETING, CONSENT_EVENT, consentBootstrapScript, } from '@owlmeans/web-consent';
|
|
8
|
+
export { useConsent, useConsentCategory, consentStore, openConsent, isConsented, readConsent, writeConsent, clearConsent, DEFAULT_CONSENT_CATEGORIES, CONSENT_KEY, CONSENT_ESSENTIAL, CONSENT_ANALYTICS, CONSENT_MARKETING, CONSENT_EVENT, consentBootstrapScript, CONSENT_REQUIRED_COUNTRIES, } from '@owlmeans/web-consent';
|
|
8
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/consent/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAC3G,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/consent/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAC3G,OAAO,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAKpD,OAAO,EACL,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EACtE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,0BAA0B,EACnE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EACpE,aAAa,EACb,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
2
|
import { useI18nLib } from '@owlmeans/client-i18n';
|
|
3
|
-
import { consentI18nHelper } from '@owlmeans/consent';
|
|
3
|
+
import { consentI18nHelper } from '@owlmeans/web-consent';
|
|
4
4
|
/**
|
|
5
5
|
* Resolve consent copy through the application first, and the packaged bundle second.
|
|
6
6
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translate.js","sourceRoot":"","sources":["../../src/consent/translate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"translate.js","sourceRoot":"","sources":["../../src/consent/translate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAEzD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,MAAc,EAAE,QAAwD,EACvB,EAAE;IACnD,MAAM,CAAC,GAAG,UAAU,CAAC,SAAS,CAAC,CAAA;IAE/B,OAAO,WAAW,CAAC,CAAC,GAAW,EAAE,YAAoB,EAAE,EAAE;QACvD,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,OAAO,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,CAAA;QACpC,CAAC;QACD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,YAAY,CAAC,CAAA;QACrF,2FAA2F;QAC3F,2FAA2F;QAC3F,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC3C,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAA;AAC3B,CAAC,CAAA"}
|
package/build/consent/types.d.ts
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
import type { ResourceRecord } from '@owlmeans/resource';
|
|
2
2
|
import type { InitializedService } from '@owlmeans/context';
|
|
3
|
+
import type { ConsentDisplayMode, ConsentGeoOptions, ConsentGeoPlugin } from '@owlmeans/web-consent';
|
|
4
|
+
/**
|
|
5
|
+
* `cfg.cookieConsent` — how the application's cookie consent asks, set where the rest of its
|
|
6
|
+
* configuration is. A prop on `PanelCookieConsent` / `PanelCookiePolicy` always wins.
|
|
7
|
+
*/
|
|
8
|
+
export interface PanelCookieConsentConfig {
|
|
9
|
+
/** `bar` (the default) or `window` — see `ConsentDisplayMode`. */
|
|
10
|
+
mode?: ConsentDisplayMode;
|
|
11
|
+
/**
|
|
12
|
+
* Ask only where the law requires it. `{ cloudflare: true }` locates the visitor through the
|
|
13
|
+
* Cloudflare edge the application is served from; a locator of the application's own is added in
|
|
14
|
+
* code with `appendConsentGeoPlugin`. See `ConsentGeoOptions`.
|
|
15
|
+
*/
|
|
16
|
+
geo?: ConsentGeoOptions;
|
|
17
|
+
}
|
|
18
|
+
/** What `appendConsentGeoPlugin` adds to the context. */
|
|
19
|
+
export interface ConsentGeoPluginAppend {
|
|
20
|
+
/** The application's own locator, registered while the context was configured. */
|
|
21
|
+
consentGeo: () => ConsentGeoPlugin;
|
|
22
|
+
}
|
|
3
23
|
/** A `single`-store presence flag: one record, no id needed. */
|
|
4
24
|
export interface ConsentWidgetPresenceRecord extends ResourceRecord {
|
|
5
25
|
present: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/consent/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/consent/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAEpG;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC,kEAAkE;IAClE,IAAI,CAAC,EAAE,kBAAkB,CAAA;IACzB;;;;OAIG;IACH,GAAG,CAAC,EAAE,iBAAiB,CAAA;CACxB;AAED,yDAAyD;AACzD,MAAM,WAAW,sBAAsB;IACrC,kFAAkF;IAClF,UAAU,EAAE,MAAM,gBAAgB,CAAA;CACnC;AAED,gEAAgE;AAChE,MAAM,WAAW,2BAA4B,SAAQ,cAAc;IACjE,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,oBAAqB,SAAQ,kBAAkB;IAC9D;;;OAGG;IACH,KAAK,EAAE,MAAM,MAAM,IAAI,CAAA;IACvB,yEAAyE;IACzE,OAAO,EAAE,MAAM,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,0BAA0B;IACzC,aAAa,EAAE,MAAM,oBAAoB,CAAA;CAC1C"}
|
package/build/entrypoints.d.ts
CHANGED
|
@@ -1,8 +1,2 @@
|
|
|
1
|
-
export declare const entrypoints:
|
|
2
|
-
query: import("@owlmeans/auth").AuthToken;
|
|
3
|
-
}, unknown>> | import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
4
|
-
query: import("@owlmeans/auth-common").SurrogateQuery;
|
|
5
|
-
}, unknown>> | import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
6
|
-
body: import("@owlmeans/auth").AuthToken;
|
|
7
|
-
}, import("@owlmeans/auth").AuthToken>>)[];
|
|
1
|
+
export declare const entrypoints: import("@owlmeans/client-entrypoint").ClientProtocolEntrypoint<any>[];
|
|
8
2
|
//# sourceMappingURL=entrypoints.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entrypoints.d.ts","sourceRoot":"","sources":["../src/entrypoints.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW
|
|
1
|
+
{"version":3,"file":"entrypoints.d.ts","sourceRoot":"","sources":["../src/entrypoints.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,uEAA4C,CAAA"}
|
package/build/types.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { WithFlowConfig } from '@owlmeans/flow';
|
|
|
2
2
|
import type { AppConfig as Config, AppContext as Context, RenderOptions } from '@owlmeans/web-client';
|
|
3
3
|
import type { FlowService } from '@owlmeans/web-flow';
|
|
4
4
|
import type { SocketClientSettings, SocketStatusServiceAppend } from '@owlmeans/client-socket';
|
|
5
|
+
import type { PanelCookieConsentConfig } from './consent/types.js';
|
|
5
6
|
export interface AppConfig extends Config, WithFlowConfig {
|
|
6
7
|
socket?: SocketClientSettings & {
|
|
7
8
|
/**
|
|
@@ -12,6 +13,8 @@ export interface AppConfig extends Config, WithFlowConfig {
|
|
|
12
13
|
*/
|
|
13
14
|
reloadDialog?: boolean;
|
|
14
15
|
};
|
|
16
|
+
/** How the cookie consent asks — the bar or the window, and where (`geo`). See `./consent`. */
|
|
17
|
+
cookieConsent?: PanelCookieConsentConfig;
|
|
15
18
|
}
|
|
16
19
|
export interface AppContext<C extends AppConfig = AppConfig> extends Context<C>, SocketStatusServiceAppend {
|
|
17
20
|
flow: () => FlowService;
|
package/build/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,KAAK,EAAE,SAAS,IAAI,MAAM,EAAE,UAAU,IAAI,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACrG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,KAAK,EAAE,SAAS,IAAI,MAAM,EAAE,UAAU,IAAI,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACrG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAA;AAC9F,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAA;AAElE,MAAM,WAAW,SAAU,SAAQ,MAAM,EAAE,cAAc;IACvD,MAAM,CAAC,EAAE,oBAAoB,GAAG;QAC9B;;;;;WAKG;QACH,YAAY,CAAC,EAAE,OAAO,CAAA;KACvB,CAAA;IACD,+FAA+F;IAC/F,aAAa,CAAC,EAAE,wBAAwB,CAAA;CACzC;AAED,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS,CAAE,SAAQ,OAAO,CAAC,CAAC,CAAC,EAAE,yBAAyB;IACxG,IAAI,EAAE,MAAM,WAAW,CAAA;CACxB;AAED,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACrD,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/web-panel",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.73",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -51,35 +51,35 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@hookform/resolvers": "^5.9.1",
|
|
54
|
-
"@owlmeans/api-config-client": "^0.1.18-rc.
|
|
55
|
-
"@owlmeans/auth": "^0.1.18-rc.
|
|
56
|
-
"@owlmeans/auth-common": "^0.1.18-rc.
|
|
57
|
-
"@owlmeans/basic-envelope": "^0.1.18-rc.
|
|
58
|
-
"@owlmeans/client": "^0.1.18-rc.
|
|
59
|
-
"@owlmeans/client-auth": "^0.1.18-rc.
|
|
60
|
-
"@owlmeans/client-config": "^0.1.18-rc.
|
|
61
|
-
"@owlmeans/client-context": "^0.1.18-rc.
|
|
62
|
-
"@owlmeans/client-entrypoint": "^0.1.18-rc.
|
|
63
|
-
"@owlmeans/api": "^0.1.18-rc.
|
|
64
|
-
"@owlmeans/client-flow": "^0.1.18-rc.
|
|
65
|
-
"@owlmeans/client-i18n": "^0.1.18-rc.
|
|
66
|
-
"@owlmeans/client-panel": "^0.1.18-rc.
|
|
67
|
-
"@owlmeans/client-route": "^0.1.18-rc.
|
|
68
|
-
"@owlmeans/client-socket": "^0.1.18-rc.
|
|
54
|
+
"@owlmeans/api-config-client": "^0.1.18-rc.47",
|
|
55
|
+
"@owlmeans/auth": "^0.1.18-rc.41",
|
|
56
|
+
"@owlmeans/auth-common": "^0.1.18-rc.49",
|
|
57
|
+
"@owlmeans/basic-envelope": "^0.1.18-rc.48",
|
|
58
|
+
"@owlmeans/client": "^0.1.18-rc.53",
|
|
59
|
+
"@owlmeans/client-auth": "^0.1.18-rc.60",
|
|
60
|
+
"@owlmeans/client-config": "^0.1.18-rc.45",
|
|
61
|
+
"@owlmeans/client-context": "^0.1.18-rc.50",
|
|
62
|
+
"@owlmeans/client-entrypoint": "^0.1.18-rc.49",
|
|
63
|
+
"@owlmeans/api": "^0.1.18-rc.49",
|
|
64
|
+
"@owlmeans/client-flow": "^0.1.18-rc.56",
|
|
65
|
+
"@owlmeans/client-i18n": "^0.1.18-rc.52",
|
|
66
|
+
"@owlmeans/client-panel": "^0.1.18-rc.60",
|
|
67
|
+
"@owlmeans/client-route": "^0.1.18-rc.50",
|
|
68
|
+
"@owlmeans/client-socket": "^0.1.18-rc.51",
|
|
69
69
|
"@owlmeans/config": "^0.1.18-rc.44",
|
|
70
70
|
"@owlmeans/context": "^0.1.18-rc.34",
|
|
71
71
|
"@owlmeans/entrypoint": "^0.1.18-rc.41",
|
|
72
72
|
"@owlmeans/error": "^0.1.18-rc.38",
|
|
73
73
|
"@owlmeans/flow": "^0.1.18-rc.45",
|
|
74
74
|
"@owlmeans/i18n": "^0.1.18-rc.39",
|
|
75
|
-
"@owlmeans/log": "^0.1.18-rc.
|
|
75
|
+
"@owlmeans/log": "^0.1.18-rc.3",
|
|
76
76
|
"@owlmeans/route": "^0.1.18-rc.35",
|
|
77
77
|
"@owlmeans/state": "^0.1.18-rc.39",
|
|
78
|
-
"@owlmeans/web-client": "^0.1.18-rc.
|
|
79
|
-
"@owlmeans/web-consent": "^0.1.18-rc.
|
|
80
|
-
"@owlmeans/web-db": "^0.1.18-rc.
|
|
81
|
-
"@owlmeans/web-flow": "^0.1.18-rc.
|
|
82
|
-
"@owlmeans/web-router": "^0.1.18-rc.
|
|
78
|
+
"@owlmeans/web-client": "^0.1.18-rc.63",
|
|
79
|
+
"@owlmeans/web-consent": "^0.1.18-rc.44",
|
|
80
|
+
"@owlmeans/web-db": "^0.1.18-rc.50",
|
|
81
|
+
"@owlmeans/web-flow": "^0.1.18-rc.55",
|
|
82
|
+
"@owlmeans/web-router": "^0.1.18-rc.51",
|
|
83
83
|
"i18next-browser-languagedetector": "^8.2.1",
|
|
84
84
|
"qrcode.react": "^4.2.0",
|
|
85
85
|
"react-google-recaptcha": "^3.1.0",
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
},
|
|
106
106
|
"devDependencies": {
|
|
107
107
|
"@owlmeans/dep-config": "workspace:*",
|
|
108
|
-
"@owlmeans/test-ui": "^0.1.18-rc.
|
|
108
|
+
"@owlmeans/test-ui": "^0.1.18-rc.51",
|
|
109
109
|
"@radix-ui/react-alert-dialog": "^1.1.23",
|
|
110
110
|
"@radix-ui/react-dialog": "^1.1.23",
|
|
111
111
|
"@radix-ui/react-label": "^2.1.15",
|
|
@@ -16,7 +16,7 @@ export interface LoginCreditProps {
|
|
|
16
16
|
export interface LoginTermsProps {
|
|
17
17
|
model: LoginTermsModel
|
|
18
18
|
translate: (key: string, defaultValue: string) => string
|
|
19
|
-
/** The current language, for `Intl.ListFormat
|
|
19
|
+
/** The current language, for `Intl.ListFormat`, document labels and explicit translated hrefs. */
|
|
20
20
|
locale?: string
|
|
21
21
|
className?: string
|
|
22
22
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { FC } from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
CookieConsent, CookiePolicy, type ConsentGeoOptions, type CookieConsentProps, type CookiePolicyProps,
|
|
4
|
+
} from '@owlmeans/web-consent'
|
|
3
5
|
import { useLanguage } from '@owlmeans/client-i18n'
|
|
4
6
|
import { useContext } from '../context.js'
|
|
5
7
|
import type { AppConfig, AppContext } from '../types.js'
|
|
6
8
|
import { useConsentTranslate } from './translate.js'
|
|
7
9
|
import { useConsentWidgetPresent } from './presence.js'
|
|
8
|
-
import type { ConsentWidgetServiceAppend } from './types.js'
|
|
10
|
+
import type { ConsentGeoPluginAppend, ConsentWidgetServiceAppend } from './types.js'
|
|
9
11
|
import { _localeParity } from './consts.local.js'
|
|
10
12
|
import type { BoundCookieConsentProps } from './types.local.js'
|
|
11
13
|
|
|
@@ -25,6 +27,17 @@ const BoundCookieConsent: FC<BoundCookieConsentProps> = ({ menuPresent, ...props
|
|
|
25
27
|
const MenuAwareCookieConsent: FC<CookieConsentProps> = props =>
|
|
26
28
|
<BoundCookieConsent {...props} menuPresent={useConsentWidgetPresent()} />
|
|
27
29
|
|
|
30
|
+
/**
|
|
31
|
+
* The geo gate in force: the prop, else `cfg.cookieConsent.geo`, else — when the application
|
|
32
|
+
* appended a locator of its own — the gate with nothing but that locator.
|
|
33
|
+
*/
|
|
34
|
+
const geoOf = (
|
|
35
|
+
prop: ConsentGeoOptions | undefined, context: AppContext<AppConfig> & Partial<ConsentGeoPluginAppend>
|
|
36
|
+
): ConsentGeoOptions | undefined =>
|
|
37
|
+
prop ?? context.cfg.cookieConsent?.geo ?? (typeof context.consentGeo === 'function' ? {} : undefined)
|
|
38
|
+
|
|
39
|
+
void _localeParity
|
|
40
|
+
|
|
28
41
|
/**
|
|
29
42
|
* The consent dialog, bound to this application's language and translations.
|
|
30
43
|
*
|
|
@@ -44,19 +57,28 @@ const MenuAwareCookieConsent: FC<CookieConsentProps> = props =>
|
|
|
44
57
|
* while the context is built, before anything renders.
|
|
45
58
|
*/
|
|
46
59
|
export const PanelCookieConsent: FC<CookieConsentProps> = props => {
|
|
47
|
-
const context = useContext<
|
|
60
|
+
const context = useContext<
|
|
61
|
+
AppConfig, AppContext<AppConfig> & Partial<ConsentWidgetServiceAppend & ConsentGeoPluginAppend>
|
|
62
|
+
>()
|
|
63
|
+
const mode = props.mode ?? context.cfg.cookieConsent?.mode
|
|
64
|
+
const geo = geoOf(props.geo, context)
|
|
65
|
+
const bound = { ...props, ...(mode != null ? { mode } : {}), ...(geo != null ? { geo } : {}) }
|
|
48
66
|
|
|
49
67
|
return typeof context.consentWidget === 'function'
|
|
50
|
-
? <MenuAwareCookieConsent {...
|
|
51
|
-
: <BoundCookieConsent {...
|
|
68
|
+
? <MenuAwareCookieConsent {...bound} />
|
|
69
|
+
: <BoundCookieConsent {...bound} />
|
|
52
70
|
}
|
|
53
71
|
|
|
72
|
+
/**
|
|
73
|
+
* The cookie-policy page, bound like the dialog — and told about the geo gate the same way, so it
|
|
74
|
+
* states the regional rule exactly when the dialog applies it.
|
|
75
|
+
*/
|
|
54
76
|
export const PanelCookiePolicy: FC<CookiePolicyProps> = props => {
|
|
77
|
+
const context = useContext<AppConfig, AppContext<AppConfig> & Partial<ConsentGeoPluginAppend>>()
|
|
55
78
|
const [lng] = useLanguage()
|
|
56
79
|
const locale = props.locale ?? lng
|
|
57
80
|
const translate = useConsentTranslate(locale, props.translate)
|
|
81
|
+
const geo = geoOf(props.geo, context)
|
|
58
82
|
|
|
59
|
-
return <CookiePolicy {...props} locale={locale} translate={translate} />
|
|
83
|
+
return <CookiePolicy {...props} {...(geo != null ? { geo } : {})} locale={locale} translate={translate} />
|
|
60
84
|
}
|
|
61
|
-
|
|
62
|
-
void _localeParity
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { consentPluginHelper, type ConsentGeoPlugin } from '@owlmeans/web-consent'
|
|
2
|
+
import type { ClientConfig } from '@owlmeans/client-context'
|
|
3
|
+
import type { ClientContext } from '@owlmeans/client'
|
|
4
|
+
import type { ConsentGeoPluginAppend } from './types.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Add the application's own country locator while its context is configured — beside
|
|
8
|
+
* `appendConsentWidgetService`, before anything renders.
|
|
9
|
+
*
|
|
10
|
+
* A locator is one async method that resolves the visitor's country or throws
|
|
11
|
+
* (`ConsentGeoPlugin`). It is registered into `@owlmeans/consent`'s plugin registry, ahead of the
|
|
12
|
+
* built-in Cloudflare one, and `PanelCookieConsent` turns the geo gate on for it even where
|
|
13
|
+
* `cfg.cookieConsent.geo` says nothing — appending a locator IS asking for the gate. Its result
|
|
14
|
+
* decides as Cloudflare's would: a visitor in a consent country is asked, anyone else is decided for
|
|
15
|
+
* automatically, and a throw hands over to the next locator — with none left, the visitor is asked.
|
|
16
|
+
*/
|
|
17
|
+
export const appendConsentGeoPlugin = <C extends ClientConfig, T extends ClientContext<C>>(
|
|
18
|
+
ctx: T, plugin: ConsentGeoPlugin
|
|
19
|
+
): T & ConsentGeoPluginAppend => {
|
|
20
|
+
const _ctx = ctx as T & ConsentGeoPluginAppend
|
|
21
|
+
|
|
22
|
+
consentPluginHelper.registerConsentPlugin(plugin)
|
|
23
|
+
_ctx.consentGeo = () => plugin
|
|
24
|
+
|
|
25
|
+
return _ctx
|
|
26
|
+
}
|
package/src/consent/index.ts
CHANGED
|
@@ -3,9 +3,11 @@ export * from './menu-widget.js'
|
|
|
3
3
|
export * from './presence.js'
|
|
4
4
|
export { installConsentLanguage } from './language.js'
|
|
5
5
|
export { createConsentWidgetService, appendConsentWidgetService, CONSENT_WIDGET_STATE } from './service.js'
|
|
6
|
+
export { appendConsentGeoPlugin } from './geo.js'
|
|
6
7
|
export { CONSENT_WIDGET_SERVICE } from './consts.js'
|
|
7
8
|
export type {
|
|
8
|
-
ConsentWidgetPresenceRecord, ConsentWidgetService, ConsentWidgetServiceAppend
|
|
9
|
+
ConsentWidgetPresenceRecord, ConsentWidgetService, ConsentWidgetServiceAppend,
|
|
10
|
+
PanelCookieConsentConfig, ConsentGeoPluginAppend,
|
|
9
11
|
} from './types.js'
|
|
10
12
|
export {
|
|
11
13
|
useConsent, useConsentCategory, consentStore, openConsent, isConsented,
|
|
@@ -13,8 +15,10 @@ export {
|
|
|
13
15
|
CONSENT_KEY, CONSENT_ESSENTIAL, CONSENT_ANALYTICS, CONSENT_MARKETING,
|
|
14
16
|
CONSENT_EVENT,
|
|
15
17
|
consentBootstrapScript,
|
|
18
|
+
CONSENT_REQUIRED_COUNTRIES,
|
|
16
19
|
} from '@owlmeans/web-consent'
|
|
17
20
|
export type {
|
|
18
21
|
ConsentCategory, ConsentMenuWidgetProps, ConsentOptions, ConsentRecord, ConsentSignal,
|
|
19
|
-
ConsentState, CookieConsentProps, CookiePolicyProps,
|
|
22
|
+
ConsentState, CookieConsentProps, CookiePolicyProps, ConsentDisplayMode, ConsentGeoOptions,
|
|
23
|
+
ConsentGeoPlugin, ConsentGeoLocation,
|
|
20
24
|
} from '@owlmeans/web-consent'
|
package/src/consent/translate.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback } from 'react'
|
|
2
2
|
import { useI18nLib } from '@owlmeans/client-i18n'
|
|
3
|
-
import { consentI18nHelper } from '@owlmeans/consent'
|
|
3
|
+
import { consentI18nHelper } from '@owlmeans/web-consent'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Resolve consent copy through the application first, and the packaged bundle second.
|
package/src/consent/types.ts
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
import type { ResourceRecord } from '@owlmeans/resource'
|
|
2
2
|
import type { InitializedService } from '@owlmeans/context'
|
|
3
|
+
import type { ConsentDisplayMode, ConsentGeoOptions, ConsentGeoPlugin } from '@owlmeans/web-consent'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `cfg.cookieConsent` — how the application's cookie consent asks, set where the rest of its
|
|
7
|
+
* configuration is. A prop on `PanelCookieConsent` / `PanelCookiePolicy` always wins.
|
|
8
|
+
*/
|
|
9
|
+
export interface PanelCookieConsentConfig {
|
|
10
|
+
/** `bar` (the default) or `window` — see `ConsentDisplayMode`. */
|
|
11
|
+
mode?: ConsentDisplayMode
|
|
12
|
+
/**
|
|
13
|
+
* Ask only where the law requires it. `{ cloudflare: true }` locates the visitor through the
|
|
14
|
+
* Cloudflare edge the application is served from; a locator of the application's own is added in
|
|
15
|
+
* code with `appendConsentGeoPlugin`. See `ConsentGeoOptions`.
|
|
16
|
+
*/
|
|
17
|
+
geo?: ConsentGeoOptions
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** What `appendConsentGeoPlugin` adds to the context. */
|
|
21
|
+
export interface ConsentGeoPluginAppend {
|
|
22
|
+
/** The application's own locator, registered while the context was configured. */
|
|
23
|
+
consentGeo: () => ConsentGeoPlugin
|
|
24
|
+
}
|
|
3
25
|
|
|
4
26
|
/** A `single`-store presence flag: one record, no id needed. */
|
|
5
27
|
export interface ConsentWidgetPresenceRecord extends ResourceRecord {
|
package/src/types.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { WithFlowConfig } from '@owlmeans/flow'
|
|
|
3
3
|
import type { AppConfig as Config, AppContext as Context, RenderOptions } from '@owlmeans/web-client'
|
|
4
4
|
import type { FlowService } from '@owlmeans/web-flow'
|
|
5
5
|
import type { SocketClientSettings, SocketStatusServiceAppend } from '@owlmeans/client-socket'
|
|
6
|
+
import type { PanelCookieConsentConfig } from './consent/types.js'
|
|
6
7
|
|
|
7
8
|
export interface AppConfig extends Config, WithFlowConfig {
|
|
8
9
|
socket?: SocketClientSettings & {
|
|
@@ -14,6 +15,8 @@ export interface AppConfig extends Config, WithFlowConfig {
|
|
|
14
15
|
*/
|
|
15
16
|
reloadDialog?: boolean
|
|
16
17
|
}
|
|
18
|
+
/** How the cookie consent asks — the bar or the window, and where (`geo`). See `./consent`. */
|
|
19
|
+
cookieConsent?: PanelCookieConsentConfig
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
export interface AppContext<C extends AppConfig = AppConfig> extends Context<C>, SocketStatusServiceAppend {
|
package/tests/consent.spec.ts
CHANGED
|
@@ -59,4 +59,54 @@ describe('@owlmeans/web-panel/consent — the dialog and the menu widget', () =>
|
|
|
59
59
|
await close()
|
|
60
60
|
}
|
|
61
61
|
}, TIMEOUT)
|
|
62
|
+
|
|
63
|
+
test('a first-time visitor meets the bar by default', async () => {
|
|
64
|
+
const { page, close } = await open('/prefs?consent=bare')
|
|
65
|
+
try {
|
|
66
|
+
await page.locator('[data-consent-bar]').waitFor()
|
|
67
|
+
expect(await page.locator('[data-consent-dialog]').getAttribute('data-consent-mode')).toBe('bar')
|
|
68
|
+
} finally {
|
|
69
|
+
await close()
|
|
70
|
+
}
|
|
71
|
+
}, TIMEOUT)
|
|
72
|
+
|
|
73
|
+
test('cfg.cookieConsent.mode picks the window, and a mode prop wins over it', async () => {
|
|
74
|
+
const configured = await open('/prefs?consent=bare&consentCfg=window')
|
|
75
|
+
try {
|
|
76
|
+
await configured.page.locator('[data-consent-dialog][data-consent-mode="window"]').waitFor()
|
|
77
|
+
} finally {
|
|
78
|
+
await configured.close()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const overridden = await open('/prefs?consent=bare&consentCfg=window&modeProp=bar')
|
|
82
|
+
try {
|
|
83
|
+
await overridden.page.locator('[data-consent-bar]').waitFor()
|
|
84
|
+
} finally {
|
|
85
|
+
await overridden.close()
|
|
86
|
+
}
|
|
87
|
+
}, TIMEOUT)
|
|
88
|
+
|
|
89
|
+
test('a locator appended to the context turns the geo gate on by itself', async () => {
|
|
90
|
+
const outside = await open('/prefs?consent=bare&consentGeo=US')
|
|
91
|
+
try {
|
|
92
|
+
await outside.page.waitForFunction(() => document.documentElement.getAttribute('data-consent') === 'decided')
|
|
93
|
+
expect(await outside.page.locator('[data-consent-dialog]').count()).toBe(0)
|
|
94
|
+
} finally {
|
|
95
|
+
await outside.close()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const inside = await open('/prefs?consent=bare&consentGeo=DE')
|
|
99
|
+
try {
|
|
100
|
+
await inside.page.locator('[data-consent-bar]').waitFor()
|
|
101
|
+
} finally {
|
|
102
|
+
await inside.close()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const unknown = await open('/prefs?consent=bare&consentGeo=fail')
|
|
106
|
+
try {
|
|
107
|
+
await unknown.page.locator('[data-consent-bar]').waitFor()
|
|
108
|
+
} finally {
|
|
109
|
+
await unknown.close()
|
|
110
|
+
}
|
|
111
|
+
}, TIMEOUT)
|
|
62
112
|
})
|
package/tests/harness/mount.tsx
CHANGED
|
@@ -18,7 +18,8 @@ import {
|
|
|
18
18
|
} from '../../src/index.js'
|
|
19
19
|
import { LoginScreen } from '../../src/components/login/index.js'
|
|
20
20
|
import {
|
|
21
|
-
PanelCookieConsent, PanelConsentMenuWidget, appendConsentWidgetService, useConsentMenuPresence
|
|
21
|
+
PanelCookieConsent, PanelConsentMenuWidget, appendConsentWidgetService, useConsentMenuPresence,
|
|
22
|
+
appendConsentGeoPlugin,
|
|
22
23
|
} from '../../src/consent/index.js'
|
|
23
24
|
import { LoginOutcome, ensureLoginService } from '@owlmeans/client-auth/login'
|
|
24
25
|
import type { LoginMethod } from '@owlmeans/client-auth/login'
|
|
@@ -87,10 +88,18 @@ const method = (id: string, over?: Partial<LoginMethod>): LoginMethod => ({
|
|
|
87
88
|
})
|
|
88
89
|
|
|
89
90
|
/** The sign-in screen, with the terms confirmation the configuration requires. */
|
|
90
|
-
const LoginHarness: FC = () =>
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
const LoginHarness: FC = () => {
|
|
92
|
+
const [locale, setLocale] = useState<string | undefined>(localizedTerms ? 'pl' : undefined)
|
|
93
|
+
|
|
94
|
+
return <>
|
|
95
|
+
{localizedTerms && <button id="login-language-fr" onClick={() => setLocale('fr')}>Français</button>}
|
|
96
|
+
<LoginScreen
|
|
97
|
+
Logo={() => <span id="login-logo">logo</span>}
|
|
98
|
+
translate={(_key, defaultValue) => defaultValue}
|
|
99
|
+
locale={locale}
|
|
100
|
+
/>
|
|
101
|
+
</>
|
|
102
|
+
}
|
|
94
103
|
|
|
95
104
|
// `?header=broken` simulates the layout-restyle bug this harness pins: a `headerClassName`
|
|
96
105
|
// carrying an invalid Tailwind v4 arbitrary-value background (v3 syntax, silently dropped) and a
|
|
@@ -110,6 +119,12 @@ const mobileMenu = new URLSearchParams(window.location.search).get('mobileMenu')
|
|
|
110
119
|
// service — an application using the dialog on its own. `?consent=menu` appends the service, so
|
|
111
120
|
// the footer's "Cookie settings" control takes over the floating button's job.
|
|
112
121
|
const consentMode = new URLSearchParams(window.location.search).get('consent')
|
|
122
|
+
// `?consentCfg=window|bar` sets `cfg.cookieConsent.mode`; `?modeProp=bar|window` passes `mode` as a
|
|
123
|
+
// prop on top of it (the prop must win). `?consentGeo=<CC>|fail` appends a context-level locator
|
|
124
|
+
// with `appendConsentGeoPlugin` — the gate turns on with no `geo` configured anywhere.
|
|
125
|
+
const consentCfg = new URLSearchParams(window.location.search).get('consentCfg')
|
|
126
|
+
const modeProp = new URLSearchParams(window.location.search).get('modeProp')
|
|
127
|
+
const consentGeo = new URLSearchParams(window.location.search).get('consentGeo')
|
|
113
128
|
// `?skip=off` passes `skipLinkLabel={false}` — an application that renders its own skip link.
|
|
114
129
|
const skipOff = new URLSearchParams(window.location.search).get('skip') === 'off'
|
|
115
130
|
// `?themeToggle=1` asks for the footer's light/dark switcher. Absent, the prop is not passed.
|
|
@@ -118,6 +133,8 @@ const themeToggle = new URLSearchParams(window.location.search).get('themeToggle
|
|
|
118
133
|
// confirmation — the shape that pins the "still exactly one checkbox" and "documents render
|
|
119
134
|
// outside the notice" rules even when there is more than terms+privacy to show.
|
|
120
135
|
const extendedTerms = new URLSearchParams(window.location.search).get('terms') === 'extended'
|
|
136
|
+
// `?terms=localized` selects known translated destinations using the live screen locale.
|
|
137
|
+
const localizedTerms = new URLSearchParams(window.location.search).get('terms') === 'localized'
|
|
121
138
|
// `?defer=bound` registers a step that CONFIRMS the terms, bound to a real screen — the sign-in
|
|
122
139
|
// screen must then show no checkbox, only the privacy notice, and block nothing.
|
|
123
140
|
// `?defer=unbound` registers the same step but never binds its entrypoint — the checkbox must
|
|
@@ -281,6 +298,16 @@ base.security = {
|
|
|
281
298
|
// is given, and blocking must SAY so rather than swallow the click.
|
|
282
299
|
terms: {
|
|
283
300
|
required: true, terms: 'https://example.test/terms', privacy: 'https://example.test/privacy',
|
|
301
|
+
...(localizedTerms ? {
|
|
302
|
+
billing: { href: 'https://example.test/billing', revisedAt: '2026-10-04' },
|
|
303
|
+
cookies: 'https://example.test/cookies', showRevision: true,
|
|
304
|
+
localizedHrefs: {
|
|
305
|
+
terms: { pl: 'https://example.test/pl/terms', fr: 'https://example.test/fr/terms' },
|
|
306
|
+
billing: { pl: 'https://example.test/pl/billing', fr: 'https://example.test/fr/billing' },
|
|
307
|
+
privacy: { pl: 'https://example.test/pl/privacy', fr: 'https://example.test/fr/privacy' },
|
|
308
|
+
cookies: { pl: 'https://example.test/pl/cookies', fr: 'https://example.test/fr/cookies' },
|
|
309
|
+
},
|
|
310
|
+
} : {}),
|
|
284
311
|
...(extendedTerms ? {
|
|
285
312
|
billing: { href: 'https://example.test/billing', revisedAt: '2026-01-01' },
|
|
286
313
|
product: { name: 'Harness', href: 'https://example.test/product' },
|
|
@@ -309,6 +336,9 @@ if (providerPlacement != null) {
|
|
|
309
336
|
// test that needs the OFF case loads `?reloadDialog=0` — one harness process, both branches.
|
|
310
337
|
const reloadDialogEnabled = new URLSearchParams(window.location.search).get('reloadDialog') !== '0'
|
|
311
338
|
;(base as { socket?: { reloadDialog?: boolean } }).socket = { reloadDialog: reloadDialogEnabled }
|
|
339
|
+
if (consentCfg === 'window' || consentCfg === 'bar') {
|
|
340
|
+
;(base as { cookieConsent?: { mode: 'window' | 'bar' } }).cookieConsent = { mode: consentCfg }
|
|
341
|
+
}
|
|
312
342
|
|
|
313
343
|
// `ready` stays false: the Router compiles the entrypoint tree into routes ONLY while the
|
|
314
344
|
// context is un-initialized, so a pre-readied context renders a blank page.
|
|
@@ -319,6 +349,18 @@ context.serviceRoute(API, true)
|
|
|
319
349
|
if (consentMode === 'menu') {
|
|
320
350
|
appendConsentWidgetService(context as never)
|
|
321
351
|
}
|
|
352
|
+
if (consentGeo != null) {
|
|
353
|
+
appendConsentGeoPlugin(context as never, {
|
|
354
|
+
alias: 'harness-geo',
|
|
355
|
+
locate: async () => {
|
|
356
|
+
if (consentGeo === 'fail') {
|
|
357
|
+
throw new Error('the harness locator cannot tell')
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return { country: consentGeo }
|
|
361
|
+
},
|
|
362
|
+
})
|
|
363
|
+
}
|
|
322
364
|
|
|
323
365
|
ensureLoginService(context as never).registerMethodSource({
|
|
324
366
|
alias: 'harness',
|
|
@@ -390,5 +432,6 @@ createRoot(document.getElementById('root')!).render(<PanelApp context={context a
|
|
|
390
432
|
{consentMode != null && <PanelCookieConsent
|
|
391
433
|
policyHref="/cookies"
|
|
392
434
|
{...(withDomains ? { linker: { domains: ['harness-partner.test'] } } : {})}
|
|
435
|
+
{...(modeProp === 'window' || modeProp === 'bar' ? { mode: modeProp } : {})}
|
|
393
436
|
/>}
|
|
394
437
|
</PanelApp>)
|
package/tests/login.spec.ts
CHANGED
|
@@ -191,6 +191,28 @@ describe('@owlmeans/web-panel — the sign-in screen', () => {
|
|
|
191
191
|
}
|
|
192
192
|
}, TIMEOUT)
|
|
193
193
|
|
|
194
|
+
test('login documents and the separate privacy notice follow an explicit live PL/FR locale map', async () => {
|
|
195
|
+
const { page, close } = await open('?terms=localized')
|
|
196
|
+
try {
|
|
197
|
+
for (const key of ['terms', 'billing', 'privacy', 'cookies']) {
|
|
198
|
+
expect(await page.locator(`[data-login-document="${key}"]`).getAttribute('href'))
|
|
199
|
+
.toBe(`https://example.test/pl/${key}`)
|
|
200
|
+
}
|
|
201
|
+
expect(await page.locator('[data-login-privacy] a').count()).toBe(2)
|
|
202
|
+
expect(await page.locator('label:has([data-login-terms]) [data-login-privacy]').count()).toBe(0)
|
|
203
|
+
const revision = await page.locator('[data-login-revised]').textContent()
|
|
204
|
+
await page.locator('#login-language-fr').click()
|
|
205
|
+
for (const key of ['terms', 'billing', 'privacy', 'cookies']) {
|
|
206
|
+
expect(await page.locator(`[data-login-document="${key}"]`).getAttribute('href'))
|
|
207
|
+
.toBe(`https://example.test/fr/${key}`)
|
|
208
|
+
}
|
|
209
|
+
expect(await page.locator('[data-login-revised]').textContent()).toBe(revision)
|
|
210
|
+
expect(await page.locator('[data-login-terms]').count()).toBe(1)
|
|
211
|
+
} finally {
|
|
212
|
+
await close()
|
|
213
|
+
}
|
|
214
|
+
}, TIMEOUT)
|
|
215
|
+
|
|
194
216
|
test('[data-login-revised] is absent when the configuration never asked to show it', async () => {
|
|
195
217
|
const { page, close } = await open()
|
|
196
218
|
try {
|