@oussemasahbeni/keycloakify-login-shadcn 250004.0.7 → 250004.0.8
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/keycloak-theme/components/langauges.tsx +1 -1
- package/keycloak-theme/components/theme-toggle.tsx +0 -1
- package/keycloak-theme/components/ui/alert.tsx +1 -2
- package/keycloak-theme/components/ui/button.tsx +1 -2
- package/keycloak-theme/components/ui/card.tsx +0 -1
- package/keycloak-theme/components/ui/checkbox.tsx +0 -1
- package/keycloak-theme/components/ui/dropdown-menu.tsx +14 -4
- package/keycloak-theme/components/ui/field.tsx +0 -1
- package/keycloak-theme/components/ui/input-otp.tsx +0 -1
- package/keycloak-theme/components/ui/input.tsx +0 -1
- package/keycloak-theme/components/ui/label.tsx +1 -2
- package/keycloak-theme/components/ui/radio-group.tsx +0 -1
- package/keycloak-theme/components/ui/select.tsx +12 -4
- package/keycloak-theme/components/ui/separator.tsx +0 -1
- package/keycloak-theme/components/ui/tooltip.tsx +0 -1
- package/keycloak-theme/login/KcPage.tsx +2 -4
- package/keycloak-theme/login/components/LogoutOtherSessions.tsx +3 -5
- package/keycloak-theme/login/components/PasswordWrapper.tsx +4 -4
- package/keycloak-theme/login/components/Template/Template.tsx +108 -56
- package/keycloak-theme/login/components/UserProfileFormFields/AddRemoveButtonsMultiValuedAttribute.tsx +7 -3
- package/keycloak-theme/login/components/UserProfileFormFields/FieldErrors.tsx +8 -4
- package/keycloak-theme/login/components/UserProfileFormFields/GroupLabel.tsx +33 -14
- package/keycloak-theme/login/components/UserProfileFormFields/InputFieldByType.tsx +9 -2
- package/keycloak-theme/login/components/UserProfileFormFields/InputLabel.tsx +0 -1
- package/keycloak-theme/login/components/UserProfileFormFields/InputTag.tsx +25 -8
- package/keycloak-theme/login/components/UserProfileFormFields/InputTagSelects.tsx +30 -16
- package/keycloak-theme/login/components/UserProfileFormFields/SelectTag.tsx +32 -11
- package/keycloak-theme/login/components/UserProfileFormFields/TextareaTag.tsx +19 -7
- package/keycloak-theme/login/components/UserProfileFormFields/UserProfileFormFields.tsx +11 -5
- package/keycloak-theme/login/mocks/KcPageStory.tsx +6 -2
- package/keycloak-theme/login/pages/PageIndex.tsx +0 -4
- package/keycloak-theme/login/pages/code/Page.stories.tsx +4 -5
- package/keycloak-theme/login/pages/code/Page.tsx +6 -6
- package/keycloak-theme/login/pages/delete-account-confirm/Page.stories.tsx +0 -2
- package/keycloak-theme/login/pages/delete-account-confirm/Page.tsx +3 -6
- package/keycloak-theme/login/pages/delete-credential/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/delete-credential/Page.tsx +11 -6
- package/keycloak-theme/login/pages/error/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/error/Page.tsx +13 -12
- package/keycloak-theme/login/pages/frontchannel-logout/Page.tsx +2 -4
- package/keycloak-theme/login/pages/idp-review-user-profile/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/idp-review-user-profile/Page.tsx +2 -5
- package/keycloak-theme/login/pages/info/Page.tsx +8 -6
- package/keycloak-theme/login/pages/link-idp-action/Page.tsx +5 -3
- package/keycloak-theme/login/pages/login/Form.tsx +52 -52
- package/keycloak-theme/login/pages/login/Page.stories.tsx +4 -3
- package/keycloak-theme/login/pages/login/Page.tsx +0 -1
- package/keycloak-theme/login/pages/login/SocialProviders.tsx +8 -22
- package/keycloak-theme/login/pages/login-config-totp/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/login-config-totp/Page.tsx +25 -12
- package/keycloak-theme/login/pages/login-idp-link-confirm/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/login-idp-link-confirm/Page.tsx +4 -7
- package/keycloak-theme/login/pages/login-idp-link-confirm-override/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/login-idp-link-confirm-override/Page.tsx +4 -7
- package/keycloak-theme/login/pages/login-idp-link-email/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/login-idp-link-email/Page.tsx +4 -7
- package/keycloak-theme/login/pages/login-oauth-grant/Page.tsx +21 -11
- package/keycloak-theme/login/pages/login-oauth2-device-verify-user-code/Page.tsx +5 -7
- package/keycloak-theme/login/pages/login-otp/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/login-otp/Page.tsx +35 -26
- package/keycloak-theme/login/pages/login-page-expired/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/login-page-expired/Page.tsx +4 -6
- package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/Page.tsx +153 -96
- package/keycloak-theme/login/pages/login-password/Page.tsx +14 -15
- package/keycloak-theme/login/pages/login-password/useScript.tsx +0 -1
- package/keycloak-theme/login/pages/login-recovery-authn-code-config/Page.tsx +5 -8
- package/keycloak-theme/login/pages/login-recovery-authn-code-input/Page.tsx +2 -3
- package/keycloak-theme/login/pages/login-reset-otp/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/login-reset-otp/Page.tsx +3 -4
- package/keycloak-theme/login/pages/login-reset-password/Form.tsx +5 -6
- package/keycloak-theme/login/pages/login-reset-password/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/login-reset-password/Page.tsx +3 -3
- package/keycloak-theme/login/pages/login-update-password/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/login-update-password/Page.tsx +5 -7
- package/keycloak-theme/login/pages/login-update-profile/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/login-update-profile/Page.tsx +6 -7
- package/keycloak-theme/login/pages/login-username/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/login-username/Page.tsx +6 -6
- package/keycloak-theme/login/pages/login-verify-email/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/login-verify-email/Page.tsx +3 -4
- package/keycloak-theme/login/pages/login-x509-info/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/login-x509-info/Page.tsx +3 -6
- package/keycloak-theme/login/pages/logout-confirm/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/logout-confirm/Page.tsx +3 -6
- package/keycloak-theme/login/pages/register/Form.tsx +8 -7
- package/keycloak-theme/login/pages/register/Page.stories.tsx +17 -8
- package/keycloak-theme/login/pages/register/TermsAcceptance.tsx +6 -7
- package/keycloak-theme/login/pages/saml-post-form/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/saml-post-form/Page.tsx +4 -6
- package/keycloak-theme/login/pages/select-authenticator/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/select-authenticator/Page.tsx +3 -6
- package/keycloak-theme/login/pages/select-organization/Page.tsx +5 -8
- package/keycloak-theme/login/pages/terms/Page.tsx +1 -3
- package/keycloak-theme/login/pages/update-email/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/update-email/Page.tsx +6 -7
- package/keycloak-theme/login/pages/webauthn-authenticate/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/webauthn-authenticate/Page.tsx +50 -46
- package/keycloak-theme/login/pages/webauthn-error/Page.stories.tsx +2 -5
- package/keycloak-theme/login/pages/webauthn-error/Page.tsx +3 -6
- package/keycloak-theme/login/pages/webauthn-register/Page.stories.tsx +0 -1
- package/keycloak-theme/login/pages/webauthn-register/Page.tsx +4 -4
- package/keycloak-theme/login/styleLevelCustomization.tsx +4 -6
- package/keycloak-theme/public/site.webmanifest +11 -1
- package/package.json +2 -2
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { Input } from '@/components/ui/input';
|
|
1
|
+
import { Input } from "@/components/ui/input";
|
|
3
2
|
import { assert } from "tsafe/assert";
|
|
4
3
|
import { useI18n } from "../../i18n";
|
|
5
4
|
import { AddRemoveButtonsMultiValuedAttribute } from "./AddRemoveButtonsMultiValuedAttribute";
|
|
@@ -47,20 +46,34 @@ export function InputTag(
|
|
|
47
46
|
disabled={attribute.readOnly}
|
|
48
47
|
autoComplete={attribute.autocomplete}
|
|
49
48
|
placeholder={
|
|
50
|
-
attribute.annotations.inputTypePlaceholder === undefined
|
|
49
|
+
attribute.annotations.inputTypePlaceholder === undefined
|
|
50
|
+
? undefined
|
|
51
|
+
: advancedMsgStr(attribute.annotations.inputTypePlaceholder)
|
|
51
52
|
}
|
|
52
53
|
pattern={attribute.annotations.inputTypePattern}
|
|
53
|
-
size={
|
|
54
|
+
size={
|
|
55
|
+
attribute.annotations.inputTypeSize === undefined
|
|
56
|
+
? undefined
|
|
57
|
+
: parseInt(`${attribute.annotations.inputTypeSize}`)
|
|
58
|
+
}
|
|
54
59
|
maxLength={
|
|
55
|
-
attribute.annotations.inputTypeMaxlength === undefined
|
|
60
|
+
attribute.annotations.inputTypeMaxlength === undefined
|
|
61
|
+
? undefined
|
|
62
|
+
: parseInt(`${attribute.annotations.inputTypeMaxlength}`)
|
|
56
63
|
}
|
|
57
64
|
minLength={
|
|
58
|
-
attribute.annotations.inputTypeMinlength === undefined
|
|
65
|
+
attribute.annotations.inputTypeMinlength === undefined
|
|
66
|
+
? undefined
|
|
67
|
+
: parseInt(`${attribute.annotations.inputTypeMinlength}`)
|
|
59
68
|
}
|
|
60
69
|
max={attribute.annotations.inputTypeMax}
|
|
61
70
|
min={attribute.annotations.inputTypeMin}
|
|
62
71
|
step={attribute.annotations.inputTypeStep}
|
|
63
|
-
{...Object.fromEntries(
|
|
72
|
+
{...Object.fromEntries(
|
|
73
|
+
Object.entries(attribute.html5DataAnnotations ?? {}).map(
|
|
74
|
+
([key, value]) => [`data-${key}`, value]
|
|
75
|
+
)
|
|
76
|
+
)}
|
|
64
77
|
onChange={event =>
|
|
65
78
|
dispatchFormAction({
|
|
66
79
|
action: "update",
|
|
@@ -101,7 +114,11 @@ export function InputTag(
|
|
|
101
114
|
|
|
102
115
|
return (
|
|
103
116
|
<>
|
|
104
|
-
<FieldErrors
|
|
117
|
+
<FieldErrors
|
|
118
|
+
attribute={attribute}
|
|
119
|
+
displayableErrors={displayableErrors}
|
|
120
|
+
fieldIndex={fieldIndex}
|
|
121
|
+
/>
|
|
105
122
|
<AddRemoveButtonsMultiValuedAttribute
|
|
106
123
|
attribute={attribute}
|
|
107
124
|
values={values}
|
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { assert } from 'tsafe/assert';
|
|
1
|
+
import { cn } from "@/components/lib/utils";
|
|
2
|
+
import { Checkbox } from "@/components/ui/checkbox";
|
|
3
|
+
import { Label } from "@/components/ui/label";
|
|
4
|
+
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
5
|
+
import { assert } from "tsafe/assert";
|
|
7
6
|
import type { InputFieldByTypeProps } from "./InputFieldByType";
|
|
8
|
-
import { InputLabel } from
|
|
7
|
+
import { InputLabel } from "./InputLabel";
|
|
9
8
|
|
|
10
9
|
export function InputTagSelects(props: InputFieldByTypeProps) {
|
|
11
10
|
const { attribute, dispatchFormAction, valueOrValues } = props;
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
12
|
const isRadio = (() => {
|
|
16
13
|
const { inputType } = attribute.annotations;
|
|
17
14
|
|
|
18
|
-
assert(
|
|
15
|
+
assert(
|
|
16
|
+
inputType === "select-radiobuttons" || inputType === "multiselect-checkboxes"
|
|
17
|
+
);
|
|
19
18
|
|
|
20
19
|
return inputType === "select-radiobuttons";
|
|
21
20
|
})();
|
|
@@ -28,7 +27,9 @@ export function InputTagSelects(props: InputFieldByTypeProps) {
|
|
|
28
27
|
break walk;
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
const validator = (
|
|
30
|
+
const validator = (
|
|
31
|
+
attribute.validators as Record<string, { options?: string[] }>
|
|
32
|
+
)[inputOptionsFromValidation];
|
|
32
33
|
|
|
33
34
|
if (validator === undefined) {
|
|
34
35
|
break walk;
|
|
@@ -74,7 +75,10 @@ export function InputTagSelects(props: InputFieldByTypeProps) {
|
|
|
74
75
|
/>
|
|
75
76
|
<Label
|
|
76
77
|
htmlFor={`${attribute.name}-${option}`}
|
|
77
|
-
className={cn(
|
|
78
|
+
className={cn(
|
|
79
|
+
"text-sm font-normal",
|
|
80
|
+
attribute.readOnly && "opacity-50 cursor-not-allowed"
|
|
81
|
+
)}
|
|
78
82
|
>
|
|
79
83
|
<InputLabel attribute={attribute} option={option} />
|
|
80
84
|
</Label>
|
|
@@ -90,7 +94,11 @@ export function InputTagSelects(props: InputFieldByTypeProps) {
|
|
|
90
94
|
<div key={option} className="flex items-center space-x-2">
|
|
91
95
|
<Checkbox
|
|
92
96
|
id={`${attribute.name}-${option}`}
|
|
93
|
-
checked={
|
|
97
|
+
checked={
|
|
98
|
+
valueOrValues instanceof Array
|
|
99
|
+
? valueOrValues.includes(option)
|
|
100
|
+
: valueOrValues === option
|
|
101
|
+
}
|
|
94
102
|
disabled={attribute.readOnly}
|
|
95
103
|
onCheckedChange={checked =>
|
|
96
104
|
dispatchFormAction({
|
|
@@ -105,7 +113,10 @@ export function InputTagSelects(props: InputFieldByTypeProps) {
|
|
|
105
113
|
if (isChecked) {
|
|
106
114
|
newValues.push(option);
|
|
107
115
|
} else {
|
|
108
|
-
newValues.splice(
|
|
116
|
+
newValues.splice(
|
|
117
|
+
newValues.indexOf(option),
|
|
118
|
+
1
|
|
119
|
+
);
|
|
109
120
|
}
|
|
110
121
|
|
|
111
122
|
return newValues;
|
|
@@ -125,7 +136,10 @@ export function InputTagSelects(props: InputFieldByTypeProps) {
|
|
|
125
136
|
/>
|
|
126
137
|
<Label
|
|
127
138
|
htmlFor={`${attribute.name}-${option}`}
|
|
128
|
-
className={cn(
|
|
139
|
+
className={cn(
|
|
140
|
+
"text-sm font-normal",
|
|
141
|
+
attribute.readOnly && "opacity-50 cursor-not-allowed"
|
|
142
|
+
)}
|
|
129
143
|
>
|
|
130
144
|
<InputLabel attribute={attribute} option={option} />
|
|
131
145
|
</Label>
|
|
@@ -133,4 +147,4 @@ export function InputTagSelects(props: InputFieldByTypeProps) {
|
|
|
133
147
|
))}
|
|
134
148
|
</div>
|
|
135
149
|
);
|
|
136
|
-
}
|
|
150
|
+
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { cn } from "@/components/lib/utils";
|
|
2
|
+
import {
|
|
3
|
+
Select,
|
|
4
|
+
SelectContent,
|
|
5
|
+
SelectItem,
|
|
6
|
+
SelectTrigger,
|
|
7
|
+
SelectValue
|
|
8
|
+
} from "@/components/ui/select";
|
|
5
9
|
import { assert } from "tsafe/assert";
|
|
6
10
|
import type { InputFieldByTypeProps } from "./InputFieldByType";
|
|
7
|
-
import { InputLabel } from
|
|
11
|
+
import { InputLabel } from "./InputLabel";
|
|
8
12
|
|
|
9
13
|
export function SelectTag(props: InputFieldByTypeProps) {
|
|
10
14
|
const { attribute, dispatchFormAction, displayableErrors, valueOrValues } = props;
|
|
@@ -21,7 +25,9 @@ export function SelectTag(props: InputFieldByTypeProps) {
|
|
|
21
25
|
|
|
22
26
|
assert(typeof inputOptionsFromValidation === "string");
|
|
23
27
|
|
|
24
|
-
const validator = (
|
|
28
|
+
const validator = (
|
|
29
|
+
attribute.validators as Record<string, { options?: string[] }>
|
|
30
|
+
)[inputOptionsFromValidation];
|
|
25
31
|
|
|
26
32
|
if (validator === undefined) {
|
|
27
33
|
break walk;
|
|
@@ -45,18 +51,25 @@ export function SelectTag(props: InputFieldByTypeProps) {
|
|
|
45
51
|
name={attribute.name}
|
|
46
52
|
className={cn(
|
|
47
53
|
"flex min-h-25 h-auto w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
48
|
-
displayableErrors.length !== 0 &&
|
|
54
|
+
displayableErrors.length !== 0 &&
|
|
55
|
+
"border-destructive ring-destructive/20 focus:ring-destructive"
|
|
49
56
|
)}
|
|
50
57
|
aria-invalid={displayableErrors.length !== 0}
|
|
51
58
|
disabled={attribute.readOnly}
|
|
52
59
|
multiple={true}
|
|
53
|
-
size={
|
|
60
|
+
size={
|
|
61
|
+
attribute.annotations.inputTypeSize === undefined
|
|
62
|
+
? undefined
|
|
63
|
+
: parseInt(`${attribute.annotations.inputTypeSize}`)
|
|
64
|
+
}
|
|
54
65
|
value={valueOrValues}
|
|
55
66
|
onChange={event =>
|
|
56
67
|
dispatchFormAction({
|
|
57
68
|
action: "update",
|
|
58
69
|
name: attribute.name,
|
|
59
|
-
valueOrValues: Array.from(event.target.selectedOptions).map(
|
|
70
|
+
valueOrValues: Array.from(event.target.selectedOptions).map(
|
|
71
|
+
option => option.value
|
|
72
|
+
)
|
|
60
73
|
})
|
|
61
74
|
}
|
|
62
75
|
onBlur={() =>
|
|
@@ -78,7 +91,11 @@ export function SelectTag(props: InputFieldByTypeProps) {
|
|
|
78
91
|
|
|
79
92
|
return (
|
|
80
93
|
<Select
|
|
81
|
-
value={
|
|
94
|
+
value={
|
|
95
|
+
typeof valueOrValues === "string" && valueOrValues !== ""
|
|
96
|
+
? valueOrValues
|
|
97
|
+
: undefined
|
|
98
|
+
}
|
|
82
99
|
onValueChange={value =>
|
|
83
100
|
dispatchFormAction({
|
|
84
101
|
action: "update",
|
|
@@ -90,7 +107,11 @@ export function SelectTag(props: InputFieldByTypeProps) {
|
|
|
90
107
|
>
|
|
91
108
|
<SelectTrigger
|
|
92
109
|
id={attribute.name}
|
|
93
|
-
className={cn(
|
|
110
|
+
className={cn(
|
|
111
|
+
"w-full",
|
|
112
|
+
displayableErrors.length !== 0 &&
|
|
113
|
+
"border-destructive ring-destructive/20 focus-visible:ring-destructive"
|
|
114
|
+
)}
|
|
94
115
|
aria-invalid={displayableErrors.length !== 0}
|
|
95
116
|
onBlur={() =>
|
|
96
117
|
dispatchFormAction({
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { cn } from '@/components/lib/utils';
|
|
1
|
+
import { cn } from "@/components/lib/utils";
|
|
3
2
|
import { assert } from "tsafe/assert";
|
|
4
3
|
import type { InputFieldByTypeProps } from "./InputFieldByType";
|
|
5
4
|
|
|
@@ -16,13 +15,26 @@ export function TextareaTag(props: InputFieldByTypeProps) {
|
|
|
16
15
|
name={attribute.name}
|
|
17
16
|
className={cn(
|
|
18
17
|
"flex min-h-20 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
19
|
-
displayableErrors.length !== 0 &&
|
|
18
|
+
displayableErrors.length !== 0 &&
|
|
19
|
+
"border-destructive ring-destructive/20 focus-visible:ring-destructive"
|
|
20
20
|
)}
|
|
21
21
|
aria-invalid={displayableErrors.length !== 0}
|
|
22
22
|
disabled={attribute.readOnly}
|
|
23
|
-
cols={
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
cols={
|
|
24
|
+
attribute.annotations.inputTypeCols === undefined
|
|
25
|
+
? undefined
|
|
26
|
+
: parseInt(`${attribute.annotations.inputTypeCols}`)
|
|
27
|
+
}
|
|
28
|
+
rows={
|
|
29
|
+
attribute.annotations.inputTypeRows === undefined
|
|
30
|
+
? undefined
|
|
31
|
+
: parseInt(`${attribute.annotations.inputTypeRows}`)
|
|
32
|
+
}
|
|
33
|
+
maxLength={
|
|
34
|
+
attribute.annotations.inputTypeMaxlength === undefined
|
|
35
|
+
? undefined
|
|
36
|
+
: parseInt(`${attribute.annotations.inputTypeMaxlength}`)
|
|
37
|
+
}
|
|
26
38
|
value={value}
|
|
27
39
|
onChange={event =>
|
|
28
40
|
dispatchFormAction({
|
|
@@ -40,4 +52,4 @@ export function TextareaTag(props: InputFieldByTypeProps) {
|
|
|
40
52
|
}
|
|
41
53
|
/>
|
|
42
54
|
);
|
|
43
|
-
}
|
|
55
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import { Field, FieldDescription, FieldLabel } from "@/components/ui/field";
|
|
3
2
|
import type { Attribute } from "@keycloakify/login-ui/KcContext";
|
|
4
3
|
import type { JSX } from "@keycloakify/login-ui/tools/JSX";
|
|
@@ -70,7 +69,9 @@ export function UserProfileFormFields(props: UserProfileFormFieldsProps) {
|
|
|
70
69
|
/>
|
|
71
70
|
)}
|
|
72
71
|
<Field
|
|
73
|
-
data-invalid={
|
|
72
|
+
data-invalid={
|
|
73
|
+
displayableErrors.length > 0 ? "true" : undefined
|
|
74
|
+
}
|
|
74
75
|
style={{
|
|
75
76
|
display:
|
|
76
77
|
attribute.annotations.inputType === "hidden"
|
|
@@ -82,12 +83,15 @@ export function UserProfileFormFields(props: UserProfileFormFieldsProps) {
|
|
|
82
83
|
{advancedMsg(attribute.displayName ?? "")}
|
|
83
84
|
{attribute.required && <> *</>}
|
|
84
85
|
</FieldLabel>
|
|
85
|
-
{attribute.annotations.inputHelperTextBefore !==
|
|
86
|
+
{attribute.annotations.inputHelperTextBefore !==
|
|
87
|
+
undefined && (
|
|
86
88
|
<FieldDescription
|
|
87
89
|
id={`form-help-text-before-${attribute.name}`}
|
|
88
90
|
aria-live="polite"
|
|
89
91
|
>
|
|
90
|
-
{advancedMsg(
|
|
92
|
+
{advancedMsg(
|
|
93
|
+
attribute.annotations.inputHelperTextBefore
|
|
94
|
+
)}
|
|
91
95
|
</FieldDescription>
|
|
92
96
|
)}
|
|
93
97
|
<InputFieldByType
|
|
@@ -106,7 +110,9 @@ export function UserProfileFormFields(props: UserProfileFormFieldsProps) {
|
|
|
106
110
|
id={`form-help-text-after-${attribute.name}`}
|
|
107
111
|
aria-live="polite"
|
|
108
112
|
>
|
|
109
|
-
{advancedMsg(
|
|
113
|
+
{advancedMsg(
|
|
114
|
+
attribute.annotations.inputHelperTextAfter
|
|
115
|
+
)}
|
|
110
116
|
</FieldDescription>
|
|
111
117
|
)}
|
|
112
118
|
{AfterField !== undefined && (
|
|
@@ -4,10 +4,14 @@ import type { KcContext } from "../KcContext";
|
|
|
4
4
|
import KcPage from "../KcPage";
|
|
5
5
|
export type { Meta, StoryObj } from "../../kc.gen";
|
|
6
6
|
|
|
7
|
-
export function createKcPageStory<PageId extends KcContext["pageId"]>(params: {
|
|
7
|
+
export function createKcPageStory<PageId extends KcContext["pageId"]>(params: {
|
|
8
|
+
pageId: PageId;
|
|
9
|
+
}) {
|
|
8
10
|
const { pageId } = params;
|
|
9
11
|
|
|
10
|
-
function KcPageStory(props: {
|
|
12
|
+
function KcPageStory(props: {
|
|
13
|
+
kcContext?: DeepPartial<Extract<KcContext, { pageId: PageId }>>;
|
|
14
|
+
}) {
|
|
11
15
|
const { kcContext: overrides } = props;
|
|
12
16
|
|
|
13
17
|
const kcContextMock = getKcContextMock({
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
1
|
import { Suspense, lazy } from "react";
|
|
3
2
|
import { useKcContext } from "../KcContext";
|
|
4
3
|
|
|
5
|
-
|
|
6
4
|
const Page_login = lazy(() => import("./login"));
|
|
7
5
|
const Page_register = lazy(() => import("./register"));
|
|
8
6
|
const Page_info = lazy(() => import("./info"));
|
|
@@ -46,7 +44,6 @@ export function PageIndex() {
|
|
|
46
44
|
const { kcContext } = useKcContext();
|
|
47
45
|
|
|
48
46
|
return (
|
|
49
|
-
|
|
50
47
|
<Suspense>
|
|
51
48
|
{(() => {
|
|
52
49
|
switch (kcContext.pageId) {
|
|
@@ -127,7 +124,6 @@ export function PageIndex() {
|
|
|
127
124
|
case "select-organization.ftl":
|
|
128
125
|
return <Page_select_organization />;
|
|
129
126
|
}
|
|
130
|
-
|
|
131
127
|
})()}
|
|
132
128
|
</Suspense>
|
|
133
129
|
);
|
|
@@ -35,13 +35,12 @@ export const French: Story = {
|
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
|
|
39
38
|
/**
|
|
40
|
-
* This reflects the state when "Dark Theme" is set to "Disabled" in the realm settings
|
|
39
|
+
* This reflects the state when "Dark Theme" is set to "Disabled" in the realm settings
|
|
41
40
|
* (Theme configuration tab of the Keycloak Admin UI).
|
|
42
|
-
*
|
|
43
|
-
* You should enable this configuration if you want to hide the "dark mode switch"
|
|
44
|
-
* and ensure that the theme always renders in light mode, even if the user's system
|
|
41
|
+
*
|
|
42
|
+
* You should enable this configuration if you want to hide the "dark mode switch"
|
|
43
|
+
* and ensure that the theme always renders in light mode, even if the user's system
|
|
45
44
|
* preference is set to dark.
|
|
46
45
|
*/
|
|
47
46
|
export const WithDarkModeForbidden: Story = {
|
|
@@ -6,15 +6,14 @@ import { Button } from "@/components/ui/button";
|
|
|
6
6
|
|
|
7
7
|
import { MdContentCopy } from "react-icons/md";
|
|
8
8
|
|
|
9
|
-
import { useI18n } from
|
|
10
|
-
import { useKcContext } from
|
|
11
|
-
import { useState } from
|
|
9
|
+
import { useI18n } from "@/login/i18n";
|
|
10
|
+
import { useKcContext } from "@/login/KcContext";
|
|
11
|
+
import { useState } from "react";
|
|
12
12
|
import { MdCheck } from "react-icons/md";
|
|
13
13
|
import { assert } from "tsafe/assert";
|
|
14
14
|
import { Template } from "../../components/Template";
|
|
15
15
|
|
|
16
16
|
export function Page() {
|
|
17
|
-
|
|
18
17
|
const { kcContext } = useKcContext();
|
|
19
18
|
assert(kcContext.pageId === "code.ftl");
|
|
20
19
|
|
|
@@ -22,7 +21,6 @@ export function Page() {
|
|
|
22
21
|
|
|
23
22
|
const { msg } = useI18n();
|
|
24
23
|
|
|
25
|
-
|
|
26
24
|
const handleCopy = async () => {
|
|
27
25
|
try {
|
|
28
26
|
await navigator.clipboard.writeText(kcContext.code.code ?? "");
|
|
@@ -36,7 +34,9 @@ export function Page() {
|
|
|
36
34
|
return (
|
|
37
35
|
<Template
|
|
38
36
|
headerNode={
|
|
39
|
-
kcContext.code.success
|
|
37
|
+
kcContext.code.success
|
|
38
|
+
? msg("codeSuccessTitle")
|
|
39
|
+
: msg("codeErrorTitle", kcContext.code.error)
|
|
40
40
|
}
|
|
41
41
|
>
|
|
42
42
|
<div id="kc-code">
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import { createKcPageStory, type Meta, type StoryObj } from "../../mocks/KcPageStory";
|
|
3
2
|
|
|
4
3
|
const { KcPageStory } = createKcPageStory({ pageId: "delete-account-confirm.ftl" });
|
|
@@ -14,7 +13,6 @@ type Story = StoryObj<typeof meta>;
|
|
|
14
13
|
|
|
15
14
|
export const Default: Story = {};
|
|
16
15
|
|
|
17
|
-
|
|
18
16
|
export const Arabic: Story = {
|
|
19
17
|
args: {
|
|
20
18
|
kcContext: {
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
|
2
2
|
import { Button } from "@/components/ui/button";
|
|
3
|
-
import { useI18n } from
|
|
4
|
-
import { useKcContext } from
|
|
3
|
+
import { useI18n } from "@/login/i18n";
|
|
4
|
+
import { useKcContext } from "@/login/KcContext";
|
|
5
5
|
import { assert } from "tsafe/assert";
|
|
6
6
|
import { Template } from "../../components/Template";
|
|
7
7
|
|
|
8
8
|
export function Page() {
|
|
9
|
-
|
|
10
9
|
const { kcContext } = useKcContext();
|
|
11
10
|
assert(kcContext.pageId === "delete-account-confirm.ftl");
|
|
12
11
|
|
|
13
12
|
const { msg, msgStr } = useI18n();
|
|
14
13
|
|
|
15
14
|
return (
|
|
16
|
-
<Template
|
|
17
|
-
headerNode={msg("deleteAccountConfirm")}
|
|
18
|
-
>
|
|
15
|
+
<Template headerNode={msg("deleteAccountConfirm")}>
|
|
19
16
|
<form action={kcContext.url.loginAction} className="space-y-6" method="post">
|
|
20
17
|
<Alert variant="warning" className="my-3">
|
|
21
18
|
<AlertDescription>
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
|
2
2
|
import { Button } from "@/components/ui/button";
|
|
3
|
-
import { useI18n } from
|
|
4
|
-
import { useKcContext } from
|
|
3
|
+
import { useI18n } from "@/login/i18n";
|
|
4
|
+
import { useKcContext } from "@/login/KcContext";
|
|
5
5
|
import { assert } from "tsafe/assert";
|
|
6
6
|
import { Template } from "../../components/Template";
|
|
7
7
|
|
|
8
|
-
export function Page(
|
|
9
|
-
) {
|
|
8
|
+
export function Page() {
|
|
10
9
|
const { kcContext } = useKcContext();
|
|
11
10
|
assert(kcContext.pageId === "delete-credential.ftl");
|
|
12
11
|
|
|
@@ -19,11 +18,17 @@ export function Page(
|
|
|
19
18
|
>
|
|
20
19
|
<Alert variant="warning" className=" my-3">
|
|
21
20
|
<AlertDescription>
|
|
22
|
-
<span>
|
|
21
|
+
<span>
|
|
22
|
+
{msg("deleteCredentialMessage", kcContext.credentialLabel)}
|
|
23
|
+
</span>
|
|
23
24
|
</AlertDescription>
|
|
24
25
|
</Alert>
|
|
25
26
|
|
|
26
|
-
<form
|
|
27
|
+
<form
|
|
28
|
+
className="form-actions"
|
|
29
|
+
action={kcContext.url.loginAction}
|
|
30
|
+
method="POST"
|
|
31
|
+
>
|
|
27
32
|
<div className="flex flex-col sm:flex-row gap-3 sm:justify-between mt-6">
|
|
28
33
|
<Button
|
|
29
34
|
variant="outline"
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import { Button } from '@/components/ui/button';
|
|
1
|
+
import { Alert, AlertDescription } from "@/components/ui/alert";
|
|
2
|
+
import { Button } from "@/components/ui/button";
|
|
4
3
|
import { kcSanitize } from "@keycloakify/login-ui/kcSanitize";
|
|
5
4
|
import { assert } from "tsafe/assert";
|
|
6
5
|
import { useKcContext } from "../../KcContext";
|
|
@@ -27,15 +26,17 @@ export function Page() {
|
|
|
27
26
|
</AlertDescription>
|
|
28
27
|
</Alert>
|
|
29
28
|
|
|
30
|
-
{!kcContext.skipLink &&
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
29
|
+
{!kcContext.skipLink &&
|
|
30
|
+
kcContext.client !== undefined &&
|
|
31
|
+
kcContext.client.baseUrl !== undefined && (
|
|
32
|
+
<div className="mt-2 flex justify-end">
|
|
33
|
+
<Button type="button">
|
|
34
|
+
<a id="backToApplication" href={kcContext.client.baseUrl}>
|
|
35
|
+
{msg("backToApplication")}
|
|
36
|
+
</a>
|
|
37
|
+
</Button>
|
|
38
|
+
</div>
|
|
39
|
+
)}
|
|
39
40
|
</div>
|
|
40
41
|
</Template>
|
|
41
42
|
);
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
|
2
2
|
import { Button } from "@/components/ui/button";
|
|
3
|
-
import { useI18n } from
|
|
4
|
-
import { useKcContext } from
|
|
3
|
+
import { useI18n } from "@/login/i18n";
|
|
4
|
+
import { useKcContext } from "@/login/KcContext";
|
|
5
5
|
import { useEffect, useState } from "react";
|
|
6
6
|
import { FiCheck, FiExternalLink } from "react-icons/fi";
|
|
7
7
|
import { assert } from "tsafe/assert";
|
|
8
8
|
import { Template } from "../../components/Template";
|
|
9
9
|
|
|
10
10
|
export function Page() {
|
|
11
|
-
|
|
12
11
|
const { kcContext } = useKcContext();
|
|
13
12
|
assert(kcContext.pageId === "frontchannel-logout.ftl");
|
|
14
13
|
|
|
@@ -27,7 +26,6 @@ export function Page() {
|
|
|
27
26
|
window.location.replace(kcContext.logout.logoutRedirectUri);
|
|
28
27
|
}, [iframeLoadCount]);
|
|
29
28
|
|
|
30
|
-
|
|
31
29
|
return (
|
|
32
30
|
<Template
|
|
33
31
|
documentTitle={msgStr("frontchannel-logout.title")}
|
|
@@ -6,10 +6,7 @@ import { Template } from "../../components/Template";
|
|
|
6
6
|
import { UserProfileFormFields } from "../../components/UserProfileFormFields";
|
|
7
7
|
import { useI18n } from "../../i18n";
|
|
8
8
|
|
|
9
|
-
|
|
10
9
|
export function Page() {
|
|
11
|
-
|
|
12
|
-
|
|
13
10
|
const { kcContext } = useKcContext();
|
|
14
11
|
assert(kcContext.pageId === "idp-review-user-profile.ftl");
|
|
15
12
|
|
|
@@ -32,8 +29,8 @@ export function Page() {
|
|
|
32
29
|
<UserProfileFormFields
|
|
33
30
|
onIsFormSubmittableValueChange={setIsFomSubmittable}
|
|
34
31
|
/>
|
|
35
|
-
<div
|
|
36
|
-
<div id="kc-form-options"
|
|
32
|
+
<div>
|
|
33
|
+
<div id="kc-form-options">
|
|
37
34
|
<div />
|
|
38
35
|
</div>
|
|
39
36
|
<div id="kc-form-buttons">
|