@oussemasahbeni/keycloakify-login-shadcn 250004.0.3 → 250004.0.7

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 (169) hide show
  1. package/keycloak-theme/login/KcContext.ts +23 -23
  2. package/keycloak-theme/login/KcPage.tsx +47 -47
  3. package/keycloak-theme/login/assets/img/auth-logo.svg +100 -100
  4. package/keycloak-theme/login/assets/img/shape.svg +71 -71
  5. package/keycloak-theme/login/components/LogoutOtherSessions.tsx +26 -26
  6. package/keycloak-theme/login/components/PasswordWrapper.tsx +35 -35
  7. package/keycloak-theme/login/components/Template/Template.tsx +227 -227
  8. package/keycloak-theme/login/components/Template/index.ts +1 -1
  9. package/keycloak-theme/login/components/Template/useInitializeTemplate.ts +61 -61
  10. package/keycloak-theme/login/components/UserProfileFormFields/AddRemoveButtonsMultiValuedAttribute.tsx +61 -61
  11. package/keycloak-theme/login/components/UserProfileFormFields/DO_MAKE_USER_CONFIRM_PASSWORD.ts +2 -2
  12. package/keycloak-theme/login/components/UserProfileFormFields/FieldErrors.tsx +28 -28
  13. package/keycloak-theme/login/components/UserProfileFormFields/GroupLabel.tsx +70 -70
  14. package/keycloak-theme/login/components/UserProfileFormFields/InputFieldByType.tsx +58 -58
  15. package/keycloak-theme/login/components/UserProfileFormFields/InputTag.tsx +116 -116
  16. package/keycloak-theme/login/components/UserProfileFormFields/InputTagSelects.tsx +135 -135
  17. package/keycloak-theme/login/components/UserProfileFormFields/SelectTag.tsx +114 -114
  18. package/keycloak-theme/login/components/UserProfileFormFields/TextareaTag.tsx +42 -42
  19. package/keycloak-theme/login/components/UserProfileFormFields/UserProfileFormFields.tsx +127 -127
  20. package/keycloak-theme/login/components/UserProfileFormFields/index.ts +1 -1
  21. package/keycloak-theme/login/i18n.ts +47 -47
  22. package/keycloak-theme/login/mocks/getKcContextMock.ts +22 -22
  23. package/keycloak-theme/login/pages/PageIndex.tsx +134 -134
  24. package/keycloak-theme/login/pages/code/Page.stories.tsx +54 -66
  25. package/keycloak-theme/login/pages/code/Page.tsx +89 -89
  26. package/keycloak-theme/login/pages/code/index.ts +3 -3
  27. package/keycloak-theme/login/pages/delete-account-confirm/Page.stories.tsx +39 -46
  28. package/keycloak-theme/login/pages/delete-account-confirm/Page.tsx +63 -63
  29. package/keycloak-theme/login/pages/delete-account-confirm/index.ts +3 -3
  30. package/keycloak-theme/login/pages/delete-credential/Page.stories.tsx +26 -30
  31. package/keycloak-theme/login/pages/delete-credential/Page.tsx +51 -51
  32. package/keycloak-theme/login/pages/delete-credential/index.ts +3 -3
  33. package/keycloak-theme/login/pages/error/Page.stories.tsx +47 -58
  34. package/keycloak-theme/login/pages/error/Page.tsx +42 -42
  35. package/keycloak-theme/login/pages/error/index.ts +3 -3
  36. package/keycloak-theme/login/pages/frontchannel-logout/Page.stories.tsx +25 -32
  37. package/keycloak-theme/login/pages/frontchannel-logout/Page.tsx +84 -84
  38. package/keycloak-theme/login/pages/frontchannel-logout/index.ts +3 -3
  39. package/keycloak-theme/login/pages/idp-review-user-profile/Page.stories.tsx +46 -58
  40. package/keycloak-theme/login/pages/idp-review-user-profile/Page.tsx +52 -52
  41. package/keycloak-theme/login/pages/idp-review-user-profile/index.ts +3 -3
  42. package/keycloak-theme/login/pages/info/Page.stories.tsx +50 -60
  43. package/keycloak-theme/login/pages/info/Page.tsx +92 -92
  44. package/keycloak-theme/login/pages/link-idp-action/Page.stories.tsx +32 -16
  45. package/keycloak-theme/login/pages/link-idp-action/Page.tsx +43 -43
  46. package/keycloak-theme/login/pages/link-idp-action/index.ts +3 -3
  47. package/keycloak-theme/login/pages/login/Form.tsx +242 -242
  48. package/keycloak-theme/login/pages/login/Info.tsx +29 -29
  49. package/keycloak-theme/login/pages/login/Page.stories.tsx +345 -365
  50. package/keycloak-theme/login/pages/login/Page.tsx +44 -44
  51. package/keycloak-theme/login/pages/login/SocialProviders.tsx +107 -107
  52. package/keycloak-theme/login/pages/login/index.ts +3 -3
  53. package/keycloak-theme/login/pages/login/providers/apple.svg +3 -3
  54. package/keycloak-theme/login/pages/login/providers/bitbucket.svg +11 -11
  55. package/keycloak-theme/login/pages/login/providers/discord.svg +4 -4
  56. package/keycloak-theme/login/pages/login/providers/facebook.svg +5 -5
  57. package/keycloak-theme/login/pages/login/providers/github.svg +3 -3
  58. package/keycloak-theme/login/pages/login/providers/gitlab.svg +7 -7
  59. package/keycloak-theme/login/pages/login/providers/google.svg +7 -7
  60. package/keycloak-theme/login/pages/login/providers/instagram.svg +31 -31
  61. package/keycloak-theme/login/pages/login/providers/linkedin.svg +3 -3
  62. package/keycloak-theme/login/pages/login/providers/microsoft.svg +6 -6
  63. package/keycloak-theme/login/pages/login/providers/oidc.svg +5 -5
  64. package/keycloak-theme/login/pages/login/providers/openshift.svg +7 -7
  65. package/keycloak-theme/login/pages/login/providers/paypal.svg +6 -6
  66. package/keycloak-theme/login/pages/login/providers/slack.svg +11 -11
  67. package/keycloak-theme/login/pages/login/providers/stackoverflow.svg +5 -5
  68. package/keycloak-theme/login/pages/login/providers/x.svg +3 -3
  69. package/keycloak-theme/login/pages/login/useProviderLogos.tsx +39 -39
  70. package/keycloak-theme/login/pages/login/useScript.tsx +62 -62
  71. package/keycloak-theme/login/pages/login-config-totp/Page.stories.tsx +45 -59
  72. package/keycloak-theme/login/pages/login-config-totp/Page.tsx +240 -240
  73. package/keycloak-theme/login/pages/login-config-totp/index.ts +3 -3
  74. package/keycloak-theme/login/pages/login-idp-link-confirm/Page.stories.tsx +30 -34
  75. package/keycloak-theme/login/pages/login-idp-link-confirm/Page.tsx +43 -43
  76. package/keycloak-theme/login/pages/login-idp-link-confirm/index.ts +3 -3
  77. package/keycloak-theme/login/pages/login-idp-link-confirm-override/Page.stories.tsx +16 -22
  78. package/keycloak-theme/login/pages/login-idp-link-confirm-override/Page.tsx +47 -47
  79. package/keycloak-theme/login/pages/login-idp-link-confirm-override/index.ts +3 -3
  80. package/keycloak-theme/login/pages/login-idp-link-email/Page.stories.tsx +54 -62
  81. package/keycloak-theme/login/pages/login-idp-link-email/Page.tsx +54 -54
  82. package/keycloak-theme/login/pages/login-idp-link-email/index.ts +3 -3
  83. package/keycloak-theme/login/pages/login-oauth-grant/Page.stories.tsx +39 -45
  84. package/keycloak-theme/login/pages/login-oauth-grant/Page.tsx +126 -126
  85. package/keycloak-theme/login/pages/login-oauth-grant/index.ts +3 -3
  86. package/keycloak-theme/login/pages/login-oauth2-device-verify-user-code/Page.stories.tsx +38 -48
  87. package/keycloak-theme/login/pages/login-oauth2-device-verify-user-code/Page.tsx +58 -58
  88. package/keycloak-theme/login/pages/login-oauth2-device-verify-user-code/index.ts +3 -3
  89. package/keycloak-theme/login/pages/login-otp/Page.stories.tsx +82 -96
  90. package/keycloak-theme/login/pages/login-otp/Page.tsx +108 -108
  91. package/keycloak-theme/login/pages/login-otp/index.ts +3 -3
  92. package/keycloak-theme/login/pages/login-page-expired/Page.stories.tsx +28 -36
  93. package/keycloak-theme/login/pages/login-page-expired/Page.tsx +47 -47
  94. package/keycloak-theme/login/pages/login-page-expired/index.ts +3 -3
  95. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/Page.stories.tsx +20 -0
  96. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/Page.tsx +233 -233
  97. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/index.ts +3 -3
  98. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/useScript.tsx +63 -63
  99. package/keycloak-theme/login/pages/login-password/Page.stories.tsx +55 -56
  100. package/keycloak-theme/login/pages/login-password/Page.tsx +149 -149
  101. package/keycloak-theme/login/pages/login-password/index.ts +3 -3
  102. package/keycloak-theme/login/pages/login-password/useScript.tsx +63 -63
  103. package/keycloak-theme/login/pages/login-recovery-authn-code-config/Page.stories.tsx +28 -36
  104. package/keycloak-theme/login/pages/login-recovery-authn-code-config/Page.tsx +181 -181
  105. package/keycloak-theme/login/pages/login-recovery-authn-code-config/index.ts +3 -3
  106. package/keycloak-theme/login/pages/login-recovery-authn-code-config/useScript.tsx +145 -145
  107. package/keycloak-theme/login/pages/login-recovery-authn-code-input/Page.stories.tsx +16 -22
  108. package/keycloak-theme/login/pages/login-recovery-authn-code-input/Page.tsx +70 -70
  109. package/keycloak-theme/login/pages/login-recovery-authn-code-input/index.ts +3 -3
  110. package/keycloak-theme/login/pages/login-reset-otp/Page.stories.tsx +62 -74
  111. package/keycloak-theme/login/pages/login-reset-otp/Page.tsx +86 -86
  112. package/keycloak-theme/login/pages/login-reset-otp/index.ts +3 -3
  113. package/keycloak-theme/login/pages/login-reset-password/Form.tsx +68 -68
  114. package/keycloak-theme/login/pages/login-reset-password/Page.stories.tsx +44 -54
  115. package/keycloak-theme/login/pages/login-reset-password/Page.tsx +27 -27
  116. package/keycloak-theme/login/pages/login-reset-password/index.ts +3 -3
  117. package/keycloak-theme/login/pages/login-update-password/Page.stories.tsx +40 -50
  118. package/keycloak-theme/login/pages/login-update-password/Page.tsx +111 -111
  119. package/keycloak-theme/login/pages/login-update-password/index.ts +3 -3
  120. package/keycloak-theme/login/pages/login-update-profile/Page.stories.tsx +28 -36
  121. package/keycloak-theme/login/pages/login-update-profile/Page.tsx +68 -68
  122. package/keycloak-theme/login/pages/login-update-profile/index.ts +3 -3
  123. package/keycloak-theme/login/pages/login-username/Page.stories.tsx +32 -42
  124. package/keycloak-theme/login/pages/login-username/Page.tsx +246 -246
  125. package/keycloak-theme/login/pages/login-username/index.ts +3 -3
  126. package/keycloak-theme/login/pages/login-username/useScript.tsx +62 -62
  127. package/keycloak-theme/login/pages/login-verify-email/Page.stories.tsx +68 -80
  128. package/keycloak-theme/login/pages/login-verify-email/Page.tsx +38 -38
  129. package/keycloak-theme/login/pages/login-verify-email/index.ts +3 -3
  130. package/keycloak-theme/login/pages/login-x509-info/Page.stories.tsx +29 -37
  131. package/keycloak-theme/login/pages/login-x509-info/Page.tsx +75 -75
  132. package/keycloak-theme/login/pages/login-x509-info/index.ts +3 -3
  133. package/keycloak-theme/login/pages/logout-confirm/Page.stories.tsx +34 -42
  134. package/keycloak-theme/login/pages/logout-confirm/Page.tsx +53 -53
  135. package/keycloak-theme/login/pages/logout-confirm/index.ts +3 -3
  136. package/keycloak-theme/login/pages/register/Form.tsx +106 -106
  137. package/keycloak-theme/login/pages/register/Page.stories.tsx +23 -6
  138. package/keycloak-theme/login/pages/register/Page.tsx +26 -26
  139. package/keycloak-theme/login/pages/register/TermsAcceptance.tsx +56 -56
  140. package/keycloak-theme/login/pages/register/index.ts +3 -3
  141. package/keycloak-theme/login/pages/saml-post-form/Page.stories.tsx +16 -22
  142. package/keycloak-theme/login/pages/saml-post-form/Page.tsx +66 -66
  143. package/keycloak-theme/login/pages/saml-post-form/index.ts +3 -3
  144. package/keycloak-theme/login/pages/select-authenticator/Page.stories.tsx +83 -95
  145. package/keycloak-theme/login/pages/select-authenticator/Page.tsx +100 -100
  146. package/keycloak-theme/login/pages/select-authenticator/index.ts +3 -3
  147. package/keycloak-theme/login/pages/select-organization/Page.stories.tsx +62 -49
  148. package/keycloak-theme/login/pages/select-organization/Page.tsx +126 -126
  149. package/keycloak-theme/login/pages/select-organization/index.ts +3 -3
  150. package/keycloak-theme/login/pages/terms/Page.stories.tsx +15 -0
  151. package/keycloak-theme/login/pages/terms/Page.tsx +51 -51
  152. package/keycloak-theme/login/pages/terms/index.ts +3 -3
  153. package/keycloak-theme/login/pages/update-email/Page.stories.tsx +27 -35
  154. package/keycloak-theme/login/pages/update-email/Page.tsx +62 -62
  155. package/keycloak-theme/login/pages/update-email/index.ts +3 -3
  156. package/keycloak-theme/login/pages/webauthn-authenticate/Page.stories.tsx +112 -126
  157. package/keycloak-theme/login/pages/webauthn-authenticate/Page.tsx +202 -202
  158. package/keycloak-theme/login/pages/webauthn-authenticate/index.ts +3 -3
  159. package/keycloak-theme/login/pages/webauthn-authenticate/useScript.tsx +55 -55
  160. package/keycloak-theme/login/pages/webauthn-error/Page.stories.tsx +54 -66
  161. package/keycloak-theme/login/pages/webauthn-error/Page.tsx +73 -73
  162. package/keycloak-theme/login/pages/webauthn-error/index.ts +3 -3
  163. package/keycloak-theme/login/pages/webauthn-register/Page.stories.tsx +39 -49
  164. package/keycloak-theme/login/pages/webauthn-register/Page.tsx +78 -78
  165. package/keycloak-theme/login/pages/webauthn-register/index.ts +3 -3
  166. package/keycloak-theme/login/pages/webauthn-register/useScript.tsx +62 -62
  167. package/keycloak-theme/login/shared/getColorScheme.ts +45 -45
  168. package/keycloak-theme/login/styleLevelCustomization.tsx +35 -35
  169. package/package.json +5 -1
