@keycloakify/keycloak-ui-shared 260007.0.5 → 260103.0.1

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.
@@ -9,9 +9,9 @@ import {
9
9
  Page,
10
10
  Text,
11
11
  TextContent,
12
- TextVariants,
13
12
  } from "../../@patternfly/react-core";
14
13
  import { useTranslation } from "react-i18next";
14
+ import { getNetworkErrorDescription } from "../utils/errors";
15
15
 
16
16
  type ErrorPageProps = {
17
17
  error?: unknown;
@@ -20,7 +20,10 @@ type ErrorPageProps = {
20
20
  export const ErrorPage = (props: ErrorPageProps) => {
21
21
  const { t } = useTranslation();
22
22
  const error = props.error;
23
- const errorMessage = getErrorMessage(error);
23
+ const errorMessage =
24
+ getErrorMessage(error) ||
25
+ getNetworkErrorDescription(error)?.replace(/\+/g, " ");
26
+ console.error(error);
24
27
 
25
28
  function onRetry() {
26
29
  location.href = location.origin + location.pathname;
@@ -30,7 +33,7 @@ export const ErrorPage = (props: ErrorPageProps) => {
30
33
  <Page>
31
34
  <Modal
32
35
  variant={ModalVariant.small}
33
- title={t("somethingWentWrong")}
36
+ title={errorMessage ? "" : t("somethingWentWrong")}
34
37
  titleIconVariant="danger"
35
38
  showClose={false}
36
39
  isOpen
@@ -41,9 +44,10 @@ export const ErrorPage = (props: ErrorPageProps) => {
41
44
  ]}
42
45
  >
43
46
  <TextContent>
44
- <Text>{t("somethingWentWrongDescription")}</Text>
45
- {errorMessage && (
46
- <Text component={TextVariants.small}>{errorMessage}</Text>
47
+ {errorMessage ? (
48
+ <Text>{t(errorMessage)}</Text>
49
+ ) : (
50
+ <Text>{t("somethingWentWrongDescription")}</Text>
47
51
  )}
48
52
  </TextContent>
49
53
  </Modal>
@@ -74,6 +74,7 @@ export const KeycloakProvider = <T extends BaseEnvironment>({
74
74
  onLoad: "check-sso",
75
75
  pkceMethod: "S256",
76
76
  responseMode: "query",
77
+ scope: environment.scope,
77
78
  });
78
79
 
79
80
  init()
@@ -83,8 +84,14 @@ export const KeycloakProvider = <T extends BaseEnvironment>({
83
84
  calledOnce.current = true;
84
85
  }, [keycloak]);
85
86
 
86
- if (error) {
87
- return <ErrorPage error={error} />;
87
+ const searchParams = new URLSearchParams(window.location.search);
88
+
89
+ if (error || searchParams.get("error_description")) {
90
+ return (
91
+ <ErrorPage
92
+ error={error ? error : searchParams.get("error_description")}
93
+ />
94
+ );
88
95
  }
89
96
 
90
97
  if (!init) {
@@ -23,6 +23,8 @@ export type BaseEnvironment = {
23
23
  logo: string;
24
24
  /** The URL to be followed when the logo is clicked. */
25
25
  logoUrl: string;
26
+ /** The scopes to be requested when sending authorization requests*/
27
+ scope?: string;
26
28
  };
27
29
 
28
30
  /**
@@ -15,6 +15,7 @@ import {
15
15
  UseControllerProps,
16
16
  useController,
17
17
  } from "react-hook-form";
18
+ import { getRuleValue } from "../utils/getRuleValue";
18
19
  import { FormLabel } from "./FormLabel";
19
20
  import { PasswordInput, PasswordInputProps } from "./PasswordInput";
20
21
 
@@ -36,7 +37,7 @@ export const PasswordControl = <
36
37
  props: PasswordControlProps<T, P>,
37
38
  ) => {
38
39
  const { labelIcon, ...rest } = props;
39
- const required = !!props.rules?.required;
40
+ const required = !!getRuleValue(props.rules?.required);
40
41
  const defaultValue = props.defaultValue ?? ("" as PathValue<T, P>);
41
42
 
42
43
  const { field, fieldState } = useController({
@@ -18,7 +18,7 @@ import {
18
18
  UseControllerProps,
19
19
  useController,
20
20
  } from "react-hook-form";
21
-
21
+ import { getRuleValue } from "../utils/getRuleValue";
22
22
  import { FormLabel } from "./FormLabel";
23
23
 
24
24
  export type TextControlProps<
@@ -31,6 +31,7 @@ export type TextControlProps<
31
31
  isDisabled?: boolean;
32
32
  helperText?: string;
33
33
  "data-testid"?: string;
34
+ type?: string;
34
35
  };
35
36
 
36
37
  export const TextControl = <
@@ -40,7 +41,7 @@ export const TextControl = <
40
41
  props: TextControlProps<T, P>,
41
42
  ) => {
42
43
  const { labelIcon, helperText, ...rest } = props;
43
- const required = !!props.rules?.required;
44
+ const required = !!getRuleValue(props.rules?.required);
44
45
  const defaultValue = props.defaultValue ?? ("" as PathValue<T, P>);
45
46
 
46
47
  const { field, fieldState } = useController({
@@ -98,7 +98,7 @@ export const SingleSelectControl = <
98
98
  }}
99
99
  isOpen={open}
100
100
  >
101
- <SelectList>
101
+ <SelectList data-testid={`select-${name}`}>
102
102
  {options.map((option) => (
103
103
  <SelectOption key={key(option)} value={key(option)}>
104
104
  {isString(option) ? option : option.value}
@@ -63,6 +63,7 @@ export const TypeaheadSelectControl = <
63
63
  const [focusedItemIndex, setFocusedItemIndex] = useState<number>(0);
64
64
  const textInputRef = useRef<HTMLInputElement>();
65
65
  const required = getRuleValue(controller.rules?.required) === true;
66
+ const isTypeaheadMulti = variant === SelectVariant.typeaheadMulti;
66
67
 
67
68
  const filteredOptions = options.filter((option) =>
68
69
  getValue(option).toLowerCase().startsWith(filterValue.toLowerCase()),
@@ -93,7 +94,7 @@ export const TypeaheadSelectControl = <
93
94
  case "Enter": {
94
95
  event.preventDefault();
95
96
 
96
- if (variant !== SelectVariant.typeaheadMulti) {
97
+ if (!isTypeaheadMulti) {
97
98
  setFilterValue(getValue(focusedItem));
98
99
  } else {
99
100
  setFilterValue("");
@@ -164,7 +165,6 @@ export const TypeaheadSelectControl = <
164
165
  render={({ field }) => (
165
166
  <Select
166
167
  {...rest}
167
- onClick={() => setOpen(!open)}
168
168
  onOpenChange={() => setOpen(false)}
169
169
  selected={
170
170
  isSelectBasedOptions(options)
@@ -177,12 +177,16 @@ export const TypeaheadSelectControl = <
177
177
  .map((o) => o.value)
178
178
  : field.value
179
179
  }
180
+ shouldFocusFirstItemOnOpen={false}
180
181
  toggle={(ref) => (
181
182
  <MenuToggle
182
183
  ref={ref}
183
184
  id={id || name.slice(name.lastIndexOf(".") + 1)}
184
185
  variant="typeahead"
185
- onClick={() => setOpen(!open)}
186
+ onClick={() => {
187
+ setOpen(!open);
188
+ textInputRef.current?.focus();
189
+ }}
186
190
  isExpanded={open}
187
191
  isFullWidth
188
192
  status={get(errors, name) ? MenuToggleStatus.danger : undefined}
@@ -247,7 +251,7 @@ export const TypeaheadSelectControl = <
247
251
  variant="plain"
248
252
  onClick={() => {
249
253
  setFilterValue("");
250
- field.onChange("");
254
+ field.onChange(isTypeaheadMulti ? [] : "");
251
255
  textInputRef?.current?.focus();
252
256
  }}
253
257
  aria-label="Clear input value"
@@ -262,10 +266,7 @@ export const TypeaheadSelectControl = <
262
266
  onSelect={(event, v) => {
263
267
  event?.stopPropagation();
264
268
  const option = v?.toString();
265
- if (
266
- variant === SelectVariant.typeaheadMulti &&
267
- Array.isArray(field.value)
268
- ) {
269
+ if (isTypeaheadMulti && Array.isArray(field.value)) {
269
270
  if (field.value.includes(option)) {
270
271
  field.onChange(
271
272
  field.value.filter((item: string) => item !== option),
@@ -3,6 +3,7 @@
3
3
  // @ts-nocheck
4
4
 
5
5
  import { Button, ButtonVariant, ToolbarItem } from "../../../@patternfly/react-core";
6
+ import { SyncAltIcon } from "../../../@patternfly/react-icons";
6
7
  import type { SVGIconProps } from "@patternfly/react-icons/dist/js/createIcon";
7
8
  import {
8
9
  ActionsColumn,
@@ -23,7 +24,7 @@ import {
23
24
  Thead,
24
25
  Tr,
25
26
  } from "../../../@patternfly/react-table";
26
- import { cloneDeep, differenceBy, get } from "lodash-es";
27
+ import { cloneDeep, get, intersectionBy } from "lodash-es";
27
28
  import {
28
29
  ComponentClass,
29
30
  ReactNode,
@@ -36,13 +37,11 @@ import {
36
37
  type JSX,
37
38
  } from "react";
38
39
  import { useTranslation } from "react-i18next";
39
-
40
- import { useStoredState } from "../../utils/useStoredState";
41
40
  import { useFetch } from "../../utils/useFetch";
41
+ import { useStoredState } from "../../utils/useStoredState";
42
+ import { KeycloakSpinner } from "../KeycloakSpinner";
42
43
  import { ListEmptyState } from "./ListEmptyState";
43
44
  import { PaginatingTableToolbar } from "./PaginatingTableToolbar";
44
- import { SyncAltIcon } from "../../../@patternfly/react-icons";
45
- import { KeycloakSpinner } from "../KeycloakSpinner";
46
45
 
47
46
  type TitleCell = { title: JSX.Element };
48
47
  type Cell<T> = keyof T | JSX.Element | TitleCell;
@@ -69,24 +68,50 @@ type DataTableProps<T> = {
69
68
  rows: (Row<T> | SubRow<T>)[];
70
69
  actions?: IActions;
71
70
  actionResolver?: IActionsResolver;
72
- onSelect?: (isSelected: boolean, rowIndex: number) => void;
71
+ selected?: T[];
72
+ onSelect?: (value: T[]) => void;
73
73
  onCollapse?: (isOpen: boolean, rowIndex: number) => void;
74
74
  canSelectAll: boolean;
75
+ canSelect: boolean;
75
76
  isNotCompact?: boolean;
76
77
  isRadio?: boolean;
77
78
  };
78
79
 
79
80
  type CellRendererProps = {
80
81
  row: IRow;
82
+ index?: number;
83
+ actions?: IActions;
84
+ actionResolver?: IActionsResolver;
81
85
  };
82
86
 
83
- const CellRenderer = ({ row }: CellRendererProps) => {
84
- const isRow = (c: ReactNode | IRowCell): c is IRowCell =>
85
- !!c && (c as IRowCell).title !== undefined;
86
- return row.cells!.map((c, i) => (
87
- <Td key={`cell-${i}`}>{(isRow(c) ? c.title : c) as ReactNode}</Td>
87
+ const isRow = (c: ReactNode | IRowCell): c is IRowCell =>
88
+ !!c && (c as IRowCell).title !== undefined;
89
+
90
+ const CellRenderer = ({
91
+ row,
92
+ index,
93
+ actions,
94
+ actionResolver,
95
+ }: CellRendererProps) => (
96
+ <>
97
+ {row.cells!.map((c, i) => (
98
+ <Td key={`cell-${i}`}>{(isRow(c) ? c.title : c) as ReactNode}</Td>
99
+ ))}
100
+ {(actions || actionResolver) && (
101
+ <Td isActionCell>
102
+ <ActionsColumn
103
+ items={actions || actionResolver?.(row, {})!}
104
+ extraData={{ rowIndex: index }}
105
+ />
106
+ </Td>
107
+ )}
108
+ </>
109
+ );
110
+
111
+ const ExpandableRowRenderer = ({ row }: CellRendererProps) =>
112
+ row.cells!.map((c, i) => (
113
+ <div key={`cell-${i}`}>{(isRow(c) ? c.title : c) as ReactNode}</div>
88
114
  ));
89
- };
90
115
 
91
116
  function DataTable<T>({
92
117
  columns,
@@ -94,37 +119,68 @@ function DataTable<T>({
94
119
  actions,
95
120
  actionResolver,
96
121
  ariaLabelKey,
122
+ selected,
97
123
  onSelect,
98
124
  onCollapse,
99
125
  canSelectAll,
126
+ canSelect,
100
127
  isNotCompact,
101
128
  isRadio,
102
129
  ...props
103
130
  }: DataTableProps<T>) {
104
131
  const { t } = useTranslation();
105
-
106
- const [selectedRows, setSelectedRows] = useState<boolean[]>([]);
132
+ const [selectedRows, setSelectedRows] = useState<T[]>(selected || []);
107
133
  const [expandedRows, setExpandedRows] = useState<boolean[]>([]);
108
134
 
109
- const updateState = (rowIndex: number, isSelected: boolean) => {
110
- const items = [
111
- ...(rowIndex === -1 ? Array(rows.length).fill(isSelected) : selectedRows),
112
- ];
113
- items[rowIndex] = isSelected;
114
- setSelectedRows(items);
115
- };
135
+ const rowsSelectedOnPage = useMemo(
136
+ () =>
137
+ intersectionBy(
138
+ selectedRows,
139
+ rows.map((row) => row.data),
140
+ "id",
141
+ ),
142
+ [selectedRows, rows],
143
+ );
116
144
 
117
145
  useEffect(() => {
118
146
  if (canSelectAll) {
119
147
  const selectAllCheckbox = document.getElementsByName("check-all").item(0);
120
148
  if (selectAllCheckbox) {
121
149
  const checkbox = selectAllCheckbox as HTMLInputElement;
122
- const selected = selectedRows.filter((r) => r === true);
123
150
  checkbox.indeterminate =
124
- selected.length < rows.length && selected.length > 0;
151
+ rowsSelectedOnPage.length < rows.length &&
152
+ rowsSelectedOnPage.length > 0;
125
153
  }
126
154
  }
127
- }, [selectedRows]);
155
+ }, [selectedRows, canSelectAll, rows]);
156
+
157
+ const updateSelectedRows = (selected: T[]) => {
158
+ setSelectedRows(selected);
159
+ onSelect?.(selected);
160
+ };
161
+
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
+ );
172
+ } else {
173
+ if (isSelected) {
174
+ updateSelectedRows([...selectedRows, rows[rowIndex].data]);
175
+ } else {
176
+ updateSelectedRows(
177
+ selectedRows.filter(
178
+ (v) => get(v, "id") !== (rows[rowIndex] as IRow).data.id,
179
+ ),
180
+ );
181
+ }
182
+ }
183
+ };
128
184
 
129
185
  return (
130
186
  <Table
@@ -134,19 +190,17 @@ function DataTable<T>({
134
190
  >
135
191
  <Thead>
136
192
  <Tr>
137
- {onCollapse && <Th />}
193
+ {onCollapse && <Th screenReaderText={t("expandRow")} />}
138
194
  {canSelectAll && (
139
195
  <Th
196
+ screenReaderText={t("selectAll")}
140
197
  select={
141
198
  !isRadio
142
199
  ? {
143
- onSelect: (_, isSelected, rowIndex) => {
144
- onSelect!(isSelected, rowIndex);
200
+ onSelect: (_, isSelected) => {
145
201
  updateState(-1, isSelected);
146
202
  },
147
- isSelected:
148
- selectedRows.filter((r) => r === true).length ===
149
- rows.length,
203
+ isSelected: rowsSelectedOnPage.length === rows.length,
150
204
  }
151
205
  : undefined
152
206
  }
@@ -154,7 +208,8 @@ function DataTable<T>({
154
208
  )}
155
209
  {columns.map((column) => (
156
210
  <Th
157
- key={column.displayKey}
211
+ screenReaderText={t("expandRow")}
212
+ key={column.displayKey || column.name}
158
213
  className={column.transforms?.[0]().className}
159
214
  >
160
215
  {t(column.displayKey || column.name)}
@@ -166,28 +221,26 @@ function DataTable<T>({
166
221
  <Tbody>
167
222
  {(rows as IRow[]).map((row, index) => (
168
223
  <Tr key={index} isExpanded={expandedRows[index]}>
169
- {onSelect && (
224
+ {canSelect && (
170
225
  <Td
171
226
  select={{
172
227
  rowIndex: index,
173
228
  onSelect: (_, isSelected, rowIndex) => {
174
- onSelect!(isSelected, rowIndex);
175
229
  updateState(rowIndex, isSelected);
176
230
  },
177
- isSelected: selectedRows[index],
231
+ isSelected: !!selectedRows.find(
232
+ (v) => get(v, "id") === row.data.id,
233
+ ),
178
234
  variant: isRadio ? "radio" : "checkbox",
179
235
  }}
180
236
  />
181
237
  )}
182
- <CellRenderer row={row} />
183
- {(actions || actionResolver) && (
184
- <Td isActionCell>
185
- <ActionsColumn
186
- items={actions || actionResolver?.(row, {})!}
187
- extraData={{ rowIndex: index }}
188
- />
189
- </Td>
190
- )}
238
+ <CellRenderer
239
+ row={row}
240
+ index={index}
241
+ actions={actions}
242
+ actionResolver={actionResolver}
243
+ />
191
244
  </Tr>
192
245
  ))}
193
246
  </Tbody>
@@ -209,14 +262,19 @@ function DataTable<T>({
209
262
  },
210
263
  }}
211
264
  />
212
- <CellRenderer row={row} />
265
+ <CellRenderer
266
+ row={row}
267
+ index={index}
268
+ actions={actions}
269
+ actionResolver={actionResolver}
270
+ />
213
271
  </Tr>
214
272
  ) : (
215
273
  <Tr isExpanded={!!expandedRows[index - 1]}>
216
274
  <Td />
217
275
  <Td colSpan={columns.length}>
218
276
  <ExpandableRowContent>
219
- <CellRenderer row={row} />
277
+ <ExpandableRowRenderer row={row} />
220
278
  </ExpandableRowContent>
221
279
  </Td>
222
280
  </Tr>
@@ -480,38 +538,6 @@ export function KeycloakDataTable<T>({
480
538
  return action;
481
539
  });
482
540
 
483
- const _onSelect = (isSelected: boolean, rowIndex: number) => {
484
- const data = filteredData || rows;
485
- if (rowIndex === -1) {
486
- setRows(
487
- data!.map((row) => {
488
- (row as Row<T>).selected = isSelected;
489
- return row;
490
- }),
491
- );
492
- } else {
493
- (data![rowIndex] as Row<T>).selected = isSelected;
494
-
495
- setRows([...rows!]);
496
- }
497
-
498
- // Keeps selected items when paginating
499
- const difference = differenceBy(
500
- selected,
501
- data!.map((row) => row.data),
502
- "id",
503
- );
504
-
505
- // Selected rows are any rows previously selected from a different page, plus current page selections
506
- const selectedRows = [
507
- ...difference,
508
- ...data!.filter((row) => (row as Row<T>).selected).map((row) => row.data),
509
- ];
510
-
511
- setSelected(selectedRows);
512
- onSelect!(selectedRows);
513
- };
514
-
515
541
  const onCollapse = (isOpen: boolean, rowIndex: number) => {
516
542
  (data![rowIndex] as Row<T>).isOpen = isOpen;
517
543
  setRows([...data!]);
@@ -561,7 +587,12 @@ export function KeycloakDataTable<T>({
561
587
  <DataTable
562
588
  {...props}
563
589
  canSelectAll={canSelectAll}
564
- onSelect={onSelect ? _onSelect : undefined}
590
+ canSelect={!!onSelect}
591
+ selected={selected}
592
+ onSelect={(selected) => {
593
+ setSelected(selected);
594
+ onSelect?.(selected);
595
+ }}
565
596
  onCollapse={detailColumns ? onCollapse : undefined}
566
597
  actions={convertAction()}
567
598
  actionResolver={actionResolver}
@@ -36,19 +36,14 @@ export const TableToolbar = ({
36
36
  const { t } = useTranslation();
37
37
  const [searchValue, setSearchValue] = useState<string>("");
38
38
 
39
- const onSearch = () => {
40
- if (searchValue !== "") {
41
- setSearchValue(searchValue);
42
- inputGroupOnEnter?.(searchValue);
43
- } else {
44
- setSearchValue("");
45
- inputGroupOnEnter?.("");
46
- }
39
+ const onSearch = (searchValue: string) => {
40
+ setSearchValue(searchValue.trim());
41
+ inputGroupOnEnter?.(searchValue.trim());
47
42
  };
48
43
 
49
44
  const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
50
45
  if (e.key === "Enter") {
51
- onSearch();
46
+ onSearch(searchValue);
52
47
  }
53
48
  };
54
49
 
@@ -69,12 +64,9 @@ export const TableToolbar = ({
69
64
  onChange={(_, value) => {
70
65
  setSearchValue(value);
71
66
  }}
72
- onSearch={onSearch}
67
+ onSearch={() => onSearch(searchValue)}
73
68
  onKeyDown={handleKeyDown}
74
- onClear={() => {
75
- setSearchValue("");
76
- inputGroupOnEnter?.("");
77
- }}
69
+ onClear={() => onSearch("")}
78
70
  />
79
71
  )}
80
72
  </InputGroup>
@@ -46,7 +46,6 @@ function getIcon(icon: string) {
46
46
  case "linkedin-openid-connect":
47
47
  return LinkedinIcon;
48
48
 
49
- case "openshift-v3":
50
49
  case "openshift-v4":
51
50
  return OpenshiftIcon;
52
51
  case "stackoverflow":
@@ -97,4 +97,3 @@ export {
97
97
  } from "./utils/ErrorBoundary";
98
98
  export type { FallbackProps } from "./utils/ErrorBoundary";
99
99
  export { OrganizationTable } from "./controls/OrganizationTable";
100
- export { initializeDarkMode } from "./utils/darkMode";
@@ -86,7 +86,7 @@ export const SelectComponent = (props: UserProfileFieldProps) => {
86
86
  }}
87
87
  selections={
88
88
  isMultiValue && Array.isArray(field.value)
89
- ? field.value
89
+ ? field.value.map((option) => fetchLabel(option))
90
90
  : fetchLabel(field.value)
91
91
  }
92
92
  variant={
@@ -21,12 +21,18 @@ export const TextComponent = (props: UserProfileFieldProps) => {
21
21
  id={attribute.name}
22
22
  data-testid={attribute.name}
23
23
  type={type}
24
- placeholder={label(
25
- props.t,
26
- attribute.annotations?.["inputTypePlaceholder"] as string,
27
- attribute.name,
28
- attribute.annotations?.["inputOptionLabelsI18nPrefix"] as string,
29
- )}
24
+ placeholder={
25
+ attribute.readOnly
26
+ ? ""
27
+ : label(
28
+ props.t,
29
+ attribute.annotations?.["inputTypePlaceholder"] as string,
30
+ attribute.name,
31
+ attribute.annotations?.[
32
+ "inputOptionLabelsI18nPrefix"
33
+ ] as string,
34
+ )
35
+ }
30
36
  readOnly={attribute.readOnly}
31
37
  isRequired={isRequired}
32
38
  {...form.register(fieldName(attribute.name))}
@@ -94,34 +94,9 @@ export function setUserProfileServerError<T>(
94
94
 
95
95
  export function isRequiredAttribute({
96
96
  required,
97
- validators,
98
97
  }: UserProfileAttributeMetadata): boolean {
99
- // Check if required is true or if the validators include a validation that would make the attribute implicitly required.
100
- return required || hasRequiredValidators(validators);
101
- }
102
-
103
- /**
104
- * Checks whether the given validators include a validation that would make the attribute implicitly required.
105
- */
106
- function hasRequiredValidators(
107
- validators?: UserProfileAttributeMetadata["validators"],
108
- ): boolean {
109
- // If we don't have any validators, the attribute is not required.
110
- if (!validators) {
111
- return false;
112
- }
113
-
114
- // If the 'length' validator is defined and has a minimal length greater than zero the attribute is implicitly required.
115
- // We have to do a lot of defensive coding here, because we don't have type information for the validators.
116
- if (
117
- "length" in validators &&
118
- "min" in validators.length &&
119
- typeof validators.length.min === "number"
120
- ) {
121
- return validators.length.min > 0;
122
- }
123
-
124
- return false;
98
+ // Check if required is true
99
+ return required as boolean;
125
100
  }
126
101
 
127
102
  export function isUserProfileError(error: unknown): error is UserProfileError {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keycloakify/keycloak-ui-shared",
3
- "version": "260007.0.5",
3
+ "version": "260103.0.1",
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.1",
13
- "@patternfly/react-icons": "^5.4.0",
14
- "@patternfly/react-styles": "^5.4.0",
15
- "@patternfly/react-table": "^5.4.1",
16
- "i18next": "^23.15.1",
12
+ "@patternfly/react-core": "^5.4.10",
13
+ "@patternfly/react-icons": "^5.4.2",
14
+ "@patternfly/react-styles": "^5.4.1",
15
+ "@patternfly/react-table": "^5.4.13",
16
+ "i18next": "^24.2.1",
17
17
  "lodash-es": "^4.17.21",
18
18
  "react": "^18.3.1",
19
- "react-hook-form": "7.53.0",
20
- "react-i18next": "^15.0.2",
21
- "@keycloak/keycloak-admin-client": "26.0.7",
22
- "keycloak-js": "26.0.7",
19
+ "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",
23
23
  "@types/lodash-es": "^4.17.12",
24
- "@types/react": "^18.3.11"
24
+ "@types/react": "^18.3.13"
25
25
  },
26
26
  "publishConfig": {
27
27
  "access": "public"