@scm-manager/ui-core 4.0.0-REACT18-20250701-125025 → 4.0.0-REACT19

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.
Files changed (56) hide show
  1. package/{src/base/buttons/a11y.test.ts → .storybook/i18n.ts} +27 -9
  2. package/.storybook/main.ts +54 -0
  3. package/.storybook/preview-head.html +6 -1
  4. package/.storybook/preview.tsx +125 -0
  5. package/.turbo/turbo-test.log +164 -0
  6. package/.turbo/turbo-typecheck.log +3 -2
  7. package/package.json +37 -42
  8. package/src/base/buttons/Button.stories.tsx +179 -70
  9. package/src/base/buttons/Button.tsx +9 -9
  10. package/src/base/forms/AddListEntryForm.tsx +8 -8
  11. package/src/base/forms/ConfigurationForm.tsx +14 -5
  12. package/src/base/forms/Form.stories.tsx +599 -289
  13. package/src/base/forms/Form.tsx +8 -8
  14. package/src/base/forms/FormPathContext.tsx +3 -3
  15. package/src/base/forms/ScmFormContext.tsx +7 -4
  16. package/src/base/forms/ScmFormListContext.tsx +2 -1
  17. package/src/base/forms/base/Field.tsx +1 -1
  18. package/src/base/forms/base/label/Label.tsx +1 -1
  19. package/src/base/forms/chip-input/ChipInputField.stories.tsx +109 -28
  20. package/src/base/forms/chip-input/ChipInputField.tsx +20 -8
  21. package/src/base/forms/chip-input/ControlledChipInputField.tsx +3 -1
  22. package/src/base/forms/combobox/Combobox.stories.tsx +216 -89
  23. package/src/base/forms/combobox/Combobox.tsx +4 -2
  24. package/src/base/forms/combobox/ComboboxField.tsx +2 -1
  25. package/src/base/forms/headless-chip-input/ChipInput.tsx +9 -9
  26. package/src/base/forms/helpers.ts +12 -9
  27. package/src/base/forms/input/ControlledSecretConfirmationField.tsx +4 -2
  28. package/src/base/forms/radio-button/RadioButton.stories.tsx +317 -124
  29. package/src/base/forms/radio-button/RadioButton.tsx +8 -4
  30. package/src/base/forms/radio-button/RadioButtonContext.tsx +2 -1
  31. package/src/base/forms/table/ControlledColumn.tsx +1 -1
  32. package/src/base/forms/table/ControlledTable.tsx +12 -4
  33. package/src/base/helpers/useDocumentTitle.test.ts +15 -7
  34. package/src/base/layout/card/Card.stories.tsx +171 -72
  35. package/src/base/layout/card/Card.tsx +4 -4
  36. package/src/base/layout/card/CardDetail.tsx +2 -3
  37. package/src/base/layout/card-list/CardList.stories.tsx +283 -169
  38. package/src/base/layout/collapsible/Collapsible.stories.tsx +54 -16
  39. package/src/base/layout/index.ts +2 -5
  40. package/src/base/layout/tabs/Tabs.stories.tsx +58 -16
  41. package/src/base/layout/templates/data-page/DataPage.stories.tsx +289 -156
  42. package/src/base/layout/templates/data-page/DataPageHeader.tsx +1 -1
  43. package/src/base/overlays/dialog/Dialog.stories.tsx +94 -34
  44. package/src/base/overlays/menu/Menu.stories.tsx +116 -48
  45. package/src/base/overlays/menu/Menu.tsx +1 -0
  46. package/src/base/overlays/popover/Popover.stories.tsx +50 -37
  47. package/src/base/shortcuts/iterator/keyboardIterator.test.tsx +16 -7
  48. package/src/base/shortcuts/iterator/keyboardIterator.tsx +13 -5
  49. package/src/base/status/StatusIcon.stories.tsx +76 -27
  50. package/src/base/status/index.ts +1 -1
  51. package/src/base/text/SplitAndReplace.stories.tsx +128 -50
  52. package/src/base/text/index.ts +1 -1
  53. package/.storybook/RemoveThemesPlugin.js +0 -49
  54. package/.storybook/main.js +0 -86
  55. package/.storybook/preview.js +0 -87
  56. package/src/base/buttons/image-snapshot.test.ts +0 -26
