@learncard/react 2.12.0 → 2.12.2

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 (121) hide show
  1. package/dist/cjs/{BoostGenericCard-71899305.js → BoostGenericCard-06bedec7.js} +3 -3
  2. package/dist/cjs/{BoostGenericCard-71899305.js.map → BoostGenericCard-06bedec7.js.map} +1 -1
  3. package/dist/cjs/{CertificateDisplayCard-f10b46b1.js → CertificateDisplayCard-9c1d58da.js} +20 -14
  4. package/dist/cjs/CertificateDisplayCard-9c1d58da.js.map +1 -0
  5. package/dist/cjs/{MeritBadgeDisplayCard-d50cfb5e.js → MeritBadgeDisplayCard-201bf5ad.js} +4 -4
  6. package/dist/cjs/{MeritBadgeDisplayCard-d50cfb5e.js.map → MeritBadgeDisplayCard-201bf5ad.js.map} +1 -1
  7. package/dist/cjs/{MeritBadgeSkillsCount-e46503f5.js → MeritBadgeSkillsCount-ef283e0d.js} +2 -2
  8. package/dist/cjs/{MeritBadgeSkillsCount-e46503f5.js.map → MeritBadgeSkillsCount-ef283e0d.js.map} +1 -1
  9. package/dist/cjs/{SchoolIDCard-2d3cbb2c.js → SchoolIDCard-10796199.js} +1184 -3
  10. package/dist/cjs/SchoolIDCard-10796199.js.map +1 -0
  11. package/dist/cjs/{VCCard-42479c97.js → VCCard-d72176f3.js} +3 -3
  12. package/dist/cjs/{VCCard-42479c97.js.map → VCCard-d72176f3.js.map} +1 -1
  13. package/dist/cjs/{VCDisplayBackFace-d1cf9f73.js → VCDisplayBackFace-27460538.js} +3 -3
  14. package/dist/cjs/{VCDisplayBackFace-d1cf9f73.js.map → VCDisplayBackFace-27460538.js.map} +1 -1
  15. package/dist/cjs/{VCDisplayCard-cf46c224.js → VCDisplayCard-b05125ab.js} +3 -3
  16. package/dist/cjs/{VCDisplayCard-cf46c224.js.map → VCDisplayCard-b05125ab.js.map} +1 -1
  17. package/dist/cjs/{VCDisplayCard2-4a201a8a.js → VCDisplayCard2-78d4cc3f.js} +26 -18
  18. package/dist/cjs/VCDisplayCard2-78d4cc3f.js.map +1 -0
  19. package/dist/cjs/{VCVerificationPill-6c9dcf80.js → VCVerificationPill-3c130965.js} +2 -2
  20. package/dist/cjs/{VCVerificationPill-6c9dcf80.js.map → VCVerificationPill-3c130965.js.map} +1 -1
  21. package/dist/cjs/{VerifierStateBadgeAndText-934239ab.js → VerifierStateBadgeAndText-57a7b85f.js} +41 -4
  22. package/dist/cjs/VerifierStateBadgeAndText-57a7b85f.js.map +1 -0
  23. package/dist/cjs/{credential.helpers-17a10601.js → credential.helpers-3d0256cf.js} +2 -2
  24. package/dist/cjs/{credential.helpers-17a10601.js.map → credential.helpers-3d0256cf.js.map} +1 -1
  25. package/dist/cjs/{i18n-8fd90b2f.js → i18n-ddbb18e7.js} +12 -1
  26. package/dist/cjs/{i18n-8fd90b2f.js.map → i18n-ddbb18e7.js.map} +1 -1
  27. package/dist/cjs/index.js +14 -15
  28. package/dist/cjs/index.js.map +1 -1
  29. package/dist/cjs/index14.js +14 -15
  30. package/dist/cjs/index14.js.map +1 -1
  31. package/dist/cjs/index22.js +6 -6
  32. package/dist/cjs/index31.js +1 -2
  33. package/dist/cjs/index31.js.map +1 -1
  34. package/dist/cjs/index39.js +11 -12
  35. package/dist/cjs/index39.js.map +1 -1
  36. package/dist/cjs/index4.js +4 -4
  37. package/dist/cjs/index40.js +4 -4
  38. package/dist/cjs/index41.js +5 -5
  39. package/dist/cjs/index42.js +7 -8
  40. package/dist/cjs/index42.js.map +1 -1
  41. package/dist/cjs/index43.js +1 -1
  42. package/dist/cjs/index46.js +2 -2
  43. package/dist/cjs/index7.js +5 -4
  44. package/dist/cjs/index7.js.map +1 -1
  45. package/dist/cjs/{types.esm-73624b33.js → types.esm-228757cd.js} +30 -2
  46. package/dist/cjs/types.esm-228757cd.js.map +1 -0
  47. package/dist/esm/{BoostGenericCard-3b3ff784.js → BoostGenericCard-d4ea46de.js} +3 -3
  48. package/dist/esm/{BoostGenericCard-3b3ff784.js.map → BoostGenericCard-d4ea46de.js.map} +1 -1
  49. package/dist/esm/{CertificateDisplayCard-f26ef278.js → CertificateDisplayCard-487a3b4e.js} +20 -14
  50. package/dist/esm/CertificateDisplayCard-487a3b4e.js.map +1 -0
  51. package/dist/esm/{MeritBadgeDisplayCard-0d0b28c7.js → MeritBadgeDisplayCard-13622cd4.js} +4 -4
  52. package/dist/esm/{MeritBadgeDisplayCard-0d0b28c7.js.map → MeritBadgeDisplayCard-13622cd4.js.map} +1 -1
  53. package/dist/esm/{MeritBadgeSkillsCount-09e211fc.js → MeritBadgeSkillsCount-11bd0ae3.js} +2 -2
  54. package/dist/esm/{MeritBadgeSkillsCount-09e211fc.js.map → MeritBadgeSkillsCount-11bd0ae3.js.map} +1 -1
  55. package/dist/esm/{SchoolIDCard-fbd1cf6c.js → SchoolIDCard-f82e40bc.js} +1183 -2
  56. package/dist/esm/SchoolIDCard-f82e40bc.js.map +1 -0
  57. package/dist/esm/{VCCard-7e5e00d7.js → VCCard-7fa8f15b.js} +3 -3
  58. package/dist/esm/{VCCard-7e5e00d7.js.map → VCCard-7fa8f15b.js.map} +1 -1
  59. package/dist/esm/{VCDisplayBackFace-ae74476d.js → VCDisplayBackFace-314c1107.js} +3 -3
  60. package/dist/esm/{VCDisplayBackFace-ae74476d.js.map → VCDisplayBackFace-314c1107.js.map} +1 -1
  61. package/dist/esm/{VCDisplayCard-ac1f367a.js → VCDisplayCard-652910f5.js} +3 -3
  62. package/dist/esm/{VCDisplayCard-ac1f367a.js.map → VCDisplayCard-652910f5.js.map} +1 -1
  63. package/dist/esm/{VCDisplayCard2-7011596e.js → VCDisplayCard2-a8b9a519.js} +26 -18
  64. package/dist/esm/VCDisplayCard2-a8b9a519.js.map +1 -0
  65. package/dist/esm/{VCVerificationPill-04ffdf56.js → VCVerificationPill-be1140c5.js} +2 -2
  66. package/dist/esm/{VCVerificationPill-04ffdf56.js.map → VCVerificationPill-be1140c5.js.map} +1 -1
  67. package/dist/esm/{VerifierStateBadgeAndText-750a6c24.js → VerifierStateBadgeAndText-97117c5d.js} +41 -4
  68. package/dist/{cjs/VerifierStateBadgeAndText-934239ab.js.map → esm/VerifierStateBadgeAndText-97117c5d.js.map} +1 -1
  69. package/dist/esm/{credential.helpers-4fbdd8e8.js → credential.helpers-3dfe6b60.js} +2 -2
  70. package/dist/esm/{credential.helpers-4fbdd8e8.js.map → credential.helpers-3dfe6b60.js.map} +1 -1
  71. package/dist/esm/{i18n-08b606cb.js → i18n-35209bfa.js} +12 -1
  72. package/dist/esm/{i18n-08b606cb.js.map → i18n-35209bfa.js.map} +1 -1
  73. package/dist/esm/index.js +14 -15
  74. package/dist/esm/index.js.map +1 -1
  75. package/dist/esm/index14.js +14 -15
  76. package/dist/esm/index14.js.map +1 -1
  77. package/dist/esm/index22.js +6 -6
  78. package/dist/esm/index31.js +1 -2
  79. package/dist/esm/index31.js.map +1 -1
  80. package/dist/esm/index39.js +11 -12
  81. package/dist/esm/index39.js.map +1 -1
  82. package/dist/esm/index4.js +4 -4
  83. package/dist/esm/index40.js +4 -4
  84. package/dist/esm/index41.js +5 -5
  85. package/dist/esm/index42.js +7 -8
  86. package/dist/esm/index42.js.map +1 -1
  87. package/dist/esm/index43.js +1 -1
  88. package/dist/esm/index46.js +2 -2
  89. package/dist/esm/index7.js +5 -4
  90. package/dist/esm/index7.js.map +1 -1
  91. package/dist/esm/{types.esm-5f75bd4a.js → types.esm-a67e86b1.js} +30 -2
  92. package/dist/esm/types.esm-a67e86b1.js.map +1 -0
  93. package/dist/main.css +1 -1
  94. package/dist/main.js +1 -1
  95. package/package.json +6 -10
  96. package/src/components/CertificateDisplayCard/AlignmentRow.tsx +4 -2
  97. package/src/components/CertificateDisplayCard/AlignmentsBox.tsx +12 -9
  98. package/src/components/CertificateDisplayCard/InfoBox.tsx +3 -1
  99. package/src/components/CertificateDisplayCard/VerifierStateBadgeAndText.tsx +10 -6
  100. package/src/components/CircleIcon/__tests__/CircleIcon.test.tsx +1 -1
  101. package/src/components/VCDisplayCard2/InfoBox.tsx +3 -1
  102. package/src/components/VCDisplayCard2/TruncateTextBox.tsx +4 -2
  103. package/src/components/VCDisplayCard2/VC2BackFace.tsx +6 -4
  104. package/src/components/VCDisplayCard2/VC2FrontFaceInfo.tsx +3 -1
  105. package/src/components/VCDisplayCard2/VCDisplayCard2.tsx +10 -2
  106. package/src/components/VCDisplayCard2/__tests__/VC2LocalizedLabels.test.tsx +122 -0
  107. package/src/helpers/credential.helpers.test.ts +1 -1
  108. package/src/i18n/index.tsx +12 -0
  109. package/dist/cjs/CertificateDisplayCard-f10b46b1.js.map +0 -1
  110. package/dist/cjs/SchoolIDCard-2d3cbb2c.js.map +0 -1
  111. package/dist/cjs/VCDisplayCard2-4a201a8a.js.map +0 -1
  112. package/dist/cjs/index-47efbf5b.js +0 -1186
  113. package/dist/cjs/index-47efbf5b.js.map +0 -1
  114. package/dist/cjs/types.esm-73624b33.js.map +0 -1
  115. package/dist/esm/CertificateDisplayCard-f26ef278.js.map +0 -1
  116. package/dist/esm/SchoolIDCard-fbd1cf6c.js.map +0 -1
  117. package/dist/esm/VCDisplayCard2-7011596e.js.map +0 -1
  118. package/dist/esm/VerifierStateBadgeAndText-750a6c24.js.map +0 -1
  119. package/dist/esm/index-cbd2e9db.js +0 -1184
  120. package/dist/esm/index-cbd2e9db.js.map +0 -1
  121. package/dist/esm/types.esm-5f75bd4a.js.map +0 -1
