@oussemasahbeni/keycloakify-login-shadcn 250004.0.2 → 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 (174) hide show
  1. package/keycloak-theme/components/ui/alert.tsx +4 -4
  2. package/keycloak-theme/components/ui/dropdown-menu.tsx +5 -5
  3. package/keycloak-theme/components/ui/input.tsx +1 -1
  4. package/keycloak-theme/components/ui/select.tsx +4 -4
  5. package/keycloak-theme/components/ui/separator.tsx +1 -1
  6. package/keycloak-theme/login/KcContext.ts +23 -19
  7. package/keycloak-theme/login/KcPage.tsx +47 -60
  8. package/keycloak-theme/login/assets/img/auth-logo.svg +100 -100
  9. package/keycloak-theme/login/assets/img/shape.svg +71 -71
  10. package/keycloak-theme/login/components/LogoutOtherSessions.tsx +26 -26
  11. package/keycloak-theme/login/components/PasswordWrapper.tsx +35 -35
  12. package/keycloak-theme/login/components/Template/Template.tsx +227 -226
  13. package/keycloak-theme/login/components/Template/index.ts +1 -1
  14. package/keycloak-theme/login/components/Template/useInitializeTemplate.ts +61 -61
  15. package/keycloak-theme/login/components/UserProfileFormFields/AddRemoveButtonsMultiValuedAttribute.tsx +61 -61
  16. package/keycloak-theme/login/components/UserProfileFormFields/DO_MAKE_USER_CONFIRM_PASSWORD.ts +2 -2
  17. package/keycloak-theme/login/components/UserProfileFormFields/FieldErrors.tsx +28 -28
  18. package/keycloak-theme/login/components/UserProfileFormFields/GroupLabel.tsx +70 -70
  19. package/keycloak-theme/login/components/UserProfileFormFields/InputFieldByType.tsx +58 -58
  20. package/keycloak-theme/login/components/UserProfileFormFields/InputTag.tsx +116 -116
  21. package/keycloak-theme/login/components/UserProfileFormFields/InputTagSelects.tsx +135 -135
  22. package/keycloak-theme/login/components/UserProfileFormFields/SelectTag.tsx +114 -114
  23. package/keycloak-theme/login/components/UserProfileFormFields/TextareaTag.tsx +42 -42
  24. package/keycloak-theme/login/components/UserProfileFormFields/UserProfileFormFields.tsx +127 -127
  25. package/keycloak-theme/login/components/UserProfileFormFields/index.ts +1 -1
  26. package/keycloak-theme/login/i18n.ts +47 -51
  27. package/keycloak-theme/login/mocks/getKcContextMock.ts +22 -18
  28. package/keycloak-theme/login/pages/PageIndex.tsx +134 -134
  29. package/keycloak-theme/login/pages/code/Page.stories.tsx +62 -78
  30. package/keycloak-theme/login/pages/code/Page.tsx +89 -89
  31. package/keycloak-theme/login/pages/code/index.ts +3 -3
  32. package/keycloak-theme/login/pages/delete-account-confirm/Page.stories.tsx +39 -46
  33. package/keycloak-theme/login/pages/delete-account-confirm/Page.tsx +63 -63
  34. package/keycloak-theme/login/pages/delete-account-confirm/index.ts +3 -3
  35. package/keycloak-theme/login/pages/delete-credential/Page.stories.tsx +26 -30
  36. package/keycloak-theme/login/pages/delete-credential/Page.tsx +51 -51
  37. package/keycloak-theme/login/pages/delete-credential/index.ts +3 -3
  38. package/keycloak-theme/login/pages/error/Page.stories.tsx +47 -58
  39. package/keycloak-theme/login/pages/error/Page.tsx +42 -42
  40. package/keycloak-theme/login/pages/error/index.ts +3 -3
  41. package/keycloak-theme/login/pages/frontchannel-logout/Page.stories.tsx +25 -32
  42. package/keycloak-theme/login/pages/frontchannel-logout/Page.tsx +84 -84
  43. package/keycloak-theme/login/pages/frontchannel-logout/index.ts +3 -3
  44. package/keycloak-theme/login/pages/idp-review-user-profile/Page.stories.tsx +46 -58
  45. package/keycloak-theme/login/pages/idp-review-user-profile/Page.tsx +52 -52
  46. package/keycloak-theme/login/pages/idp-review-user-profile/index.ts +3 -3
  47. package/keycloak-theme/login/pages/info/Page.stories.tsx +50 -60
  48. package/keycloak-theme/login/pages/info/Page.tsx +92 -92
  49. package/keycloak-theme/login/pages/link-idp-action/Page.stories.tsx +32 -16
  50. package/keycloak-theme/login/pages/link-idp-action/Page.tsx +43 -43
  51. package/keycloak-theme/login/pages/link-idp-action/index.ts +3 -3
  52. package/keycloak-theme/login/pages/login/Form.tsx +242 -242
  53. package/keycloak-theme/login/pages/login/Info.tsx +29 -29
  54. package/keycloak-theme/login/pages/login/Page.stories.tsx +345 -365
  55. package/keycloak-theme/login/pages/login/Page.tsx +44 -44
  56. package/keycloak-theme/login/pages/login/SocialProviders.tsx +107 -107
  57. package/keycloak-theme/login/pages/login/index.ts +3 -3
  58. package/keycloak-theme/login/pages/login/providers/apple.svg +3 -3
  59. package/keycloak-theme/login/pages/login/providers/bitbucket.svg +11 -11
  60. package/keycloak-theme/login/pages/login/providers/discord.svg +4 -4
  61. package/keycloak-theme/login/pages/login/providers/facebook.svg +5 -5
  62. package/keycloak-theme/login/pages/login/providers/github.svg +3 -3
  63. package/keycloak-theme/login/pages/login/providers/gitlab.svg +7 -7
  64. package/keycloak-theme/login/pages/login/providers/google.svg +7 -7
  65. package/keycloak-theme/login/pages/login/providers/instagram.svg +31 -31
  66. package/keycloak-theme/login/pages/login/providers/linkedin.svg +3 -3
  67. package/keycloak-theme/login/pages/login/providers/microsoft.svg +6 -6
  68. package/keycloak-theme/login/pages/login/providers/oidc.svg +5 -5
  69. package/keycloak-theme/login/pages/login/providers/openshift.svg +7 -7
  70. package/keycloak-theme/login/pages/login/providers/paypal.svg +6 -6
  71. package/keycloak-theme/login/pages/login/providers/slack.svg +11 -11
  72. package/keycloak-theme/login/pages/login/providers/stackoverflow.svg +5 -5
  73. package/keycloak-theme/login/pages/login/providers/x.svg +3 -3
  74. package/keycloak-theme/login/pages/login/useProviderLogos.tsx +39 -39
  75. package/keycloak-theme/login/pages/login/useScript.tsx +62 -62
  76. package/keycloak-theme/login/pages/login-config-totp/Page.stories.tsx +45 -59
  77. package/keycloak-theme/login/pages/login-config-totp/Page.tsx +240 -240
  78. package/keycloak-theme/login/pages/login-config-totp/index.ts +3 -3
  79. package/keycloak-theme/login/pages/login-idp-link-confirm/Page.stories.tsx +30 -34
  80. package/keycloak-theme/login/pages/login-idp-link-confirm/Page.tsx +43 -43
  81. package/keycloak-theme/login/pages/login-idp-link-confirm/index.ts +3 -3
  82. package/keycloak-theme/login/pages/login-idp-link-confirm-override/Page.stories.tsx +16 -22
  83. package/keycloak-theme/login/pages/login-idp-link-confirm-override/Page.tsx +47 -47
  84. package/keycloak-theme/login/pages/login-idp-link-confirm-override/index.ts +3 -3
  85. package/keycloak-theme/login/pages/login-idp-link-email/Page.stories.tsx +54 -62
  86. package/keycloak-theme/login/pages/login-idp-link-email/Page.tsx +54 -54
  87. package/keycloak-theme/login/pages/login-idp-link-email/index.ts +3 -3
  88. package/keycloak-theme/login/pages/login-oauth-grant/Page.stories.tsx +39 -45
  89. package/keycloak-theme/login/pages/login-oauth-grant/Page.tsx +126 -126
  90. package/keycloak-theme/login/pages/login-oauth-grant/index.ts +3 -3
  91. package/keycloak-theme/login/pages/login-oauth2-device-verify-user-code/Page.stories.tsx +38 -48
  92. package/keycloak-theme/login/pages/login-oauth2-device-verify-user-code/Page.tsx +58 -58
  93. package/keycloak-theme/login/pages/login-oauth2-device-verify-user-code/index.ts +3 -3
  94. package/keycloak-theme/login/pages/login-otp/Page.stories.tsx +82 -96
  95. package/keycloak-theme/login/pages/login-otp/Page.tsx +108 -108
  96. package/keycloak-theme/login/pages/login-otp/index.ts +3 -3
  97. package/keycloak-theme/login/pages/login-page-expired/Page.stories.tsx +28 -36
  98. package/keycloak-theme/login/pages/login-page-expired/Page.tsx +47 -47
  99. package/keycloak-theme/login/pages/login-page-expired/index.ts +3 -3
  100. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/Page.stories.tsx +20 -0
  101. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/Page.tsx +233 -233
  102. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/index.ts +3 -3
  103. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/useScript.tsx +63 -63
  104. package/keycloak-theme/login/pages/login-password/Page.stories.tsx +55 -56
  105. package/keycloak-theme/login/pages/login-password/Page.tsx +149 -149
  106. package/keycloak-theme/login/pages/login-password/index.ts +3 -3
  107. package/keycloak-theme/login/pages/login-password/useScript.tsx +63 -63
  108. package/keycloak-theme/login/pages/login-recovery-authn-code-config/Page.stories.tsx +28 -36
  109. package/keycloak-theme/login/pages/login-recovery-authn-code-config/Page.tsx +181 -181
  110. package/keycloak-theme/login/pages/login-recovery-authn-code-config/index.ts +3 -3
  111. package/keycloak-theme/login/pages/login-recovery-authn-code-config/useScript.tsx +145 -145
  112. package/keycloak-theme/login/pages/login-recovery-authn-code-input/Page.stories.tsx +16 -22
  113. package/keycloak-theme/login/pages/login-recovery-authn-code-input/Page.tsx +70 -70
  114. package/keycloak-theme/login/pages/login-recovery-authn-code-input/index.ts +3 -3
  115. package/keycloak-theme/login/pages/login-reset-otp/Page.stories.tsx +62 -74
  116. package/keycloak-theme/login/pages/login-reset-otp/Page.tsx +86 -86
  117. package/keycloak-theme/login/pages/login-reset-otp/index.ts +3 -3
  118. package/keycloak-theme/login/pages/login-reset-password/Form.tsx +68 -68
  119. package/keycloak-theme/login/pages/login-reset-password/Page.stories.tsx +44 -54
  120. package/keycloak-theme/login/pages/login-reset-password/Page.tsx +27 -27
  121. package/keycloak-theme/login/pages/login-reset-password/index.ts +3 -3
  122. package/keycloak-theme/login/pages/login-update-password/Page.stories.tsx +40 -50
  123. package/keycloak-theme/login/pages/login-update-password/Page.tsx +111 -111
  124. package/keycloak-theme/login/pages/login-update-password/index.ts +3 -3
  125. package/keycloak-theme/login/pages/login-update-profile/Page.stories.tsx +28 -36
  126. package/keycloak-theme/login/pages/login-update-profile/Page.tsx +68 -68
  127. package/keycloak-theme/login/pages/login-update-profile/index.ts +3 -3
  128. package/keycloak-theme/login/pages/login-username/Page.stories.tsx +32 -42
  129. package/keycloak-theme/login/pages/login-username/Page.tsx +246 -246
  130. package/keycloak-theme/login/pages/login-username/index.ts +3 -3
  131. package/keycloak-theme/login/pages/login-username/useScript.tsx +62 -62
  132. package/keycloak-theme/login/pages/login-verify-email/Page.stories.tsx +68 -80
  133. package/keycloak-theme/login/pages/login-verify-email/Page.tsx +38 -38
  134. package/keycloak-theme/login/pages/login-verify-email/index.ts +3 -3
  135. package/keycloak-theme/login/pages/login-x509-info/Page.stories.tsx +29 -37
  136. package/keycloak-theme/login/pages/login-x509-info/Page.tsx +75 -75
  137. package/keycloak-theme/login/pages/login-x509-info/index.ts +3 -3
  138. package/keycloak-theme/login/pages/logout-confirm/Page.stories.tsx +34 -42
  139. package/keycloak-theme/login/pages/logout-confirm/Page.tsx +53 -53
  140. package/keycloak-theme/login/pages/logout-confirm/index.ts +3 -3
  141. package/keycloak-theme/login/pages/register/Form.tsx +106 -106
  142. package/keycloak-theme/login/pages/register/Page.stories.tsx +23 -6
  143. package/keycloak-theme/login/pages/register/Page.tsx +26 -26
  144. package/keycloak-theme/login/pages/register/TermsAcceptance.tsx +56 -56
  145. package/keycloak-theme/login/pages/register/index.ts +3 -3
  146. package/keycloak-theme/login/pages/saml-post-form/Page.stories.tsx +16 -22
  147. package/keycloak-theme/login/pages/saml-post-form/Page.tsx +66 -66
  148. package/keycloak-theme/login/pages/saml-post-form/index.ts +3 -3
  149. package/keycloak-theme/login/pages/select-authenticator/Page.stories.tsx +83 -95
  150. package/keycloak-theme/login/pages/select-authenticator/Page.tsx +100 -100
  151. package/keycloak-theme/login/pages/select-authenticator/index.ts +3 -3
  152. package/keycloak-theme/login/pages/select-organization/Page.stories.tsx +62 -49
  153. package/keycloak-theme/login/pages/select-organization/Page.tsx +126 -126
  154. package/keycloak-theme/login/pages/select-organization/index.ts +3 -3
  155. package/keycloak-theme/login/pages/terms/Page.stories.tsx +15 -0
  156. package/keycloak-theme/login/pages/terms/Page.tsx +51 -51
  157. package/keycloak-theme/login/pages/terms/index.ts +3 -3
  158. package/keycloak-theme/login/pages/update-email/Page.stories.tsx +27 -35
  159. package/keycloak-theme/login/pages/update-email/Page.tsx +62 -62
  160. package/keycloak-theme/login/pages/update-email/index.ts +3 -3
  161. package/keycloak-theme/login/pages/webauthn-authenticate/Page.stories.tsx +112 -126
  162. package/keycloak-theme/login/pages/webauthn-authenticate/Page.tsx +202 -202
  163. package/keycloak-theme/login/pages/webauthn-authenticate/index.ts +3 -3
  164. package/keycloak-theme/login/pages/webauthn-authenticate/useScript.tsx +55 -55
  165. package/keycloak-theme/login/pages/webauthn-error/Page.stories.tsx +54 -66
  166. package/keycloak-theme/login/pages/webauthn-error/Page.tsx +73 -73
  167. package/keycloak-theme/login/pages/webauthn-error/index.ts +3 -3
  168. package/keycloak-theme/login/pages/webauthn-register/Page.stories.tsx +39 -49
  169. package/keycloak-theme/login/pages/webauthn-register/Page.tsx +78 -78
  170. package/keycloak-theme/login/pages/webauthn-register/index.ts +3 -3
  171. package/keycloak-theme/login/pages/webauthn-register/useScript.tsx +62 -62
  172. package/keycloak-theme/login/shared/getColorScheme.ts +45 -0
  173. package/keycloak-theme/login/styleLevelCustomization.tsx +36 -17
  174. package/package.json +6 -5