@@ -1,145 +1,145 @@
1
- import { useInsertScriptTags } from "@keycloakify/login-ui/tools/useInsertScriptTags";
2
- import { waitForElementMountedOnDom } from "@keycloakify/login-ui/tools/waitForElementMountedOnDom";
3
- import { useEffect } from "react";
4
- import { useI18n } from "../../i18n";
5
-
6
- export function useScript(params: { olRecoveryCodesListId: string }) {
7
- const { olRecoveryCodesListId } = params;
8
-
9
- const { msgStr, isFetchingTranslations } = useI18n();
10
-
11
- const { insertScriptTags } = useInsertScriptTags({
12
- effectId: "LoginRecoveryAuthnCodeConfig",
13
- scriptTags: [
14
- {
15
- type: "text/javascript",
16
- textContent: () => `
17
-
18
- /* copy recovery codes */
19
- function copyRecoveryCodes() {
20
- var tmpTextarea = document.createElement("textarea");
21
- var codes = document.querySelectorAll("#${olRecoveryCodesListId} li");
22
- for (i = 0; i < codes.length; i++) {
23
- tmpTextarea.value = tmpTextarea.value + codes[i].innerText + "\\n";
24
- }
25
- document.body.appendChild(tmpTextarea);
26
- tmpTextarea.select();
27
- document.execCommand("copy");
28
- document.body.removeChild(tmpTextarea);
29
- }
30
-
31
- var copyButton = document.getElementById("copyRecoveryCodes");
32
- copyButton && copyButton.addEventListener("click", function () {
33
- copyRecoveryCodes();
34
- });
35
-
36
- /* download recovery codes */
37
- function formatCurrentDateTime() {
38
- var dt = new Date();
39
- var options = {
40
- month: 'long',
41
- day: 'numeric',
42
- year: 'numeric',
43
- hour: 'numeric',
44
- minute: 'numeric',
45
- timeZoneName: 'short'
46
- };
47
-
48
- return dt.toLocaleString('en-US', options);
49
- }
50
-
51
- function parseRecoveryCodeList() {
52
- var recoveryCodes = document.querySelectorAll("#${olRecoveryCodesListId} li");
53
- var recoveryCodeList = "";
54
-
55
- for (var i = 0; i < recoveryCodes.length; i++) {
56
- var recoveryCodeLiElement = recoveryCodes[i].innerText;
57
- recoveryCodeList += recoveryCodeLiElement + "\\r\\n";
58
- }
59
-
60
- return recoveryCodeList;
61
- }
62
-
63
- function buildDownloadContent() {
64
- var recoveryCodeList = parseRecoveryCodeList();
65
- var dt = new Date();
66
- var options = {
67
- month: 'long',
68
- day: 'numeric',
69
- year: 'numeric',
70
- hour: 'numeric',
71
- minute: 'numeric',
72
- timeZoneName: 'short'
73
- };
74
-
75
- return fileBodyContent =
76
- ${JSON.stringify(msgStr("recovery-codes-download-file-header"))} + "\\n\\n" +
77
- recoveryCodeList + "\\n" +
78
- ${JSON.stringify(msgStr("recovery-codes-download-file-description"))} + "\\n\\n" +
79
- ${JSON.stringify(msgStr("recovery-codes-download-file-date"))} + " " + formatCurrentDateTime();
80
- }
81
-
82
- function setUpDownloadLinkAndDownload(filename, text) {
83
- var el = document.createElement('a');
84
- el.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
85
- el.setAttribute('download', filename);
86
- el.style.display = 'none';
87
- document.body.appendChild(el);
88
- el.click();
89
- document.body.removeChild(el);
90
- }
91
-
92
- function downloadRecoveryCodes() {
93
- setUpDownloadLinkAndDownload('kc-download-recovery-codes.txt', buildDownloadContent());
94
- }
95
-
96
- var downloadButton = document.getElementById("downloadRecoveryCodes");
97
- downloadButton && downloadButton.addEventListener("click", downloadRecoveryCodes);
98
-
99
- /* print recovery codes */
100
- function buildPrintContent() {
101
- var recoveryCodeListHTML = document.getElementById('${olRecoveryCodesListId}').innerHTML;
102
- var styles =
103
- \`@page { size: auto; margin-top: 0; }
104
- body { width: 480px; }
105
- div { list-style-type: none; font-family: monospace }
106
- p:first-of-type { margin-top: 48px }\`;
107
-
108
- return printFileContent =
109
- "<html><style>" + styles + "</style><body>" +
110
- "<title>kc-download-recovery-codes</title>" +
111
- "<p>" + ${JSON.stringify(msgStr("recovery-codes-download-file-header"))} + "</p>" +
112
- "<div>" + recoveryCodeListHTML + "</div>" +
113
- "<p>" + ${JSON.stringify(msgStr("recovery-codes-download-file-description"))} + "</p>" +
114
- "<p>" + ${JSON.stringify(msgStr("recovery-codes-download-file-date"))} + " " + formatCurrentDateTime() + "</p>" +
115
- "</body></html>";
116
- }
117
-
118
- function printRecoveryCodes() {
119
- var w = window.open();
120
- w.document.write(buildPrintContent());
121
- w.print();
122
- w.close();
123
- }
124
-
125
- var printButton = document.getElementById("printRecoveryCodes");
126
- printButton && printButton.addEventListener("click", printRecoveryCodes);
127
- `
128
- }
129
- ]
130
- });
131
-
132
- useEffect(() => {
133
- if (isFetchingTranslations) {
134
- return;
135
- }
136
-
137
- (async () => {
138
- await waitForElementMountedOnDom({
139
- elementId: olRecoveryCodesListId
140
- });
141
-
142
- insertScriptTags();
143
- })();
144
- }, [isFetchingTranslations]);
145
- }
1
+ import { useInsertScriptTags } from "@keycloakify/login-ui/tools/useInsertScriptTags";
2
+ import { waitForElementMountedOnDom } from "@keycloakify/login-ui/tools/waitForElementMountedOnDom";
3
+ import { useEffect } from "react";
4
+ import { useI18n } from "../../i18n";
5
+
6
+ export function useScript(params: { olRecoveryCodesListId: string }) {
7
+ const { olRecoveryCodesListId } = params;
8
+
9
+ const { msgStr, isFetchingTranslations } = useI18n();
10
+
11
+ const { insertScriptTags } = useInsertScriptTags({
12
+ effectId: "LoginRecoveryAuthnCodeConfig",
13
+ scriptTags: [
14
+ {
15
+ type: "text/javascript",
16
+ textContent: () => `
17
+
18
+ /* copy recovery codes */
19
+ function copyRecoveryCodes() {
20
+ var tmpTextarea = document.createElement("textarea");
21
+ var codes = document.querySelectorAll("#${olRecoveryCodesListId} li");
22
+ for (i = 0; i < codes.length; i++) {
23
+ tmpTextarea.value = tmpTextarea.value + codes[i].innerText + "\\n";
24
+ }
25
+ document.body.appendChild(tmpTextarea);
26
+ tmpTextarea.select();
27
+ document.execCommand("copy");
28
+ document.body.removeChild(tmpTextarea);
29
+ }
30
+
31
+ var copyButton = document.getElementById("copyRecoveryCodes");
32
+ copyButton && copyButton.addEventListener("click", function () {
33
+ copyRecoveryCodes();
34
+ });
35
+
36
+ /* download recovery codes */
37
+ function formatCurrentDateTime() {
38
+ var dt = new Date();
39
+ var options = {
40
+ month: 'long',
41
+ day: 'numeric',
42
+ year: 'numeric',
43
+ hour: 'numeric',
44
+ minute: 'numeric',
45
+ timeZoneName: 'short'
46
+ };
47
+
48
+ return dt.toLocaleString('en-US', options);
49
+ }
50
+
51
+ function parseRecoveryCodeList() {
52
+ var recoveryCodes = document.querySelectorAll("#${olRecoveryCodesListId} li");
53
+ var recoveryCodeList = "";
54
+
55
+ for (var i = 0; i < recoveryCodes.length; i++) {
56
+ var recoveryCodeLiElement = recoveryCodes[i].innerText;
57
+ recoveryCodeList += recoveryCodeLiElement + "\\r\\n";
58
+ }
59
+
60
+ return recoveryCodeList;
61
+ }
62
+
63
+ function buildDownloadContent() {
64
+ var recoveryCodeList = parseRecoveryCodeList();
65
+ var dt = new Date();
66
+ var options = {
67
+ month: 'long',
68
+ day: 'numeric',
69
+ year: 'numeric',
70
+ hour: 'numeric',
71
+ minute: 'numeric',
72
+ timeZoneName: 'short'
73
+ };
74
+
75
+ return fileBodyContent =
76
+ ${JSON.stringify(msgStr("recovery-codes-download-file-header"))} + "\\n\\n" +
77
+ recoveryCodeList + "\\n" +
78
+ ${JSON.stringify(msgStr("recovery-codes-download-file-description"))} + "\\n\\n" +
79
+ ${JSON.stringify(msgStr("recovery-codes-download-file-date"))} + " " + formatCurrentDateTime();
80
+ }
81
+
82
+ function setUpDownloadLinkAndDownload(filename, text) {
83
+ var el = document.createElement('a');
84
+ el.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
85
+ el.setAttribute('download', filename);
86
+ el.style.display = 'none';
87
+ document.body.appendChild(el);
88
+ el.click();
89
+ document.body.removeChild(el);
90
+ }
91
+
92
+ function downloadRecoveryCodes() {
93
+ setUpDownloadLinkAndDownload('kc-download-recovery-codes.txt', buildDownloadContent());
94
+ }
95
+
96
+ var downloadButton = document.getElementById("downloadRecoveryCodes");
97
+ downloadButton && downloadButton.addEventListener("click", downloadRecoveryCodes);
98
+
99
+ /* print recovery codes */
100
+ function buildPrintContent() {
101
+ var recoveryCodeListHTML = document.getElementById('${olRecoveryCodesListId}').innerHTML;
102
+ var styles =
103
+ \`@page { size: auto; margin-top: 0; }
104
+ body { width: 480px; }
105
+ div { list-style-type: none; font-family: monospace }
106
+ p:first-of-type { margin-top: 48px }\`;
107
+
108
+ return printFileContent =
109
+ "<html><style>" + styles + "</style><body>" +
110
+ "<title>kc-download-recovery-codes</title>" +
111
+ "<p>" + ${JSON.stringify(msgStr("recovery-codes-download-file-header"))} + "</p>" +
112
+ "<div>" + recoveryCodeListHTML + "</div>" +
113
+ "<p>" + ${JSON.stringify(msgStr("recovery-codes-download-file-description"))} + "</p>" +
114
+ "<p>" + ${JSON.stringify(msgStr("recovery-codes-download-file-date"))} + " " + formatCurrentDateTime() + "</p>" +
115
+ "</body></html>";
116
+ }
117
+
118
+ function printRecoveryCodes() {
119
+ var w = window.open();
120
+ w.document.write(buildPrintContent());
121
+ w.print();
122
+ w.close();
123
+ }
124
+
125
+ var printButton = document.getElementById("printRecoveryCodes");
126
+ printButton && printButton.addEventListener("click", printRecoveryCodes);
127
+ `
128
+ }
129
+ ]
130
+ });
131
+
132
+ useEffect(() => {
133
+ if (isFetchingTranslations) {
134
+ return;
135
+ }
136
+
137
+ (async () => {
138
+ await waitForElementMountedOnDom({
139
+ elementId: olRecoveryCodesListId
140
+ });
141
+
142
+ insertScriptTags();
143
+ })();
144
+ }, [isFetchingTranslations]);
145
+ }
@@ -13,30 +13,24 @@ export default meta;
13
13
 