@@ -3,6 +3,7 @@ import UnknownVerifierBadge from '../svgs/UnknownVerifierBadge';
3
3
  import VerifiedBadge from '../svgs/VerifiedBadge';
4
4
  import PersonBadge from '../svgs/PersonBadge';
5
5
  import RedFlag from '../svgs/RedFlag';
6
+ import { useT } from '../../i18n';
6
7
 
7
8
  export const VERIFIER_STATES = {
8
9
  selfVerified: 'Self Issued',
@@ -22,6 +23,7 @@ export type VerifierStateBadgeAndTextProps = {
22
23
 
23
24
  const VerifierStateBadgeAndText = forwardRef<HTMLButtonElement, VerifierStateBadgeAndTextProps>(
24
25
  ({ verifierState, className = '', unknownVerifierTitle, onClick }, ref) => {
26
+ const t = useT();
25
27
  // For Scouts: if we have a role-based title, show it with trusted styling
26
28
  // BUT don't overwrite self-issued or untrusted status
27
29
  const effectiveState =
@@ -37,35 +39,37 @@ const VerifierStateBadgeAndText = forwardRef<HTMLButtonElement, VerifierStateBad
37
39
  {effectiveState === VERIFIER_STATES.selfVerified && (
38
40
  <div className="flex items-center gap-0.5 font-poppins font-[500] text-[12px] leading-tight text-green-dark">
39
41
  <PersonBadge />
40
- <span className="whitespace-nowrap">Self Issued</span>
42
+ <span className="whitespace-nowrap">{t('verification.selfIssued')}</span>
41
43
  </div>
42
44
  )}
43
45
  {effectiveState === VERIFIER_STATES.trustedVerifier && (
44
46
  <div className="flex items-center gap-0.5 font-poppins font-[500] text-[12px] leading-tight text-green-600">
45
47
  <VerifiedBadge />
46
48
  <span className="whitespace-nowrap">
47
- {unknownVerifierTitle ?? 'Trusted Issuer'}
49
+ {unknownVerifierTitle ?? t('verification.trustedIssuer')}
48
50
  </span>
49
51
  </div>
50
52
  )}
51
53
  {effectiveState === VERIFIER_STATES.unknownVerifier && (
52
- <div className="flex items-center gap-0.5 font-poppins font-[500] text-[12px] leading-tight text-orange-500">
54
+ <div className="flex items-center gap-0.5 font-poppins font-[500] text-[12px] leading-tight text-orange-800">
53
55
  <UnknownVerifierBadge />
54
56
  <span className="whitespace-nowrap">
55
- {unknownVerifierTitle ?? VERIFIER_STATES.unknownVerifier}
57
+ {unknownVerifierTitle ?? t('verification.unknownIssuer')}
56
58
  </span>
57
59
  </div>
58
60
  )}
59
61
  {effectiveState === VERIFIER_STATES.appIssuer && (
60
62
  <div className="flex items-center gap-0.5 font-poppins font-[500] text-[12px] leading-tight text-cyan-600">
61
63
  <VerifiedBadge />
62
- <span className="whitespace-nowrap">App Issuer</span>
64
+ <span className="whitespace-nowrap">{t('verification.appIssuer')}</span>
63
65
  </div>
64
66
  )}
65
67
  {effectiveState === VERIFIER_STATES.untrustedVerifier && (
66
68
  <div className="flex items-center gap-0.5 font-poppins font-[500] text-[12px] leading-tight text-red-mastercard">
67
69
  <RedFlag />
68
- <span className="whitespace-nowrap">Untrusted Issuer</span>
70
+ <span className="whitespace-nowrap">
71
+ {t('verification.untrustedIssuer')}
72
+ </span>
69
73
  </div>
70
74
  )}
71
75
  </>
@@ -26,7 +26,7 @@ describe('Running Tests for CircleIcon', () => {
26
26
  // });
27
27
 
28
28
  // it('calls onClick', async () => {
29
- // const handleOnClick = jest.fn();
29
+ // const handleOnClick = vi.fn();
30
30
  // const user = userEvent.setup();
31
31
 
32
32
  // const { container } = render(<CircleIcon onClick={handleOnClick} />);
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { useT } from '../../i18n';
2
3
 
3
4
  type InfoBoxProps = {
4
5
  text: string;
@@ -7,6 +8,7 @@ type InfoBoxProps = {
7
8
  };
8
9
 
9
10
  const InfoBox: React.FC<InfoBoxProps> = ({ text, handleClose, backgroundColor = '#6366F1' }) => {
11
+ const t = useT();
10
12
  const bgColorWithOpacity = `${backgroundColor}1F`; // 12% opacity
11
13
  return (
12
14
  <div
@@ -21,7 +23,7 @@ const InfoBox: React.FC<InfoBoxProps> = ({ text, handleClose, backgroundColor =
21
23
  }}
22
24
  className="text-indigo-500 font-[700] select-none"
23
25
  >
24
- Close
26
+ {t('credential.close')}
25
27
  </button>
26
28
  </div>
27
29
  );
@@ -1,4 +1,5 @@
1
1
  import React, { useState } from 'react';
2
+ import { useT } from '../../i18n';
2
3
 
3
4
  type TruncateTextBoxProps = {
4
5
  headerText?: string;
@@ -29,6 +30,7 @@ const TruncateTextBox: React.FC<TruncateTextBoxProps> = ({
29
30
  containerClassName = '',
30
31
  textClassName = '',
31
32
  }) => {
33
+ const t = useT();
32
34
  const needsTruncate = text?.length > truncateThreshold;
33
35
  const [showFullText, setShowFullText] = useState(false);
34
36
  const truncated = needsTruncate && !showFullText;
@@ -61,7 +63,7 @@ const TruncateTextBox: React.FC<TruncateTextBoxProps> = ({
61
63
  setShowFullText(true);
62
64
  }}
63
65
  >
64
- More
66
+ {t('credential.more')}
65
67
  </button>
66
68
  </>
67
69
  )}
@@ -75,7 +77,7 @@ const TruncateTextBox: React.FC<TruncateTextBoxProps> = ({
75
77
  setShowFullText(false);
76
78
  }}
77
79
  >
78
- Close
80
+ {t('credential.close')}
79
81
  </button>
80
82
  </>
81
83
  )}
@@ -16,6 +16,7 @@ import {
16
16
  VideoMetadata,
17
17
  } from '../../types';
18
18
  import LeftArrow from '../svgs/LeftArrow';
19
+ import { useT } from '../../i18n';
19
20
 
20
21
  type FlippedComponentProps = React.PropsWithChildren<{
21
22
  flipId?: string;
@@ -69,6 +70,7 @@ const VC2BackFace: React.FC<VC2BackFaceProps> = ({
69
70
  enableLightbox,
70
71
  customLinkedCredentialsComponent,
71
72
  }) => {
73
+ const t = useT();
72
74
  const expiration = credential.expirationDate
73
75
  ? format(new Date(credential.expirationDate), 'MMM dd, yyyy')
74
76
  : undefined;
@@ -107,7 +109,7 @@ const VC2BackFace: React.FC<VC2BackFaceProps> = ({
107
109
 
108
110
  {customDescription && (
109
111
  <TruncateTextBox
110
- headerText="About"
112
+ headerText={t('credential.about')}
111
113
  text={description}
112
114
  className="description-box"
113
115
  >
@@ -116,7 +118,7 @@ const VC2BackFace: React.FC<VC2BackFaceProps> = ({
116
118
  )}
117
119
  {!customDescription && (description || expiration) && (
118
120
  <TruncateTextBox
119
- headerText="About"
121
+ headerText={t('credential.about')}
120
122
  text={description}
121
123
  className="description-box"
122
124
  >
@@ -130,7 +132,7 @@ const VC2BackFace: React.FC<VC2BackFaceProps> = ({
130
132
 
131
133
  {customCriteria && (
132
134
  <TruncateTextBox
133
- headerText="Criteria"
135
+ headerText={t('credential.criteria')}
134
136
  text={description}
135
137
  className="description-box"
136
138
  >
@@ -139,7 +141,7 @@ const VC2BackFace: React.FC<VC2BackFaceProps> = ({
139
141
  )}
140
142
  {!customCriteria && criteria && (
141
143
  <TruncateTextBox
142
- headerText="Criteria"
144
+ headerText={t('credential.criteria')}
143
145
  text={criteria}
144
146
  className="criteria-box"
145
147
  />
@@ -13,6 +13,7 @@ import VerifierStateBadgeAndText, {
13
13
  } from '../CertificateDisplayCard/VerifierStateBadgeAndText';
14
14
  import { BoostAchievementCredential } from '../../types';
15
15
  import { KnownDIDRegistryType } from '../../types';
16
+ import { useT } from '../../i18n';
16
17
 
17
18
  type FlippedComponentProps = React.PropsWithChildren<{
18
19
  flipId?: string;
@@ -82,6 +83,7 @@ const VC2FrontFaceInfo: React.FC<VC2FrontFaceInfoProps> = ({
82
83
  unknownVerifierTitle,
83
84
  onVerifierClick,
84
85
  }) => {
86
+ const t = useT();
85
87
  const issuerImage = getImageFromProfile(issuer ?? '');
86
88
  const issueeImage = getImageFromProfile(issuee ?? '');
87
89
 
@@ -241,7 +243,7 @@ const VC2FrontFaceInfo: React.FC<VC2FrontFaceInfoProps> = ({
241
243
  {!issuerDisplay.isMissing && (
242
244
  <span className="issued-by max-w-full break-words text-center line-clamp-2 text-[14px]">
243
245
  <span className="font-medium text-grayscale-900">
244
- By
246
+ {t('credential.by')}
245
247
  </span>{' '}
246
248
  {issuerDisplay.isDidValue ? (
247
249
  <span className="font-[600] font-poppins break-words">
@@ -29,6 +29,7 @@ import {
29
29
  import { CertificateDisplayCard } from '../CertificateDisplayCard';
30
30
  import { MeritBadgeDisplayCard } from '../MeritBadgeDisplayCard';
31
31
  import { KnownDIDRegistryType } from '../../types';
32
+ import { useT } from '../../i18n';
32
33
 
33
34
  type FlippedComponentProps = React.PropsWithChildren<{
34
35
  flipId?: string;
@@ -169,6 +170,7 @@ export const VCDisplayCard2: React.FC<VCDisplayCard2Props> = ({
169
170
  hideFrontFaceDetails,
170
171
  onVerifierClick,
171
172
  }) => {
173
+ const t = useT();
172
174
  const {
173
175
  title = '',
174
176
  createdAt,
@@ -537,12 +539,18 @@ export const VCDisplayCard2: React.FC<VCDisplayCard2Props> = ({
537
539
  />
538
540
  )}
539
541
  <div className="vc-footer-text font-montserrat flex flex-col items-center justify-center text-[12px] font-[700] leading-[15px] select-none">
540
- <span className="text-[#4F4F4F]">Verified Credential</span>
542
+ <span className="text-[#4F4F4F]">
543
+ {t('credential.verified')}
544
+ </span>
541
545
  <span
542
546
  className="vc-footer-status uppercase"
543
547
  style={{ color: statusColor }}
544
548
  >
545
- {worstVerificationStatus}
549
+ {t(
550
+ `verification.status.${String(
551
+ worstVerificationStatus
552
+ ).toLowerCase()}`
553
+ )}
546
554
  </span>
547
555
  </div>
548
556
  <div
@@ -0,0 +1,122 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import React from 'react';
4
+ import { fireEvent, render, screen, within } from '@testing-library/react';
5
+ import { describe, expect, it, vi } from 'vitest';
6
+
7
+ import { VerificationStatusEnum } from '@learncard/types';
8
+
9
+ import { I18nProvider } from '../../../i18n';
10
+ import VerifierStateBadgeAndText, {
11
+ VERIFIER_STATES,
12
+ } from '../../CertificateDisplayCard/VerifierStateBadgeAndText';
13
+ import VC2BackFace from '../VC2BackFace';
14
+ import VCDisplayCard2 from '../VCDisplayCard2';
15
+
16
+ vi.mock('react-flip-toolkit', () => ({
17
+ Flipper: ({ children }: React.PropsWithChildren) => <>{children}</>,
18
+ Flipped: ({ children }: React.PropsWithChildren) => <>{children}</>,
19
+ }));
20
+
21
+ const translations: Record<string, string> = {
22
+ 'credential.about': 'Acerca de',
23
+ 'credential.criteria': 'Criterios',
24
+ 'credential.alignments': 'Alineaciones',
25
+ 'credential.alignmentInfo': 'Las alineaciones vinculan este logro con marcos establecidos.',
26
+ 'credential.verifiedSource': 'Fuente verificada',
27
+ 'credential.verifiedLabel': 'Verificada',
28
+ 'credential.openAlignmentSource': 'Abrir la fuente de la alineación',
29
+ 'credential.occupationsPrograms': 'Ocupaciones y programas',
30
+ 'credential.more': 'Más',
31
+ 'credential.close': 'Cerrar',
32
+ 'credential.by': 'Por',
33
+ 'credential.verified': 'Credencial verificada',
34
+ 'verification.selfIssued': 'Autoemitido',
35
+ 'verification.status.success': 'Éxito',
36
+ };
37
+
38
+ const renderLocalized = (node: React.ReactNode) =>
39
+ render(
40
+ <I18nProvider resolve={key => translations[key]} locale="es">
41
+ {node}
42
+ </I18nProvider>
43
+ );
44
+
45
+ const credential = {
46
+ issuer: 'did:example:issuer',
47
+ issuanceDate: '2026-08-14T00:00:00.000Z',
48
+ credentialSubject: {
49
+ id: 'did:example:subject',
50
+ achievement: {
51
+ name: 'Critical Thinking',
52
+ description:
53
+ 'A long description that deliberately exceeds the truncation threshold so the translated expansion control is rendered for this regression test.',
54
+ criteria: { narrative: 'Complete the critical-thinking requirements.' },
55
+ alignment: [
56
+ {
57
+ targetUrl: 'https://example.com/framework/critical-thinking',
58
+ targetName: 'Critical Thinking',
59
+ targetFramework: 'Example Framework',
60
+ targetType: 'ceterms:Credential',
61
+ },
62
+ ],
63
+ },
64
+ },
65
+ display: { displayType: 'badge' },
66
+ };
67
+
68
+ describe('VCDisplayCard2 localized labels', () => {
69
+ it('localizes back-face headings, alignment copy, and truncation controls', () => {
70
+ const { container } = renderLocalized(
71
+ <VC2BackFace
72
+ credential={credential as never}
73
+ verificationItems={[]}
74
+ showFrontFace={vi.fn()}
75
+ />
76
+ );
77
+
78
+ expect(screen.getByRole('heading', { name: 'Acerca de' })).toBeTruthy();
79
+ expect(screen.getByRole('heading', { name: 'Criterios' })).toBeTruthy();
80
+
81
+ const alignmentHeading = screen.getByRole('heading', { name: 'Alineaciones' });
82
+ expect(alignmentHeading.className).toContain('text-[20px]');
83
+ expect(alignmentHeading.className).not.toContain('text-[22px]');
84
+
85
+ fireEvent.click(screen.getByRole('button', { name: 'Más' }));
86
+ expect(screen.getByRole('button', { name: 'Cerrar' })).toBeTruthy();
87
+
88
+ const alignmentBox = alignmentHeading.closest('div.bg-white');
89
+ expect(alignmentBox).not.toBeNull();
90
+ fireEvent.click(within(alignmentBox as HTMLElement).getByRole('button'));
91
+
92
+ expect(
93
+ screen.getByText('Las alineaciones vinculan este logro con marcos establecidos.')
94
+ ).toBeTruthy();
95
+ expect(screen.getByText('Fuente verificada')).toBeTruthy();
96
+ expect(screen.getByText('Verificada')).toBeTruthy();
97
+ expect(screen.getByRole('link', { name: 'Abrir la fuente de la alineación' })).toBeTruthy();
98
+ expect(container.textContent).not.toContain('Alignments');
99
+ });
100
+
101
+ it('localizes the verifier badge used on the credential front face', () => {
102
+ renderLocalized(<VerifierStateBadgeAndText verifierState={VERIFIER_STATES.selfVerified} />);
103
+
104
+ expect(screen.getByText('Autoemitido')).toBeTruthy();
105
+ expect(screen.queryByText('Self Issued')).toBeNull();
106
+ });
107
+
108
+ it('localizes the verified-credential footer and verification status', () => {
109
+ renderLocalized(
110
+ <VCDisplayCard2
111
+ credential={credential as never}
112
+ verificationItems={[{ status: VerificationStatusEnum.Success } as never]}
113
+ hideNavButtons
114
+ />
115
+ );
116
+
117
+ expect(screen.getByText('Credencial verificada')).toBeTruthy();
118
+ expect(screen.getByText('Éxito')).toBeTruthy();
119
+ expect(screen.getByText('Por')).toBeTruthy();
120
+ expect(screen.queryByText('Verified Credential')).toBeNull();
121
+ });
122
+ });
@@ -88,7 +88,7 @@ describe('Credential Helpers', () => {
88
88
  ...JffCredential,
89
89
  issuanceDate: 'invalid-date-string',
90
90
  };
91
- const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
91
+ const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
92
92
  const result = getInfoFromCredential(credentialWithInvalidDate as any);
93
93
  expect(result.createdAt).toEqual('');
94
94
  expect(consoleSpy).toHaveBeenCalledWith(
@@ -32,7 +32,19 @@ export const EN_DEFAULTS: Record<string, string> = {
32
32
  'verification.unknownIssuer': 'Unknown Issuer',
33
33
  'verification.appIssuer': 'Trusted App',
34
34
  'verification.untrustedIssuer': 'Untrusted Issuer',
35
+ 'credential.about': 'About',
36
+ 'credential.criteria': 'Criteria',
37
+ 'credential.alignments': 'Alignments',
38
+ 'credential.alignmentInfo':
39
+ 'Alignments in your Open Badge credential link your achievement to established frameworks, standards, or competencies. Each alignment shows how your boost directly relates to skills, knowledge areas, and professional standards that are recognized in your field.',
40
+ 'credential.verifiedSource': 'Verified source',
41
+ 'credential.occupationsPrograms': 'Occupations & programs',
42
+ 'credential.verifiedLabel': 'Verified',
43
+ 'credential.openAlignmentSource': 'Open alignment source',
44
+ 'credential.more': 'More',
45
+ 'credential.close': 'Close',
35
46
  'credential.by': 'By',
47
+ 'credential.verified': 'Verified Credential',
36
48
  'verification.title': 'Credential Verifications',
37
49
  'verification.infoText':
38
50
  'Credential verifications check the cryptographic proof of digital credentials to ensure their authenticity and accuracy.',