@@ -14,104 +14,231 @@
14
14
  * along with this program. If not, see https://www.gnu.org/licenses/.
15
15
  */
16
16
 
17
- import { storiesOf } from "@storybook/react";
17
+ // import { storiesOf } from "@storybook/react";
18
+ // import React, { Fragment, useState } from "react";
19
+ // import Combobox from "./Combobox";
20
+ // import { Combobox as HeadlessCombobox } from "@headlessui/react";
21
+ // import { Option } from "@scm-manager/ui-types";
22
+ // import { Link, BrowserRouter } from "react-router-dom";
23
+ //
24
+ // const waitFor = (ms: number) =>
25
+ // function <T>(result: T) {
26
+ // return new Promise<T>((resolve) => setTimeout(() => resolve(result), ms));
27
+ // };
28
+ //
29
+ // const data = [
30
+ // { label: "Trillian", value: "1" },
31
+ // { label: "Arthur", value: "2" },
32
+ // { label: "Zaphod", value: "3" },
33
+ // ];
34
+ //
35
+ // const linkData = [{ label: "Link111111111111111111111111111111111111", value: "1" }];
36
+ //
37
+ // storiesOf("Combobox", module)
38
+ // .add("Options array", () => {
39
+ // const [value, setValue] = useState<Option<string>>();
40
+ // return <Combobox options={data} value={value} onChange={setValue} />;
41
+ // })
42
+ // .add("Options function", () => {
43
+ // const [value, setValue] = useState<Option<string>>();
44
+ // return <Combobox options={() => data} value={value} onChange={setValue} />;
45
+ // })
46
+ // .add("Options function as promise", () => {
47
+ // const [value, setValue] = useState<Option<string>>();
48
+ // return (
49
+ // <Combobox
50
+ // value={value}
51
+ // onChange={setValue}
52
+ // options={(query) => Promise.resolve(data.filter((t) => t.label.startsWith(query))).then(waitFor(1000))}
53
+ // />
54
+ // );
55
+ // })
56
+ // .add("Children as Element", () => {
57
+ // const [value, setValue] = useState<Option<string>>();
58
+ // const [query, setQuery] = useState("");
59
+ //
60
+ // return (
61
+ // <Combobox value={value} onChange={setValue} onQueryChange={setQuery}>
62
+ // {query ? (
63
+ // <HeadlessCombobox.Option value={{ label: query, value: query }} key={query} as={Fragment}>
64
+ // {({ active }) => <Combobox.Option isActive={active}>{`Create ${query}`}</Combobox.Option>}
65
+ // </HeadlessCombobox.Option>
66
+ // ) : null}
67
+ // <HeadlessCombobox.Option value={{ label: "All", value: "All" }} key="all" as={Fragment}>
68
+ // {({ active }) => <Combobox.Option isActive={active}>All</Combobox.Option>}
69
+ // </HeadlessCombobox.Option>
70
+ // <>
71
+ // {data.map((o) => (
72
+ // <HeadlessCombobox.Option value={o} key={o.value} as={Fragment}>
73
+ // {({ active }) => <Combobox.Option isActive={active}>{o.label}</Combobox.Option>}
74
+ // </HeadlessCombobox.Option>
75
+ // ))}
76
+ // </>
77
+ // </Combobox>
78
+ // );
79
+ // })
80
+ // .add("Children as render props", () => {
81
+ // const [value, setValue] = useState<Option<string>>();
82
+ // return (
83
+ // <Combobox options={data} value={value} onChange={setValue}>
84
+ // {(o) => (
85
+ // <HeadlessCombobox.Option value={o} key={o.value} as={Fragment}>
86
+ // {({ active }) => <Combobox.Option isActive={active}>{o.label}</Combobox.Option>}
87
+ // </HeadlessCombobox.Option>
88
+ // )}
89
+ // </Combobox>
90
+ // );
91
+ // })
92
+ // .add("Links as render props", () => {
93
+ // const [value, setValue] = useState<Option<string>>();
94
+ // const [query, setQuery] = useState("Hello");
95
+ // return (
96
+ // <BrowserRouter>
97
+ // <Combobox
98
+ // className="input is-small omni-search-bar"
99
+ // placeholder={"Placeholder"}
100
+ // value={value}
101
+ // options={linkData}
102
+ // onChange={setValue}
103
+ // onQueryChange={setQuery}
104
+ // >
105
+ // {(o) => (
106
+ // <HeadlessCombobox.Option value={{ label: o.label, value: query, displayValue: o.value }} key={o.value} as={Fragment}>
107
+ // {({ active }) => (
108
+ // <Combobox.Option isActive={active}>
109
+ // <Link to={o.label}>{o.label}</Link>
110
+ // </Combobox.Option>
111
+ // )}
112
+ // </HeadlessCombobox.Option>
113
+ // )}
114
+ // </Combobox>
115
+ // </BrowserRouter>
116
+ // );
117
+ // });
118
+
18
119
  import React, { Fragment, useState } from "react";
