@transferwise/components 46.140.1 → 46.141.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/build/avatarWrapper/AvatarWrapper.js +3 -4
- package/build/avatarWrapper/AvatarWrapper.js.map +1 -1
- package/build/avatarWrapper/AvatarWrapper.mjs +4 -5
- package/build/avatarWrapper/AvatarWrapper.mjs.map +1 -1
- package/build/button/LegacyButton.js.map +1 -1
- package/build/button/LegacyButton.mjs.map +1 -1
- package/build/common/hooks/useHasIntersected/useHasIntersected.js +6 -4
- package/build/common/hooks/useHasIntersected/useHasIntersected.js.map +1 -1
- package/build/common/hooks/useHasIntersected/useHasIntersected.mjs +6 -4
- package/build/common/hooks/useHasIntersected/useHasIntersected.mjs.map +1 -1
- package/build/common/liveRegion/LiveRegion.js +4 -1
- package/build/common/liveRegion/LiveRegion.js.map +1 -1
- package/build/common/liveRegion/LiveRegion.mjs +4 -1
- package/build/common/liveRegion/LiveRegion.mjs.map +1 -1
- package/build/dateInput/DateInput.js +10 -10
- package/build/dateInput/DateInput.js.map +1 -1
- package/build/dateInput/DateInput.mjs +10 -10
- package/build/dateInput/DateInput.mjs.map +1 -1
- package/build/dateLookup/monthCalendar/table/MonthCalendarTable.js +1 -1
- package/build/dateLookup/monthCalendar/table/MonthCalendarTable.js.map +1 -1
- package/build/dateLookup/monthCalendar/table/MonthCalendarTable.mjs +1 -1
- package/build/dateLookup/monthCalendar/table/MonthCalendarTable.mjs.map +1 -1
- package/build/dateLookup/yearCalendar/table/YearCalendarTable.js +1 -1
- package/build/dateLookup/yearCalendar/table/YearCalendarTable.js.map +1 -1
- package/build/dateLookup/yearCalendar/table/YearCalendarTable.mjs +1 -1
- package/build/dateLookup/yearCalendar/table/YearCalendarTable.mjs.map +1 -1
- package/build/expressiveMoneyInput/ExpressiveMoneyInput.js.map +1 -1
- package/build/expressiveMoneyInput/ExpressiveMoneyInput.mjs.map +1 -1
- package/build/expressiveMoneyInput/amountInput/AmountInput.js +17 -11
- package/build/expressiveMoneyInput/amountInput/AmountInput.js.map +1 -1
- package/build/expressiveMoneyInput/amountInput/AmountInput.mjs +18 -12
- package/build/expressiveMoneyInput/amountInput/AmountInput.mjs.map +1 -1
- package/build/expressiveMoneyInput/hooks/useInputStyle.js +8 -6
- package/build/expressiveMoneyInput/hooks/useInputStyle.js.map +1 -1
- package/build/expressiveMoneyInput/hooks/useInputStyle.mjs +9 -7
- package/build/expressiveMoneyInput/hooks/useInputStyle.mjs.map +1 -1
- package/build/header/Header.js +1 -1
- package/build/header/Header.js.map +1 -1
- package/build/header/Header.mjs +1 -1
- package/build/header/Header.mjs.map +1 -1
- package/build/inputs/SelectInput/BottomSheet/SelectInputBottomSheet.js.map +1 -1
- package/build/inputs/SelectInput/BottomSheet/SelectInputBottomSheet.mjs.map +1 -1
- package/build/inputs/SelectInput/Options/SelectInputOptions.js +34 -22
- package/build/inputs/SelectInput/Options/SelectInputOptions.js.map +1 -1
- package/build/inputs/SelectInput/Options/SelectInputOptions.mjs +35 -23
- package/build/inputs/SelectInput/Options/SelectInputOptions.mjs.map +1 -1
- package/build/inputs/SelectInput/Popover/SelectInputPopover.js.map +1 -1
- package/build/inputs/SelectInput/Popover/SelectInputPopover.mjs.map +1 -1
- package/build/inputs/SelectInput/SelectInput.js +8 -6
- package/build/inputs/SelectInput/SelectInput.js.map +1 -1
- package/build/inputs/SelectInput/SelectInput.mjs +9 -7
- package/build/inputs/SelectInput/SelectInput.mjs.map +1 -1
- package/build/inputs/SelectInput/TriggerButton/SelectInputTriggerButton.js.map +1 -1
- package/build/inputs/SelectInput/TriggerButton/SelectInputTriggerButton.mjs.map +1 -1
- package/build/nudge/Nudge.js +31 -15
- package/build/nudge/Nudge.js.map +1 -1
- package/build/nudge/Nudge.mjs +32 -16
- package/build/nudge/Nudge.mjs.map +1 -1
- package/build/phoneNumberInput/PhoneNumberInput.js +9 -12
- package/build/phoneNumberInput/PhoneNumberInput.js.map +1 -1
- package/build/phoneNumberInput/PhoneNumberInput.mjs +9 -12
- package/build/phoneNumberInput/PhoneNumberInput.mjs.map +1 -1
- package/build/promoCard/PromoCardGroup.js +34 -16
- package/build/promoCard/PromoCardGroup.js.map +1 -1
- package/build/promoCard/PromoCardGroup.mjs +35 -17
- package/build/promoCard/PromoCardGroup.mjs.map +1 -1
- package/build/segmentedControl/SegmentedControl.js +6 -1
- package/build/segmentedControl/SegmentedControl.js.map +1 -1
- package/build/segmentedControl/SegmentedControl.mjs +7 -2
- package/build/segmentedControl/SegmentedControl.mjs.map +1 -1
- package/build/tabs/Tabs.js +1 -1
- package/build/tabs/Tabs.js.map +1 -1
- package/build/tabs/Tabs.mjs +1 -1
- package/build/tabs/Tabs.mjs.map +1 -1
- package/build/tooltip/Tooltip.js +6 -3
- package/build/tooltip/Tooltip.js.map +1 -1
- package/build/tooltip/Tooltip.mjs +6 -3
- package/build/tooltip/Tooltip.mjs.map +1 -1
- package/build/types/avatarWrapper/AvatarWrapper.d.ts.map +1 -1
- package/build/types/common/hooks/useHasIntersected/useHasIntersected.d.ts.map +1 -1
- package/build/types/common/liveRegion/LiveRegion.d.ts.map +1 -1
- package/build/types/dateLookup/monthCalendar/table/MonthCalendarTable.d.ts.map +1 -1
- package/build/types/expressiveMoneyInput/ExpressiveMoneyInput.d.ts.map +1 -1
- package/build/types/expressiveMoneyInput/amountInput/AmountInput.d.ts.map +1 -1
- package/build/types/expressiveMoneyInput/hooks/useInputStyle.d.ts +2 -2
- package/build/types/expressiveMoneyInput/hooks/useInputStyle.d.ts.map +1 -1
- package/build/types/expressiveMoneyInput/hooks/useSelectionRange.d.ts.map +1 -1
- package/build/types/inputs/SelectInput/BottomSheet/SelectInputBottomSheet.d.ts.map +1 -1
- package/build/types/inputs/SelectInput/Options/SelectInputOptions.d.ts.map +1 -1
- package/build/types/inputs/SelectInput/Popover/SelectInputPopover.d.ts.map +1 -1
- package/build/types/inputs/SelectInput/SelectInput.d.ts.map +1 -1
- package/build/types/nudge/Nudge.d.ts.map +1 -1
- package/build/types/phoneNumberInput/PhoneNumberInput.d.ts.map +1 -1
- package/build/types/promoCard/PromoCardGroup.d.ts.map +1 -1
- package/build/types/segmentedControl/SegmentedControl.d.ts.map +1 -1
- package/build/types/tooltip/Tooltip.d.ts.map +1 -1
- package/build/types/uploadInput/UploadInput.d.ts.map +1 -1
- package/build/uploadInput/UploadInput.js +29 -25
- package/build/uploadInput/UploadInput.js.map +1 -1
- package/build/uploadInput/UploadInput.mjs +29 -25
- package/build/uploadInput/UploadInput.mjs.map +1 -1
- package/package.json +2 -2
- package/src/avatarWrapper/AvatarWrapper.test.tsx +33 -3
- package/src/avatarWrapper/AvatarWrapper.tsx +5 -6
- package/src/button/LegacyButton.tsx +1 -1
- package/src/button/_stories/Button.test.story.tsx +3 -3
- package/src/common/hooks/useContainerSize.test.tsx +1 -1
- package/src/common/hooks/useHasIntersected/useHasIntersected.ts +12 -4
- package/src/common/liveRegion/LiveRegion.tsx +5 -2
- package/src/dateInput/DateInput.tsx +10 -10
- package/src/dateLookup/monthCalendar/table/MonthCalendarTable.tsx +1 -5
- package/src/dateLookup/yearCalendar/table/YearCalendarTable.tsx +1 -1
- package/src/expressiveMoneyInput/ExpressiveMoneyInput.tsx +1 -1
- package/src/expressiveMoneyInput/amountInput/AmountInput.tsx +22 -15
- package/src/expressiveMoneyInput/hooks/useInputStyle.ts +20 -8
- package/src/expressiveMoneyInput/hooks/useSelectionRange.ts +2 -0
- package/src/header/Header.tsx +2 -2
- package/src/inputs/SelectInput/BottomSheet/SelectInputBottomSheet.tsx +4 -0
- package/src/inputs/SelectInput/Options/SelectInputOptions.tsx +43 -27
- package/src/inputs/SelectInput/Popover/SelectInputPopover.tsx +4 -0
- package/src/inputs/SelectInput/SelectInput.tsx +21 -15
- package/src/inputs/SelectInput/TriggerButton/SelectInputTriggerButton.tsx +1 -1
- package/src/nudge/Nudge.tsx +29 -20
- package/src/phoneNumberInput/PhoneNumberInput.test.tsx +16 -0
- package/src/phoneNumberInput/PhoneNumberInput.tsx +11 -13
- package/src/promoCard/PromoCard.story.tsx +3 -3
- package/src/promoCard/PromoCardGroup.tsx +39 -21
- package/src/segmentedControl/SegmentedControl.test.tsx +25 -0
- package/src/segmentedControl/SegmentedControl.tsx +7 -1
- package/src/select/Select.story.tsx +1 -1
- package/src/tabs/Tabs.tsx +1 -1
- package/src/tooltip/Tooltip.tsx +3 -0
- package/src/uploadInput/UploadInput.test.tsx +19 -0
- package/src/uploadInput/UploadInput.tsx +28 -24
|
@@ -67,8 +67,45 @@ const PromoCardGroup: FunctionComponent<PromoCardGroupProps> = ({
|
|
|
67
67
|
onChange = () => {},
|
|
68
68
|
testId,
|
|
69
69
|
}) => {
|
|
70
|
-
const [
|
|
71
|
-
|
|
70
|
+
const [promoCardState, setPromoCardState] = useState<{
|
|
71
|
+
defaultChecked: string;
|
|
72
|
+
state: string;
|
|
73
|
+
}>({
|
|
74
|
+
defaultChecked,
|
|
75
|
+
state: defaultChecked,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (promoCardState.defaultChecked !== defaultChecked) {
|
|
80
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect -- Syncing defaultChecked prop to internal state
|
|
81
|
+
setPromoCardState({
|
|
82
|
+
defaultChecked,
|
|
83
|
+
state: defaultChecked,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}, [defaultChecked, promoCardState.defaultChecked]);
|
|
87
|
+
|
|
88
|
+
const { state } = promoCardState;
|
|
89
|
+
const setState = (newState: string) =>
|
|
90
|
+
setPromoCardState((prev) => ({ ...prev, state: newState }));
|
|
91
|
+
|
|
92
|
+
// Derive container role from children
|
|
93
|
+
const containerRole = useMemo(() => {
|
|
94
|
+
// Collect an array of types from the children PromoCard components
|
|
95
|
+
const types =
|
|
96
|
+
React.Children.map(children, (child) => {
|
|
97
|
+
if (React.isValidElement<PromoCardProps>(child) && child.props.type) {
|
|
98
|
+
return child.props.type;
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
})?.filter((type): type is 'radio' | 'checkbox' => type !== null && type !== undefined) ?? [];
|
|
102
|
+
|
|
103
|
+
// Check if all types are the same
|
|
104
|
+
const allTypesAreTheSame = types.every((type) => type === types[0]);
|
|
105
|
+
|
|
106
|
+
// If all types are the same and the type is 'radio', return 'radiogroup'
|
|
107
|
+
return allTypesAreTheSame && types[0] === 'radio' ? 'radiogroup' : null;
|
|
108
|
+
}, [children]);
|
|
72
109
|
|
|
73
110
|
/**
|
|
74
111
|
* The context value for the PromoCardGroup.
|
|
@@ -103,25 +140,6 @@ const PromoCardGroup: FunctionComponent<PromoCardGroupProps> = ({
|
|
|
103
140
|
role: containerRole as AriaRoleRadioGroup | undefined, // Add the role attribute here
|
|
104
141
|
};
|
|
105
142
|
|
|
106
|
-
useEffect(() => {
|
|
107
|
-
setState(defaultChecked);
|
|
108
|
-
|
|
109
|
-
// Collect an array of types from the children PromoCard components
|
|
110
|
-
const types =
|
|
111
|
-
React.Children.map(children, (child) => {
|
|
112
|
-
if (React.isValidElement<PromoCardProps>(child) && child.props.type) {
|
|
113
|
-
return child.props.type;
|
|
114
|
-
}
|
|
115
|
-
return null;
|
|
116
|
-
})?.filter((type): type is 'radio' | 'checkbox' => type !== null && type !== undefined) ?? [];
|
|
117
|
-
|
|
118
|
-
// Check if all types are the same
|
|
119
|
-
const allTypesAreTheSame = types.every((type) => type === types[0]);
|
|
120
|
-
|
|
121
|
-
// If all types are the same and the type is 'radio', set the container role
|
|
122
|
-
setContainerRole(allTypesAreTheSame && types[0] === 'radio' ? 'radiogroup' : null);
|
|
123
|
-
}, [defaultChecked, children]);
|
|
124
|
-
|
|
125
143
|
return (
|
|
126
144
|
<PromoCardContext.Provider value={contextValue}>
|
|
127
145
|
<div {...commonProps}>{children}</div>
|
|
@@ -188,4 +188,29 @@ describe('SegmentedControl', () => {
|
|
|
188
188
|
'SegmentedControl only supports up to 3 segments. Please refer to: https://wise.design/components/segmented-control',
|
|
189
189
|
);
|
|
190
190
|
});
|
|
191
|
+
|
|
192
|
+
describe('animation behavior', () => {
|
|
193
|
+
it('skips animation on initial render', () => {
|
|
194
|
+
renderSegmentedControl();
|
|
195
|
+
|
|
196
|
+
// On initial render, the component should render without triggering animation
|
|
197
|
+
// The isMountedRef ensures animation is skipped on first render
|
|
198
|
+
const segmentedControl = screen.getByTestId('segmented-control');
|
|
199
|
+
|
|
200
|
+
// Verify the component renders successfully
|
|
201
|
+
expect(segmentedControl).toBeInTheDocument();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('enables animation after value change', async () => {
|
|
205
|
+
renderSegmentedControl();
|
|
206
|
+
|
|
207
|
+
// Change value by clicking on a different segment
|
|
208
|
+
const payroll = screen.getByRole('radio', { name: 'Payroll' });
|
|
209
|
+
await userEvent.click(payroll);
|
|
210
|
+
|
|
211
|
+
// After a value change, the component should have completed at least one update cycle
|
|
212
|
+
// verifying that the isMountedRef tracking works correctly
|
|
213
|
+
expect(onChange).toHaveBeenCalledWith('payroll');
|
|
214
|
+
});
|
|
215
|
+
});
|
|
191
216
|
});
|
|
@@ -38,6 +38,7 @@ const SegmentedControl = ({
|
|
|
38
38
|
segments,
|
|
39
39
|
onChange,
|
|
40
40
|
}: SegmentedControlProps) => {
|
|
41
|
+
const isMountedRef = useRef(false);
|
|
41
42
|
const [animate, setAnimate] = useState(false);
|
|
42
43
|
|
|
43
44
|
const segmentsRef = useRef<HTMLDivElement>(null);
|
|
@@ -67,7 +68,12 @@ const SegmentedControl = ({
|
|
|
67
68
|
};
|
|
68
69
|
|
|
69
70
|
useEffect(() => {
|
|
70
|
-
|
|
71
|
+
if (isMountedRef.current) {
|
|
72
|
+
setAnimate(true);
|
|
73
|
+
} else {
|
|
74
|
+
isMountedRef.current = true;
|
|
75
|
+
}
|
|
76
|
+
|
|
71
77
|
updateSegmentPosition();
|
|
72
78
|
|
|
73
79
|
const handleWindowSizeChange = () => {
|
|
@@ -43,7 +43,7 @@ const ImageIcon = () => (
|
|
|
43
43
|
);
|
|
44
44
|
|
|
45
45
|
const isSelectOptionItem = (option: SelectItem | null): option is SelectOptionItem => {
|
|
46
|
-
return
|
|
46
|
+
return typeof option?.value !== 'undefined';
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
export const Basic: Story = {
|
package/src/tabs/Tabs.tsx
CHANGED
|
@@ -254,7 +254,7 @@ export default class Tabs extends Component<TabsProps, TabsState> {
|
|
|
254
254
|
};
|
|
255
255
|
|
|
256
256
|
onKeyDown = (index: number) => (event: React.KeyboardEvent<HTMLLIElement>) => {
|
|
257
|
-
if (event
|
|
257
|
+
if (event?.key === 'Enter') {
|
|
258
258
|
this.switchTab(index);
|
|
259
259
|
}
|
|
260
260
|
};
|
package/src/tooltip/Tooltip.tsx
CHANGED
|
@@ -51,6 +51,7 @@ const Tooltip = ({
|
|
|
51
51
|
middleware: [
|
|
52
52
|
offset(16),
|
|
53
53
|
flip({ fallbackPlacements: [Position.TOP] }),
|
|
54
|
+
// eslint-disable-next-line react-hooks/refs -- arrowRef is passed to floating-ui middleware, legitimate API usage
|
|
54
55
|
arrowMiddleware({ element: arrowRef, padding: 8 }),
|
|
55
56
|
],
|
|
56
57
|
whileElementsMounted: open ? autoUpdate : undefined,
|
|
@@ -63,6 +64,7 @@ const Tooltip = ({
|
|
|
63
64
|
};
|
|
64
65
|
|
|
65
66
|
return (
|
|
67
|
+
/* eslint-disable react-hooks/refs -- setReference and setFloating are callback refs from floating-ui, safe to pass during render */
|
|
66
68
|
<span
|
|
67
69
|
ref={refs.setReference}
|
|
68
70
|
className="tw-tooltip-container"
|
|
@@ -96,6 +98,7 @@ const Tooltip = ({
|
|
|
96
98
|
</div>
|
|
97
99
|
</div>
|
|
98
100
|
</span>
|
|
101
|
+
/* eslint-enable react-hooks/refs */
|
|
99
102
|
);
|
|
100
103
|
};
|
|
101
104
|
|
|
@@ -66,6 +66,25 @@ describe('UploadInput', () => {
|
|
|
66
66
|
jest.useRealTimers();
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
+
describe('onFilesChange mount behavior', () => {
|
|
70
|
+
it('should not call onFilesChange on initial mount', () => {
|
|
71
|
+
const onFilesChange = jest.fn();
|
|
72
|
+
renderComponent({
|
|
73
|
+
...props,
|
|
74
|
+
files: [
|
|
75
|
+
{
|
|
76
|
+
id: 1,
|
|
77
|
+
filename: 'existing-file.pdf',
|
|
78
|
+
status: Status.SUCCEEDED,
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
onFilesChange,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
expect(onFilesChange).not.toHaveBeenCalled();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
69
88
|
describe('single file upload', () => {
|
|
70
89
|
it('should trigger onUploadFiles & onFilesChange with a single FormData entry containing `file` field', async () => {
|
|
71
90
|
const date = Date.now();
|
|
@@ -170,10 +170,10 @@ const UploadInput = ({
|
|
|
170
170
|
const inputAttributes = useInputAttributes({ nonLabelable: true });
|
|
171
171
|
const [markedFileForDelete, setMarkedFileForDelete] = useState<UploadedFile | null>(null);
|
|
172
172
|
const [lastAttemptedDeleteId, setLastAttemptedDeleteId] = useState<string | number | null>(null);
|
|
173
|
-
const
|
|
173
|
+
const mountedRef = useRef(false);
|
|
174
174
|
const { formatMessage } = useIntl();
|
|
175
175
|
const uploadInputRef = useRef<HTMLInputElement | null>(null);
|
|
176
|
-
|
|
176
|
+
const fileRefs = useRef<(HTMLDivElement | UploadItemRef | null)[]>([]);
|
|
177
177
|
|
|
178
178
|
const PROGRESS_STATUSES = new Set([Status.PENDING, Status.PROCESSING]);
|
|
179
179
|
|
|
@@ -196,7 +196,7 @@ const UploadInput = ({
|
|
|
196
196
|
updateFileList((list) =>
|
|
197
197
|
list.filter((fileInList) => file !== fileInList && file.id !== fileInList.id),
|
|
198
198
|
);
|
|
199
|
-
fileRefs = fileRefs.filter((ref) => ref && ref.id !== file.id);
|
|
199
|
+
fileRefs.current = fileRefs.current.filter((ref) => ref && ref.id !== file.id);
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
function modifyFileInList(file: UploadedFile, updates: Partial<UploadedFile>) {
|
|
@@ -208,18 +208,18 @@ const UploadInput = ({
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
const removeFile = async (file: UploadedFile) => {
|
|
211
|
-
const { id, status } = file;
|
|
212
|
-
fileRefs = fileRefs.filter((item) => item && item.id !== file.id);
|
|
211
|
+
const { id: fileId, status } = file;
|
|
212
|
+
fileRefs.current = fileRefs.current.filter((item) => item && item.id !== file.id);
|
|
213
213
|
|
|
214
214
|
if (status === Status.FAILED) {
|
|
215
215
|
removeFileFromList(file);
|
|
216
216
|
return Promise.resolve();
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
if (onDeleteFile &&
|
|
219
|
+
if (onDeleteFile && fileId) {
|
|
220
220
|
modifyFileInList(file, { status: Status.PROCESSING, error: undefined });
|
|
221
221
|
|
|
222
|
-
return onDeleteFile(
|
|
222
|
+
return onDeleteFile(fileId)
|
|
223
223
|
.then(() => {
|
|
224
224
|
removeFileFromList(file);
|
|
225
225
|
})
|
|
@@ -248,6 +248,7 @@ const UploadInput = ({
|
|
|
248
248
|
|
|
249
249
|
function getNumberOfFilesUploaded() {
|
|
250
250
|
const uploadInitiatedStatus = new Set([Status.SUCCEEDED, Status.PENDING]);
|
|
251
|
+
|
|
251
252
|
const validFiles = uploadedFilesListReference.current.filter(
|
|
252
253
|
(file) => file.status && uploadInitiatedStatus.has(file.status),
|
|
253
254
|
);
|
|
@@ -321,14 +322,16 @@ const UploadInput = ({
|
|
|
321
322
|
};
|
|
322
323
|
|
|
323
324
|
useEffect(() => {
|
|
324
|
-
|
|
325
|
-
|
|
325
|
+
// Skip onFilesChange call on initial mount, only call on updates
|
|
326
|
+
if (!mountedRef.current) {
|
|
327
|
+
mountedRef.current = true;
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
326
330
|
|
|
327
|
-
|
|
328
|
-
if (onFilesChange && mounted) {
|
|
331
|
+
if (onFilesChange) {
|
|
329
332
|
onFilesChange([...uploadedFiles]);
|
|
330
333
|
}
|
|
331
|
-
}, [onFilesChange, uploadedFiles]);
|
|
334
|
+
}, [onFilesChange, uploadedFiles]);
|
|
332
335
|
|
|
333
336
|
type NextFocusable =
|
|
334
337
|
| HTMLDivElement
|
|
@@ -336,14 +339,14 @@ const UploadInput = ({
|
|
|
336
339
|
| { ref: HTMLDivElement | UploadItemRef; target: 'button' | 'link' }
|
|
337
340
|
| null;
|
|
338
341
|
|
|
339
|
-
const [nextFocusable, setNextFocusable] = useState<NextFocusable>(
|
|
342
|
+
const [nextFocusable, setNextFocusable] = useState<NextFocusable>(null);
|
|
340
343
|
|
|
341
344
|
const handleFocus = (fileId: string | number) => {
|
|
342
|
-
fileRefs = fileRefs.filter((ref) => {
|
|
345
|
+
fileRefs.current = fileRefs.current.filter((ref) => {
|
|
343
346
|
return ref && ref.id !== markedFileForDelete?.id;
|
|
344
347
|
});
|
|
345
348
|
|
|
346
|
-
const filesCount = fileRefs.length;
|
|
349
|
+
const filesCount = fileRefs.current.length;
|
|
347
350
|
let next: UploadItemRef | HTMLDivElement | null = uploadInputRef.current;
|
|
348
351
|
let focusTarget: 'button' | 'link' = 'button';
|
|
349
352
|
|
|
@@ -355,15 +358,15 @@ const UploadInput = ({
|
|
|
355
358
|
}
|
|
356
359
|
|
|
357
360
|
if (filesCount > 1) {
|
|
358
|
-
const currentFileIndex = fileRefs.findIndex((file) => file?.id === fileId);
|
|
359
|
-
const currentFileId = fileRefs?.[currentFileIndex]?.id;
|
|
360
|
-
const lastFileId = fileRefs?.[filesCount - 1]?.id;
|
|
361
|
+
const currentFileIndex = fileRefs.current.findIndex((file) => file?.id === fileId);
|
|
362
|
+
const currentFileId = fileRefs.current?.[currentFileIndex]?.id;
|
|
363
|
+
const lastFileId = fileRefs.current?.[filesCount - 1]?.id;
|
|
361
364
|
|
|
362
365
|
// if last file, select a previous one
|
|
363
366
|
if (currentFileId === lastFileId) {
|
|
364
|
-
next = fileRefs[filesCount - 2];
|
|
367
|
+
next = fileRefs.current[filesCount - 2];
|
|
365
368
|
} else {
|
|
366
|
-
next = fileRefs[currentFileIndex + 1];
|
|
369
|
+
next = fileRefs.current[currentFileIndex + 1];
|
|
367
370
|
}
|
|
368
371
|
|
|
369
372
|
// If next is an UploadItemRef, check if it has a URL (succeeded)
|
|
@@ -401,7 +404,7 @@ const UploadInput = ({
|
|
|
401
404
|
typeof uploadInputRef.current.focus === 'function'
|
|
402
405
|
) {
|
|
403
406
|
setTimeout(() => {
|
|
404
|
-
uploadInputRef.current
|
|
407
|
+
uploadInputRef.current?.focus();
|
|
405
408
|
}, 0);
|
|
406
409
|
} else if (
|
|
407
410
|
typeof focusTarget === 'object' &&
|
|
@@ -436,17 +439,17 @@ const UploadInput = ({
|
|
|
436
439
|
aria-relevant="all"
|
|
437
440
|
role="region"
|
|
438
441
|
>
|
|
439
|
-
{uploadedFiles.map((file
|
|
442
|
+
{uploadedFiles.map((file) => (
|
|
440
443
|
<UploadItem
|
|
441
444
|
key={file.id}
|
|
442
445
|
ref={(el: UploadItemRef | null) => {
|
|
443
446
|
if (
|
|
444
447
|
el &&
|
|
445
448
|
el.id !== markedFileForDelete?.id &&
|
|
446
|
-
!fileRefs.some((ref) => ref
|
|
449
|
+
!fileRefs.current.some((ref) => ref?.id === el.id) &&
|
|
447
450
|
el.status !== 'processing'
|
|
448
451
|
) {
|
|
449
|
-
fileRefs.push(el);
|
|
452
|
+
fileRefs.current.push(el);
|
|
450
453
|
}
|
|
451
454
|
}}
|
|
452
455
|
file={file}
|
|
@@ -478,6 +481,7 @@ const UploadInput = ({
|
|
|
478
481
|
ref={uploadInputRef}
|
|
479
482
|
id={id}
|
|
480
483
|
uploadButtonTitle={uploadButtonTitle}
|
|
484
|
+
// eslint-disable-next-line react-hooks/refs -- Function reads ref for file count check
|
|
481
485
|
disabled={areMaximumFilesUploadedAlready() || disabled}
|
|
482
486
|
multiple={multiple}
|
|
483
487
|
fileTypes={fileTypes}
|