react-lgpd-consent 0.3.6 → 0.4.0

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/CHANGELOG.md CHANGED
@@ -4,6 +4,37 @@ Todas as mudanças notáveis neste projeto serão documentadas neste arquivo.
4
4
 
5
5
  O formato é baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.0.0/), e este projeto segue [Semantic Versioning](https://semver.org/lang/pt-BR/).
6
6
 
7
+ ## [0.4.0] - 2025-09-09 — Custom categories
8
+
9
+ ### Added
10
+ - Support for `customCategories` in `ConsentProvider.categories`.
11
+ - Included in preferences initialization and validation.
12
+ - Shown in the Preferences modal (with name/description).
13
+ - Exposed via developer guidance/context for custom UIs.
14
+ - Quickstart PT/EN sections with `customCategories` examples.
15
+ - Storybook story: WithCustomCategories.
16
+
17
+ ### Notes
18
+ - Non-breaking change; existing configurations continue to work.
19
+
20
+ ## [0.3.7] - 2025-09-08 - Testes de UI e carregamento de scripts
21
+
22
+ ### 🧪 Novos testes e cobertura
23
+
24
+ - CookieBanner
25
+ - Testes para renderização condicional em modos bloqueante (overlay) e não-bloqueante (Snackbar)
26
+ - Verificação de abertura do modal ao clicar em “Preferências” e persistência ao clicar em “Recusar”
27
+ - ConsentScriptLoader / Hook
28
+ - Gating por consentimento e categoria; não carrega scripts quando não consentido ou categoria desabilitada
29
+ - Tratamento de erros (log `logger.error` quando `loadScript` rejeita)
30
+ - `reloadOnChange` reexecuta o carregamento ao reabilitar a categoria; default não recarrega
31
+ - Integrações de script
32
+ - Verificação de `attrs` em integrações (GA define `async: 'true'`)
33
+
34
+ ### 🔧 Interno
35
+
36
+ - Aumento da estabilidade para refatorações futuras na camada de UI e utilitários de carregamento.
37
+
7
38
  ## [0.3.6] - 2025-08-28 - Correção crítica: Herança de ThemeProvider
8
39
 
9
40
  ### ✨ Novas funcionalidades e melhorias
package/QUICKSTART.en.md CHANGED
@@ -39,6 +39,49 @@ function App() {
39
39
  export default App
40
40
  ```
41
41
 
42
+ ## 🧩 Custom categories (customCategories)
43
+ Available since v0.4.0.
44
+
45
+ Add project-specific categories (e.g., support chat, video players, A/B testing):
46
+
47
+ ```tsx
48
+ <ConsentProvider
49
+ categories={{
50
+ enabledCategories: ['analytics'],
51
+ customCategories: [
52
+ { id: 'chat', name: 'Support Chat', description: 'Chat widget' },
53
+ { id: 'video', name: 'Video', description: 'Embedded players' },
54
+ { id: 'abTesting', name: 'A/B Testing', description: 'Interface experiments' },
55
+ ],
56
+ }}
57
+ >
58
+ <App />
59
+ </ConsentProvider>
60
+ ```
61
+
62
+ ### Using custom categories in your code
63
+
64
+ ```tsx
65
+ import { useConsent } from 'react-lgpd-consent'
66
+
67
+ function MyComponent() {
68
+ const { consent } = useConsent()
69
+
70
+ // Check if user consented to specific categories
71
+ const canShowChat = consent?.preferences?.chat === true
72
+ const canLoadVideos = consent?.preferences?.video === true
73
+ const canRunABTests = consent?.preferences?.abTesting === true
74
+
75
+ return (
76
+ <div>
77
+ {canShowChat && <ChatWidget />}
78
+ {canLoadVideos && <VideoPlayer src="..." />}
79
+ {canRunABTests && <ABTestVariant />}
80
+ </div>
81
+ )
82
+ }
83
+ ```
84
+
42
85
  ## 🧭 Storybook — quick note
43
86
 
44
87
  This repository ships an interactive Storybook playground used for manual testing and visual exploration of components. Quick commands:
package/QUICKSTART.md CHANGED
@@ -62,6 +62,49 @@ export default App
62
62
 
63
63
  ````
64
64
 
65
+ ## 🧩 Categorias customizadas (customCategories)
66
+ Disponível a partir da v0.4.0.
67
+
68
+ Adicione categorias específicas do seu projeto (ex.: chat de suporte, players de vídeo, AB testing):
69
+
70
+ ```tsx
71
+ <ConsentProvider
72
+ categories={{
73
+ enabledCategories: ['analytics'],
74
+ customCategories: [
75
+ { id: 'chat', name: 'Chat de Suporte', description: 'Widget de chat' },
76
+ { id: 'video', name: 'Vídeo', description: 'Players incorporados' },
77
+ { id: 'abTesting', name: 'A/B Testing', description: 'Experimentos de interface' },
78
+ ],
79
+ }}
80
+ >
81
+ <App />
82
+ </ConsentProvider>
83
+ ```
84
+
85
+ ### Usando categorias customizadas no seu código
86
+
87
+ ```tsx
88
+ import { useConsent } from 'react-lgpd-consent'
89
+
90
+ function MyComponent() {
91
+ const { consent } = useConsent()
92
+
93
+ // Verificar se o usuário consentiu com categorias específicas
94
+ const canShowChat = consent?.preferences?.chat === true
95
+ const canLoadVideos = consent?.preferences?.video === true
96
+ const canRunABTests = consent?.preferences?.abTesting === true
97
+
98
+ return (
99
+ <div>
100
+ {canShowChat && <ChatWidget />}
101
+ {canLoadVideos && <VideoPlayer src="..." />}
102
+ {canRunABTests && <ABTestVariant />}
103
+ </div>
104
+ )
105
+ }
106
+ ```
107
+
65
108
  ## 📋 Tabela Completa de Props do ConsentProvider
66
109
 
67
110
  | Prop | Tipo | Obrigatória | Padrão | Descrição |
package/README.en.md CHANGED
@@ -63,6 +63,7 @@ export default function App() {
63
63
  - For full guides, TypeScript examples, props table and integrations see:
64
64
  -
65
65
  - **[QUICKSTART.en.md](./QUICKSTART.en.md)** (recommended)
66
+ - New in v0.4.0: `customCategories` support — see the “Custom categories (customCategories)” section in the Quickstart.
66
67
  - **[Docs / API](./API.md)**
67
68
  - **[LGPD Compliance](./CONFORMIDADE.md)**
68
69
  - **[Integrations](./INTEGRACOES.md)**
package/README.md CHANGED
@@ -87,6 +87,7 @@ Para mais detalhes sobre customização, hooks e funcionalidades, consulte os se
87
87
  ### 📋 Documentação Principal
88
88
 
89
89
  - **[📚 Guia de Início Rápido (`QUICKSTART.md`)](./QUICKSTART.md)**: Tutorial passo a passo com exemplos práticos, tabela completa de props, debugging e integrações.
90
+ - Novo na v0.4.0: suporte a `customCategories` — veja a seção “Categorias customizadas (customCategories)” no Quickstart.
90
91
  - **[Guia da API (`API.md`)](./API.md)**: Referência completa de todos os componentes, hooks e tipos.
91
92
  - **[Guia de Conformidade (`CONFORMIDADE.md`)](./CONFORMIDADE.md)**: Detalhes sobre as funcionalidades de conformidade com a LGPD.
92
93
  - **[Guia de Integrações (`INTEGRACOES.md`)](./INTEGRACOES.md)**: Como usar as integrações nativas e criar as suas.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  PreferencesModal
3
- } from "./chunk-MHCQFGRJ.js";
3
+ } from "./chunk-B5FNONG3.js";
4
4
  export {
5
5
  PreferencesModal
6
6
  };
@@ -73,6 +73,17 @@ function analyzeDeveloperConfiguration(config) {
73
73
  });
74
74
  }
75
75
  });
76
+ const custom = finalConfig.customCategories || [];
77
+ custom.forEach((cat) => {
78
+ if (!cat?.id || cat.id === "necessary") return;
79
+ guidance.activeCategoriesInfo.push({
80
+ id: cat.id,
81
+ name: cat.name,
82
+ description: cat.description,
83
+ essential: !!cat.essential,
84
+ uiRequired: !cat.essential
85
+ });
86
+ });
76
87
  const totalToggleable = guidance.activeCategoriesInfo.filter((c) => c.uiRequired).length;
77
88
  if (totalToggleable === 0) {
78
89
  guidance.suggestions.push(
@@ -87,9 +98,10 @@ function analyzeDeveloperConfiguration(config) {
87
98
  return guidance;
88
99
  }
89
100
  function logDeveloperGuidance(guidance, disableGuidanceProp) {
90
- const nodeEnv = typeof globalThis.process !== "undefined" ? globalThis.process.env?.NODE_ENV : void 0;
91
- const isProd = nodeEnv === "production" || globalThis.__LGPD_PRODUCTION__ === true && typeof globalThis !== "undefined";
92
- const isDisabled = !!disableGuidanceProp || globalThis.__LGPD_DISABLE_GUIDANCE__ === true && typeof globalThis !== "undefined";
101
+ const gt = globalThis;
102
+ const nodeEnv = typeof gt.process !== "undefined" ? gt.process?.env?.NODE_ENV : void 0;
103
+ const isProd = nodeEnv === "production" || gt.__LGPD_PRODUCTION__ === true;
104
+ const isDisabled = !!disableGuidanceProp || gt.__LGPD_DISABLE_GUIDANCE__ === true;
93
105
  if (isProd || isDisabled) return;
94
106
  const PREFIX = "[\u{1F36A} LGPD-CONSENT]";
95
107
  if (guidance.warnings.length > 0) {
@@ -217,7 +229,7 @@ var _ConsentLogger = class _ConsentLogger {
217
229
  }
218
230
  /**
219
231
  * Registra uma mensagem de erro.
220
- * @param {...any[]} args Os argumentos a serem logados.
232
+ * @param {...unknown[]} args Argumentos a serem logados.
221
233
  */
222
234
  error(...args) {
223
235
  if (this.enabled && this.level >= 0 /* ERROR */) {
@@ -226,7 +238,7 @@ var _ConsentLogger = class _ConsentLogger {
226
238
  }
227
239
  /**
228
240
  * Registra uma mensagem de aviso.
229
- * @param {...any[]} args Os argumentos a serem logados.
241
+ * @param {...unknown[]} args Argumentos a serem logados.
230
242
  */
231
243
  warn(...args) {
232
244
  if (this.enabled && this.level >= 1 /* WARN */) {
@@ -235,7 +247,7 @@ var _ConsentLogger = class _ConsentLogger {
235
247
  }
236
248
  /**
237
249
  * Registra uma mensagem informativa.
238
- * @param {...any[]} args Os argumentos a serem logados.
250
+ * @param {...unknown[]} args Argumentos a serem logados.
239
251
  */
240
252
  info(...args) {
241
253
  if (this.enabled && this.level >= 2 /* INFO */) {
@@ -244,7 +256,7 @@ var _ConsentLogger = class _ConsentLogger {
244
256
  }
245
257
  /**
246
258
  * Registra uma mensagem de depuração.
247
- * @param {...any[]} args Os argumentos a serem logados.
259
+ * @param {...unknown[]} args Argumentos a serem logados.
248
260
  */
249
261
  debug(...args) {
250
262
  if (this.enabled && this.level >= 3 /* DEBUG */) {
@@ -253,7 +265,7 @@ var _ConsentLogger = class _ConsentLogger {
253
265
  }
254
266
  /**
255
267
  * Inicia um grupo de logs no console.
256
- * @param {...any[]} args Os argumentos para o título do grupo.
268
+ * @param {...unknown[]} args Argumentos para o título do grupo.
257
269
  */
258
270
  group(...args) {
259
271
  if (this.enabled && this.level >= 3 /* DEBUG */) {
@@ -270,8 +282,8 @@ var _ConsentLogger = class _ConsentLogger {
270
282
  }
271
283
  /**
272
284
  * Exibe dados tabulares no console.
273
- * @param {any} tabularData Os dados a serem exibidos na tabela.
274
- * @param {string[]} [properties] Um array opcional de propriedades para exibir.
285
+ * @param {unknown} tabularData Dados a serem exibidos na tabela.
286
+ * @param {string[]} [properties] Propriedades opcionais para exibir.
275
287
  */
276
288
  table(tabularData, properties) {
277
289
  if (this.enabled && this.level >= 3 /* DEBUG */) {
@@ -280,21 +292,27 @@ var _ConsentLogger = class _ConsentLogger {
280
292
  }
281
293
  /**
282
294
  * Registra informações sobre a compatibilidade do tema Material-UI.
283
- * @param {any} themeInfo Objeto com informações do tema.
295
+ * @param {unknown} themeInfo Objeto potencialmente parcial do tema (inspeção segura).
284
296
  */
285
297
  themeCompatibility(themeInfo) {
298
+ const isRecord = (v) => typeof v === "object" && v !== null;
299
+ const theme = isRecord(themeInfo) ? themeInfo : void 0;
300
+ const palette = theme && isRecord(theme["palette"]) ? theme["palette"] : void 0;
301
+ const primary = palette && isRecord(palette["primary"]);
302
+ const transitions = theme && isRecord(theme["transitions"]) ? theme["transitions"] : void 0;
303
+ const duration = transitions && isRecord(transitions["duration"]);
286
304
  this.debug("Theme compatibility check:", {
287
- hasTheme: !!themeInfo,
288
- hasPalette: !!themeInfo?.palette,
289
- hasPrimary: !!themeInfo?.palette?.primary,
290
- hasTransitions: !!themeInfo?.transitions,
291
- hasDuration: !!themeInfo?.transitions?.duration
305
+ hasTheme: !!theme,
306
+ hasPalette: !!palette,
307
+ hasPrimary: !!primary,
308
+ hasTransitions: !!transitions,
309
+ hasDuration: !!duration
292
310
  });
293
311
  }
294
312
  /**
295
313
  * Registra mudanças no estado de consentimento.
296
- * @param {string} action A ação que causou a mudança de estado.
297
- * @param {any} state O estado atual do consentimento.
314
+ * @param {string} action Ação que causou a mudança de estado.
315
+ * @param {{ consented?: boolean; isModalOpen?: boolean; preferences?: Record<string, unknown> }} state Estado atual.
298
316
  */
299
317
  consentState(action, state) {
300
318
  this.debug(`Consent state change [${action}]:`, {
@@ -305,9 +323,9 @@ var _ConsentLogger = class _ConsentLogger {
305
323
  }
306
324
  /**
307
325
  * Registra operações de cookie (leitura, escrita, remoção).
308
- * @param {'read' | 'write' | 'delete'} operation O tipo de operação de cookie.
309
- * @param {string} cookieName O nome do cookie.
310
- * @param {any} [data] Os dados do cookie, se aplicável.
326
+ * @param {'read' | 'write' | 'delete'} operation Tipo de operação.
327
+ * @param {string} cookieName Nome do cookie.
328
+ * @param {unknown} [data] Dados associados, se aplicável.
311
329
  */
312
330
  cookieOperation(operation, cookieName, data) {
313
331
  this.debug(`Cookie ${operation}:`, {
@@ -318,8 +336,8 @@ var _ConsentLogger = class _ConsentLogger {
318
336
  }
319
337
  /**
320
338
  * Registra a renderização de um componente.
321
- * @param {string} componentName O nome do componente.
322
- * @param {any} [props] As propriedades do componente.
339
+ * @param {string} componentName Nome do componente.
340
+ * @param {Record<string, unknown>} [props] Propriedades do componente.
323
341
  */
324
342
  componentRender(componentName, props) {
325
343
  this.debug(`Component render [${componentName}]:`, {
@@ -338,8 +356,8 @@ var _ConsentLogger = class _ConsentLogger {
338
356
  }
339
357
  /**
340
358
  * Registra chamadas à API interna da biblioteca.
341
- * @param {string} method O nome do método da API chamado.
342
- * @param {any} [params] Os parâmetros passados para o método.
359
+ * @param {string} method Nome do método da API chamado.
360
+ * @param {unknown} [params] Parâmetros passados para o método.
343
361
  */
344
362
  apiUsage(method, params) {
345
363
  this.debug(`API call [${method}]:`, params);
@@ -397,8 +415,8 @@ function migrateLegacyCookie(legacyData) {
397
415
  const now = (/* @__PURE__ */ new Date()).toISOString();
398
416
  return {
399
417
  version: COOKIE_SCHEMA_VERSION,
400
- consented: legacyData.consented || false,
401
- preferences: legacyData.preferences || { necessary: true },
418
+ consented: Boolean(legacyData.consented) || false,
419
+ preferences: legacyData.preferences && typeof legacyData.preferences === "object" ? legacyData.preferences : { necessary: true },
402
420
  consentDate: now,
403
421
  lastUpdate: now,
404
422
  source: "banner",
@@ -460,6 +478,12 @@ function createProjectPreferences(config, defaultValue = false) {
460
478
  preferences[category] = defaultValue;
461
479
  }
462
480
  });
481
+ const custom = config?.customCategories || [];
482
+ custom.forEach((cat) => {
483
+ if (cat.id && cat.id !== "necessary") {
484
+ preferences[cat.id] = defaultValue;
485
+ }
486
+ });
463
487
  return preferences;
464
488
  }
465
489
  function validateProjectPreferences(preferences, config) {
@@ -473,6 +497,13 @@ function validateProjectPreferences(preferences, config) {
473
497
  validPreferences[category] = preferences[category];
474
498
  }
475
499
  });
500
+ const custom = config?.customCategories || [];
501
+ custom.forEach((cat) => {
502
+ const id = cat.id;
503
+ if (id && id !== "necessary" && preferences[id] !== void 0) {
504
+ validPreferences[id] = preferences[id];
505
+ }
506
+ });
476
507
  return validPreferences;
477
508
  }
478
509
  function getAllProjectCategories(config) {
@@ -490,6 +521,12 @@ function getAllProjectCategories(config) {
490
521
  allCategories.push(getDefaultCategoryDefinition(category));
491
522
  }
492
523
  });
524
+ const custom = config?.customCategories || [];
525
+ custom.forEach((cat) => {
526
+ if (cat.id && cat.id !== "necessary") {
527
+ allCategories.push({ ...cat, essential: !!cat.essential });
528
+ }
529
+ });
493
530
  return allCategories;
494
531
  }
495
532
  function getDefaultCategoryDefinition(category) {
@@ -848,7 +885,7 @@ function FloatingPreferencesButton({
848
885
  // src/context/ConsentContext.tsx
849
886
  import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
850
887
  var PreferencesModal = React4.lazy(
851
- () => import("./PreferencesModal-KAZMVPBD.js").then((m) => ({
888
+ () => import("./PreferencesModal-D2SEVH3N.js").then((m) => ({
852
889
  default: m.PreferencesModal
853
890
  }))
854
891
  );
@@ -1142,12 +1179,17 @@ function ConsentProvider({
1142
1179
  }
1143
1180
  ) : (
1144
1181
  // Encaminha `floatingPreferencesButtonProps` para o componente padrão
1145
- /* @__PURE__ */ jsx6(FloatingPreferencesButton, { ...floatingPreferencesButtonProps })
1182
+ /* @__PURE__ */ jsx6(
1183
+ FloatingPreferencesButton,
1184
+ {
1185
+ ...floatingPreferencesButtonProps ?? {}
1186
+ }
1187
+ )
1146
1188
  ))
1147
1189
  ]
1148
1190
  }
1149
1191
  ) }) }) }) }) });
1150
- if (theme) {
1192
+ if (mergedTheme) {
1151
1193
  return /* @__PURE__ */ jsx6(ThemeProvider, { theme: mergedTheme, children: content });
1152
1194
  }
1153
1195
  return content;