keycloakify 11.3.2 → 11.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,6 +43,11 @@
43
43
 
44
44
  Keycloakify is fully compatible with Keycloak from version 11 to 26...[and beyond](https://github.com/keycloakify/keycloakify/discussions/346#discussioncomment-5889791)
45
45
 
46
+ > 📣 **Keycloakify 26 Released**
47
+ > Themes built with Keycloakify versions **prior** to Keycloak 26 are **incompatible** with Keycloak 26.
48
+ > To ensure compatibility, simply upgrade to the latest Keycloakify version for your major release (v10 or v11) and rebuild your theme.
49
+ > No breaking changes have been introduced, but the target version ranges have been updated. For more details, see [this guide](https://docs.keycloakify.dev/targeting-specific-keycloak-versions).
50
+
46
51
  ## Sponsors
47
52
 
48
53
  Friends for the project, we trust and recommend their services.
@@ -136,6 +141,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
136
141
  <td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/oes-rioniz/"><img src="https://avatars.githubusercontent.com/u/5172296?v=4?s=100" width="100px;" alt="Omid"/><br /><sub><b>Omid</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=uchar" title="Tests">⚠️</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=uchar" title="Code">💻</a></td>
137
142
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/kathari00"><img src="https://avatars.githubusercontent.com/u/42547712?v=4?s=100" width="100px;" alt="Katharina Eiserfey"/><br /><sub><b>Katharina Eiserfey</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=kathari00" title="Code">💻</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=kathari00" title="Tests">⚠️</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=kathari00" title="Documentation">📖</a></td>
138
143
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/luca-peruzzo"><img src="https://avatars.githubusercontent.com/u/69015314?v=4?s=100" width="100px;" alt="Luca Peruzzo"/><br /><sub><b>Luca Peruzzo</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=luca-peruzzo" title="Code">💻</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=luca-peruzzo" title="Tests">⚠️</a></td>
144
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/nima70"><img src="https://avatars.githubusercontent.com/u/5094767?v=4?s=100" width="100px;" alt="Nima Shokouhfar"/><br /><sub><b>Nima Shokouhfar</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=nima70" title="Code">💻</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=nima70" title="Tests">⚠️</a></td>
139
145
  </tr>
140
146
  </tbody>
141
147
  </table>
package/bin/main.js CHANGED
@@ -16244,7 +16244,7 @@ program
16244
16244
  });
16245
16245
  program
16246
16246
  .command({
16247
- name: "initialize-login-theme",
16247
+ name: "initialize-email-theme",
16248
16248
  description: "Initialize an email theme."
16249
16249
  })