19
- import Combobox from "./Combobox";
120
+ import { BrowserRouter, Link } from "react-router-dom";
121
+ import type { Meta, StoryObj } from "@storybook/react";
20
122
  import { Combobox as HeadlessCombobox } from "@headlessui/react";
21
123
  import { Option } from "@scm-manager/ui-types";
22
- import { Link, BrowserRouter } from "react-router-dom";
23
124
 
24
- const waitFor = (ms: number) =>
25
- function <T>(result: T) {
26
- return new Promise<T>((resolve) => setTimeout(() => resolve(result), ms));
27
- };
125
+ import Combobox from "./Combobox";
126
+
127
+ const waitFor =
128
+ (ms: number) =>
129
+ <T,>(result: T) =>
130
+ new Promise<T>((resolve) => setTimeout(() => resolve(result), ms));
28
131
 
29
- const data = [
132
+ const data: Option<string>[] = [
30
133
  { label: "Trillian", value: "1" },
31
134
  { label: "Arthur", value: "2" },
32
135
  { label: "Zaphod", value: "3" },
33
136
  ];
34
137
 
35
- const linkData = [{ label: "Link111111111111111111111111111111111111", value: "1" }];
138
+ const linkData: Option<string>[] = [{ label: "/user/trillian", value: "1" }];
139
+
140
+ const meta: Meta<typeof Combobox> = {
141
+ title: "Forms/Combobox",
142
+ // @ts-ignore
143
+ component: Combobox,
144
+ decorators: [(Story) => <div style={{ margin: "2rem", maxWidth: "20rem", minHeight: "15rem" }}>{Story()}</div>],
145
+ tags: ["autodocs"],
146
+ };
147
+
148
+ export default meta;
149
+
150
+ type Story = StoryObj<typeof meta>;
36
151
 
37
- storiesOf("Combobox", module)
38
- .add("Options array", () => {
39
- const [value, setValue] = useState<Option<string>>();
40
- return <Combobox options={data} value={value} onChange={setValue} />;
41
- })
42
- .add("Options function", () => {
43
- const [value, setValue] = useState<Option<string>>();
44
- return <Combobox options={() => data} value={value} onChange={setValue} />;
45
- })
46
- .add("Options function as promise", () => {
47
- const [value, setValue] = useState<Option<string>>();
48
- return (
49
- <Combobox
50
- value={value}
51
- onChange={setValue}
52
- options={(query) => Promise.resolve(data.filter((t) => t.label.startsWith(query))).then(waitFor(1000))}
53
- />
54
- );
55
- })
56
- .add("Children as Element", () => {
57
- const [value, setValue] = useState<Option<string>>();
58
- const [query, setQuery] = useState("");
152
+ // export const OptionsAsArray: Story = {
153
+ // name: "Options from Array",
154
+ // render: () => {
155
+ // const [value, setValue] = useState<Option<string>>();
156
+ // return <Combobox options={data} value={value} onChange={setValue} />;
157
+ // },
158
+ // };
159
+ //
160
+ // export const OptionsAsFunction: Story = {
161
+ // name: "Options from Function",
162
+ // render: () => {
163
+ // const [value, setValue] = useState<Option<string>>();
164
+ // return <Combobox options={() => data} value={value} onChange={setValue} />;
165
+ // },
166
+ // };
167
+ //
168
+ // export const OptionsAsPromise: Story = {
169
+ // name: "Options from Promise",
170
+ // render: () => {
171
+ // const [value, setValue] = useState<Option<string>>();
172
+ // return (
173
+ // <Combobox
174
+ // value={value}
175
+ // onChange={setValue}
176
+ // options={(query) => Promise.resolve(data.filter((t) => t.label.startsWith(query))).then(waitFor(1000))}
177
+ // />
178
+ // );
179
+ // },
180
+ // };
59
181
 
60
- return (
61
- <Combobox value={value} onChange={setValue} onQueryChange={setQuery}>
62
- {query ? (
63
- <HeadlessCombobox.Option value={{ label: query, value: query }} key={query} as={Fragment}>
64
- {({ active }) => <Combobox.Option isActive={active}>{`Create ${query}`}</Combobox.Option>}
65
- </HeadlessCombobox.Option>
66
- ) : null}
67
- <HeadlessCombobox.Option value={{ label: "All", value: "All" }} key="all" as={Fragment}>
68
- {({ active }) => <Combobox.Option isActive={active}>All</Combobox.Option>}
69
- </HeadlessCombobox.Option>
70
- <>
71
- {data.map((o) => (
72
- <HeadlessCombobox.Option value={o} key={o.value} as={Fragment}>
73
- {({ active }) => <Combobox.Option isActive={active}>{o.label}</Combobox.Option>}
74
- </HeadlessCombobox.Option>
75
- ))}
76
- </>
77
- </Combobox>
78
- );
79
- })
80
- .add("Children as render props", () => {
81
- const [value, setValue] = useState<Option<string>>();
82
- return (
83
- <Combobox options={data} value={value} onChange={setValue}>
84
- {(o) => (
85
- <HeadlessCombobox.Option value={o} key={o.value} as={Fragment}>
86
- {({ active }) => <Combobox.Option isActive={active}>{o.label}</Combobox.Option>}
87
- </HeadlessCombobox.Option>
88
- )}
89
- </Combobox>
90
- );
91
- })
92
- .add("Links as render props", () => {
93
- const [value, setValue] = useState<Option<string>>();
94
- const [query, setQuery] = useState("Hello");
95
- return (
96
- <BrowserRouter>
97
- <Combobox
98
- className="input is-small omni-search-bar"
99
- placeholder={"Placeholder"}
100
- value={value}
101
- options={linkData}
102
- onChange={setValue}
103
- onQueryChange={setQuery}
104
- >
105
- {(o) => (
106
- <HeadlessCombobox.Option value={{ label: o.label, value: query, displayValue: o.value }} key={o.value} as={Fragment}>
107
- {({ active }) => (
108
- <Combobox.Option isActive={active}>
109
- <Link to={o.label}>{o.label}</Link>
110
- </Combobox.Option>
111
- )}
112
- </HeadlessCombobox.Option>
113
- )}
114
- </Combobox>
115
- </BrowserRouter>
116
- );
117
- });
182
+ // export const ChildrenAsElement: Story = {
183
+ // name: "Children as Element",
184
+ // render: () => {
185
+ // const [value, setValue] = useState<Option<string>>();
186
+ // const [query, setQuery] = useState("");
187
+ // return (
188
+ // <Combobox value={value} onChange={setValue} onQueryChange={setQuery}>
189
+ // {query ? (
190
+ // <HeadlessCombobox.Option value={{ label: query, value: query }} key={query} as={Fragment}>
191
+ // {({ active }) => <Combobox.Option isActive={active}>{`Create ${query}`}</Combobox.Option>}
192
+ // </HeadlessCombobox.Option>
193
+ // ) : null}
194
+ // <HeadlessCombobox.Option value={{ label: "All", value: "All" }} key="all" as={Fragment}>
195
+ // {({ active }) => <Combobox.Option isActive={active}>All</Combobox.Option>}
196
+ // </HeadlessCombobox.Option>
197
+ // {data.map((o) => (
198
+ // <HeadlessCombobox.Option value={o} key={o.value} as={Fragment}>
199
+ // {({ active }) => <Combobox.Option isActive={active}>{o.label}</Combobox.Option>}
200
+ // </HeadlessCombobox.Option>
201
+ // ))}
202
+ // </Combobox>
203
+ // );
204
+ // },
205
+ // };
206
+ //
207
+ // export const ChildrenAsRenderProps: Story = {
208
+ // name: "Children as Render Props",
209
+ // render: () => {
210
+ // const [value, setValue] = useState<Option<string>>();
211
+ // return (
212
+ // <Combobox options={data} value={value} onChange={setValue}>
213
+ // {(o) => (
214
+ // <HeadlessCombobox.Option value={o} key={o.value} as={Fragment}>
215
+ // {({ active }) => <Combobox.Option isActive={active}>{o.label}</Combobox.Option>}
216
+ // </HeadlessCombobox.Option>
217
+ // )}
218
+ // </Combobox>
219
+ // );
220
+ // },
221
+ // };
222
+ //
223
+ // export const LinksAsRenderProps: Story = {
224
+ // name: "Links as Render Props",
225
+ // render: () => {
226
+ // const [value, setValue] = useState<Option<string>>();
227
+ // return (
228
+ // // Diese Story benötigt ihren eigenen Router-Kontext, um die `Link`-Komponente zu rendern.
229
+ // <BrowserRouter>
230
+ // <Combobox placeholder="Placeholder" value={value} options={linkData} onChange={setValue}>
231
+ // {(o) => (
232
+ // <HeadlessCombobox.Option value={o} key={o.value} as={Fragment}>
233
+ // {({ active }) => (
234
+ // <Combobox.Option isActive={active}>
235
+ // <Link to={o.label}>{o.label}</Link>
236
+ // </Combobox.Option>
237
+ // )}
238
+ // </HeadlessCombobox.Option>
239
+ // )}
240
+ // </Combobox>
241
+ // </BrowserRouter>
242
+ // );
243
+ // },
244
+ // };
@@ -124,7 +124,7 @@ function ComboboxComponent<T>(props: ComboboxProps<T>, ref: ForwardedRef<HTMLInp
124
124
  <HeadlessCombobox.Option value={o} key={o.label} as={Fragment}>
125
125
  {({ active }) => <StyledComboboxOption isActive={active}>{o.displayValue ?? o.label}</StyledComboboxOption>}
126
126
  </HeadlessCombobox.Option>
127
- )
127
+ ),
128
128
  );