@@ -11,8 +11,27 @@ export default meta;
11
11
 
12
12
  type Story = StoryObj<typeof meta>;
13
13
 
14
- export const Default: Story = {
15
- render: () => <KcPageStory />
14
+ export const Default: Story = {};
15
+
16
+ export const Arabic: Story = {
17
+ args: {
18
+ kcContext: {
19
+ locale: {
20
+ currentLanguageTag: "ar",
21
+ rtl: true
22
+ }
23
+ }
24
+ }
25
+ };
26
+ export const French: Story = {
27
+ args: {
28
+ kcContext: {
29
+ locale: {
30
+ currentLanguageTag: "fr",
31
+ rtl: false
32
+ }
33
+ }
34
+ }
16
35
  };
17
36
 
18
37
  /**
@@ -22,25 +41,23 @@ export const Default: Story = {
22
41
  * - Key Aspect: Ensures that when there are more than 3 organizations, they are displayed in a grid.
23
42
  */
24
43
  export const WithManyOrganizations: Story = {
25
- render: () => (
26
- <KcPageStory
27
- kcContext={{
28
- url: {
29
- loginAction: "/mock-login-action"
30
- },
31
- user: {
32
- organizations: [
33
- { alias: "org1", name: "Organization 1" },
34
- { alias: "org2", name: "Organization 2" },
35
- { alias: "org3", name: "Organization 3" },
36
- { alias: "org4", name: "Organization 4" },
37
- { alias: "org5", name: "Organization 5" },
38
- { alias: "org6", name: "Organization 6" }
39
- ]
40
- }
41
- }}
42
- />
43
- )
44
+ args: {
45
+ kcContext: {
46
+ url: {
47
+ loginAction: "/mock-login-action"
48
+ },
49
+ user: {
50
+ organizations: [
51
+ { alias: "org1", name: "Organization 1" },
52
+ { alias: "org2", name: "Organization 2" },
53
+ { alias: "org3", name: "Organization 3" },
54
+ { alias: "org4", name: "Organization 4" },
55
+ { alias: "org5", name: "Organization 5" },
56
+ { alias: "org6", name: "Organization 6" }
57
+ ]
58
+ }
59
+ }
60
+ }
44
61
  };
45
62
 
46
63
  /**
@@ -50,22 +67,20 @@ export const WithManyOrganizations: Story = {
50
67
  * - Key Aspect: Ensures that when there are 3 or fewer organizations, they are displayed in a list.
51
68
  */
52
69
  export const WithFewOrganizations: Story = {
53
- render: () => (
54
- <KcPageStory
55
- kcContext={{
56
- url: {
57
- loginAction: "/mock-login-action"
58
- },
59
- user: {
60
- organizations: [
61
- { alias: "org1", name: "Organization 1" },
62
- { alias: "org2", name: "Organization 2" },
63
- { alias: "org3", name: "Organization 3" }
64
- ]
65
- }
66
- }}
67
- />
68
- )
70
+ args: {
71
+ kcContext: {
72
+ url: {
73
+ loginAction: "/mock-login-action"
74
+ },
75
+ user: {
76
+ organizations: [
77
+ { alias: "org1", name: "Organization 1" },
78
+ { alias: "org2", name: "Organization 2" },
79
+ { alias: "org3", name: "Organization 3" }
80
+ ]
81
+ }
82
+ }
83
+ }
69
84
  };
70
85
 
71
86
  /**
@@ -75,16 +90,14 @@ export const WithFewOrganizations: Story = {
75
90
  * - Key Aspect: Ensures that a single organization is displayed correctly.
76
91
  */
77
92
  export const WithSingleOrganization: Story = {
78
- render: () => (
79
- <KcPageStory
80
- kcContext={{
81
- url: {
82
- loginAction: "/mock-login-action"
83
- },
84
- user: {
85
- organizations: [{ alias: "org1", name: "My Organization" }]
86
- }
87
- }}
88
- />
89
- )
93
+ args: {
94
+ kcContext: {
95
+ url: {
96
+ loginAction: "/mock-login-action"
97
+ },
98
+ user: {
99
+ organizations: [{ alias: "org1", name: "My Organization" }]
100
+ }
101
+ }
102
+ }
90
103
  };
@@ -1,126 +1,126 @@
1
- import { Button } from "@/components/ui/button";
2
- import {
3
- Select,
4
- SelectContent,
5
- SelectItem,
6
- SelectTrigger,
7
- SelectValue
8
- } from "@/components/ui/select";
9
- import { useI18n } from '@/login/i18n';
10
- import { useKcContext } from '@/login/KcContext';
11
- import { Building2 } from "lucide-react";
12
- import { MouseEvent, useRef, useState } from "react";
13
- import { assert } from 'tsafe/assert';
14
- import { Template } from "../../components/Template";
15
-
16
-
17
- export function Page() {
18
- const { kcContext } = useKcContext();
19
- assert(kcContext.pageId === "select-organization.ftl");
20
-
21
- const { msg } = useI18n();
22
-
23
- const [isSubmitting, setIsSubmitting] = useState(false);
24
- const [selectedOrg, setSelectedOrg] = useState<string>("");
25
- const formRef = useRef<HTMLFormElement>(null);
26
- const organizationInputRef = useRef<HTMLInputElement>(null);
27
-
28
- const onOrganizationClick =
29
- (organizationAlias: string) => (event: MouseEvent<HTMLButtonElement>) => {
30
- event.preventDefault();
31
-
32
- if (!organizationInputRef.current || !formRef.current) {
33
- return;
34
- }
35
-
36
- organizationInputRef.current.value = organizationAlias;
37
- setIsSubmitting(true);
38
-
39
- if (typeof formRef.current.requestSubmit === "function") {
40
- formRef.current.requestSubmit();
41
- return;
42
- }
43
-
44
- formRef.current.submit();
45
- };
46
-
47
- const onSelectChange = (value: string) => {
48
- setSelectedOrg(value);
49
- if (!organizationInputRef.current || !formRef.current) {
50
- return;
51
- }
52
-
53
- organizationInputRef.current.value = value;
54
- setIsSubmitting(true);
55
-
56
- if (typeof formRef.current.requestSubmit === "function") {
57
- formRef.current.requestSubmit();
58
- return;
59
- }
60
-
61
- formRef.current.submit();
62
- };
63
-
64
- const organizations = kcContext.user.organizations ?? [];
65
- const useSelect = organizations.length > 3;
66
-
67
- return (
68
- <Template
69
- headerNode={msg("organization.selectTitle")}
70
- >
71
- <form ref={formRef} action={kcContext.url.loginAction} method="post">
72
- <div id="kc-user-organizations" className="space-y-2">
73
- <h2 className="text-md font-semibold">
74
- {msg("organization.select")}
75
- </h2>
76
- {useSelect ? (
77
- <div className="space-y-2">
78
- <Select
79
- value={selectedOrg}
80
- onValueChange={onSelectChange}
81
- disabled={isSubmitting}
82
- >
83
- <SelectTrigger className="w-full">
84
- <SelectValue
85
- placeholder={msg("organization.pickPlaceholder")}
86
- />
87
- </SelectTrigger>
88
- <SelectContent>
89
- {organizations.map(({ alias, name }) => (
90
- <SelectItem key={alias} value={alias}>
91
- <div className="flex items-center gap-2">
92
- <Building2 className="w-4 h-4 text-muted-foreground" />
93
- {name ?? alias}
94
- </div>
95
- </SelectItem>
96
- ))}
97
- </SelectContent>
98
- </Select>
99
- </div>
100
- ) : (
101
- <ul className="space-y-3">
102
- {organizations.map(({ alias, name }) => (
103
- <li key={alias}>
104
- <Button
105
- id={`organization-${alias}`}
106
- type="button"
107
- variant="outline"
108
- onClick={onOrganizationClick(alias)}
109
- disabled={isSubmitting}
110
- className="w-full h-auto p-4 flex items-center gap-3 justify-start hover:bg-accent hover:border-primary transition-colors"
111
- >
112
- <Building2 className="w-5 h-5 text-muted-foreground shrink-0" />
113
- <span className="font-medium text-sm">
114
- {name ?? alias}
115
- </span>
116
- </Button>
117
- </li>
118
- ))}
119
- </ul>
120
- )}
121
- </div>
122
- <input ref={organizationInputRef} type="hidden" name="kc.org" />
123
- </form>
124
- </Template>
125
- );
126
- }
1
+ import { Button } from "@/components/ui/button";
2
+ import {
3
+ Select,
4
+ SelectContent,
5
+ SelectItem,
6
+ SelectTrigger,
7
+ SelectValue
8
+ } from "@/components/ui/select";
9
+ import { useI18n } from '@/login/i18n';
10
+ import { useKcContext } from '@/login/KcContext';
11
+ import { Building2 } from "lucide-react";
12
+ import { MouseEvent, useRef, useState } from "react";
13
+ import { assert } from 'tsafe/assert';
14
+ import { Template } from "../../components/Template";
15
+
16
+
17
+ export function Page() {
18
+ const { kcContext } = useKcContext();
19
+ assert(kcContext.pageId === "select-organization.ftl");
20
+
21
+ const { msg } = useI18n();
22
+
23
+ const [isSubmitting, setIsSubmitting] = useState(false);
24
+ const [selectedOrg, setSelectedOrg] = useState<string>("");
25
+ const formRef = useRef<HTMLFormElement>(null);
26
+ const organizationInputRef = useRef<HTMLInputElement>(null);
27
+
28
+ const onOrganizationClick =
29
+ (organizationAlias: string) => (event: MouseEvent<HTMLButtonElement>) => {
30
+ event.preventDefault();
31
+
32
+ if (!organizationInputRef.current || !formRef.current) {
33
+ return;
34
+ }
35
+
36
+ organizationInputRef.current.value = organizationAlias;
37
+ setIsSubmitting(true);
38
+
39
+ if (typeof formRef.current.requestSubmit === "function") {
40
+ formRef.current.requestSubmit();
41
+ return;
42
+ }
43
+
44
+ formRef.current.submit();
45
+ };
46
+
47
+ const onSelectChange = (value: string) => {
48
+ setSelectedOrg(value);
49
+ if (!organizationInputRef.current || !formRef.current) {
50
+ return;
51
+ }
52
+
53
+ organizationInputRef.current.value = value;
54
+ setIsSubmitting(true);
55
+
56
+ if (typeof formRef.current.requestSubmit === "function") {
57
+ formRef.current.requestSubmit();
58
+ return;
59
+ }
60
+
61
+ formRef.current.submit();
62
+ };
63
+
64
+ const organizations = kcContext.user.organizations ?? [];
65
+ const useSelect = organizations.length > 3;
66
+
67
+ return (
68
+ <Template
69
+ headerNode={msg("organization.selectTitle")}
70
+ >
71
+ <form ref={formRef} action={kcContext.url.loginAction} method="post">
72
+ <div id="kc-user-organizations" className="space-y-2">
73
+ <h2 className="text-md font-semibold">
74
+ {msg("organization.select")}
75
+ </h2>
76
+ {useSelect ? (
77
+ <div className="space-y-2">
78
+ <Select
79
+ value={selectedOrg}
80
+ onValueChange={onSelectChange}
81
+ disabled={isSubmitting}
82
+ >
83
+ <SelectTrigger className="w-full">
84
+ <SelectValue
85
+ placeholder={msg("organization.pickPlaceholder")}
86
+ />
87
+ </SelectTrigger>
88
+ <SelectContent>
89
+ {organizations.map(({ alias, name }) => (
90
+ <SelectItem key={alias} value={alias}>
91
+ <div className="flex items-center gap-2">
92
+ <Building2 className="w-4 h-4 text-muted-foreground" />
93
+ {name ?? alias}
94
+ </div>
95
+ </SelectItem>
96
+ ))}
97
+ </SelectContent>
98
+ </Select>
99
+ </div>
100
+ ) : (
101
+ <ul className="space-y-3">
102
+ {organizations.map(({ alias, name }) => (
103
+ <li key={alias}>
104
+ <Button
105
+ id={`organization-${alias}`}
106
+ type="button"
107
+ variant="outline"
108
+ onClick={onOrganizationClick(alias)}
109
+ disabled={isSubmitting}
110
+ className="w-full h-auto p-4 flex items-center gap-3 justify-start hover:bg-accent hover:border-primary transition-colors"
111
+ >
112
+ <Building2 className="w-5 h-5 text-muted-foreground shrink-0" />
113
+ <span className="font-medium text-sm">
114
+ {name ?? alias}
115
+ </span>
116
+ </Button>
117
+ </li>
118
+ ))}
119
+ </ul>
120
+ )}
121
+ </div>
122
+ <input ref={organizationInputRef} type="hidden" name="kc.org" />
123
+ </form>
124
+ </Template>
125
+ );
126
+ }
@@ -1,3 +1,3 @@
1
- import { Page } from "./Page";
2
-
3
- export default Page;
1
+ import { Page } from "./Page";
2
+
3
+ export default Page;
@@ -23,6 +23,21 @@ export const Default: Story = {
23
23
  }
24
24
  };