16250
16250
  .task({
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { getKcClsx } from "../../login/lib/kcClsx";
3
+ import { kcSanitize } from "../../lib/kcSanitize";
3
4
  export default function Code(props) {
4
5
  const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
5
6
  const { kcClsx } = getKcClsx({
@@ -8,6 +9,8 @@ export default function Code(props) {
8
9
  });
9
10
  const { code } = kcContext;
10
11
  const { msg } = i18n;
11
- return (_jsx(Template, Object.assign({ kcContext: kcContext, i18n: i18n, doUseDefaultCss: doUseDefaultCss, classes: classes, headerNode: code.success ? msg("codeSuccessTitle") : msg("codeErrorTitle", code.error) }, { children: _jsx("div", Object.assign({ id: "kc-code" }, { children: code.success ? (_jsxs(_Fragment, { children: [_jsx("p", { children: msg("copyCodeInstruction") }), _jsx("input", { id: "code", className: kcClsx("kcTextareaClass"), defaultValue: code.code })] })) : (_jsx("p", Object.assign({ id: "error" }, { children: code.error }))) })) })));
12
+ return (_jsx(Template, Object.assign({ kcContext: kcContext, i18n: i18n, doUseDefaultCss: doUseDefaultCss, classes: classes, headerNode: code.success ? msg("codeSuccessTitle") : msg("codeErrorTitle", code.error) }, { children: _jsx("div", Object.assign({ id: "kc-code" }, { children: code.success ? (_jsxs(_Fragment, { children: [_jsx("p", { children: msg("copyCodeInstruction") }), _jsx("input", { id: "code", className: kcClsx("kcTextareaClass"), defaultValue: code.code })] })) : (code.error && (_jsx("p", { id: "error", dangerouslySetInnerHTML: {
13
+ __html: kcSanitize(code.error)
14
+ } }))) })) })));
12
15
  }
13
16
  //# sourceMappingURL=Code.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Code.js","sourceRoot":"","sources":["../../src/login/pages/Code.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAKzD,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,KAAkE;IAC3F,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,eAAe,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAEtE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QACzB,eAAe;QACf,OAAO;KACV,CAAC,CAAC;IAEH,MAAM,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC;IAE3B,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAErB,OAAO,CACH,KAAC,QAAQ,kBACL,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,IAAI,EACV,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC,KAAK,CAAC,gBAEtF,4BAAK,EAAE,EAAC,SAAS,gBACZ,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CACZ,8BACI,sBAAI,GAAG,CAAC,qBAAqB,CAAC,GAAK,EACnC,gBAAO,EAAE,EAAC,MAAM,EAAC,SAAS,EAAE,MAAM,CAAC,iBAAiB,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,IAAI,GAAI,IACnF,CACN,CAAC,CAAC,CAAC,CACA,0BAAG,EAAE,EAAC,OAAO,gBAAE,IAAI,CAAC,KAAK,IAAK,CACjC,IACC,IACC,CACd,CAAC;AACN,CAAC"}
1
+ {"version":3,"file":"Code.js","sourceRoot":"","sources":["../../src/login/pages/Code.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAIzD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAExD,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,KAAkE;IAC3F,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,eAAe,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAEtE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QACzB,eAAe;QACf,OAAO;KACV,CAAC,CAAC;IAEH,MAAM,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC;IAE3B,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAErB,OAAO,CACH,KAAC,QAAQ,kBACL,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,IAAI,EACV,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC,KAAK,CAAC,gBAEtF,4BAAK,EAAE,EAAC,SAAS,gBACZ,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CACZ,8BACI,sBAAI,GAAG,CAAC,qBAAqB,CAAC,GAAK,EACnC,gBAAO,EAAE,EAAC,MAAM,EAAC,SAAS,EAAE,MAAM,CAAC,iBAAiB,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,IAAI,GAAI,IACnF,CACN,CAAC,CAAC,CAAC,CACA,IAAI,CAAC,KAAK,IAAI,CACV,YACI,EAAE,EAAC,OAAO,EACV,uBAAuB,EAAE;oBACrB,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;iBACjC,GACH,CACL,CACJ,IACC,IACC,CACd,CAAC;AACN,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keycloakify",
3
- "version": "11.3.2",
3
+ "version": "11.3.4",
4
4
  "description": "Framework to create custom Keycloak UIs",
5
5
  "repository": {
6
6
  "type": "git",
package/src/bin/main.ts CHANGED
@@ -171,7 +171,7 @@ program
171
171
 
172
172
  program
173
173
  .command({
174
- name: "initialize-login-theme",
174
+ name: "initialize-email-theme",
175
175
  description: "Initialize an email theme."
176
176
  })
177
177
  .task({
@@ -2,6 +2,7 @@ import { getKcClsx } from "keycloakify/login/lib/kcClsx";
2
2
  import type { PageProps } from "keycloakify/login/pages/PageProps";
3
3
  import type { KcContext } from "../KcContext";
4
4
  import type { I18n } from "../i18n";
5
+ import { kcSanitize } from "keycloakify/lib/kcSanitize";
5
6
 
6
7
  export default function Code(props: PageProps<Extract<KcContext, { pageId: "code.ftl" }>, I18n>) {
7
8
  const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
@@ -30,7 +31,14 @@ export default function Code(props: PageProps<Extract<KcContext, { pageId: "code
30
31
  <input id="code" className={kcClsx("kcTextareaClass")} defaultValue={code.code} />
31
32
  </>
32
33
  ) : (
33
- <p id="error">{code.error}</p>
34
+ code.error && (
35
+ <p
36
+ id="error"
37
+ dangerouslySetInnerHTML={{
38
+ __html: kcSanitize(code.error)
39
+ }}
40
+ />
41
+ )
34
42
  )}
35
43
  </div>
36
44
  </Template>
@@ -16,3 +16,42 @@ type Story = StoryObj<typeof meta>;
16
16
  export const Default: Story = {
17
17
  render: () => <KcPageStory />
18
18
  };
19
+ export const WithErrorCode: Story = {
20
+ render: () => (
21
+ <KcPageStory
22
+ kcContext={{
23
+ code: {
24
+ success: false,
25
+ error: "Failed to generate code"
26
+ }
27
+ }}
28
+ />
29
+ )
30
+ };
31
+ export const WithFrenchLanguage: Story = {
32
+ render: () => (
33
+ <KcPageStory
34
+ kcContext={{
35
+ locale: {
36
+ currentLanguageTag: "fr"
37
+ },
38
+ code: {
39
+ success: true,
40
+ code: "XYZ789"
41
+ }
42
+ }}
43
+ />
44
+ )
45
+ };
46
+ export const WithHtmlErrorMessage: Story = {
47
+ render: () => (
48
+ <KcPageStory
49
+ kcContext={{
50
+ code: {
51
+ success: false,
52
+ error: "Something went wrong. <a href='https://example.com'>Try again</a>"
53
+ }
54
+ }}
55
+ />
56
+ )
57
+ };
@@ -16,3 +16,33 @@ type Story = StoryObj<typeof meta>;
16
16
  export const Default: Story = {
17
17
  render: () => <KcPageStory />
18
18
  };
19
+ export const WithAIAFlow: Story = {
20
+ render: () => (
21
+ <KcPageStory
22
+ kcContext={{
23
+ triggered_from_aia: true,
24
+ url: { loginAction: "/login-action" }
25
+ }}
26
+ />
27
+ )
28
+ };
29
+ export const WithoutAIAFlow: Story = {
30
+ render: () => (
31
+ <KcPageStory
32
+ kcContext={{
33
+ triggered_from_aia: false,
34
+ url: { loginAction: "/login-action" }
35
+ }}
36
+ />
37
+ )
38
+ };
39
+ export const WithCustomButtonStyle: Story = {
40
+ render: () => (
41
+ <KcPageStory
42
+ kcContext={{
43
+ triggered_from_aia: true,
44
+ url: { loginAction: "/login-action" }
45
+ }}
46
+ />
47
+ )
48
+ };
@@ -16,3 +16,13 @@ type Story = StoryObj<typeof meta>;
16
16
  export const Default: Story = {
17
17
  render: () => <KcPageStory />
18
18
  };
19
+ export const WithCustomCredentialLabel: Story = {
20
+ render: () => (
21
+ <KcPageStory
22
+ kcContext={{
23
+ credentialLabel: "Test Credential",
24
+ url: { loginAction: "/login-action" }
25
+ }}
26
+ />
27
+ )
28
+ };
@@ -26,3 +26,38 @@ export const WithAnotherMessage: Story = {
26
26
  />
27
27
  )
28
28
  };
29
+
30
+ export const WithHtmlErrorMessage: Story = {
31
+ render: () => (
32
+ <KcPageStory
33
+ kcContext={{
34
+ message: {
35
+ summary: "<strong>Error:</strong> Something went wrong. <a href='https://example.com'>Go back</a>"
36
+ }
37
+ }}
38
+ />
39
+ )
40
+ };
41
+ export const FrenchError: Story = {
42
+ render: () => (
43
+ <KcPageStory
44
+ kcContext={{
45
+ locale: { currentLanguageTag: "fr" },
46
+ message: { summary: "Une erreur s'est produite" }
47
+ }}
48
+ />
49
+ )
50
+ };
51
+ export const WithSkipLink: Story = {
52
+ render: () => (
53
+ <KcPageStory
54
+ kcContext={{
55
+ message: { summary: "An error occurred" },
56
+ skipLink: true,
57
+ client: {
58
+ baseUrl: "https://example.com"
59
+ }
60
+ }}
61
+ />
62
+ )
63
+ };
@@ -16,3 +16,14 @@ type Story = StoryObj<typeof meta>;
16
16
  export const Default: Story = {
17
17
  render: () => <KcPageStory />
18
18
  };
19
+ export const WithoutRedirectUrl: Story = {
20
+ render: () => (
21
+ <KcPageStory
22
+ kcContext={{
23
+ logout: {
24
+ clients: []
25
+ }
26
+ }}
27
+ />
28
+ )
29
+ };
@@ -16,3 +16,47 @@ type Story = StoryObj<typeof meta>;
16
16
  export const Default: Story = {
17
17
  render: () => <KcPageStory />
18
18
  };
19
+ export const WithFormValidationErrors: Story = {
20
+ render: () => (
21
+ <KcPageStory
22
+ kcContext={{
23
+ messagesPerField: {
24
+ existsError: (fieldName: string) => ["email", "firstName"].includes(fieldName),
25
+ get: (fieldName: string) => {
26
+ if (fieldName === "email") return "Invalid email format.";
27
+ if (fieldName === "firstName") return "First name is required.";
28
+ }
29
+ }
30
+ }}
31
+ />
32
+ )
33
+ };
34
+ export const WithReadOnlyFields: Story = {
35
+ render: () => (
36
+ <KcPageStory
37
+ kcContext={{
38
+ profile: {
39
+ attributesByName: {
40
+ email: { value: "jane.doe@example.com", readOnly: true },
41
+ firstName: { value: "Jane", readOnly: false }
42
+ }
43
+ }
44
+ }}
45
+ />
46
+ )
47
+ };
48
+ export const WithPrefilledFormFields: Story = {
49
+ render: () => (
50
+ <KcPageStory
51
+ kcContext={{
52
+ profile: {
53
+ attributesByName: {
54
+ firstName: { value: "Jane" },
55
+ lastName: { value: "Doe" },
56
+ email: { value: "jane.doe@example.com" }
57
+ }
58
+ }
59
+ }}
60
+ />
61
+ )
62
+ };
@@ -55,3 +55,42 @@ export const WithRequiredActions: Story = {
55
55
  />
56
56
  )
57
57
  };
58
+ export const WithPageRedirect: Story = {
59
+ render: () => (
60
+ <KcPageStory
61
+ kcContext={{
62
+ message: { summary: "You will be redirected shortly." },
63
+ pageRedirectUri: "https://example.com"
64
+ }}
65
+ />
66
+ )
67
+ };
68
+ export const WithoutClientBaseUrl: Story = {
69
+ render: () => (
70
+ <KcPageStory
71
+ kcContext={{
72
+ message: { summary: "No client base URL defined." },
73
+ client: { baseUrl: undefined }
74
+ }}
75
+ />
76
+ )
77
+ };
78
+ export const WithMessageHeader: Story = {
79
+ render: () => (
80
+ <KcPageStory
81
+ kcContext={{
82
+ messageHeader: "Important Notice",
83
+ message: { summary: "This is an important message." }
84
+ }}
85
+ />
86
+ )
87
+ };
88
+ export const WithAdvancedMessage: Story = {
89
+ render: () => (
90
+ <KcPageStory
91
+ kcContext={{
92
+ message: { summary: "Please take note of this <strong>important</strong> information." }
93
+ }}
94
+ />
95
+ )
96
+ };
@@ -231,3 +231,131 @@ export const WithErrorMessage: Story = {
231
231
  />
232
232
  )
233
233
  };
234
+
235
+ export const WithOneSocialProvider: Story = {
236
+ render: args => (
237
+ <KcPageStory
238
+ {...args}
239
+ kcContext={{
240
+ social: {
241
+ displayInfo: true,
242
+ providers: [
243
+ {
244
+ loginUrl: "google",
245
+ alias: "google",
246
+ providerId: "google",
247
+ displayName: "Google",
248
+ iconClasses: "fa fa-google"
249
+ }
250
+ ]
251
+ }
252
+ }}
253
+ />
254
+ )
255
+ };
256
+
257
+ export const WithTwoSocialProviders: Story = {
258
+ render: args => (
259
+ <KcPageStory
260
+ {...args}
261
+ kcContext={{
262
+ social: {
263
+ displayInfo: true,
264
+ providers: [
265
+ {
266
+ loginUrl: "google",
267
+ alias: "google",
268
+ providerId: "google",
269
+ displayName: "Google",
270
+ iconClasses: "fa fa-google"
271
+ },
272
+ {
273
+ loginUrl: "microsoft",
274
+ alias: "microsoft",
275
+ providerId: "microsoft",
276
+ displayName: "Microsoft",
277
+ iconClasses: "fa fa-windows"
278
+ }
279
+ ]
280
+ }
281
+ }}
282
+ />
283
+ )
284
+ };
285
+ export const WithNoSocialProviders: Story = {
286
+ render: args => (
287
+ <KcPageStory
288
+ {...args}
289
+ kcContext={{
290
+ social: {
291
+ displayInfo: true,
292
+ providers: []
293
+ }
294
+ }}
295
+ />
296
+ )
297
+ };
298
+ export const WithMoreThanTwoSocialProviders: Story = {
299
+ render: args => (
300
+ <KcPageStory
301
+ {...args}
302
+ kcContext={{
303
+ social: {
304
+ displayInfo: true,
305
+ providers: [
306
+ {
307
+ loginUrl: "google",
308
+ alias: "google",
309
+ providerId: "google",
310
+ displayName: "Google",
311
+ iconClasses: "fa fa-google"
312
+ },
313
+ {
314
+ loginUrl: "microsoft",
315
+ alias: "microsoft",
316
+ providerId: "microsoft",
317
+ displayName: "Microsoft",
318
+ iconClasses: "fa fa-windows"
319
+ },
320
+ {
321
+ loginUrl: "facebook",
322
+ alias: "facebook",
323
+ providerId: "facebook",
324
+ displayName: "Facebook",
325
+ iconClasses: "fa fa-facebook"
326
+ },
327
+ {
328
+ loginUrl: "twitter",
329
+ alias: "twitter",
330
+ providerId: "twitter",
331
+ displayName: "Twitter",
332
+ iconClasses: "fa fa-twitter"
333
+ }
334
+ ]
335
+ }
336
+ }}
337
+ />
338
+ )
339
+ };
340
+ export const WithSocialProvidersAndWithoutRememberMe: Story = {
341
+ render: args => (
342
+ <KcPageStory
343
+ {...args}
344
+ kcContext={{
345
+ social: {
346
+ displayInfo: true,
347
+ providers: [
348
+ {
349
+ loginUrl: "google",
350
+ alias: "google",
351
+ providerId: "google",
352
+ displayName: "Google",
353
+ iconClasses: "fa fa-google"
354
+ }
355
+ ]
356
+ },
357
+ realm: { rememberMe: false }
358
+ }}
359
+ />
360
+ )
361
+ };
@@ -41,3 +41,24 @@ export const WithError: Story = {
41
41
  />
42
42
  )
43
43
  };
44
+ export const WithAppInitiatedAction: Story = {
45
+ render: () => (
46
+ <KcPageStory
47
+ kcContext={{
48
+ isAppInitiatedAction: true
49
+ }}
50
+ />
51
+ )
52
+ };
53
+
54
+ export const WithPreFilledUserLabel: Story = {
55
+ render: () => (
56
+ <KcPageStory
57
+ kcContext={{
58
+ totp: {
59
+ otpCredentials: [{ userLabel: "MyDevice" }]
60
+ }
61
+ }}
62
+ />
63
+ )
64
+ };
@@ -16,3 +16,49 @@ type Story = StoryObj<typeof meta>;
16
16
  export const Default: Story = {
17
17
  render: () => <KcPageStory />
18
18
  };
19
+ export const WithIdpAlias: Story = {
20
+ render: () => (
21
+ <KcPageStory
22
+ kcContext={{
23
+ idpAlias: "Google",
24
+ brokerContext: {
25
+ username: "john.doe"
26
+ },
27
+ realm: {
28
+ displayName: "MyRealm"
29
+ }
30
+ }}
31
+ />
32
+ )
33
+ };
34
+ export const WithoutIdpAlias: Story = {
35
+ render: () => (
36
+ <KcPageStory
37
+ kcContext={{
38
+ idpAlias: undefined,
39
+ brokerContext: {
40
+ username: "john.doe"
41
+ },
42
+ realm: {
43
+ displayName: "MyRealm"
44
+ }
45
+ }}
46
+ />
47
+ )
48
+ };
49
+
50
+ export const WithCustomRealmDisplayName: Story = {
51
+ render: () => (
52
+ <KcPageStory
53
+ kcContext={{
54
+ idpAlias: "Facebook",
55
+ brokerContext: {
56
+ username: "jane.doe"
57
+ },
58
+ realm: {
59
+ displayName: "CustomRealm"
60
+ }
61
+ }}
62
+ />
63
+ )
64
+ };
@@ -215,3 +215,65 @@ export const WithTermsAcceptance: Story = {
215
215
  />
216
216
  )
217
217
  };
218
+ export const WithTermsNotAccepted: Story = {
219
+ render: args => (
220
+ <KcPageStory
221
+ {...args}
222
+ kcContext={{
223
+ termsAcceptanceRequired: true,
224
+ messagesPerField: {
225
+ existsError: (fieldName: string) => fieldName === "termsAccepted",
226
+ get: (fieldName: string) => (fieldName === "termsAccepted" ? "You must accept the terms." : undefined)
227
+ }
228
+ }}
229
+ />
230
+ )
231
+ };
232
+ export const WithFieldErrors: Story = {
233
+ render: () => (
234
+ <KcPageStory
235
+ kcContext={{
236
+ profile: {
237
+ attributesByName: {
238
+ username: { value: "" },
239
+ email: { value: "invalid-email" }
240
+ }
241
+ },
242
+ messagesPerField: {
243
+ existsError: fieldName => ["username", "email"].includes(fieldName),
244
+ get: fieldName => {
245
+ if (fieldName === "username") return "Username is required.";
246
+ if (fieldName === "email") return "Invalid email format.";
247
+ }
248
+ }
249
+ }}
250
+ />
251
+ )
252
+ };
253
+ export const WithReadOnlyFields: Story = {
254
+ render: () => (
255
+ <KcPageStory
256
+ kcContext={{
257
+ profile: {
258
+ attributesByName: {
259
+ username: { value: "johndoe", readOnly: true },
260
+ email: { value: "jhon.doe@gmail.com", readOnly: false }
261
+ }
262
+ }
263
+ }}
264
+ />
265
+ )
266
+ };
267
+ export const WithAutoGeneratedUsername: Story = {
268
+ render: () => (
269
+ <KcPageStory
270
+ kcContext={{
271
+ profile: {
272
+ attributesByName: {
273
+ username: { value: "autogenerated_username" }
274
+ }
275
+ }
276
+ }}
277
+ />
278
+ )
279
+ };
@@ -45,3 +45,32 @@ export const French: Story = {
45
45
  />
46
46
  )
47
47
  };
48
+ export const WithErrorMessage: Story = {
49
+ render: () => (
50
+ <KcPageStory
51
+ kcContext={{
52
+ messagesPerField: {
53
+ existsError: () => true,
54
+ get: () => "An error occurred while processing your request."
55
+ }
56
+ }}
57
+ />
58
+ )
59
+ };
60
+
61
+ export const Spanish: Story = {
62
+ render: () => (
63
+ <KcPageStory
64
+ kcContext={{
65
+ locale: {
66
+ currentLanguageTag: "es"
67
+ },
68
+ "x-keycloakify": {
69
+ messages: {
70
+ termsText: "<p>Mis términos en <strong>Español</strong></p>"
71
+ }
72
+ }
73
+ }}
74
+ />
75
+ )
76
+ };