@transferwise/components 0.0.0-experimental-af6dee0 → 0.0.0-experimental-199a021
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 +31 -22
- package/build/inputs/SelectInput/Options/SelectInputOptions.js.map +1 -1
- package/build/inputs/SelectInput/Options/SelectInputOptions.mjs +32 -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/loader/Loader.js +6 -16
- package/build/loader/Loader.js.map +1 -1
- package/build/loader/Loader.mjs +7 -17
- package/build/loader/Loader.mjs.map +1 -1
- package/build/main.css +4 -15
- package/build/nudge/Nudge.js +26 -15
- package/build/nudge/Nudge.js.map +1 -1
- package/build/nudge/Nudge.mjs +26 -15
- 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/styles/loader/Loader.css +4 -14
- package/build/styles/main.css +4 -15
- 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/loader/Loader.d.ts +1 -4
- package/build/types/loader/Loader.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 -28
- package/build/uploadInput/UploadInput.js.map +1 -1
- package/build/uploadInput/UploadInput.mjs +29 -28
- package/build/uploadInput/UploadInput.mjs.map +1 -1
- package/package.json +3 -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 +39 -26
- 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/loader/Loader.css +4 -14
- package/src/loader/Loader.less +88 -54
- package/src/loader/Loader.story.tsx +0 -4
- package/src/loader/Loader.test.tsx +5 -20
- package/src/loader/Loader.tsx +7 -18
- package/src/main.css +4 -15
- package/src/nudge/Nudge.tsx +22 -19
- 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.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.tsx +28 -27
package/src/main.css
CHANGED
|
@@ -30839,17 +30839,6 @@ button.np-link {
|
|
|
30839
30839
|
}
|
|
30840
30840
|
|
|
30841
30841
|
.tw-loader {
|
|
30842
|
-
display: flex;
|
|
30843
|
-
flex-direction: column;
|
|
30844
|
-
align-items: center;
|
|
30845
|
-
gap: 16px;
|
|
30846
|
-
gap: var(--size-16);
|
|
30847
|
-
width: 190px;
|
|
30848
|
-
text-align: center;
|
|
30849
|
-
word-break: break-word;
|
|
30850
|
-
}
|
|
30851
|
-
|
|
30852
|
-
.tw-loader-asset {
|
|
30853
30842
|
overflow: hidden;
|
|
30854
30843
|
border-radius: 50%;
|
|
30855
30844
|
backface-visibility: hidden;
|
|
@@ -30866,11 +30855,11 @@ button.np-link {
|
|
|
30866
30855
|
transition: opacity 300ms cubic-bezier(0.24, 0, 0.3, 1);
|
|
30867
30856
|
}
|
|
30868
30857
|
|
|
30869
|
-
.tw-loader
|
|
30858
|
+
.tw-loader--visible {
|
|
30870
30859
|
opacity: 1;
|
|
30871
30860
|
}
|
|
30872
30861
|
|
|
30873
|
-
.tw-loader
|
|
30862
|
+
.tw-loader::before {
|
|
30874
30863
|
content: "";
|
|
30875
30864
|
display: block;
|
|
30876
30865
|
width: 4.5rem;
|
|
@@ -30890,7 +30879,7 @@ button.np-link {
|
|
|
30890
30879
|
animation-iteration-count: infinite;
|
|
30891
30880
|
}
|
|
30892
30881
|
|
|
30893
|
-
.tw-loader
|
|
30882
|
+
.tw-loader--sm {
|
|
30894
30883
|
--coin-size: 3rem;
|
|
30895
30884
|
--coin-edge-width-offset: translateX(0.275rem);
|
|
30896
30885
|
--coin-edge-width-negative-offset: translateX(-0.275rem);
|
|
@@ -30898,7 +30887,7 @@ button.np-link {
|
|
|
30898
30887
|
--box-shadow-backwards: -0.025rem 0 0 var(--coin-colour), -0.05rem 0 0 var(--coin-colour), -0.075rem 0 0 var(--coin-colour), -0.1rem 0 0 var(--coin-colour), -0.125rem 0 0 var(--coin-colour), -0.15rem 0 0 var(--coin-colour), -0.175rem 0 0 var(--coin-colour), -0.2rem 0 0 var(--coin-colour), -0.225rem 0 0 var(--coin-colour), -0.25rem 0 0 var(--coin-colour), -0.275rem 0 0 var(--coin-colour), -0.3rem 0 0 var(--coin-colour), -0.325rem 0 0 var(--coin-colour), -0.35rem 0 0 var(--coin-colour), -0.375rem 0 0 var(--coin-colour), -0.4rem 0 0 var(--coin-colour), -0.425rem 0 0 var(--coin-colour), -0.45rem 0 0 var(--coin-colour), -0.475rem 0 0 var(--coin-colour);
|
|
30899
30888
|
}
|
|
30900
30889
|
|
|
30901
|
-
.tw-loader
|
|
30890
|
+
.tw-loader--md {
|
|
30902
30891
|
--coin-size: 4.5rem;
|
|
30903
30892
|
--coin-edge-width-offset: translateX(0.375rem);
|
|
30904
30893
|
--coin-edge-width-negative-offset: translateX(-0.375rem);
|
package/src/nudge/Nudge.tsx
CHANGED
|
@@ -96,8 +96,21 @@ const Nudge = ({
|
|
|
96
96
|
action,
|
|
97
97
|
}: Props) => {
|
|
98
98
|
const intl = useIntl();
|
|
99
|
-
const [
|
|
100
|
-
|
|
99
|
+
const [nudgeState, setNudgeState] = useState(() => {
|
|
100
|
+
const initialDismissed =
|
|
101
|
+
persistDismissal && id ? !!getLocalStorage()?.find((item) => item === id) : false;
|
|
102
|
+
return {
|
|
103
|
+
isDismissed: initialDismissed,
|
|
104
|
+
isMounted: false,
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect -- Setting mount state in mount effect
|
|
110
|
+
setNudgeState((prev) => ({ ...prev, isMounted: true }));
|
|
111
|
+
}, []);
|
|
112
|
+
|
|
113
|
+
const { isDismissed } = nudgeState;
|
|
101
114
|
|
|
102
115
|
const handleOnDismiss = () => {
|
|
103
116
|
const dismissedNudgesStorage = getLocalStorage();
|
|
@@ -105,9 +118,9 @@ const Nudge = ({
|
|
|
105
118
|
if (persistDismissal && id) {
|
|
106
119
|
try {
|
|
107
120
|
localStorage.setItem(STORAGE_NAME, JSON.stringify([...dismissedNudgesStorage, id]));
|
|
108
|
-
} catch
|
|
121
|
+
} catch {}
|
|
109
122
|
|
|
110
|
-
|
|
123
|
+
setNudgeState((prev) => ({ ...prev, isDismissed: true }));
|
|
111
124
|
}
|
|
112
125
|
|
|
113
126
|
if (onDismiss) {
|
|
@@ -116,25 +129,15 @@ const Nudge = ({
|
|
|
116
129
|
};
|
|
117
130
|
|
|
118
131
|
useEffect(() => {
|
|
119
|
-
if (persistDismissal && id) {
|
|
132
|
+
if (persistDismissal && id && isPreviouslyDismissed) {
|
|
120
133
|
const dismissedNudgesStorage = getLocalStorage();
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (dismissedNudgesStorage?.find((item) => item === id)) {
|
|
124
|
-
setIsDismissed(true);
|
|
125
|
-
isDismissed = true;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (isPreviouslyDismissed) {
|
|
129
|
-
isPreviouslyDismissed(isDismissed);
|
|
130
|
-
}
|
|
134
|
+
const wasDismissed = !!dismissedNudgesStorage?.find((item) => item === id);
|
|
135
|
+
isPreviouslyDismissed(wasDismissed);
|
|
131
136
|
}
|
|
132
|
-
|
|
133
|
-
setIsMounted(true);
|
|
134
137
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
135
138
|
}, [id, persistDismissal]);
|
|
136
139
|
|
|
137
|
-
if (persistDismissal && (isDismissed || !isMounted)) {
|
|
140
|
+
if (persistDismissal && (isDismissed || !nudgeState.isMounted)) {
|
|
138
141
|
return null;
|
|
139
142
|
}
|
|
140
143
|
|
|
@@ -143,7 +146,7 @@ const Nudge = ({
|
|
|
143
146
|
{!!mediaName && (
|
|
144
147
|
<div className="wds-nudge-media">
|
|
145
148
|
<Illustration
|
|
146
|
-
name={mediaName
|
|
149
|
+
name={mediaName}
|
|
147
150
|
className={clsx(`wds-nudge-media-${mediaName}`)}
|
|
148
151
|
size="small"
|
|
149
152
|
disablePadding
|
|
@@ -75,12 +75,13 @@ const PhoneNumberInput = ({
|
|
|
75
75
|
|
|
76
76
|
const { locale, formatMessage } = useIntl();
|
|
77
77
|
|
|
78
|
+
const [randomId] = useState(() => Math.random().toString(36).slice(2, 8));
|
|
79
|
+
|
|
78
80
|
const createId = (customID: string | undefined, backup: string): string => {
|
|
79
81
|
if (customID) {
|
|
80
82
|
return customID + (backup ? `-${backup}` : '');
|
|
81
83
|
}
|
|
82
|
-
|
|
83
|
-
return `${backup}-${random}`;
|
|
84
|
+
return `${backup}-${randomId}`;
|
|
84
85
|
};
|
|
85
86
|
|
|
86
87
|
// Link the first non-disabled input to the the Field label, if present
|
|
@@ -107,14 +108,16 @@ const PhoneNumberInput = ({
|
|
|
107
108
|
|
|
108
109
|
return explodeNumberModel(cleanValue);
|
|
109
110
|
});
|
|
110
|
-
const
|
|
111
|
+
const broadcastedValueRef = useRef<PhoneNumber>(internalValue);
|
|
111
112
|
|
|
112
113
|
const [suffixDirty, setSuffixDirty] = useState(false);
|
|
114
|
+
|
|
113
115
|
useEffect(() => {
|
|
114
|
-
if (internalValue.suffix) {
|
|
116
|
+
if (!suffixDirty && internalValue.suffix) {
|
|
117
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect -- Tracking when suffix becomes dirty
|
|
115
118
|
setSuffixDirty(true);
|
|
116
119
|
}
|
|
117
|
-
}, [internalValue.suffix]);
|
|
120
|
+
}, [internalValue.suffix, suffixDirty]);
|
|
118
121
|
|
|
119
122
|
const countriesByPrefix = useMemo(
|
|
120
123
|
() =>
|
|
@@ -152,13 +155,8 @@ const PhoneNumberInput = ({
|
|
|
152
155
|
};
|
|
153
156
|
|
|
154
157
|
useEffect(() => {
|
|
155
|
-
if (broadcastedValue === null) {
|
|
156
|
-
setBroadcastedValue(internalValue);
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
158
|
const internalPhoneNumber = `${internalValue.prefix ?? ''}${internalValue.suffix}`;
|
|
161
|
-
const broadcastedPhoneNumber = `${
|
|
159
|
+
const broadcastedPhoneNumber = `${broadcastedValueRef.current.prefix ?? ''}${broadcastedValueRef.current.suffix}`;
|
|
162
160
|
|
|
163
161
|
if (internalPhoneNumber === broadcastedPhoneNumber) {
|
|
164
162
|
return;
|
|
@@ -172,8 +170,8 @@ const PhoneNumberInput = ({
|
|
|
172
170
|
newValue,
|
|
173
171
|
internalValue.prefix ?? '', // TODO: Allow `null` in public API
|
|
174
172
|
);
|
|
175
|
-
|
|
176
|
-
}, [onChange,
|
|
173
|
+
broadcastedValueRef.current = internalValue;
|
|
174
|
+
}, [onChange, internalValue]);
|
|
177
175
|
|
|
178
176
|
useEffect(() => {
|
|
179
177
|
const labelRef = fieldLabelRef?.current;
|
|
@@ -47,7 +47,7 @@ export const TaskCard: Story = {
|
|
|
47
47
|
isSmall: true,
|
|
48
48
|
useDisplayFont: false,
|
|
49
49
|
className: 'taskCard',
|
|
50
|
-
}
|
|
50
|
+
},
|
|
51
51
|
decorators: [
|
|
52
52
|
(Story) => (
|
|
53
53
|
<div>
|
|
@@ -90,7 +90,7 @@ export const TaskCardWithCustomIcon: Story = {
|
|
|
90
90
|
args: {
|
|
91
91
|
...TaskCard.args,
|
|
92
92
|
indicatorIcon: <StarFill size={24} aria-hidden="true" />,
|
|
93
|
-
}
|
|
93
|
+
},
|
|
94
94
|
decorators: TaskCard.decorators,
|
|
95
95
|
};
|
|
96
96
|
|
|
@@ -101,7 +101,7 @@ export const TaskCardCompleted: Story = {
|
|
|
101
101
|
href: undefined,
|
|
102
102
|
indicatorIcon: 'check',
|
|
103
103
|
className: 'taskCard taskCard--completed np-theme--personal np-theme-personal--forest-green',
|
|
104
|
-
}
|
|
104
|
+
},
|
|
105
105
|
decorators: TaskCard.decorators,
|
|
106
106
|
};
|
|
107
107
|
|
|
@@ -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>
|
|
@@ -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
|
|
|
@@ -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
|
);
|
|
@@ -274,13 +275,11 @@ const UploadInput = ({
|
|
|
274
275
|
|
|
275
276
|
if (!isTypeValid(file, allowedFileTypes)) {
|
|
276
277
|
handleFileUploadFailure(file, formatMessage(MESSAGES.fileTypeNotSupported));
|
|
277
|
-
continue;
|
|
278
278
|
}
|
|
279
279
|
|
|
280
280
|
if (typeof sizeLimit === 'number' && !isSizeValid(file, sizeLimit * 1000)) {
|
|
281
281
|
const failureMessage = sizeLimitErrorMessage || formatMessage(MESSAGES.fileIsTooLarge);
|
|
282
282
|
handleFileUploadFailure(file, failureMessage);
|
|
283
|
-
continue;
|
|
284
283
|
}
|
|
285
284
|
|
|
286
285
|
if (areMaximumFilesUploadedAlready()) {
|
|
@@ -288,7 +287,6 @@ const UploadInput = ({
|
|
|
288
287
|
maxFilesErrorMessage ||
|
|
289
288
|
formatMessage(MESSAGES.maximumFilesAlreadyUploaded, { maxFilesAllowed: maxFiles });
|
|
290
289
|
handleFileUploadFailure(file, failureMessage);
|
|
291
|
-
continue;
|
|
292
290
|
}
|
|
293
291
|
|
|
294
292
|
const existingFile = uploadedFiles.find((f) => f.filename === file.name);
|
|
@@ -321,14 +319,16 @@ const UploadInput = ({
|
|
|
321
319
|
};
|
|
322
320
|
|
|
323
321
|
useEffect(() => {
|
|
324
|
-
|
|
325
|
-
|
|
322
|
+
// Skip onFilesChange call on initial mount, only call on updates
|
|
323
|
+
if (!mountedRef.current) {
|
|
324
|
+
mountedRef.current = true;
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
326
327
|
|
|
327
|
-
|
|
328
|
-
if (onFilesChange && mounted) {
|
|
328
|
+
if (onFilesChange) {
|
|
329
329
|
onFilesChange([...uploadedFiles]);
|
|
330
330
|
}
|
|
331
|
-
}, [onFilesChange, uploadedFiles]);
|
|
331
|
+
}, [onFilesChange, uploadedFiles]);
|
|
332
332
|
|
|
333
333
|
type NextFocusable =
|
|
334
334
|
| HTMLDivElement
|
|
@@ -336,14 +336,14 @@ const UploadInput = ({
|
|
|
336
336
|
| { ref: HTMLDivElement | UploadItemRef; target: 'button' | 'link' }
|
|
337
337
|
| null;
|
|
338
338
|
|
|
339
|
-
const [nextFocusable, setNextFocusable] = useState<NextFocusable>(
|
|
339
|
+
const [nextFocusable, setNextFocusable] = useState<NextFocusable>(null);
|
|
340
340
|
|
|
341
341
|
const handleFocus = (fileId: string | number) => {
|
|
342
|
-
fileRefs = fileRefs.filter((ref) => {
|
|
342
|
+
fileRefs.current = fileRefs.current.filter((ref) => {
|
|
343
343
|
return ref && ref.id !== markedFileForDelete?.id;
|
|
344
344
|
});
|
|
345
345
|
|
|
346
|
-
const filesCount = fileRefs.length;
|
|
346
|
+
const filesCount = fileRefs.current.length;
|
|
347
347
|
let next: UploadItemRef | HTMLDivElement | null = uploadInputRef.current;
|
|
348
348
|
let focusTarget: 'button' | 'link' = 'button';
|
|
349
349
|
|
|
@@ -355,15 +355,15 @@ const UploadInput = ({
|
|
|
355
355
|
}
|
|
356
356
|
|
|
357
357
|
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;
|
|
358
|
+
const currentFileIndex = fileRefs.current.findIndex((file) => file?.id === fileId);
|
|
359
|
+
const currentFileId = fileRefs.current?.[currentFileIndex]?.id;
|
|
360
|
+
const lastFileId = fileRefs.current?.[filesCount - 1]?.id;
|
|
361
361
|
|
|
362
362
|
// if last file, select a previous one
|
|
363
363
|
if (currentFileId === lastFileId) {
|
|
364
|
-
next = fileRefs[filesCount - 2];
|
|
364
|
+
next = fileRefs.current[filesCount - 2];
|
|
365
365
|
} else {
|
|
366
|
-
next = fileRefs[currentFileIndex + 1];
|
|
366
|
+
next = fileRefs.current[currentFileIndex + 1];
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
// If next is an UploadItemRef, check if it has a URL (succeeded)
|
|
@@ -401,7 +401,7 @@ const UploadInput = ({
|
|
|
401
401
|
typeof uploadInputRef.current.focus === 'function'
|
|
402
402
|
) {
|
|
403
403
|
setTimeout(() => {
|
|
404
|
-
uploadInputRef.current
|
|
404
|
+
uploadInputRef.current?.focus();
|
|
405
405
|
}, 0);
|
|
406
406
|
} else if (
|
|
407
407
|
typeof focusTarget === 'object' &&
|
|
@@ -436,17 +436,17 @@ const UploadInput = ({
|
|
|
436
436
|
aria-relevant="all"
|
|
437
437
|
role="region"
|
|
438
438
|
>
|
|
439
|
-
{uploadedFiles.map((file
|
|
439
|
+
{uploadedFiles.map((file) => (
|
|
440
440
|
<UploadItem
|
|
441
441
|
key={file.id}
|
|
442
442
|
ref={(el: UploadItemRef | null) => {
|
|
443
443
|
if (
|
|
444
444
|
el &&
|
|
445
445
|
el.id !== markedFileForDelete?.id &&
|
|
446
|
-
!fileRefs.some((ref) => ref
|
|
446
|
+
!fileRefs.current.some((ref) => ref?.id === el.id) &&
|
|
447
447
|
el.status !== 'processing'
|
|
448
448
|
) {
|
|
449
|
-
fileRefs.push(el);
|
|
449
|
+
fileRefs.current.push(el);
|
|
450
450
|
}
|
|
451
451
|
}}
|
|
452
452
|
file={file}
|
|
@@ -478,6 +478,7 @@ const UploadInput = ({
|
|
|
478
478
|
ref={uploadInputRef}
|
|
479
479
|
id={id}
|
|
480
480
|
uploadButtonTitle={uploadButtonTitle}
|
|
481
|
+
// eslint-disable-next-line react-hooks/refs -- Function reads ref for file count check
|
|
481
482
|
disabled={areMaximumFilesUploadedAlready() || disabled}
|
|
482
483
|
multiple={multiple}
|
|
483
484
|
fileTypes={fileTypes}
|