@redacto.io/consent-sdk-react 9.2.0 → 9.3.0-beta.1

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.
Files changed (41) hide show
  1. package/.turbo/turbo-build.log +25 -26
  2. package/.turbo/turbo-test.log +203 -0
  3. package/.turbo/turbo-verify-pack.log +6 -0
  4. package/CHANGELOG.md +12 -0
  5. package/dist/privacy-center.js +3242 -2956
  6. package/dist/privacy-center.mjs +3068 -2774
  7. package/package.json +3 -3
  8. package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +6 -3
  9. package/src/RedactoPrivacyCenter/components/AppShell/AppShell.tsx +36 -5
  10. package/src/RedactoPrivacyCenter/components/AppShell/TopAppBar.tsx +3 -2
  11. package/src/RedactoPrivacyCenter/components/Form.tsx +127 -8
  12. package/src/RedactoPrivacyCenter/components/PrivacyCenterEmptyState.tsx +76 -0
  13. package/src/RedactoPrivacyCenter/components/ReceiptsList.tsx +3 -41
  14. package/src/RedactoPrivacyCenter/context/DataAvailabilityContext.tsx +169 -0
  15. package/src/RedactoPrivacyCenter/locales/as/translation.json +2 -0
  16. package/src/RedactoPrivacyCenter/locales/bho/translation.json +2 -0
  17. package/src/RedactoPrivacyCenter/locales/bn/translation.json +2 -0
  18. package/src/RedactoPrivacyCenter/locales/brx/translation.json +2 -0
  19. package/src/RedactoPrivacyCenter/locales/doi/translation.json +2 -0
  20. package/src/RedactoPrivacyCenter/locales/en/translation.json +2 -0
  21. package/src/RedactoPrivacyCenter/locales/gom/translation.json +2 -0
  22. package/src/RedactoPrivacyCenter/locales/gu/translation.json +2 -0
  23. package/src/RedactoPrivacyCenter/locales/hi/translation.json +2 -0
  24. package/src/RedactoPrivacyCenter/locales/kn/translation.json +2 -0
  25. package/src/RedactoPrivacyCenter/locales/ks/translation.json +2 -0
  26. package/src/RedactoPrivacyCenter/locales/mai/translation.json +2 -0
  27. package/src/RedactoPrivacyCenter/locales/ml/translation.json +2 -0
  28. package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +2 -0
  29. package/src/RedactoPrivacyCenter/locales/mr/translation.json +2 -0
  30. package/src/RedactoPrivacyCenter/locales/ne/translation.json +2 -0
  31. package/src/RedactoPrivacyCenter/locales/or/translation.json +2 -0
  32. package/src/RedactoPrivacyCenter/locales/pa/translation.json +2 -0
  33. package/src/RedactoPrivacyCenter/locales/sa/translation.json +2 -0
  34. package/src/RedactoPrivacyCenter/locales/sat/translation.json +2 -0
  35. package/src/RedactoPrivacyCenter/locales/sd/translation.json +2 -0
  36. package/src/RedactoPrivacyCenter/locales/ta/translation.json +2 -0
  37. package/src/RedactoPrivacyCenter/locales/te/translation.json +2 -0
  38. package/src/RedactoPrivacyCenter/locales/ur/translation.json +2 -0
  39. package/src/RedactoPrivacyCenter/styles/injectStyles.ts +1 -1
  40. package/tests/DataAvailabilityContext.test.tsx +123 -0
  41. package/tests/Form.test.tsx +93 -1
@@ -694,6 +694,13 @@ describe('Form (P2-SDK-3)', () => {
694
694
  fireEvent.click(submitButton);
695
695
  });
696
696
 
697
+ // ERASURE asks whether to also revoke consent before it submits.
698
+ // Choosing "Yes" (revokeConsentYes) sets revoke_consent_on_fulfilment=true.
699
+ const revokeYesButton = await screen.findByRole('button', { name: 'Yes' });
700
+ await act(async () => {
701
+ fireEvent.click(revokeYesButton);
702
+ });
703
+
697
704
  await waitFor(() => {
698
705
  expect(createCaseRequest).toHaveBeenCalledTimes(1);
699
706
  });
@@ -703,7 +710,7 @@ describe('Form (P2-SDK-3)', () => {
703
710
  expect('data_principal_uuid' in payload).toBe(false);
704
711
  expect(payload.uuid).toBe('form-session-uuid');
705
712
  expect(payload.request_type).toBe('erasure');
706
- // ERASURE submits must set this flag so the backend's
713
+ // The user chose "Yes" in the consent-revoke modal, so the backend's
707
714
  // auto-revoke task fires when the case is fulfilled (DPDPA Sec 12:
708
715
  // erasing data implies revoking the consent that authorized
709
716
  // processing it).
@@ -714,6 +721,86 @@ describe('Form (P2-SDK-3)', () => {
714
721
  });
715
722
  });
