diff --git a/src/next-appdir/index.ts b/src/next-appdir/index.ts
index e0b44f2b..97685805 100644
--- a/src/next-appdir/index.ts
+++ b/src/next-appdir/index.ts
@@ -1,3 +1,4 @@
export type { RegisterLink } from "../link";
export type { DefaultColorScheme } from "./zz_internal/defaultColorScheme";
export { startReactDsfr } from "./zz_internal/start";
+export type { EulerianAnalytics } from "../start";
diff --git a/src/next-appdir/zz_internal/start.ts b/src/next-appdir/zz_internal/start.ts
index aa2f19c1..761ba668 100644
--- a/src/next-appdir/zz_internal/start.ts
+++ b/src/next-appdir/zz_internal/start.ts
@@ -1,5 +1,5 @@
import type { ReactNode } from "react";
-import { start } from "../../start";
+import { start, type EulerianAnalytics } from "../../start";
import type { RegisteredLinkProps } from "../../link";
import { setLink } from "../../link";
import { type DefaultColorScheme, setDefaultColorSchemeClientSide } from "./defaultColorScheme";
@@ -14,8 +14,9 @@ export function startReactDsfr(params: {
verbose?: boolean;
/** Default: */
Link?: (props: RegisteredLinkProps & { children: ReactNode }) => ReturnType;
+ eulerianAnalytics?: EulerianAnalytics;
}) {
- const { defaultColorScheme, verbose = false, Link } = params;
+ const { defaultColorScheme, verbose = false, Link, eulerianAnalytics } = params;
setDefaultColorSchemeClientSide({ defaultColorScheme });
@@ -27,6 +28,7 @@ export function startReactDsfr(params: {
start({
defaultColorScheme,
verbose,
+ eulerianAnalytics,
"nextParams": {
"doPersistDarkModePreferenceWithCookie": false,
"registerEffectAction": action => {
diff --git a/src/next-pagesdir.tsx b/src/next-pagesdir.tsx
index 3f58c480..8fe86965 100644
--- a/src/next-pagesdir.tsx
+++ b/src/next-pagesdir.tsx
@@ -16,13 +16,15 @@ import FaviconSvg from "./dsfr/favicon/favicon.svg";
import FaviconIco from "./dsfr/favicon/favicon.ico";
import { getAssetUrl } from "./tools/getAssetUrl";
import { getColors } from "./fr/colors";
-import { start } from "./start";
+import { start, type EulerianAnalytics } from "./start";
import type { RegisterLink, RegisteredLinkProps } from "./link";
import { setLink } from "./link";
import { setUseLang } from "./i18n";
import { assert } from "tsafe/assert";
import "./assets/dsfr_plus_icons.css";
+export type { EulerianAnalytics };
+
const isProduction = process.env.NODE_ENV !== "development";
export type { RegisterLink, RegisteredLinkProps };
@@ -41,6 +43,7 @@ export type CreateNextDsfrIntegrationApiParams = {
doPersistDarkModePreferenceWithCookie?: boolean;
/** Default: ()=> "fr" */
useLang?: () => string;
+ eulerianAnalytics?: EulerianAnalytics;
};
function readIsDarkInCookie(cookie: string) {
@@ -88,7 +91,8 @@ export function createNextDsfrIntegrationApi(
Link,
preloadFonts = [],
doPersistDarkModePreferenceWithCookie = false,
- useLang
+ useLang,
+ eulerianAnalytics
} = params;
let isAfterFirstEffect = false;
@@ -106,6 +110,7 @@ export function createNextDsfrIntegrationApi(
start({
defaultColorScheme,
verbose,
+ eulerianAnalytics,
"nextParams": {
doPersistDarkModePreferenceWithCookie,
"registerEffectAction": action => {
diff --git a/src/spa.ts b/src/spa.ts
index 2b25d6f3..18598738 100644
--- a/src/spa.ts
+++ b/src/spa.ts
@@ -1,10 +1,12 @@
import type { ReactNode } from "react";
-import { start } from "./start";
+import { start, type EulerianAnalytics } from "./start";
import type { RegisterLink, RegisteredLinkProps } from "./link";
import { setLink } from "./link";
import { setUseLang } from "./i18n";
import type { ColorScheme } from "./useIsDark";
+export type { EulerianAnalytics };
+
export type { RegisterLink, RegisteredLinkProps };
export function startReactDsfr(params: {
@@ -15,8 +17,9 @@ export function startReactDsfr(params: {
Link?: (props: RegisteredLinkProps & { children: ReactNode }) => ReturnType;
/** Default: ()=> "fr" */
useLang?: () => string;
+ eulerianAnalytics?: EulerianAnalytics;
}) {
- const { defaultColorScheme, verbose = false, Link, useLang } = params;
+ const { defaultColorScheme, verbose = false, Link, useLang, eulerianAnalytics } = params;
if (Link !== undefined) {
setLink({ Link });
@@ -29,6 +32,7 @@ export function startReactDsfr(params: {
start({
defaultColorScheme,
verbose,
+ eulerianAnalytics,
"nextParams": undefined
});
}
diff --git a/src/start.ts b/src/start.ts
index 19b6fd90..eae2b2aa 100644
--- a/src/start.ts
+++ b/src/start.ts
@@ -6,6 +6,7 @@ import { startClientSideIsDarkLogic } from "./useIsDark/client";
type Params = {
defaultColorScheme: ColorScheme | "system";
verbose: boolean;
+ eulerianAnalytics: EulerianAnalytics | undefined;
nextParams:
| {
doPersistDarkModePreferenceWithCookie: boolean;
@@ -17,7 +18,7 @@ type Params = {
let isStarted = false;
export async function start(params: Params) {
- const { defaultColorScheme, verbose, nextParams } = params;
+ const { defaultColorScheme, verbose, nextParams, eulerianAnalytics } = params;
assert(isBrowser);
@@ -37,11 +38,89 @@ export async function start(params: Params) {
registerEffectAction
});
- (window as any).dsfr = { verbose, "mode": "react" };
+ (window as any).dsfr = {
+ verbose,
+ "mode": "react",
+ "analytics": eulerianAnalytics
+ };
await import("./dsfr/dsfr.module" as any);
+ if (eulerianAnalytics !== undefined) {
+ await import("./dsfr/analytics/analytics.module.js" as any);
+ }
+
const { dsfr } = window as unknown as { dsfr: { start: () => void } };
registerEffectAction(() => dsfr.start());
}
+
+export type EulerianAnalytics = {
+ domain: string;
+ /** default: false */
+ enableRating?: boolean;
+ page?: Partial<{
+ path: string; // path for page tracking
+ referrer: string; // referrer for virtual pages (not for real page, eulerian automatically collects document.referrer)
+ id: string; // unique page id (string)
+ title: string; // page title for virtual pages
+ name: string; // equivalent to title if not defined
+ author: string; // page author name
+ date: string; // page creation date
+ labels: string[];
+ tags: string[]; // no tags limit
+ template: string; // page template
+ group: string; // page group. if not defined, fallback to template value
+ segment: string; // site segment. if not defined, fallback to template value
+ subtemplate: string; // page subtemplate
+ theme: string; // page theme
+ subtheme: string; // page subtheme
+ related: string; // related page id
+ depth: number; // page depth
+ isError: boolean; // is this an error page (404, 500, 503...)
+ current: number; // In case of pagination, current page number
+ total: number; // In case of pagination, total pages number
+ filters: string; // array of filters that were applied on the page (strings)
+ }>;
+ site?: Partial<{
+ environment: "production" | "stage" | "production"; // by default development ['development', 'stage', 'production']
+ entity: string; // Entity responsible for website
+ language: string; // language of the website (ISO 639-1). default to html lang
+ target: string; // site target
+ type: string; // site type
+ region: string; // region of the website (ISO 3166-2:FR)
+ department: string; // department of the website (ISO 3166-2:FR)
+ }>;
+ user?: Partial<{
+ connect: {
+ uid: string; // user id - required when connected
+ email: string; // encoded user email - required when connected
+ isNew: boolean; // user just registered
+ };
+ profile: string; // user profile
+ language: string;
+ type: string;
+ }>;
+ search?: Partial<{
+ engine: string;
+ results: number;
+ terms: string;
+ category: string;
+ theme: string;
+ type: string;
+ method: string;
+ }>;
+ funnel?: Partial<{
+ id: string;
+ type: string;
+ name: string;
+ step: string; // step name
+ current: number; // step number
+ total: number; // total number of steps
+ objective: string; // form objective
+ error: string; // form's error type
+ }>;
+ cmp?: Partial<{
+ id: string;
+ }>;
+};