14
14
  type Story = StoryObj<typeof meta>;
15
15
 
16
- export const Default: Story = {
17
- render: () => <KcPageStory />
18
- };
16
+ export const Default: Story = {};
19
17
 
20
18
  export const Arabic: Story = {
21
- render: () => (
22
- <KcPageStory
23
- kcContext={{
24
- locale: {
25
- currentLanguageTag: "ar",
26
- rtl: true
27
- }
28
- }}
29
- />
30
- )
19
+ args: {
20
+ kcContext: {
21
+ locale: {
22
+ currentLanguageTag: "ar",
23
+ rtl: true
24
+ }
25
+ }
26
+ }
31
27
  };
32
28
  export const French: Story = {
33
- render: () => (
34
- <KcPageStory
35
- kcContext={{
36
- locale: {
37
- currentLanguageTag: "fr"
38
- }
39
- }}
40
- />
41
- )
29
+ args: {
30
+ kcContext: {
31
+ locale: {
32
+ currentLanguageTag: "fr"
33
+ }
34
+ }
35
+ }
42
36
  };
@@ -1,70 +1,70 @@
1
- import { Button } from "@/components/ui/button";
2
- import { Field, FieldError, FieldLabel } from "@/components/ui/field";
3
- import { Input } from "@/components/ui/input";
4
- import { useI18n } from '@/login/i18n';
5
- import { useKcContext } from '@/login/KcContext';
6
- import { kcSanitize } from "keycloakify/lib/kcSanitize";
7
- import { assert } from "tsafe/assert";
8
- import { Template } from "../../components/Template";
9
-
10
- export function Page() {
11
-
12
- const { kcContext } = useKcContext();
13
- assert(kcContext.pageId === "login-recovery-authn-code-input.ftl");
14
-
15
- const { url, messagesPerField, recoveryAuthnCodesInputBean } = kcContext;
16
-
17
- const { msg, msgStr } = useI18n();
18
- return (
19
- <Template
20
- headerNode={msg("auth-recovery-code-header")}
21
- displayMessage={!messagesPerField.existsError("recoveryCodeInput")}
22
- >
23
- <form
24
- id="kc-recovery-code-login-form"
25
- className="space-y-6"
26
- action={url.loginAction}
27
- method="post"
28
- >
29
- <Field>
30
- <FieldLabel htmlFor="recoveryCodeInput">
31
- {" "}
32
- {msg(
33
- "auth-recovery-code-prompt",
34
- `${recoveryAuthnCodesInputBean.codeNumber}`
35
- )}
36
- </FieldLabel>
37
- <Input
38
- tabIndex={1}
39
- id="recoveryCodeInput"
40
- name="recoveryCodeInput"
41
- autoComplete="off"
42
- type="text"
43
- autoFocus
44
- placeholder="Enter recovery code"
45
- aria-invalid={messagesPerField.existsError("recoveryCodeInput")}
46
- />
47
- {messagesPerField.existsError("recoveryCodeInput") && (
48
- <FieldError>
49
- <span
50
- id="input-error"
51
- aria-live="polite"
52
- dangerouslySetInnerHTML={{
53
- __html: kcSanitize(
54
- messagesPerField.getFirstError(
55
- "recoveryCodeInput"
56
- )
57
- )
58
- }}
59
- />
60
- </FieldError>
61
- )}
62
- </Field>
63
-
64
- <Button className="w-full" name="login" id="kc-login" type="submit">
65
- {msgStr("doLogIn")}
66
- </Button>
67
- </form>
68
- </Template>
69
- );
70
- }
1
+ import { Button } from "@/components/ui/button";
2
+ import { Field, FieldError, FieldLabel } from "@/components/ui/field";
3
+ import { Input } from "@/components/ui/input";
4
+ import { useI18n } from '@/login/i18n';
5
+ import { useKcContext } from '@/login/KcContext';
6
+ import { kcSanitize } from "keycloakify/lib/kcSanitize";
7
+ import { assert } from "tsafe/assert";
8
+ import { Template } from "../../components/Template";
9
+
10
+ export function Page() {
11
+
12
+ const { kcContext } = useKcContext();
13
+ assert(kcContext.pageId === "login-recovery-authn-code-input.ftl");
14
+
15
+ const { url, messagesPerField, recoveryAuthnCodesInputBean } = kcContext;
16
+
17
+ const { msg, msgStr } = useI18n();
18
+ return (
19
+ <Template
20
+ headerNode={msg("auth-recovery-code-header")}
21
+ displayMessage={!messagesPerField.existsError("recoveryCodeInput")}
22
+ >
23
+ <form
24
+ id="kc-recovery-code-login-form"
25
+ className="space-y-6"
26
+ action={url.loginAction}
27
+ method="post"
28
+ >
29
+ <Field>
30
+ <FieldLabel htmlFor="recoveryCodeInput">
31
+ {" "}
32
+ {msg(
33
+ "auth-recovery-code-prompt",
34
+ `${recoveryAuthnCodesInputBean.codeNumber}`
35
+ )}
36
+ </FieldLabel>
37
+ <Input
38
+ tabIndex={1}
39
+ id="recoveryCodeInput"
40
+ name="recoveryCodeInput"
41
+ autoComplete="off"
42
+ type="text"
43
+ autoFocus
44
+ placeholder="Enter recovery code"
45
+ aria-invalid={messagesPerField.existsError("recoveryCodeInput")}
46
+ />
47
+ {messagesPerField.existsError("recoveryCodeInput") && (
48
+ <FieldError>
49
+ <span
50
+ id="input-error"
51
+ aria-live="polite"
52
+ dangerouslySetInnerHTML={{
53
+ __html: kcSanitize(
54
+ messagesPerField.getFirstError(
55
+ "recoveryCodeInput"
56
+ )
57
+ )
58
+ }}
59
+ />
60
+ </FieldError>
61
+ )}
62
+ </Field>
63
+
64
+ <Button className="w-full" name="login" id="kc-login" type="submit">
65
+ {msgStr("doLogIn")}
66
+ </Button>
67
+ </form>
68
+ </Template>
69
+ );
70
+ }
@@ -1,3 +1,3 @@
1
- import { Page } from "./Page";
2
-
3
- export default Page;
1
+ import { Page } from "./Page";
2
+
3
+ export default Page;
@@ -12,32 +12,26 @@ export default meta;
12
12
 
