@keycloakify/keycloak-account-ui 25.0.4-rc.6 → 26.0.0-rc.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/KcAccountUiLoader.d.ts +2 -1
- package/KcAccountUiLoader.js +5 -2
- package/KcAccountUiLoader.js.map +1 -1
- package/README.md +26 -28
- package/account-security/AccountRow.js +5 -4
- package/account-security/AccountRow.js.map +1 -1
- package/account-security/DeviceActivity.js +5 -4
- package/account-security/DeviceActivity.js.map +1 -1
- package/account-security/LinkedAccounts.js +24 -9
- package/account-security/LinkedAccounts.js.map +1 -1
- package/account-security/LinkedAccountsToolbar.d.ts +12 -0
- package/account-security/LinkedAccountsToolbar.js +24 -0
- package/account-security/LinkedAccountsToolbar.js.map +1 -0
- package/account-security/SigningIn.js +1 -1
- package/account-security/SigningIn.js.map +1 -1
- package/api/methods.d.ts +7 -1
- package/api/methods.js +9 -2
- package/api/methods.js.map +1 -1
- package/api/parse-links.js +3 -3
- package/api/parse-links.js.map +1 -1
- package/api/parse-response.d.ts +2 -0
- package/api/parse-response.js +11 -15
- package/api/parse-response.js.map +1 -1
- package/api/request.js +1 -1
- package/api/request.js.map +1 -1
- package/api.js +1 -7
- package/api.js.map +1 -1
- package/applications/Applications.js +5 -4
- package/applications/Applications.js.map +1 -1
- package/environment.d.ts +1 -0
- package/environment.js.map +1 -1
- package/messages/messages_ca.properties +105 -12
- package/messages/messages_de.properties +23 -1
- package/messages/messages_en.properties +16 -1
- package/messages/messages_es.properties +1 -1
- package/messages/messages_fr.properties +64 -19
- package/messages/messages_it.properties +3 -0
- package/messages/messages_ka.properties +15 -0
- package/messages/messages_nl.properties +2 -0
- package/organizations/Organizations.d.ts +2 -0
- package/organizations/Organizations.js +19 -0
- package/organizations/Organizations.js.map +1 -0
- package/package.json +89 -29
- package/personal-info/PersonalInfo.js +11 -9
- package/personal-info/PersonalInfo.js.map +1 -1
- package/public/content.d.ts +4 -0
- package/public/content.js +5 -0
- package/public/content.js.map +1 -1
- package/resources/EditTheResource.js +4 -3
- package/resources/EditTheResource.js.map +1 -1
- package/resources/PermissionRequest.js +4 -3
- package/resources/PermissionRequest.js.map +1 -1
- package/resources/ResourcesTab.js +4 -3
- package/resources/ResourcesTab.js.map +1 -1
- package/resources/ShareTheResource.js +4 -3
- package/resources/ShareTheResource.js.map +1 -1
- package/root/Header.js +1 -1
- package/root/Header.js.map +1 -1
- package/root/PageNav.js +1 -1
- package/root/PageNav.js.map +1 -1
- package/routes.d.ts +1 -0
- package/routes.js +6 -0
- package/routes.js.map +1 -1
- package/src/KcAccountUiLoader.tsx +7 -2
- package/src/account-security/AccountRow.tsx +6 -8
- package/src/account-security/DeviceActivity.tsx +10 -9
- package/src/account-security/LinkedAccounts.tsx +107 -30
- package/src/account-security/LinkedAccountsToolbar.tsx +88 -0
- package/src/account-security/SigningIn.tsx +1 -1
- package/src/api/methods.ts +22 -2
- package/src/api/parse-links.ts +3 -3
- package/src/api/parse-response.ts +22 -23
- package/src/api/request.ts +1 -1
- package/src/api.ts +1 -7
- package/src/applications/Applications.tsx +19 -11
- package/src/environment.ts +1 -0
- package/src/organizations/Organizations.tsx +48 -0
- package/src/personal-info/PersonalInfo.tsx +10 -8
- package/src/public/content.ts +5 -0
- package/src/resources/EditTheResource.tsx +8 -7
- package/src/resources/PermissionRequest.tsx +5 -3
- package/src/resources/ResourcesTab.tsx +8 -7
- package/src/resources/ShareTheResource.tsx +9 -8
- package/src/root/Header.tsx +0 -1
- package/src/root/PageNav.tsx +1 -1
- package/src/routes.tsx +7 -0
- package/src/ui-shared/alerts/AlertPanel.tsx +43 -0
- package/src/ui-shared/alerts/Alerts.tsx +48 -52
- package/src/ui-shared/context/environment.ts +1 -1
- package/src/ui-shared/controls/KeycloakSpinner.tsx +12 -0
- package/src/ui-shared/controls/OrganizationTable.tsx +122 -0
- package/src/ui-shared/controls/select-control/SingleSelectControl.tsx +3 -1
- package/src/ui-shared/controls/select-control/TypeaheadSelectControl.tsx +5 -3
- package/src/ui-shared/controls/table/KeycloakDataTable.tsx +597 -0
- package/src/ui-shared/controls/table/ListEmptyState.tsx +86 -0
- package/src/ui-shared/controls/table/PaginatingTableToolbar.tsx +106 -0
- package/src/ui-shared/controls/table/TableToolbar.tsx +92 -0
- package/src/ui-shared/main.ts +35 -1
- package/src/ui-shared/masthead/Masthead.tsx +64 -48
- package/src/ui-shared/select/SingleSelect.tsx +2 -0
- package/src/ui-shared/select/TypeaheadSelect.tsx +2 -0
- package/src/ui-shared/user-profile/LocaleSelector.tsx +1 -1
- package/src/ui-shared/user-profile/UserProfileFields.tsx +18 -21
- package/src/ui-shared/user-profile/UserProfileGroup.tsx +3 -2
- package/src/ui-shared/user-profile/utils.ts +12 -6
- package/src/ui-shared/utils/ErrorBoundary.tsx +77 -0
- package/src/ui-shared/utils/darkMode.ts +19 -0
- package/src/ui-shared/utils/errors.ts +55 -0
- package/src/ui-shared/utils/generateId.ts +1 -0
- package/src/ui-shared/utils/useFetch.ts +44 -0
- package/src/ui-shared/utils/useSetTimeout.ts +40 -0
- package/src/utils/useAccountAlerts.ts +28 -0
- package/src/utils/usePromise.ts +8 -3
- package/src/zKcContextLike.ts +2 -1
- package/ui-shared/alerts/AlertPanel.d.ts +6 -0
- package/ui-shared/alerts/AlertPanel.js +6 -0
- package/ui-shared/alerts/AlertPanel.js.map +1 -0
- package/ui-shared/alerts/Alerts.d.ts +2 -3
- package/ui-shared/alerts/Alerts.js +32 -22
- package/ui-shared/alerts/Alerts.js.map +1 -1
- package/ui-shared/context/environment.js +1 -1
- package/ui-shared/context/environment.js.map +1 -1
- package/ui-shared/controls/KeycloakSpinner.d.ts +1 -0
- package/ui-shared/controls/KeycloakSpinner.js +8 -0
- package/ui-shared/controls/KeycloakSpinner.js.map +1 -0
- package/ui-shared/controls/OrganizationTable.d.ts +16 -0
- package/ui-shared/controls/OrganizationTable.js +45 -0
- package/ui-shared/controls/OrganizationTable.js.map +1 -0
- package/ui-shared/controls/select-control/SingleSelectControl.js +3 -1
- package/ui-shared/controls/select-control/SingleSelectControl.js.map +1 -1
- package/ui-shared/controls/select-control/TypeaheadSelectControl.js +5 -3
- package/ui-shared/controls/select-control/TypeaheadSelectControl.js.map +1 -1
- package/ui-shared/controls/table/KeycloakDataTable.d.ts +64 -0
- package/ui-shared/controls/table/KeycloakDataTable.js +279 -0
- package/ui-shared/controls/table/KeycloakDataTable.js.map +1 -0
- package/ui-shared/controls/table/ListEmptyState.d.ts +20 -0
- package/ui-shared/controls/table/ListEmptyState.js +11 -0
- package/ui-shared/controls/table/ListEmptyState.js.map +1 -0
- package/ui-shared/controls/table/PaginatingTableToolbar.d.ts +21 -0
- package/ui-shared/controls/table/PaginatingTableToolbar.js +27 -0
- package/ui-shared/controls/table/PaginatingTableToolbar.js.map +1 -0
- package/ui-shared/controls/table/TableToolbar.d.ts +12 -0
- package/ui-shared/controls/table/TableToolbar.js +30 -0
- package/ui-shared/controls/table/TableToolbar.js.map +1 -0
- package/ui-shared/main.d.ts +15 -1
- package/ui-shared/main.js +13 -1
- package/ui-shared/main.js.map +1 -1
- package/ui-shared/masthead/Masthead.d.ts +4 -7
- package/ui-shared/masthead/Masthead.js +14 -14
- package/ui-shared/masthead/Masthead.js.map +1 -1
- package/ui-shared/select/SingleSelect.d.ts +1 -1
- package/ui-shared/select/SingleSelect.js +2 -2
- package/ui-shared/select/SingleSelect.js.map +1 -1
- package/ui-shared/select/TypeaheadSelect.d.ts +1 -1
- package/ui-shared/select/TypeaheadSelect.js +2 -2
- package/ui-shared/select/TypeaheadSelect.js.map +1 -1
- package/ui-shared/user-profile/LocaleSelector.js +1 -1
- package/ui-shared/user-profile/LocaleSelector.js.map +1 -1
- package/ui-shared/user-profile/UserProfileFields.d.ts +2 -4
- package/ui-shared/user-profile/UserProfileFields.js +0 -18
- package/ui-shared/user-profile/UserProfileFields.js.map +1 -1
- package/ui-shared/user-profile/UserProfileGroup.js.map +1 -1
- package/ui-shared/user-profile/utils.js +2 -2
- package/ui-shared/user-profile/utils.js.map +1 -1
- package/ui-shared/utils/ErrorBoundary.d.ts +26 -0
- package/ui-shared/utils/ErrorBoundary.js +29 -0
- package/ui-shared/utils/ErrorBoundary.js.map +1 -0
- package/ui-shared/utils/darkMode.d.ts +1 -0
- package/ui-shared/utils/darkMode.js +16 -0
- package/ui-shared/utils/darkMode.js.map +1 -0
- package/ui-shared/utils/errors.d.ts +4 -0
- package/ui-shared/utils/errors.js +42 -0
- package/ui-shared/utils/errors.js.map +1 -0
- package/ui-shared/utils/generateId.d.ts +1 -0
- package/ui-shared/utils/generateId.js +2 -0
- package/ui-shared/utils/generateId.js.map +1 -0
- package/ui-shared/utils/useFetch.d.ts +17 -0
- package/ui-shared/utils/useFetch.js +38 -0
- package/ui-shared/utils/useFetch.js.map +1 -0
- package/ui-shared/utils/useSetTimeout.d.ts +1 -0
- package/ui-shared/utils/useSetTimeout.js +32 -0
- package/ui-shared/utils/useSetTimeout.js.map +1 -0
- package/utils/useAccountAlerts.d.ts +4 -0
- package/utils/useAccountAlerts.js +19 -0
- package/utils/useAccountAlerts.js.map +1 -0
- package/utils/usePromise.js +7 -3
- package/utils/usePromise.js.map +1 -1
- package/zKcContextLike.js +2 -1
- package/zKcContextLike.js.map +1 -1
- package/src/utils/isRecord.ts +0 -2
- package/utils/isRecord.d.ts +0 -1
- package/utils/isRecord.js +0 -2
- package/utils/isRecord.js.map +0 -1
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Pagination,
|
|
3
|
+
PaginationToggleTemplateProps,
|
|
4
|
+
ToolbarItem,
|
|
5
|
+
} from "@patternfly/react-core";
|
|
6
|
+
import { PropsWithChildren, ReactNode } from "react";
|
|
7
|
+
import { useTranslation } from "react-i18next";
|
|
8
|
+
|
|
9
|
+
import { TableToolbar } from "@keycloakify/keycloak-account-ui/ui-shared/controls/table/TableToolbar";
|
|
10
|
+
|
|
11
|
+
type KeycloakPaginationProps = {
|
|
12
|
+
id?: string;
|
|
13
|
+
count: number;
|
|
14
|
+
first: number;
|
|
15
|
+
max: number;
|
|
16
|
+
onNextClick: (page: number) => void;
|
|
17
|
+
onPreviousClick: (page: number) => void;
|
|
18
|
+
onPerPageSelect: (max: number, first: number) => void;
|
|
19
|
+
variant?: "top" | "bottom";
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type TableToolbarProps = KeycloakPaginationProps & {
|
|
23
|
+
searchTypeComponent?: ReactNode;
|
|
24
|
+
toolbarItem?: ReactNode;
|
|
25
|
+
subToolbar?: ReactNode;
|
|
26
|
+
inputGroupName?: string;
|
|
27
|
+
inputGroupPlaceholder?: string;
|
|
28
|
+
inputGroupOnEnter?: (value: string) => void;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const KeycloakPagination = ({
|
|
32
|
+
id,
|
|
33
|
+
variant = "top",
|
|
34
|
+
count,
|
|
35
|
+
first,
|
|
36
|
+
max,
|
|
37
|
+
onNextClick,
|
|
38
|
+
onPreviousClick,
|
|
39
|
+
onPerPageSelect,
|
|
40
|
+
}: KeycloakPaginationProps) => {
|
|
41
|
+
const { t } = useTranslation();
|
|
42
|
+
const page = Math.round(first / max);
|
|
43
|
+
return (
|
|
44
|
+
<Pagination
|
|
45
|
+
widgetId={id}
|
|
46
|
+
titles={{
|
|
47
|
+
paginationAriaLabel: `${t("pagination")} ${variant} `,
|
|
48
|
+
}}
|
|
49
|
+
isCompact
|
|
50
|
+
toggleTemplate={({
|
|
51
|
+
firstIndex,
|
|
52
|
+
lastIndex,
|
|
53
|
+
}: PaginationToggleTemplateProps) => (
|
|
54
|
+
<b>
|
|
55
|
+
{firstIndex} - {lastIndex}
|
|
56
|
+
</b>
|
|
57
|
+
)}
|
|
58
|
+
itemCount={count + page * max}
|
|
59
|
+
page={page + 1}
|
|
60
|
+
perPage={max}
|
|
61
|
+
onNextClick={(_, p) => onNextClick((p - 1) * max)}
|
|
62
|
+
onPreviousClick={(_, p) => onPreviousClick((p - 1) * max)}
|
|
63
|
+
onPerPageSelect={(_, m, f) => onPerPageSelect(f - 1, m)}
|
|
64
|
+
variant={variant}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const PaginatingTableToolbar = ({
|
|
70
|
+
count,
|
|
71
|
+
searchTypeComponent,
|
|
72
|
+
toolbarItem,
|
|
73
|
+
subToolbar,
|
|
74
|
+
children,
|
|
75
|
+
inputGroupName,
|
|
76
|
+
inputGroupPlaceholder,
|
|
77
|
+
inputGroupOnEnter,
|
|
78
|
+
...rest
|
|
79
|
+
}: PropsWithChildren<TableToolbarProps>) => {
|
|
80
|
+
return (
|
|
81
|
+
<TableToolbar
|
|
82
|
+
searchTypeComponent={searchTypeComponent}
|
|
83
|
+
toolbarItem={
|
|
84
|
+
<>
|
|
85
|
+
{toolbarItem}
|
|
86
|
+
<ToolbarItem variant="pagination">
|
|
87
|
+
<KeycloakPagination count={count} {...rest} />
|
|
88
|
+
</ToolbarItem>
|
|
89
|
+
</>
|
|
90
|
+
}
|
|
91
|
+
subToolbar={subToolbar}
|
|
92
|
+
toolbarItemFooter={
|
|
93
|
+
count !== 0 ? (
|
|
94
|
+
<ToolbarItem variant="pagination">
|
|
95
|
+
<KeycloakPagination count={count} variant="bottom" {...rest} />
|
|
96
|
+
</ToolbarItem>
|
|
97
|
+
) : null
|
|
98
|
+
}
|
|
99
|
+
inputGroupName={inputGroupName}
|
|
100
|
+
inputGroupPlaceholder={inputGroupPlaceholder}
|
|
101
|
+
inputGroupOnEnter={inputGroupOnEnter}
|
|
102
|
+
>
|
|
103
|
+
{children}
|
|
104
|
+
</TableToolbar>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Divider,
|
|
3
|
+
InputGroup,
|
|
4
|
+
SearchInput,
|
|
5
|
+
Toolbar,
|
|
6
|
+
ToolbarContent,
|
|
7
|
+
ToolbarItem,
|
|
8
|
+
} from "@patternfly/react-core";
|
|
9
|
+
import { KeyboardEvent, PropsWithChildren, ReactNode, useState } from "react";
|
|
10
|
+
import { useTranslation } from "react-i18next";
|
|
11
|
+
|
|
12
|
+
type TableToolbarProps = {
|
|
13
|
+
toolbarItem?: ReactNode;
|
|
14
|
+
subToolbar?: ReactNode;
|
|
15
|
+
toolbarItemFooter?: ReactNode;
|
|
16
|
+
searchTypeComponent?: ReactNode;
|
|
17
|
+
inputGroupName?: string;
|
|
18
|
+
inputGroupPlaceholder?: string;
|
|
19
|
+
inputGroupOnEnter?: (value: string) => void;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const TableToolbar = ({
|
|
23
|
+
toolbarItem,
|
|
24
|
+
subToolbar,
|
|
25
|
+
toolbarItemFooter,
|
|
26
|
+
children,
|
|
27
|
+
searchTypeComponent,
|
|
28
|
+
inputGroupName,
|
|
29
|
+
inputGroupPlaceholder,
|
|
30
|
+
inputGroupOnEnter,
|
|
31
|
+
}: PropsWithChildren<TableToolbarProps>) => {
|
|
32
|
+
const { t } = useTranslation();
|
|
33
|
+
const [searchValue, setSearchValue] = useState<string>("");
|
|
34
|
+
|
|
35
|
+
const onSearch = () => {
|
|
36
|
+
if (searchValue !== "") {
|
|
37
|
+
setSearchValue(searchValue);
|
|
38
|
+
inputGroupOnEnter?.(searchValue);
|
|
39
|
+
} else {
|
|
40
|
+
setSearchValue("");
|
|
41
|
+
inputGroupOnEnter?.("");
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
|
|
46
|
+
if (e.key === "Enter") {
|
|
47
|
+
onSearch();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<>
|
|
53
|
+
<Toolbar>
|
|
54
|
+
<ToolbarContent>
|
|
55
|
+
{inputGroupName && (
|
|
56
|
+
<ToolbarItem>
|
|
57
|
+
<InputGroup data-testid={inputGroupName}>
|
|
58
|
+
{searchTypeComponent}
|
|
59
|
+
{inputGroupPlaceholder && (
|
|
60
|
+
<SearchInput
|
|
61
|
+
data-testid="table-search-input"
|
|
62
|
+
placeholder={inputGroupPlaceholder}
|
|
63
|
+
aria-label={t("search")}
|
|
64
|
+
value={searchValue}
|
|
65
|
+
onChange={(_, value) => {
|
|
66
|
+
setSearchValue(value);
|
|
67
|
+
}}
|
|
68
|
+
onSearch={onSearch}
|
|
69
|
+
onKeyDown={handleKeyDown}
|
|
70
|
+
onClear={() => {
|
|
71
|
+
setSearchValue("");
|
|
72
|
+
inputGroupOnEnter?.("");
|
|
73
|
+
}}
|
|
74
|
+
/>
|
|
75
|
+
)}
|
|
76
|
+
</InputGroup>
|
|
77
|
+
</ToolbarItem>
|
|
78
|
+
)}
|
|
79
|
+
{toolbarItem}
|
|
80
|
+
</ToolbarContent>
|
|
81
|
+
</Toolbar>
|
|
82
|
+
{subToolbar && (
|
|
83
|
+
<Toolbar>
|
|
84
|
+
<ToolbarContent>{subToolbar}</ToolbarContent>
|
|
85
|
+
</Toolbar>
|
|
86
|
+
)}
|
|
87
|
+
<Divider />
|
|
88
|
+
{children}
|
|
89
|
+
<Toolbar>{toolbarItemFooter}</Toolbar>
|
|
90
|
+
</>
|
|
91
|
+
);
|
|
92
|
+
};
|
package/src/ui-shared/main.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {
|
|
2
|
+
AlertProvider,
|
|
3
|
+
useAlerts,
|
|
4
|
+
type AddAlertFunction,
|
|
5
|
+
type AddErrorFunction,
|
|
6
|
+
type AlertProps,
|
|
7
|
+
} from "@keycloakify/keycloak-account-ui/ui-shared/alerts/Alerts";
|
|
2
8
|
export { ErrorPage } from "@keycloakify/keycloak-account-ui/ui-shared/context/ErrorPage";
|
|
3
9
|
export { Help, useHelp } from "@keycloakify/keycloak-account-ui/ui-shared/context/HelpContext";
|
|
4
10
|
export {
|
|
@@ -54,9 +60,37 @@ export {
|
|
|
54
60
|
} from "@keycloakify/keycloak-account-ui/ui-shared/user-profile/utils";
|
|
55
61
|
export type { UserFormFields } from "@keycloakify/keycloak-account-ui/ui-shared/user-profile/utils";
|
|
56
62
|
export { createNamedContext } from "@keycloakify/keycloak-account-ui/ui-shared/utils/createNamedContext";
|
|
63
|
+
export {
|
|
64
|
+
getErrorDescription,
|
|
65
|
+
getErrorMessage,
|
|
66
|
+
getNetworkErrorMessage,
|
|
67
|
+
getNetworkErrorDescription,
|
|
68
|
+
} from "@keycloakify/keycloak-account-ui/ui-shared/utils/errors";
|
|
57
69
|
export { isDefined } from "@keycloakify/keycloak-account-ui/ui-shared/utils/isDefined";
|
|
58
70
|
export { useRequiredContext } from "@keycloakify/keycloak-account-ui/ui-shared/utils/useRequiredContext";
|
|
59
71
|
export { useStoredState } from "@keycloakify/keycloak-account-ui/ui-shared/utils/useStoredState";
|
|
72
|
+
export { useSetTimeout } from "@keycloakify/keycloak-account-ui/ui-shared/utils/useSetTimeout";
|
|
73
|
+
export { generateId } from "@keycloakify/keycloak-account-ui/ui-shared/utils/generateId";
|
|
60
74
|
export { default as KeycloakMasthead } from "@keycloakify/keycloak-account-ui/ui-shared/masthead/Masthead";
|
|
61
75
|
export { KeycloakSelect } from "@keycloakify/keycloak-account-ui/ui-shared/select/KeycloakSelect";
|
|
62
76
|
export type { Variant, KeycloakSelectProps } from "@keycloakify/keycloak-account-ui/ui-shared/select/KeycloakSelect";
|
|
77
|
+
export { KeycloakDataTable } from "@keycloakify/keycloak-account-ui/ui-shared/controls/table/KeycloakDataTable";
|
|
78
|
+
export type {
|
|
79
|
+
Action,
|
|
80
|
+
Field,
|
|
81
|
+
DetailField,
|
|
82
|
+
LoaderFunction,
|
|
83
|
+
} from "@keycloakify/keycloak-account-ui/ui-shared/controls/table/KeycloakDataTable";
|
|
84
|
+
export { PaginatingTableToolbar } from "@keycloakify/keycloak-account-ui/ui-shared/controls/table/PaginatingTableToolbar";
|
|
85
|
+
export { TableToolbar } from "@keycloakify/keycloak-account-ui/ui-shared/controls/table/TableToolbar";
|
|
86
|
+
export { ListEmptyState } from "@keycloakify/keycloak-account-ui/ui-shared/controls/table/ListEmptyState";
|
|
87
|
+
export { KeycloakSpinner } from "@keycloakify/keycloak-account-ui/ui-shared/controls/KeycloakSpinner";
|
|
88
|
+
export { useFetch } from "@keycloakify/keycloak-account-ui/ui-shared/utils/useFetch";
|
|
89
|
+
export {
|
|
90
|
+
useErrorBoundary,
|
|
91
|
+
ErrorBoundaryFallback,
|
|
92
|
+
ErrorBoundaryProvider,
|
|
93
|
+
} from "@keycloakify/keycloak-account-ui/ui-shared/utils/ErrorBoundary";
|
|
94
|
+
export type { FallbackProps } from "@keycloakify/keycloak-account-ui/ui-shared/utils/ErrorBoundary";
|
|
95
|
+
export { OrganizationTable } from "@keycloakify/keycloak-account-ui/ui-shared/controls/OrganizationTable";
|
|
96
|
+
export { initializeDarkMode } from "@keycloakify/keycloak-account-ui/ui-shared/utils/darkMode";
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Avatar,
|
|
3
3
|
AvatarProps,
|
|
4
|
-
Brand,
|
|
5
|
-
BrandProps,
|
|
6
4
|
DropdownItem,
|
|
5
|
+
Masthead,
|
|
6
|
+
MastheadBrand,
|
|
7
|
+
MastheadBrandProps,
|
|
8
|
+
MastheadContent,
|
|
9
|
+
MastheadMainProps,
|
|
10
|
+
MastheadToggle,
|
|
11
|
+
PageToggleButton,
|
|
12
|
+
Toolbar,
|
|
13
|
+
ToolbarContent,
|
|
14
|
+
ToolbarItem,
|
|
7
15
|
} from "@patternfly/react-core";
|
|
8
|
-
import {
|
|
9
|
-
PageHeader,
|
|
10
|
-
PageHeaderProps,
|
|
11
|
-
PageHeaderTools,
|
|
12
|
-
PageHeaderToolsGroup,
|
|
13
|
-
PageHeaderToolsItem,
|
|
14
|
-
} from "@patternfly/react-core/deprecated";
|
|
16
|
+
import { BarsIcon } from "@patternfly/react-icons";
|
|
15
17
|
import { TFunction } from "i18next";
|
|
16
18
|
import Keycloak, { type KeycloakTokenParsed } from "keycloak-js";
|
|
17
19
|
import { ReactNode } from "react";
|
|
@@ -38,11 +40,9 @@ function loggedInUserName(
|
|
|
38
40
|
return givenName || familyName || preferredUsername || t("unknownUser");
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
type BrandLogo =
|
|
42
|
-
href: string;
|
|
43
|
-
};
|
|
43
|
+
type BrandLogo = MastheadBrandProps;
|
|
44
44
|
|
|
45
|
-
type KeycloakMastheadProps =
|
|
45
|
+
type KeycloakMastheadProps = MastheadMainProps & {
|
|
46
46
|
keycloak: Keycloak;
|
|
47
47
|
brand: BrandLogo;
|
|
48
48
|
avatar?: AvatarProps;
|
|
@@ -58,7 +58,7 @@ type KeycloakMastheadProps = PageHeaderProps & {
|
|
|
58
58
|
|
|
59
59
|
const KeycloakMasthead = ({
|
|
60
60
|
keycloak,
|
|
61
|
-
brand: {
|
|
61
|
+
brand: { src, alt, className, ...brandProps },
|
|
62
62
|
avatar,
|
|
63
63
|
features: {
|
|
64
64
|
hasLogout = true,
|
|
@@ -92,33 +92,28 @@ const KeycloakMasthead = ({
|
|
|
92
92
|
|
|
93
93
|
const picture = keycloak.idTokenParsed?.picture;
|
|
94
94
|
return (
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
]}
|
|
114
|
-
/>
|
|
115
|
-
</PageHeaderToolsItem>
|
|
116
|
-
<PageHeaderToolsItem>{toolbarItems}</PageHeaderToolsItem>
|
|
117
|
-
<PageHeaderToolsItem
|
|
95
|
+
<Masthead {...rest}>
|
|
96
|
+
<MastheadToggle>
|
|
97
|
+
<PageToggleButton variant="plain" aria-label={t("navigation")}>
|
|
98
|
+
<BarsIcon />
|
|
99
|
+
</PageToggleButton>
|
|
100
|
+
</MastheadToggle>
|
|
101
|
+
<MastheadBrand {...brandProps}>
|
|
102
|
+
<img src={src} alt={alt} className={className} />
|
|
103
|
+
</MastheadBrand>
|
|
104
|
+
<MastheadContent>
|
|
105
|
+
<Toolbar>
|
|
106
|
+
<ToolbarContent>
|
|
107
|
+
{toolbarItems?.map((item, index) => (
|
|
108
|
+
<ToolbarItem key={index} align={{ default: "alignRight" }}>
|
|
109
|
+
{item}
|
|
110
|
+
</ToolbarItem>
|
|
111
|
+
))}
|
|
112
|
+
<ToolbarItem
|
|
118
113
|
visibility={{
|
|
119
114
|
default: "hidden",
|
|
120
115
|
md: "visible",
|
|
121
|
-
}}
|
|
116
|
+
}} /** this user dropdown is hidden on mobile sizes */
|
|
122
117
|
>
|
|
123
118
|
<KeycloakDropdown
|
|
124
119
|
data-testid="options"
|
|
@@ -129,16 +124,37 @@ const KeycloakMasthead = ({
|
|
|
129
124
|
: undefined
|
|
130
125
|
}
|
|
131
126
|
/>
|
|
132
|
-
</
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
127
|
+
</ToolbarItem>
|
|
128
|
+
<ToolbarItem
|
|
129
|
+
align={{ default: "alignLeft" }}
|
|
130
|
+
visibility={{
|
|
131
|
+
md: "hidden",
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
<KeycloakDropdown
|
|
135
|
+
data-testid="options-kebab"
|
|
136
|
+
isKebab
|
|
137
|
+
dropDownItems={[
|
|
138
|
+
...(kebabDropdownItems || dropdownItems),
|
|
139
|
+
extraItems,
|
|
140
|
+
]}
|
|
141
|
+
/>
|
|
142
|
+
</ToolbarItem>
|
|
143
|
+
<ToolbarItem
|
|
144
|
+
variant="overflow-menu"
|
|
145
|
+
align={{ default: "alignRight" }}
|
|
146
|
+
className="pf-v5-u-m-0-on-lg"
|
|
147
|
+
>
|
|
148
|
+
{picture || avatar?.src ? (
|
|
149
|
+
<Avatar {...{ src: picture, alt: t("avatar"), ...avatar }} />
|
|
150
|
+
) : (
|
|
151
|
+
<DefaultAvatar {...avatar} />
|
|
152
|
+
)}
|
|
153
|
+
</ToolbarItem>
|
|
154
|
+
</ToolbarContent>
|
|
155
|
+
</Toolbar>
|
|
156
|
+
</MastheadContent>
|
|
157
|
+
</Masthead>
|
|
142
158
|
);
|
|
143
159
|
};
|
|
144
160
|
|
|
@@ -21,6 +21,7 @@ export const SingleSelect = ({
|
|
|
21
21
|
maxHeight,
|
|
22
22
|
toggleIcon,
|
|
23
23
|
className,
|
|
24
|
+
isDisabled,
|
|
24
25
|
children,
|
|
25
26
|
...props
|
|
26
27
|
}: SingleSelectProps) => {
|
|
@@ -69,6 +70,7 @@ export const SingleSelect = ({
|
|
|
69
70
|
isExpanded={isOpen}
|
|
70
71
|
aria-label={props["aria-label"]}
|
|
71
72
|
icon={toggleIcon}
|
|
73
|
+
isDisabled={isDisabled}
|
|
72
74
|
isFullWidth
|
|
73
75
|
>
|
|
74
76
|
{childArray.find((c) => c.props.value === selections)?.props
|
|
@@ -37,6 +37,7 @@ export const TypeaheadSelect = ({
|
|
|
37
37
|
chipGroupComponent,
|
|
38
38
|
chipGroupProps,
|
|
39
39
|
footer,
|
|
40
|
+
isDisabled,
|
|
40
41
|
children,
|
|
41
42
|
...rest
|
|
42
43
|
}: KeycloakSelectProps) => {
|
|
@@ -124,6 +125,7 @@ export const TypeaheadSelect = ({
|
|
|
124
125
|
variant="typeahead"
|
|
125
126
|
onClick={() => onToggle?.(true)}
|
|
126
127
|
icon={toggleIcon}
|
|
128
|
+
isDisabled={isDisabled}
|
|
127
129
|
isExpanded={rest.isOpen}
|
|
128
130
|
isFullWidth
|
|
129
131
|
status={validated === "error" ? MenuToggleStatus.danger : undefined}
|
|
@@ -6,7 +6,7 @@ import { UserProfileFieldProps } from "@keycloakify/keycloak-account-ui/ui-share
|
|
|
6
6
|
const localeToDisplayName = (locale: string) => {
|
|
7
7
|
try {
|
|
8
8
|
return new Intl.DisplayNames([locale], { type: "language" }).of(locale);
|
|
9
|
-
} catch
|
|
9
|
+
} catch {
|
|
10
10
|
return locale;
|
|
11
11
|
}
|
|
12
12
|
};
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "@keycloak/keycloak-admin-client/lib/defs/userProfileMetadata";
|
|
6
6
|
import { Text } from "@patternfly/react-core";
|
|
7
7
|
import { TFunction } from "i18next";
|
|
8
|
-
import { ReactNode, useMemo } from "react";
|
|
8
|
+
import { ReactNode, useMemo, type JSX } from "react";
|
|
9
9
|
import { FieldPath, UseFormReturn } from "react-hook-form";
|
|
10
10
|
|
|
11
11
|
import { ScrollForm } from "@keycloakify/keycloak-account-ui/ui-shared/main";
|
|
@@ -25,26 +25,23 @@ export type Options = {
|
|
|
25
25
|
options?: string[];
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
"text"
|
|
30
|
-
"textarea"
|
|
31
|
-
"select"
|
|
32
|
-
"select-radiobuttons"
|
|
33
|
-
"multiselect"
|
|
34
|
-
"multiselect-checkboxes"
|
|
35
|
-
"html5-email"
|
|
36
|
-
"html5-tel"
|
|
37
|
-
"html5-url"
|
|
38
|
-
"html5-number"
|
|
39
|
-
"html5-range"
|
|
40
|
-
"html5-datetime-local"
|
|
41
|
-
"html5-date"
|
|
42
|
-
"html5-month"
|
|
43
|
-
"html5-time"
|
|
44
|
-
"multi-input"
|
|
45
|
-
] as const;
|
|
46
|
-
|
|
47
|
-
export type InputType = (typeof INPUT_TYPES)[number];
|
|
28
|
+
export type InputType =
|
|
29
|
+
| "text"
|
|
30
|
+
| "textarea"
|
|
31
|
+
| "select"
|
|
32
|
+
| "select-radiobuttons"
|
|
33
|
+
| "multiselect"
|
|
34
|
+
| "multiselect-checkboxes"
|
|
35
|
+
| "html5-email"
|
|
36
|
+
| "html5-tel"
|
|
37
|
+
| "html5-url"
|
|
38
|
+
| "html5-number"
|
|
39
|
+
| "html5-range"
|
|
40
|
+
| "html5-datetime-local"
|
|
41
|
+
| "html5-date"
|
|
42
|
+
| "html5-month"
|
|
43
|
+
| "html5-time"
|
|
44
|
+
| "multi-input";
|
|
48
45
|
|
|
49
46
|
export type UserProfileFieldProps = {
|
|
50
47
|
t: TFunction;
|
|
@@ -3,7 +3,7 @@ import { FormGroup, InputGroup } from "@patternfly/react-core";
|
|
|
3
3
|
import { TFunction } from "i18next";
|
|
4
4
|
import { get } from "lodash-es";
|
|
5
5
|
import { PropsWithChildren, ReactNode } from "react";
|
|
6
|
-
import { UseFormReturn } from "react-hook-form";
|
|
6
|
+
import { UseFormReturn, type FieldError } from "react-hook-form";
|
|
7
7
|
|
|
8
8
|
import { FormErrorText } from "@keycloakify/keycloak-account-ui/ui-shared/controls/FormErrorText";
|
|
9
9
|
import { HelpItem } from "@keycloakify/keycloak-account-ui/ui-shared/controls/HelpItem";
|
|
@@ -38,7 +38,8 @@ export const UserProfileGroup = ({
|
|
|
38
38
|
} = form;
|
|
39
39
|
|
|
40
40
|
const component = renderer?.(attribute);
|
|
41
|
-
const error = get(errors, fieldName(attribute.name));
|
|
41
|
+
const error = get(errors, fieldName(attribute.name)) as FieldError;
|
|
42
|
+
|
|
42
43
|
return (
|
|
43
44
|
<FormGroup
|
|
44
45
|
key={attribute.name}
|
|
@@ -50,7 +50,10 @@ export const isRootAttribute = (attr?: string) =>
|
|
|
50
50
|
attr && ROOT_ATTRIBUTES.includes(attr);
|
|
51
51
|
|
|
52
52
|
export const fieldName = (name?: string) =>
|
|
53
|
-
`${isRootAttribute(name) ? "" : "attributes."}${name?.replaceAll(
|
|
53
|
+
`${isRootAttribute(name) ? "" : "attributes."}${name?.replaceAll(
|
|
54
|
+
".",
|
|
55
|
+
"🍺",
|
|
56
|
+
)}` as FieldPath<UserFormFields>;
|
|
54
57
|
|
|
55
58
|
export const beerify = <T extends string>(name: T) =>
|
|
56
59
|
name.replaceAll(".", "🍺");
|
|
@@ -70,13 +73,16 @@ export function setUserProfileServerError<T>(
|
|
|
70
73
|
).forEach((e) => {
|
|
71
74
|
const params = Object.assign(
|
|
72
75
|
{},
|
|
73
|
-
e.params?.map((p) =>
|
|
76
|
+
e.params?.map((p) => (isBundleKey(p.toString()) ? t(unWrap(p)) : p)),
|
|
74
77
|
);
|
|
75
78
|
setError(fieldName(e.field) as keyof T, {
|
|
76
|
-
message: t(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
message: t(
|
|
80
|
+
isBundleKey(e.errorMessage) ? unWrap(e.errorMessage) : e.errorMessage,
|
|
81
|
+
{
|
|
82
|
+
...params,
|
|
83
|
+
defaultValue: e.errorMessage || e.field,
|
|
84
|
+
},
|
|
85
|
+
),
|
|
80
86
|
type: "server",
|
|
81
87
|
});
|
|
82
88
|
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
type ComponentType,
|
|
4
|
+
type FunctionComponent,
|
|
5
|
+
type GetDerivedStateFromError,
|
|
6
|
+
type ReactNode,
|
|
7
|
+
} from "react";
|
|
8
|
+
import { createNamedContext } from "@keycloakify/keycloak-account-ui/ui-shared/utils/createNamedContext";
|
|
9
|
+
import { useRequiredContext } from "@keycloakify/keycloak-account-ui/ui-shared/utils/useRequiredContext";
|
|
10
|
+
|
|
11
|
+
export interface ErrorBoundaryContextValue {
|
|
12
|
+
error?: Error;
|
|
13
|
+
showBoundary: (error: Error) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const ErrorBoundaryContext = createNamedContext<
|
|
17
|
+
ErrorBoundaryContextValue | undefined
|
|
18
|
+
>("ErrorBoundaryContext", undefined);
|
|
19
|
+
|
|
20
|
+
export const useErrorBoundary = () => useRequiredContext(ErrorBoundaryContext);
|
|
21
|
+
|
|
22
|
+
export interface ErrorBoundaryProviderProps {
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ErrorBoundaryProviderState {
|
|
27
|
+
error?: Error;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class ErrorBoundaryProvider extends Component<
|
|
31
|
+
ErrorBoundaryProviderProps,
|
|
32
|
+
ErrorBoundaryProviderState
|
|
33
|
+
> {
|
|
34
|
+
state: ErrorBoundaryProviderState = {};
|
|
35
|
+
|
|
36
|
+
static getDerivedStateFromError: GetDerivedStateFromError<
|
|
37
|
+
ErrorBoundaryProviderProps,
|
|
38
|
+
ErrorBoundaryProviderState
|
|
39
|
+
> = (error) => {
|
|
40
|
+
return { error };
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
showBoundary = (error: Error) => {
|
|
44
|
+
this.setState({ error });
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
render() {
|
|
48
|
+
return (
|
|
49
|
+
<ErrorBoundaryContext.Provider
|
|
50
|
+
value={{ error: this.state.error, showBoundary: this.showBoundary }}
|
|
51
|
+
>
|
|
52
|
+
{this.props.children}
|
|
53
|
+
</ErrorBoundaryContext.Provider>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface FallbackProps {
|
|
59
|
+
error: Error;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ErrorBoundaryFallbackProps {
|
|
63
|
+
fallback: ComponentType<FallbackProps>;
|
|
64
|
+
children: ReactNode;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const ErrorBoundaryFallback: FunctionComponent<
|
|
68
|
+
ErrorBoundaryFallbackProps
|
|
69
|
+
> = ({ children, fallback: FallbackComponent }) => {
|
|
70
|
+
const { error } = useErrorBoundary();
|
|
71
|
+
|
|
72
|
+
if (error) {
|
|
73
|
+
return <FallbackComponent error={error} />;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return children;
|
|
77
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const DARK_MODE_CLASS = "pf-v5-theme-dark";
|
|
2
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
3
|
+
|
|
4
|
+
function updateDarkMode(isEnabled: boolean) {
|
|
5
|
+
const { classList } = document.documentElement;
|
|
6
|
+
|
|
7
|
+
if (isEnabled) {
|
|
8
|
+
classList.add(DARK_MODE_CLASS);
|
|
9
|
+
} else {
|
|
10
|
+
classList.remove(DARK_MODE_CLASS);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function initializeDarkMode() {
|
|
15
|
+
updateDarkMode(mediaQuery.matches);
|
|
16
|
+
mediaQuery.addEventListener("change", (event) =>
|
|
17
|
+
updateDarkMode(event.matches),
|
|
18
|
+
);
|
|
19
|
+
}
|