25
25
 
26
+ export const Arabic: Story = {
27
+ args: {
28
+ kcContext: {
29
+ locale: {
30
+ currentLanguageTag: "ar",
31
+ rtl: true
32
+ },
33
+ "x-keycloakify": {
34
+ messages: {
35
+ termsText: "<p>شروطي باللغة <strong>العربية</strong></p>"
36
+ }
37
+ }
38
+ }
39
+ }
40
+ };
26
41
  export const French: Story = {
27
42
  args: {
28
43
  kcContext: {
@@ -1,51 +1,51 @@
1
- import { Button } from '@/components/ui/button';
2
- import { assert } from "tsafe/assert";
3
- import { useKcContext } from "../../KcContext";
4
- import { Template } from "../../components/Template";
5
- import { useI18n } from "../../i18n";
6
-
7
- export function Page() {
8
- const { kcContext } = useKcContext();
9
- assert(kcContext.pageId === "terms.ftl");
10
-
11
-
12
- const { msg, msgStr } = useI18n();
13
-
14
- const { url } = kcContext;
15
-
16
- return (
17
- <Template displayMessage={false} headerNode={msg("termsTitle")}>
18
- <div className="space-y-6">
19
- <div
20
- id="kc-terms-text"
21
- className="p-4 bg-muted/50 rounded-lg max-h-64 overflow-y-auto text-sm leading-relaxed"
22
- >
23
- {msg("termsText")}
24
- </div>
25
-
26
- <form className="space-y-4" action={url.loginAction} method="POST">
27
- <div className="flex flex-col sm:flex-row gap-3 sm:justify-between">
28
- <Button
29
- variant="outline"
30
- name="cancel"
31
- id="kc-decline"
32
- type="submit"
33
- className="sm:flex-1"
34
- >
35
- {msgStr("doDecline")}
36
- </Button>
37
- <Button
38
- name="accept"
39
- id="kc-accept"
40
- type="submit"
41
- className="sm:flex-1"
42
- >
43
- {msgStr("doAccept")}
44
- </Button>
45
-
46
- </div>
47
- </form>
48
- </div>
49
- </Template>
50
- );
51
- }
1
+ import { Button } from '@/components/ui/button';
2
+ import { assert } from "tsafe/assert";
3
+ import { useKcContext } from "../../KcContext";
4
+ import { Template } from "../../components/Template";
5
+ import { useI18n } from "../../i18n";
6
+
7
+ export function Page() {
8
+ const { kcContext } = useKcContext();
9
+ assert(kcContext.pageId === "terms.ftl");
10
+
11
+
12
+ const { msg, msgStr } = useI18n();
13
+
14
+ const { url } = kcContext;
15
+
16
+ return (
17
+ <Template displayMessage={false} headerNode={msg("termsTitle")}>
18
+ <div className="space-y-6">
19
+ <div
20
+ id="kc-terms-text"
21
+ className="p-4 bg-muted/50 rounded-lg max-h-64 overflow-y-auto text-sm leading-relaxed"
22
+ >
23
+ {msg("termsText")}
24
+ </div>
25
+
26
+ <form className="space-y-4" action={url.loginAction} method="POST">
27
+ <div className="flex flex-col sm:flex-row gap-3 sm:justify-between">
28
+ <Button
29
+ variant="outline"
30
+ name="cancel"
31
+ id="kc-decline"
32
+ type="submit"
33
+ className="sm:flex-1"
34
+ >
35
+ {msgStr("doDecline")}
36
+ </Button>
37
+ <Button
38
+ name="accept"
39
+ id="kc-accept"
40
+ type="submit"
41
+ className="sm:flex-1"
42
+ >
43
+ {msgStr("doAccept")}
44
+ </Button>
45
+
46
+ </div>
47
+ </form>
48
+ </div>
49
+ </Template>
50
+ );
51
+ }
@@ -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,17 +41,15 @@ export const French: Story = {
47
41
  * - Key Aspect: Ensures the "Cancel" button is visible and functional during app-initiated actions.
48
42
  */
49
43
  export const WithAppInitiatedAction: Story = {
50
- render: () => (
51
- <KcPageStory
52
- kcContext={{
53
- url: {
54
- loginAction: "/mock-login-action"
55
- },
56
- messagesPerField: {
57
- exists: () => false
58
- },
59
- isAppInitiatedAction: true
60
- }}
61
- />
62
- )
44
+ args: {
45
+ kcContext: {
46
+ url: {
47
+ loginAction: "/mock-login-action"
48
+ },
49
+ messagesPerField: {
50
+ exists: () => false
51
+ },
52
+ isAppInitiatedAction: true
53
+ }
54
+ }
63
55
  };