13
13
  type Story = StoryObj<typeof meta>;
14
14
 
15
- export const Default: Story = {
16
- render: () => <KcPageStory />
17
- };
15
+ export const Default: Story = {};
18
16
 
19
17
  export const Arabic: Story = {
20
- render: () => (
21
- <KcPageStory
22
- kcContext={{
23
- locale: {
24
- currentLanguageTag: "ar",
25
- rtl: true
26
- }
27
- }}
28
- />
29
- )
18
+ args: {
19
+ kcContext: {
20
+ locale: {
21
+ currentLanguageTag: "ar",
22
+ rtl: true
23
+ }
24
+ }
25
+ }
30
26
  };
31
27
  export const French: Story = {
32
- render: () => (
33
- <KcPageStory
34
- kcContext={{
35
- locale: {
36
- currentLanguageTag: "fr"
37
- }
38
- }}
39
- />
40
- )
28
+ args: {
29
+ kcContext: {
30
+ locale: {
31
+ currentLanguageTag: "fr"
32
+ }
33
+ }
34
+ }
41
35
  };
42
36
 
43
37
  /**
@@ -47,22 +41,20 @@ export const French: Story = {
47
41
  * - Key Aspect: Ensures that the component handles the absence of OTP credentials correctly.
48
42
  */
