@keycloakify/keycloak-ui-shared 260502.0.0 → 260601.0.0
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/shared/keycloak-ui-shared/context/KeycloakContext.tsx +13 -3
- package/keycloak-theme/shared/keycloak-ui-shared/controls/HelpItem.tsx +15 -5
- package/keycloak-theme/shared/keycloak-ui-shared/controls/OrganizationTable.tsx +8 -11
- package/keycloak-theme/shared/keycloak-ui-shared/controls/select-control/SelectControl.tsx +2 -0
- package/keycloak-theme/shared/keycloak-ui-shared/controls/select-control/SingleSelectControl.tsx +11 -2
- package/keycloak-theme/shared/keycloak-ui-shared/controls/select-control/TypeaheadSelectControl.tsx +7 -1
- package/keycloak-theme/shared/keycloak-ui-shared/user-profile/SelectComponent.tsx +7 -3
- package/package.json +3 -3
|
@@ -41,17 +41,22 @@ export const useEnvironment = <
|
|
|
41
41
|
|
|
42
42
|
interface KeycloakContextProps<T extends BaseEnvironment> {
|
|
43
43
|
environment: T;
|
|
44
|
+
keycloak?: Keycloak;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
export const KeycloakProvider = <T extends BaseEnvironment>({
|
|
47
48
|
environment,
|
|
49
|
+
keycloak: externalKeycloak,
|
|
48
50
|
children,
|
|
49
51
|
}: PropsWithChildren<KeycloakContextProps<T>>) => {
|
|
50
52
|
KeycloakEnvContext = createKeycloakEnvContext<T>();
|
|
51
53
|
const calledOnce = useRef(false);
|
|
52
|
-
const [init, setInit] = useState(
|
|
54
|
+
const [init, setInit] = useState(!!externalKeycloak);
|
|
53
55
|
const [error, setError] = useState<unknown>();
|
|
54
56
|
const keycloak = useMemo(() => {
|
|
57
|
+
if (externalKeycloak) {
|
|
58
|
+
return externalKeycloak;
|
|
59
|
+
}
|
|
55
60
|
const keycloak = new Keycloak({
|
|
56
61
|
url: environment.serverBaseUrl,
|
|
57
62
|
realm: environment.realm,
|
|
@@ -61,9 +66,14 @@ export const KeycloakProvider = <T extends BaseEnvironment>({
|
|
|
61
66
|
|
|
62
67
|
|
|
63
68
|
return keycloak;
|
|
64
|
-
}, [environment]);
|
|
69
|
+
}, [environment, externalKeycloak]);
|
|
65
70
|
|
|
66
71
|
useEffect(() => {
|
|
72
|
+
// Skip initialization if using external keycloak (already initialized)
|
|
73
|
+
if (externalKeycloak) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
67
77
|
// only needed in dev mode
|
|
68
78
|
if (calledOnce.current) {
|
|
69
79
|
return;
|
|
@@ -82,7 +92,7 @@ export const KeycloakProvider = <T extends BaseEnvironment>({
|
|
|
82
92
|
.catch((error) => setError(error));
|
|
83
93
|
|
|
84
94
|
calledOnce.current = true;
|
|
85
|
-
}, [keycloak]);
|
|
95
|
+
}, [keycloak, externalKeycloak]);
|
|
86
96
|
|
|
87
97
|
if (error) {
|
|
88
98
|
return <ErrorPage error={error} />;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// @ts-nocheck
|
|
4
4
|
|
|
5
5
|
import { Icon, Popover } from "../../@patternfly/react-core";
|
|
6
|
-
import { HelpIcon } from "../../@patternfly/react-icons";
|
|
6
|
+
import { HelpIcon, ExclamationTriangleIcon } from "../../@patternfly/react-icons";
|
|
7
7
|
import { ReactNode } from "react";
|
|
8
8
|
import { useHelp } from "../context/HelpContext";
|
|
9
9
|
|
|
@@ -12,6 +12,7 @@ type HelpItemProps = {
|
|
|
12
12
|
fieldLabelId: string;
|
|
13
13
|
noVerticalAlign?: boolean;
|
|
14
14
|
unWrap?: boolean;
|
|
15
|
+
isRecommendation?: boolean;
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
export const HelpItem = ({
|
|
@@ -19,8 +20,11 @@ export const HelpItem = ({
|
|
|
19
20
|
fieldLabelId,
|
|
20
21
|
noVerticalAlign = true,
|
|
21
22
|
unWrap = false,
|
|
23
|
+
isRecommendation = false,
|
|
22
24
|
}: HelpItemProps) => {
|
|
23
25
|
const { enabled } = useHelp();
|
|
26
|
+
const IconComponent = isRecommendation ? ExclamationTriangleIcon : HelpIcon;
|
|
27
|
+
|
|
24
28
|
return enabled ? (
|
|
25
29
|
<Popover bodyContent={helpText}>
|
|
26
30
|
<>
|
|
@@ -31,14 +35,20 @@ export const HelpItem = ({
|
|
|
31
35
|
onClick={(e) => e.preventDefault()}
|
|
32
36
|
className="pf-v5-c-form__group-label-help"
|
|
33
37
|
>
|
|
34
|
-
<Icon
|
|
35
|
-
|
|
38
|
+
<Icon
|
|
39
|
+
isInline={noVerticalAlign}
|
|
40
|
+
status={isRecommendation ? "warning" : undefined}
|
|
41
|
+
>
|
|
42
|
+
<IconComponent />
|
|
36
43
|
</Icon>
|
|
37
44
|
</button>
|
|
38
45
|
)}
|
|
39
46
|
{unWrap && (
|
|
40
|
-
<Icon
|
|
41
|
-
|
|
47
|
+
<Icon
|
|
48
|
+
isInline={noVerticalAlign}
|
|
49
|
+
status={isRecommendation ? "warning" : undefined}
|
|
50
|
+
>
|
|
51
|
+
<IconComponent />
|
|
42
52
|
</Icon>
|
|
43
53
|
)}
|
|
44
54
|
</>
|
|
@@ -7,7 +7,8 @@ import { Badge, Chip, ChipGroup } from "../../@patternfly/react-core";
|
|
|
7
7
|
import { TableText } from "../../@patternfly/react-table";
|
|
8
8
|
import { FunctionComponent, PropsWithChildren, ReactNode } from "react";
|
|
9
9
|
import { useTranslation } from "react-i18next";
|
|
10
|
-
import {
|
|
10
|
+
import type { Action, LoaderFunction } from "./table/KeycloakDataTable";
|
|
11
|
+
import { KeycloakDataTable } from "./table/KeycloakDataTable";
|
|
11
12
|
|
|
12
13
|
type OrgDetailLinkProps = {
|
|
13
14
|
link: FunctionComponent<
|
|
@@ -71,6 +72,7 @@ export type OrganizationTableProps = PropsWithChildren & {
|
|
|
71
72
|
onSelect?: (orgs: OrganizationRepresentation[]) => void;
|
|
72
73
|
onDelete?: (org: OrganizationRepresentation) => void;
|
|
73
74
|
deleteLabel?: string;
|
|
75
|
+
actions?: Action<OrganizationRepresentation>[];
|
|
74
76
|
};
|
|
75
77
|
|
|
76
78
|
export const OrganizationTable = ({
|
|
@@ -84,6 +86,7 @@ export const OrganizationTable = ({
|
|
|
84
86
|
deleteLabel = "delete",
|
|
85
87
|
link,
|
|
86
88
|
children,
|
|
89
|
+
actions,
|
|
87
90
|
}: OrganizationTableProps) => {
|
|
88
91
|
const { t } = useTranslation();
|
|
89
92
|
|
|
@@ -97,16 +100,10 @@ export const OrganizationTable = ({
|
|
|
97
100
|
toolbarItem={toolbarItem}
|
|
98
101
|
onSelect={onSelect}
|
|
99
102
|
canSelectAll={onSelect !== undefined}
|
|
100
|
-
actions={
|
|
101
|
-
onDelete
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
title: t(deleteLabel),
|
|
105
|
-
onRowClick: onDelete,
|
|
106
|
-
},
|
|
107
|
-
]
|
|
108
|
-
: undefined
|
|
109
|
-
}
|
|
103
|
+
actions={[
|
|
104
|
+
...(onDelete ? [{ title: t(deleteLabel), onRowClick: onDelete }] : []),
|
|
105
|
+
...(actions ?? []),
|
|
106
|
+
]}
|
|
110
107
|
columns={[
|
|
111
108
|
{
|
|
112
109
|
name: "name",
|
|
@@ -23,6 +23,7 @@ export enum SelectVariant {
|
|
|
23
23
|
export type SelectControlOption = {
|
|
24
24
|
key: string;
|
|
25
25
|
value: string;
|
|
26
|
+
description?: string;
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
export type OptionType = string[] | SelectControlOption[];
|
|
@@ -51,6 +52,7 @@ export type SelectControlProps<
|
|
|
51
52
|
onFilter?: (value: string) => void;
|
|
52
53
|
variant?: Variant;
|
|
53
54
|
isDisabled?: boolean;
|
|
55
|
+
isFullWidth?: boolean;
|
|
54
56
|
menuAppendTo?: string;
|
|
55
57
|
placeholderText?: string;
|
|
56
58
|
chipGroupProps?: ChipGroupProps;
|
package/keycloak-theme/shared/keycloak-ui-shared/controls/select-control/SingleSelectControl.tsx
CHANGED
|
@@ -38,6 +38,7 @@ export const SingleSelectControl = <
|
|
|
38
38
|
controller,
|
|
39
39
|
labelIcon,
|
|
40
40
|
isDisabled,
|
|
41
|
+
isFullWidth = true,
|
|
41
42
|
onSelect,
|
|
42
43
|
...rest
|
|
43
44
|
}: SelectControlProps<T, P>) => {
|
|
@@ -84,7 +85,7 @@ export const SingleSelectControl = <
|
|
|
84
85
|
ref={ref}
|
|
85
86
|
onClick={() => setOpen(!open)}
|
|
86
87
|
isExpanded={open}
|
|
87
|
-
isFullWidth
|
|
88
|
+
isFullWidth={isFullWidth}
|
|
88
89
|
status={get(errors, name) ? MenuToggleStatus.danger : undefined}
|
|
89
90
|
aria-label={label}
|
|
90
91
|
isDisabled={isDisabled}
|
|
@@ -111,7 +112,15 @@ export const SingleSelectControl = <
|
|
|
111
112
|
>
|
|
112
113
|
<SelectList data-testid={`select-${name}`}>
|
|
113
114
|
{[...options, ...selectedOptions].map((option) => (
|
|
114
|
-
<SelectOption
|
|
115
|
+
<SelectOption
|
|
116
|
+
key={key(option)}
|
|
117
|
+
value={key(option)}
|
|
118
|
+
description={
|
|
119
|
+
!isString(option) && "description" in option
|
|
120
|
+
? option.description
|
|
121
|
+
: undefined
|
|
122
|
+
}
|
|
123
|
+
>
|
|
115
124
|
{isString(option) ? option : option.value}
|
|
116
125
|
</SelectOption>
|
|
117
126
|
))}
|
package/keycloak-theme/shared/keycloak-ui-shared/controls/select-control/TypeaheadSelectControl.tsx
CHANGED
|
@@ -54,6 +54,7 @@ export const TypeaheadSelectControl = <
|
|
|
54
54
|
placeholderText,
|
|
55
55
|
onFilter,
|
|
56
56
|
variant,
|
|
57
|
+
isFullWidth = true,
|
|
57
58
|
...rest
|
|
58
59
|
}: SelectControlProps<T, P>) => {
|
|
59
60
|
const {
|
|
@@ -210,7 +211,7 @@ export const TypeaheadSelectControl = <
|
|
|
210
211
|
textInputRef.current?.focus();
|
|
211
212
|
}}
|
|
212
213
|
isExpanded={open}
|
|
213
|
-
isFullWidth
|
|
214
|
+
isFullWidth={isFullWidth}
|
|
214
215
|
status={get(errors, name) ? MenuToggleStatus.danger : undefined}
|
|
215
216
|
>
|
|
216
217
|
<TextInputGroup isPlain>
|
|
@@ -307,6 +308,11 @@ export const TypeaheadSelectControl = <
|
|
|
307
308
|
value={key(option)}
|
|
308
309
|
isFocused={focusedItemIndex === index}
|
|
309
310
|
isActive={field.value.includes(getValue(option))}
|
|
311
|
+
description={
|
|
312
|
+
!isString(option) && "description" in option
|
|
313
|
+
? option.description
|
|
314
|
+
: undefined
|
|
315
|
+
}
|
|
310
316
|
>
|
|
311
317
|
{getValue(option)}
|
|
312
318
|
</SelectOption>
|
|
@@ -25,7 +25,7 @@ export const SelectComponent = (props: UserProfileFieldProps) => {
|
|
|
25
25
|
field: ControllerRenderProps<UserFormFields>,
|
|
26
26
|
) => {
|
|
27
27
|
if (isMultiValue) {
|
|
28
|
-
if (field.value
|
|
28
|
+
if (field.value?.includes(value)) {
|
|
29
29
|
field.onChange(field.value.filter((item: string) => item !== value));
|
|
30
30
|
} else {
|
|
31
31
|
if (Array.isArray(field.value)) {
|
|
@@ -51,14 +51,18 @@ export const SelectComponent = (props: UserProfileFieldProps) => {
|
|
|
51
51
|
const fetchLabel = (option: string) =>
|
|
52
52
|
label(props.t, optionLabel[option], option, prefix);
|
|
53
53
|
|
|
54
|
-
const convertOptions = (selected: string) =>
|
|
54
|
+
const convertOptions = (selected: string | string[]) =>
|
|
55
55
|
options
|
|
56
56
|
.filter((o) =>
|
|
57
57
|
fetchLabel(o)!.toLowerCase().includes(filter.toLowerCase()),
|
|
58
58
|
)
|
|
59
59
|
.map((option) => (
|
|
60
60
|
<SelectOption
|
|
61
|
-
selected={
|
|
61
|
+
selected={
|
|
62
|
+
Array.isArray(selected)
|
|
63
|
+
? selected.includes(option)
|
|
64
|
+
: selected === option
|
|
65
|
+
}
|
|
62
66
|
key={option}
|
|
63
67
|
value={option}
|
|
64
68
|
>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keycloakify/keycloak-ui-shared",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "260601.0.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git://github.com/keycloakify/keycloak-ui-shared.git"
|
|
@@ -14,11 +14,11 @@
|
|
|
14
14
|
"@patternfly/react-styles": "^5.4.1",
|
|
15
15
|
"@patternfly/react-table": "^5.4.16",
|
|
16
16
|
"i18next": "^25.7.3",
|
|
17
|
-
"lodash-es": "^4.
|
|
17
|
+
"lodash-es": "^4.18.1",
|
|
18
18
|
"react": "^18.3.1",
|
|
19
19
|
"react-hook-form": "7.70.0",
|
|
20
20
|
"react-i18next": "^16.5.1",
|
|
21
|
-
"@keycloak/keycloak-admin-client": "26.
|
|
21
|
+
"@keycloak/keycloak-admin-client": "26.6.1",
|
|
22
22
|
"@types/lodash-es": "^4.17.12",
|
|
23
23
|
"@types/react": "^18.3.18",
|
|
24
24
|
"oidc-spa": "~10.0.5"
|