129
129
  }
130
130
 
@@ -132,6 +132,7 @@ function ComboboxComponent<T>(props: ComboboxProps<T>, ref: ForwardedRef<HTMLInp
132
132
  <HeadlessCombobox
133
133
  as="div"
134
134
  value={props.value}
135
+ // @ts-ignore
135
136
  onChange={(e?: Option<T>) => props.onChange && props.onChange(e)}
136
137
  disabled={props.disabled || props.readOnly}
137
138
  name={props.name}
@@ -173,6 +174,7 @@ type ComboboxOptionsProps<T> = {
173
174
 
174
175
  function ComboboxOptions<T>({ query, options, children }: ComboboxOptionsProps<T>) {
175
176
  const [optionsData, setOptionsData] = useState<Array<Option<T>>>([]);
177
+ // @ts-ignore
176
178
  const activePromise = useRef<Promise<Array<Option<T>>>>();
177
179
 
178
180
  useEffect(() => {
@@ -204,7 +206,7 @@ function ComboboxOptions<T>({ query, options, children }: ComboboxOptionsProps<T
204
206
  <HeadlessCombobox.Option value={o} key={o.label} as={Fragment}>
205
207
  {({ active }) => <StyledComboboxOption isActive={active}>{o.displayValue ?? o.label}</StyledComboboxOption>}
206
208
  </HeadlessCombobox.Option>
207
- )
209
+ ),
208
210
  )}
209
211
  </>
210
212
  );
@@ -38,7 +38,7 @@ const ComboboxField = function ComboboxField<T>(
38
38
  required,
39
39
  ...props
40
40
  }: ComboboxProps<T> & { label: string; helpText?: string; error?: string; isLoading?: boolean; required?: boolean },
41
- ref: React.ForwardedRef<HTMLInputElement>
41
+ ref: React.ForwardedRef<HTMLInputElement>,
42
42
  ) {
43
43
  const labelId = useAriaId();
44
44
  return (
@@ -49,6 +49,7 @@ const ComboboxField = function ComboboxField<T>(
49
49
  {helpText ? <Help className="ml-1" text={helpText} /> : null}
50
50
  </Label>
51
51
  <div className={classNames("control", { "is-loading": isLoading })}>
52
+ {/* @ts-ignore */}
52
53
  <Combobox {...props} ref={ref} aria-labelledby={labelId} />
53
54
  </div>
54
55
  </Field>
@@ -38,7 +38,7 @@ import { Button } from "../../buttons";
38
38
  type ChipInputContextType<T> = {
39
39
  add(newValue: Option<T>): void;
40
40
  remove(index: number): void;
41
- inputRef: RefObject<HTMLInputElement>;
41
+ inputRef: RefObject<HTMLInputElement | null>;
42
42
  disabled?: boolean;
43
43
  readOnly?: boolean;
44
44
  };
@@ -73,7 +73,7 @@ const DefaultNewChipInput = React.forwardRef<HTMLInputElement, CustomNewChipInpu
73
73
  return false;
74
74
  }
75
75
  },
76
- [onChange]
76
+ [onChange],
77
77
  );