49
43
  export const WithoutOtpCredentials: Story = {
50
- render: () => (
51
- <KcPageStory
52
- kcContext={{
53
- url: {
54
- loginAction: "/mock-login"
55
- },
56
- configuredOtpCredentials: {
57
- userOtpCredentials: [],
58
- selectedCredentialId: undefined
59
- },
60
- messagesPerField: {
61
- existsError: () => false
62
- }
63
- }}
64
- />
65
- )
44
+ args: {
45
+ kcContext: {
46
+ url: {
47
+ loginAction: "/mock-login"
48
+ },
49
+ configuredOtpCredentials: {
50
+ userOtpCredentials: [],
51
+ selectedCredentialId: undefined
52
+ },
53
+ messagesPerField: {
54
+ existsError: () => false
55
+ }
56
+ }
57
+ }
66
58
  };
67
59
 
68
60
  /**
@@ -72,26 +64,24 @@ export const WithoutOtpCredentials: Story = {
72
64
  * - Key Aspect: Ensures that error messages are displayed correctly for OTP-related errors.
73
65
  */
74
66
  export const WithOtpError: Story = {
75
- render: () => (
76
- <KcPageStory
77
- kcContext={{
78
- url: {
79
- loginAction: "/mock-login"
80
- },
81
- configuredOtpCredentials: {
82
- userOtpCredentials: [
83
- { id: "otp1", userLabel: "Device 1" },
84
- { id: "otp2", userLabel: "Device 2" }
85
- ],
86
- selectedCredentialId: "otp1"
87
- },
88
- messagesPerField: {
89
- existsError: (field: string) => field === "totp",
90
- get: () => "Invalid OTP selection"
91
- }
92
- }}
93
- />
94
- )
67
+ args: {
68
+ kcContext: {
69
+ url: {
70
+ loginAction: "/mock-login"
71
+ },
72
+ configuredOtpCredentials: {
73
+ userOtpCredentials: [
74
+ { id: "otp1", userLabel: "Device 1" },
75
+ { id: "otp2", userLabel: "Device 2" }
76
+ ],
77
+ selectedCredentialId: "otp1"
78
+ },
79
+ messagesPerField: {
80
+ existsError: (field: string) => field === "totp",
81
+ get: () => "Invalid OTP selection"
82
+ }
83
+ }
84
+ }
95
85
  };
96
86
 
97
87
  /**
@@ -101,20 +91,18 @@ export const WithOtpError: Story = {
101
91
  * - Key Aspect: Ensures that the component renders correctly with only one OTP credential pre-selected.
102
92
  */
103
93
  export const WithOnlyOneOtpCredential: Story = {
104
- render: () => (
105
- <KcPageStory
106
- kcContext={{
107
- url: {
108
- loginAction: "/mock-login"
109
- },
110
- configuredOtpCredentials: {
111
- userOtpCredentials: [{ id: "otp1", userLabel: "Device 1" }],
112
- selectedCredentialId: "otp1"
113
- },
114
- messagesPerField: {
115
- existsError: () => false
116
- }
117
- }}
118
- />
119
- )
94
+ args: {
95
+ kcContext: {
96
+ url: {
97
+ loginAction: "/mock-login"
98
+ },
99
+ configuredOtpCredentials: {
100
+ userOtpCredentials: [{ id: "otp1", userLabel: "Device 1" }],
101
+ selectedCredentialId: "otp1"
102
+ },
103
+ messagesPerField: {
104
+ existsError: () => false
105
+ }
106
+ }
107
+ }
120
108
  };