716
723
 
724
+ it('opens the consent-revoke modal on erasure and sends revoke_consent_on_fulfilment=false when the user declines', async () => {
725
+ setupSession();
726
+ const directGroup = makeGroup({
727
+ product_uuid: 'prod-direct',
728
+ product_name: 'Acme Loyalty',
729
+ purposes: [basePurpose('purp-direct', 'Marketing')],
730
+ });
731
+ const nominatedGroup = makeGroup({
732
+ product_uuid: 'prod-nominated',
733
+ product_name: 'Partner Bank',
734
+ nominator: {
735
+ uuid: 'nominator-uuid',
736
+ org_user_id: 'org-user-1',
737
+ name: 'Alice',
738
+ email: 'alice@example.com',
739
+ },
740
+ purposes: [basePurpose('purp-nominated', 'Analytics')],
741
+ });
742
+ getFormData.mockResolvedValue(
743
+ makeFormData({
744
+ direct_groups: [directGroup],
745
+ nominated_groups: [nominatedGroup],
746
+ }),
747
+ );
748
+ createCaseRequest.mockResolvedValueOnce({
749
+ case_id: 'CASE-2',
750
+ uuid: 'uuid-2',
751
+ });
752
+
753
+ render(<Form onCreateRequest={vi.fn()} />);
754
+ await waitFor(() => expect(getFormData).toHaveBeenCalled());
755
+
756
+ pickRequestType(RequestTypeEnum.ERASURE);
757
+
758
+ await waitFor(() =>
759
+ expect(screen.getByText('Acme Loyalty')).toBeInTheDocument(),
760
+ );
761
+ clickProductChip('prod-direct');
762
+
763
+ await waitFor(() => {
764
+ expect(
765
+ screen.getByTestId('per-product-purposes-prod-direct'),
766
+ ).toBeInTheDocument();
767
+ });
768
+
769
+ const marketingCheckbox = screen
770
+ .getByText('Marketing')
771
+ .closest('label')
772
+ ?.querySelector('input[type="checkbox"]') as HTMLInputElement | null;
773
+ expect(marketingCheckbox).toBeTruthy();
774
+ fireEvent.click(marketingCheckbox!);
775
+
776
+ const confirmCheckbox = (
777
+ screen.getAllByRole('checkbox') as HTMLInputElement[]
778
+ ).find((c) => c.closest('label')?.textContent?.includes('I confirm'));
779
+ expect(confirmCheckbox).toBeTruthy();
780
+ fireEvent.click(confirmCheckbox!);
781
+
782
+ await act(async () => {
783
+ fireEvent.click(screen.getByRole('button', { name: /^submit$/i }));
784
+ });
785
+
786
+ // The consent-revoke modal must appear, and no request fires until the
787
+ // user makes a choice. ('Revoke consent?' === revokeConsentModalTitle stub.)
788
+ expect(await screen.findByText('Revoke consent?')).toBeInTheDocument();
789
+ expect(createCaseRequest).not.toHaveBeenCalled();
790
+
791
+ // Declining ('No' === revokeConsentNo) submits with the flag explicitly false.
792
+ await act(async () => {
793
+ fireEvent.click(screen.getByRole('button', { name: 'No' }));
794
+ });
795
+
796
+ await waitFor(() => {
797
+ expect(createCaseRequest).toHaveBeenCalledTimes(1);
798
+ });
799
+ const payload = createCaseRequest.mock.calls[0][2];
800
+ expect(payload.request_type).toBe('erasure');
801
+ expect(payload.revoke_consent_on_fulfilment).toBe(false);
802
+ });
803
+
717
804
  it('switches the inline purpose panel when another product chip is selected', async () => {
718
805
  setupSession();
719
806
  const creditCard = makeGroup({
@@ -1052,6 +1139,11 @@ describe('Form (P2-SDK-3)', () => {
1052
1139
  fireEvent.click(screen.getByRole('button', { name: /^submit$/i }));
1053
1140
  });
1054
1141
 
1142
+ // ERASURE routes through the consent-revoke modal before submitting.
1143
+ await act(async () => {
1144
+ fireEvent.click(await screen.findByRole('button', { name: 'Yes' }));
1145
+ });
1146
+
1055
1147
  await waitFor(() => {
1056
1148
  expect(createCaseRequest).toHaveBeenCalledTimes(1);
1057
1149
  });