@keycloakify/keycloak-ui-shared 260103.0.1 → 260200.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.
@@ -67,6 +67,7 @@ export type OrganizationTableProps = PropsWithChildren & {
67
67
  toolbarItem?: ReactNode;
68
68
  isPaginated?: boolean;
69
69
  isSearching?: boolean;
70
+ searchPlaceholderKey?: string;
70
71
  onSelect?: (orgs: OrganizationRepresentation[]) => void;
71
72
  onDelete?: (org: OrganizationRepresentation) => void;
72
73
  deleteLabel?: string;
@@ -77,6 +78,7 @@ export const OrganizationTable = ({
77
78
  toolbarItem,
78
79
  isPaginated = false,
79
80
  isSearching = false,
81
+ searchPlaceholderKey,
80
82
  onSelect,
81
83
  onDelete,
82
84
  deleteLabel = "delete",
@@ -91,6 +93,7 @@ export const OrganizationTable = ({
91
93
  isPaginated={isPaginated}
92
94
  isSearching={isSearching}
93
95
  ariaLabelKey="organizationList"
96
+ searchPlaceholderKey={searchPlaceholderKey}
94
97
  toolbarItem={toolbarItem}
95
98
  onSelect={onSelect}
96
99
  canSelectAll={onSelect !== undefined}
@@ -12,6 +12,7 @@ import {
12
12
  } from "react-hook-form";
13
13
  import { SwitchProps, Switch } from "../../@patternfly/react-core";
14
14
  import { FormLabel } from "./FormLabel";
15
+ import { debeerify } from "../user-profile/utils";
15
16
 
16
17
  export type SwitchControlProps<
17
18
  T extends FieldValues,
@@ -55,7 +56,7 @@ export const SwitchControl = <
55
56
  <Switch
56
57
  {...props}
57
58
  id={props.name}
58
- data-testid={props.name}
59
+ data-testid={debeerify(props.name)}
59
60
  label={labelOn}
60
61
  isChecked={stringify ? value === "true" : value}
61
62
  onChange={(e, checked) => {
@@ -57,6 +57,7 @@ export const TextAreaControl = <
57
57
  fieldState.error ? ValidatedOptions.error : ValidatedOptions.default
58
58
  }
59
59
  isDisabled={props.isDisabled}
60
+ {...props}
60
61
  {...field}
61
62
  />
62
63
  </FormLabel>
@@ -53,6 +53,10 @@ export type SelectControlProps<
53
53
  menuAppendTo?: string;
54
54
  placeholderText?: string;
55
55
  chipGroupProps?: ChipGroupProps;
56
+ onSelect?: (
57
+ value: string | string[],
58
+ onChangeHandler: (value: string | string[]) => void,
59
+ ) => void;
56
60
  };
57
61
 
58
62
  export const isSelectBasedOptions = (
@@ -36,6 +36,8 @@ export const SingleSelectControl = <
36
36
  options,
37
37
  controller,
38
38
  labelIcon,
39
+ isDisabled,
40
+ onSelect,
39
41
  ...rest
40
42
  }: SelectControlProps<T, P>) => {
41
43
  const {
@@ -60,6 +62,7 @@ export const SingleSelectControl = <
60
62
  render={({ field: { onChange, value } }) => (
61
63
  <Select
62
64
  {...rest}
65
+ variant="default"
63
66
  onClick={() => setOpen(!open)}
64
67
  onOpenChange={() => setOpen(false)}
65
68
  selected={
@@ -81,7 +84,8 @@ export const SingleSelectControl = <
81
84
  isExpanded={open}
82
85
  isFullWidth
83
86
  status={get(errors, name) ? MenuToggleStatus.danger : undefined}
84
- aria-label="toggle"
87
+ aria-label={label}
88
+ isDisabled={isDisabled}
85
89
  >
86
90
  {isSelectBasedOptions(options)
87
91
  ? options.find(
@@ -92,8 +96,13 @@ export const SingleSelectControl = <
92
96
  </MenuToggle>
93
97
  )}
94
98
  onSelect={(_event, v) => {
95
- const option = v?.toString();
96
- onChange(Array.isArray(value) ? [option] : option);
99
+ const option = v?.toString()!;
100
+ const convertedValue = Array.isArray(value) ? [option] : option;
101
+ if (onSelect) {
102
+ onSelect(convertedValue, onChange);
103
+ } else {
104
+ onChange(convertedValue);
105
+ }
97
106
  setOpen(false);
98
107
  }}
99
108
  isOpen={open}
@@ -160,24 +160,31 @@ function DataTable<T>({
160
160
  };
161
161
 
162
162
  const updateState = (rowIndex: number, isSelected: boolean) => {
163
- if (rowIndex === -1) {
164
- const rowsSelectedOnPageIds = rowsSelectedOnPage.map((v) => get(v, "id"));
165
- updateSelectedRows(
166
- isSelected
167
- ? [...selectedRows, ...rows.map((row) => row.data)]
168
- : selectedRows.filter(
169
- (v) => !rowsSelectedOnPageIds.includes(get(v, "id")),
170
- ),
171
- );
163
+ if (isRadio) {
164
+ const selectedRow = isSelected ? [rows[rowIndex].data] : [];
165
+ updateSelectedRows(selectedRow);
172
166
  } else {
173
- if (isSelected) {
174
- updateSelectedRows([...selectedRows, rows[rowIndex].data]);
175
- } else {
167
+ if (rowIndex === -1) {
168
+ const rowsSelectedOnPageIds = rowsSelectedOnPage.map((v) =>
169
+ get(v, "id"),
170
+ );
176
171
  updateSelectedRows(
177
- selectedRows.filter(
178
- (v) => get(v, "id") !== (rows[rowIndex] as IRow).data.id,
179
- ),
172
+ isSelected
173
+ ? [...selectedRows, ...rows.map((row) => row.data)]
174
+ : selectedRows.filter(
175
+ (v) => !rowsSelectedOnPageIds.includes(get(v, "id")),
176
+ ),
180
177
  );
178
+ } else {
179
+ if (isSelected) {
180
+ updateSelectedRows([...selectedRows, rows[rowIndex].data]);
181
+ } else {
182
+ updateSelectedRows(
183
+ selectedRows.filter(
184
+ (v) => get(v, "id") !== (rows[rowIndex] as IRow).data.id,
185
+ ),
186
+ );
187
+ }
181
188
  }
182
189
  }
183
190
  };
@@ -250,17 +257,21 @@ function DataTable<T>({
250
257
  {index % 2 === 0 ? (
251
258
  <Tr>
252
259
  <Td
253
- expand={{
254
- isExpanded: !!expandedRows[index],
255
- rowIndex: index,
256
- expandId: `${index}`,
257
- onToggle: (_, rowIndex, isOpen) => {
258
- onCollapse(isOpen, rowIndex);
259
- const expand = [...expandedRows];
260
- expand[index] = isOpen;
261
- setExpandedRows(expand);
262
- },
263
- }}
260
+ expand={
261
+ rows[index + 1].cells.length === 0
262
+ ? undefined
263
+ : {
264
+ isExpanded: !!expandedRows[index],
265
+ rowIndex: index,
266
+ expandId: "expandable-row-",
267
+ onToggle: (_, rowIndex, isOpen) => {
268
+ onCollapse(isOpen, rowIndex);
269
+ const expand = [...expandedRows];
270
+ expand[index] = isOpen;
271
+ setExpandedRows(expand);
272
+ },
273
+ }
274
+ }
264
275
  />
265
276
  <CellRenderer
266
277
  row={row}
@@ -552,7 +563,7 @@ export function KeycloakDataTable<T>({
552
563
 
553
564
  return (
554
565
  <>
555
- {(loading || !noData || searching) && (
566
+ {(!noData || searching) && (
556
567
  <PaginatingTableToolbar
557
568
  id={id}
558
569
  count={rowLength}
@@ -575,7 +586,7 @@ export function KeycloakDataTable<T>({
575
586
  <>
576
587
  {toolbarItem} <ToolbarItem variant="separator" />{" "}
577
588
  <ToolbarItem>
578
- <Button variant="link" onClick={refresh}>
589
+ <Button variant="link" onClick={refresh} data-testid="refresh">
579
590
  <SyncAltIcon /> {t("refresh")}
580
591
  </Button>
581
592
  </ToolbarItem>
@@ -623,9 +634,9 @@ export function KeycloakDataTable<T>({
623
634
  }
624
635
  />
625
636
  )}
626
- {loading && <KeycloakSpinner />}
627
637
  </PaginatingTableToolbar>
628
638
  )}
639
+ {loading && <KeycloakSpinner />}
629
640
  {!loading && noData && !searching && emptyState}
630
641
  </>
631
642
  );
@@ -49,7 +49,7 @@ export const TableToolbar = ({
49
49
 
50
50
  return (
51
51
  <>
52
- <Toolbar>
52
+ <Toolbar data-testid="table-toolbar">
53
53
  <ToolbarContent>
54
54
  {inputGroupName && (
55
55
  <ToolbarItem>
@@ -58,6 +58,7 @@ type KeycloakMastheadProps = MastheadMainProps & {
58
58
  kebabDropdownItems?: ReactNode[];
59
59
  dropdownItems?: ReactNode[];
60
60
  toolbarItems?: ReactNode[];
61
+ toolbar?: ReactNode;
61
62
  };
62
63
 
63
64
  const KeycloakMasthead = ({
@@ -72,6 +73,7 @@ const KeycloakMasthead = ({
72
73
  kebabDropdownItems,
73
74
  dropdownItems = [],
74
75
  toolbarItems,
76
+ toolbar,
75
77
  ...rest
76
78
  }: KeycloakMastheadProps) => {
77
79
  const { t } = useTranslation();
@@ -106,6 +108,7 @@ const KeycloakMasthead = ({
106
108
  <img src={src} alt={alt} className={className} />
107
109
  </MastheadBrand>
108
110
  <MastheadContent>
111
+ {toolbar}
109
112
  <Toolbar>
110
113
  <ToolbarContent>
111
114
  {toolbarItems?.map((item, index) => (
@@ -19,7 +19,7 @@ export const propertyToString = (prop: string | number | undefined) =>
19
19
 
20
20
  export type KeycloakSelectProps = Omit<
21
21
  SelectProps,
22
- "name" | "toggle" | "selected" | "onClick" | "onSelect"
22
+ "name" | "toggle" | "selected" | "onClick" | "onSelect" | "variant"
23
23
  > & {
24
24
  toggleId?: string;
25
25
  onFilter?: (value: string) => JSX.Element[];
@@ -119,7 +119,11 @@ export const TypeaheadSelect = ({
119
119
  {...rest}
120
120
  onClick={toggle}
121
121
  onOpenChange={(isOpen) => onToggle?.(isOpen)}
122
- onSelect={(_, value) => onSelect?.(value || "")}
122
+ onSelect={(_, value) => {
123
+ onSelect?.(value || "");
124
+ onFilter?.("");
125
+ setFilterValue("");
126
+ }}
123
127
  maxMenuHeight={propertyToString(maxHeight)}
124
128
  popperProps={{ direction, width: propertyToString(width) }}
125
129
  toggle={(ref) => (
@@ -47,7 +47,7 @@ export const LocaleSelector = ({
47
47
  name="attributes.locale"
48
48
  label={t("selectALocale")}
49
49
  controller={{ defaultValue: "" }}
50
- options={locales}
50
+ options={[{ key: "", value: t("defaultLocale") }, ...locales]}
51
51
  variant={locales.length >= 10 ? "typeahead" : "single"}
52
52
  />
53
53
  </FormProvider>
@@ -27,7 +27,7 @@ export const TextComponent = (props: UserProfileFieldProps) => {
27
27
  : label(
28
28
  props.t,
29
29
  attribute.annotations?.["inputTypePlaceholder"] as string,
30
- attribute.name,
30
+ "",
31
31
  attribute.annotations?.[
32
32
  "inputOptionLabelsI18nPrefix"
33
33
  ] as string,
@@ -77,12 +77,13 @@ export function setUserProfileServerError<T>(
77
77
  ).forEach((e) => {
78
78
  const params = Object.assign(
79
79
  {},
80
- e.params?.map((p) => (isBundleKey(p.toString()) ? t(unWrap(p)) : p)),
80
+ e.params?.map((p) => (isBundleKey(p?.toString()) ? t(unWrap(p)) : p)),
81
81
  );
82
82
  setError(fieldName(e.field) as keyof T, {
83
83
  message: t(
84
84
  isBundleKey(e.errorMessage) ? unWrap(e.errorMessage) : e.errorMessage,
85
85
  {
86
+ /* eslint-disable @typescript-eslint/no-misused-spread */
86
87
  ...params,
87
88
  defaultValue: e.errorMessage || e.field,
88
89
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keycloakify/keycloak-ui-shared",
3
- "version": "260103.0.1",
3
+ "version": "260200.0.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git://github.com/keycloakify/keycloak-ui-shared.git"
@@ -9,19 +9,19 @@
9
9
  "author": "The Keycloak Team, re-packaged by u/garronej",
10
10
  "homepage": "https://github.com/keycloakify/keycloak-ui-shared",
11
11
  "peerDependencies": {
12
- "@patternfly/react-core": "^5.4.10",
12
+ "@patternfly/react-core": "^5.4.14",
13
13
  "@patternfly/react-icons": "^5.4.2",
14
14
  "@patternfly/react-styles": "^5.4.1",
15
- "@patternfly/react-table": "^5.4.13",
16
- "i18next": "^24.2.1",
15
+ "@patternfly/react-table": "^5.4.16",
16
+ "i18next": "^24.2.3",
17
+ "keycloak-js": "^26.2.0",
17
18
  "lodash-es": "^4.17.21",
18
19
  "react": "^18.3.1",
19
20
  "react-hook-form": "7.54.2",
20
- "react-i18next": "^15.4.0",
21
- "@keycloak/keycloak-admin-client": "26.1.3",
22
- "keycloak-js": "26.1.3",
21
+ "react-i18next": "^15.4.1",
22
+ "@keycloak/keycloak-admin-client": "26.2.0",
23
23
  "@types/lodash-es": "^4.17.12",
24
- "@types/react": "^18.3.13"
24
+ "@types/react": "^18.3.18"
25
25
  },
26
26
  "publishConfig": {
27
27
  "access": "public"