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 +31 -0
- package/QUICKSTART.en.md +43 -0
- package/QUICKSTART.md +43 -0
- package/README.en.md +1 -0
- package/README.md +1 -0
- package/dist/{PreferencesModal-KAZMVPBD.js → PreferencesModal-D2SEVH3N.js} +1 -1
- package/dist/{chunk-MHCQFGRJ.js → chunk-B5FNONG3.js} +72 -30
- package/dist/index.cjs +87 -54
- package/dist/index.d.cts +85 -38
- package/dist/index.d.ts +85 -38
- package/dist/index.js +17 -26
- package/package.json +48 -46
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.
|
|
@@ -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
|
|
91
|
-
const
|
|
92
|
-
const
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {
|
|
274
|
-
* @param {string[]} [properties]
|
|
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 {
|
|
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: !!
|
|
288
|
-
hasPalette: !!
|
|
289
|
-
hasPrimary: !!
|
|
290
|
-
hasTransitions: !!
|
|
291
|
-
hasDuration: !!
|
|
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
|
|
297
|
-
* @param {
|
|
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
|
|
309
|
-
* @param {string} cookieName
|
|
310
|
-
* @param {
|
|
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
|
|
322
|
-
* @param {
|
|
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
|
|
342
|
-
* @param {
|
|
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
|
|
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-
|
|
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(
|
|
1182
|
+
/* @__PURE__ */ jsx6(
|
|
1183
|
+
FloatingPreferencesButton,
|
|
1184
|
+
{
|
|
1185
|
+
...floatingPreferencesButtonProps ?? {}
|
|
1186
|
+
}
|
|
1187
|
+
)
|
|
1146
1188
|
))
|
|
1147
1189
|
]
|
|
1148
1190
|
}
|
|
1149
1191
|
) }) }) }) }) });
|
|
1150
|
-
if (
|
|
1192
|
+
if (mergedTheme) {
|
|
1151
1193
|
return /* @__PURE__ */ jsx6(ThemeProvider, { theme: mergedTheme, children: content });
|
|
1152
1194
|
}
|
|
1153
1195
|
return content;
|