78
78
 
79
79
  return (
@@ -81,7 +81,7 @@ const DefaultNewChipInput = React.forwardRef<HTMLInputElement, CustomNewChipInpu
81
81
  <input onKeyDown={handleKeyDown} {...props} ref={ref} />
82
82
  </div>
83
83
  );
84
- }
84
+ },
85
85
  );
86
86
 
87
87
  type ChipDeleteProps = {
@@ -119,7 +119,7 @@ type NewChipInputProps = {
119
119
  */
120
120
  export const NewChipInput = withForwardRef(function NewChipInput<T>(
121
121
  props: NewChipInputProps,
122
- ref: React.ForwardedRef<HTMLInputElement>
122
+ ref: React.ForwardedRef<HTMLInputElement>,
123
123
  ) {
124
124
  const { add, disabled, readOnly, inputRef } = useContext(getChipInputContext<T>());
125
125
 
@@ -160,7 +160,7 @@ type Props<T> = {
160
160
  */
161
161
  const ChipInput = withForwardRef(function ChipInput<T>(
162
162
  { children, value = [], disabled, readOnly, onChange, isNewItemDuplicate, ...props }: Props<T>,
163
- ref: React.ForwardedRef<HTMLUListElement>
163
+ ref: React.ForwardedRef<HTMLUListElement>,
164
164
  ) {
165
165
  const inputRef = useRef<HTMLInputElement>(null);
166
166
  const isInactive = useMemo(() => disabled || readOnly, [disabled, readOnly]);
@@ -171,7 +171,7 @@ const ChipInput = withForwardRef(function ChipInput<T>(
171
171
  !value?.some((item) =>
172
172
  isNewItemDuplicate
173
173
  ? isNewItemDuplicate(item, newItem)
174
- : item.label === newItem.label || item.value === newItem.value
174
+ : item.label === newItem.label || item.value === newItem.value,
175
175
  )
176
176
  ) {
177
177
  if (onChange) {
@@ -182,11 +182,11 @@ const ChipInput = withForwardRef(function ChipInput<T>(
182
182
  }
183
183
  }
184
184
  },
185
- [isInactive, isNewItemDuplicate, onChange, value]
185
+ [isInactive, isNewItemDuplicate, onChange, value],
186
186
  );
187
187
  const remove = useCallback(
188
188
  (index: number) => !isInactive && onChange && onChange(value?.filter((_, itdx) => itdx !== index)),
189
- [isInactive, onChange, value]
189
+ [isInactive, onChange, value],
190
190
  );
191
191
  const Context = getChipInputContext<T>();
192
192
  return (
@@ -199,7 +199,7 @@ const ChipInput = withForwardRef(function ChipInput<T>(
199
199
  remove,
200
200
  inputRef,
201
201
  }),
202
- [add, disabled, readOnly, remove]
202
+ [add, disabled, readOnly, remove],
203
203
  )}
204
204
  >
205
205
  <ul {...props} ref={ref}>
@@ -14,7 +14,7 @@
14
14
  * along with this program. If not, see https://www.gnu.org/licenses/.
15
15
  */
16
16
 
17
- import { UseFormReturn } from "react-hook-form";
17
+ import { FieldValues, DefaultValues, UseFormReturn } from "react-hook-form";
18
18
  import { ForwardedRef, forwardRef, MutableRefObject, Ref, RefCallback } from "react";
19
19
 
20
20
  export function prefixWithoutIndices(path: string): string {
@@ -28,14 +28,17 @@ export function prefixWithoutIndices(path: string): string {
28
28
  * > but does **NOT** add or remove items to existing arrays.
29
29
  * > This can therefore not be used to clear lists.
30
30
  */
31
- export function setValues<T>(newValues: T, setValue: UseFormReturn<T>["setValue"], path = "") {
32
- for (const [key, val] of Object.entries(newValues)) {
33
- if (val !== null && typeof val === "object") {
34
- if (Array.isArray(val)) {
35
- val.forEach((subVal, idx) => setValues(subVal, setValue, path ? `${path}.${key}.${idx}` : `${key}.${idx}`));
36
- } else {
37
- setValues(val, setValue, path ? `${path}.${key}` : key);
38
- }
31
+ export function setValues<T extends FieldValues>(
32
+ newValues: DefaultValues<T>,
33
+ setValue: UseFormReturn<T>["setValue"],
34
+ path = ""
35
+ ) {
36
+ for (const key of Object.keys(newValues)) {
37
+ const val = newValues[key as keyof typeof newValues];
38
+ const fullPath = path ? `${path}.${key}` : key;
39
+
40
+ if (val !== null && typeof val === "object" && !Array.isArray(val)) {
41
+ setValues(val as DefaultValues<T>, setValue, fullPath);
39
42
  } else {
40
43
  const fullPath = path ? `${path}.${key}` : key;
41
44
  setValue(fullPath as any, val, { shouldValidate: !fullPath.endsWith("Confirmation"), shouldDirty: true });
@@ -33,6 +33,7 @@ type Props<T extends Record<string, unknown>> = Omit<
33
33
  confirmationHelpText?: string;
34
34
  confirmationErrorMessage?: string;
35
35
  confirmationTestId?: string;
36
+ disabled?: boolean;
36
37
  };
37
38
 
38
39
  export default function ControlledSecretConfirmationField<T extends Record<string, unknown>>({
@@ -48,6 +49,7 @@ export default function ControlledSecretConfirmationField<T extends Record<strin
48
49
  confirmationTestId,
49
50
  defaultValue,
50
51
  readOnly,
52
+ disabled,
51
53
  ...props
52
54
  }: Props<T>) {
53
55
  const { control, watch, t, readOnly: formReadonly, formId } = useScmFormContext();
@@ -63,7 +65,7 @@ export default function ControlledSecretConfirmationField<T extends Record<strin
63
65
  confirmationErrorMessage || t(`${prefixedNameWithoutIndices}.confirmation.errorMessage`);
64
66
  const secretValue = watch(nameWithPrefix);
65
67
  const validateConfirmField = useCallback(
66
- (value) => secretValue === value || confirmationErrorMessageTranslation,
68
+ (value: any) => secretValue === value || confirmationErrorMessageTranslation,
67
69
  [confirmationErrorMessageTranslation, secretValue]
68
70
  );
69
71
  const confirmFieldRules = useMemo(
@@ -112,7 +114,7 @@ export default function ControlledSecretConfirmationField<T extends Record<strin
112
114
  className={classNames("column", className)}
113
115
  type="password"
114
116
  readOnly={readOnly ?? formReadonly}
115
- disabled={props.disabled}
117
+ disabled={disabled}
116
118
  {...field}
117
119
  form={formId}
118
120
  label={confirmationLabelTranslation}