carbon-react 124.2.2 → 124.3.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.
Files changed (49) hide show
  1. package/esm/__internal__/utils/helpers/tags/tags-specs/tags-specs.d.ts +1 -1
  2. package/esm/__internal__/utils/helpers/tags/tags-specs/tags-specs.js +6 -4
  3. package/esm/__spec_helper__/index.js +3 -1
  4. package/esm/__spec_helper__/mock-element-scrollto.d.ts +2 -0
  5. package/esm/__spec_helper__/mock-element-scrollto.js +6 -0
  6. package/esm/__spec_helper__/select-test-utils.d.ts +3 -0
  7. package/esm/__spec_helper__/select-test-utils.js +39 -0
  8. package/esm/components/file-input/__internal__/file-upload-status/file-upload-status.component.d.ts +35 -0
  9. package/esm/components/file-input/__internal__/file-upload-status/file-upload-status.component.js +73 -0
  10. package/esm/components/file-input/__internal__/file-upload-status/file-upload-status.style.d.ts +12 -0
  11. package/esm/components/file-input/__internal__/file-upload-status/file-upload-status.style.js +97 -0
  12. package/esm/components/file-input/__internal__/file-upload-status/index.d.ts +2 -0
  13. package/esm/components/file-input/__internal__/file-upload-status/index.js +1 -0
  14. package/esm/components/file-input/file-input.component.d.ts +36 -0
  15. package/esm/components/file-input/file-input.component.js +353 -0
  16. package/esm/components/file-input/file-input.style.d.ts +14 -0
  17. package/esm/components/file-input/file-input.style.js +58 -0
  18. package/esm/components/file-input/index.d.ts +3 -0
  19. package/esm/components/file-input/index.js +1 -0
  20. package/esm/components/select/select-list/select-list.component.js +41 -7
  21. package/esm/locales/en-gb.js +10 -0
  22. package/esm/locales/locale.d.ts +10 -0
  23. package/esm/locales/pl-pl.js +10 -0
  24. package/lib/__internal__/utils/helpers/tags/tags-specs/tags-specs.d.ts +1 -1
  25. package/lib/__internal__/utils/helpers/tags/tags-specs/tags-specs.js +6 -4
  26. package/lib/__spec_helper__/index.js +3 -1
  27. package/lib/__spec_helper__/mock-element-scrollto.d.ts +2 -0
  28. package/lib/__spec_helper__/mock-element-scrollto.js +12 -0
  29. package/lib/__spec_helper__/select-test-utils.d.ts +3 -0
  30. package/lib/__spec_helper__/select-test-utils.js +47 -0
  31. package/lib/components/file-input/__internal__/file-upload-status/file-upload-status.component.d.ts +35 -0
  32. package/lib/components/file-input/__internal__/file-upload-status/file-upload-status.component.js +81 -0
  33. package/lib/components/file-input/__internal__/file-upload-status/file-upload-status.style.d.ts +12 -0
  34. package/lib/components/file-input/__internal__/file-upload-status/file-upload-status.style.js +106 -0
  35. package/lib/components/file-input/__internal__/file-upload-status/index.d.ts +2 -0
  36. package/lib/components/file-input/__internal__/file-upload-status/index.js +13 -0
  37. package/lib/components/file-input/__internal__/file-upload-status/package.json +6 -0
  38. package/lib/components/file-input/file-input.component.d.ts +36 -0
  39. package/lib/components/file-input/file-input.component.js +361 -0
  40. package/lib/components/file-input/file-input.style.d.ts +14 -0
  41. package/lib/components/file-input/file-input.style.js +67 -0
  42. package/lib/components/file-input/index.d.ts +3 -0
  43. package/lib/components/file-input/index.js +13 -0
  44. package/lib/components/file-input/package.json +6 -0
  45. package/lib/components/select/select-list/select-list.component.js +40 -6
  46. package/lib/locales/en-gb.js +10 -0
  47. package/lib/locales/locale.d.ts +10 -0
  48. package/lib/locales/pl-pl.js +10 -0
  49. package/package.json +3 -2
@@ -0,0 +1,353 @@
1
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
+ import React, { useRef, useState, useEffect } from "react";
3
+ import PropTypes from "prop-types";
4
+ import { filterStyledSystemMarginProps } from "../../style/utils";
5
+ import { InputBehaviour } from "../../__internal__/input-behaviour";
6
+ import FormField from "../../__internal__/form-field";
7
+ import useUniqueId from "../../hooks/__internal__/useUniqueId";
8
+ import useInputAccessibility from "../../hooks/__internal__/useInputAccessibility/useInputAccessibility";
9
+ import ValidationMessage from "../../__internal__/validation-message";
10
+ import { StyledHiddenFileInput, StyledFileInputPresentation } from "./file-input.style";
11
+ import { StyledHintText, ErrorBorder } from "../textbox/textbox.style";
12
+ import ButtonMinor from "../button-minor";
13
+ import Typography from "../typography";
14
+ import FileUploadStatus from "./__internal__/file-upload-status";
15
+ import Box from "../box";
16
+ import useLocale from "../../hooks/__internal__/useLocale";
17
+ const FileInput = /*#__PURE__*/React.forwardRef(({
18
+ accept,
19
+ buttonText,
20
+ "data-element": dataElement,
21
+ "data-role": dataRole,
22
+ dragAndDropText,
23
+ error,
24
+ label,
25
+ id,
26
+ inputHint,
27
+ isVertical,
28
+ maxHeight,
29
+ maxWidth,
30
+ minHeight = "40px",
31
+ minWidth = "256px",
32
+ name,
33
+ onChange,
34
+ required,
35
+ uploadStatus = [],
36
+ ...rest
37
+ }, ref) => {
38
+ const locale = useLocale();
39
+ const textOnButton = buttonText || locale.fileInput.selectFile();
40
+ const mainText = dragAndDropText || locale.fileInput.dragAndDrop();
41
+ const sizeProps = {
42
+ maxHeight: maxHeight || (isVertical ? undefined : minHeight),
43
+ maxWidth: maxWidth || minWidth,
44
+ minHeight,
45
+ minWidth
46
+ };
47
+ const [uniqueId, uniqueName] = useUniqueId(id, name);
48
+ const [isDraggedOver, setIsDraggedOver] = useState(false);
49
+ const [isDraggingFile, setIsDraggingFile] = useState(false);
50
+ const internalInputRef = useRef(null);
51
+ const internalCallbackRef = fileInput => {
52
+ internalInputRef.current = fileInput;
53
+ if (typeof ref === "function") {
54
+ ref(fileInput);
55
+ } else if (ref) {
56
+ ref.current = fileInput;
57
+ }
58
+ };
59
+ const startDrag = e => {
60
+ e.preventDefault();
61
+ if (e.dataTransfer?.types.includes("Files")) {
62
+ setIsDraggingFile(true);
63
+ }
64
+ };
65
+ const stopDrag = e => {
66
+ e.preventDefault();
67
+ setIsDraggingFile(false);
68
+ };
69
+ useEffect(() => {
70
+ document.addEventListener("dragover", startDrag);
71
+ document.addEventListener("drop", stopDrag);
72
+ document.addEventListener("dragleave", stopDrag);
73
+ return () => {
74
+ document.removeEventListener("dragover", startDrag);
75
+ document.removeEventListener("drop", stopDrag);
76
+ document.removeEventListener("dragleave", stopDrag);
77
+ };
78
+ }, []);
79
+ const onSelectFileClick = () => {
80
+ internalInputRef.current?.click();
81
+ };
82
+ const onFileAdded = files => {
83
+ onChange?.(files);
84
+ };
85
+ const onDragOver = e => {
86
+ e.preventDefault();
87
+ if (e.dataTransfer?.types.includes("Files")) {
88
+ setIsDraggedOver(true);
89
+ }
90
+ };
91
+ const onDragLeave = e => {
92
+ e.preventDefault();
93
+ e.stopPropagation(); // stop event triggering the document listener that resets the styles
94
+ setIsDraggedOver(false);
95
+ };
96
+ const onDrop = e => {
97
+ e.preventDefault();
98
+ setIsDraggedOver(false);
99
+ onFileAdded(e.dataTransfer.files);
100
+ };
101
+ const onInputChange = e => {
102
+ onFileAdded(e.target.files);
103
+ };
104
+ const {
105
+ labelId,
106
+ validationId
107
+ } = useInputAccessibility({
108
+ id: uniqueId,
109
+ validationRedesignOptIn: true,
110
+ error,
111
+ label
112
+ });
113
+
114
+ // allow for single input that a single set of status props is provided
115
+ const filesUploaded = Array.isArray(uploadStatus) ? uploadStatus : [uploadStatus];
116
+ const input = /*#__PURE__*/React.createElement(React.Fragment, null, inputHint && /*#__PURE__*/React.createElement(StyledHintText, null, inputHint), /*#__PURE__*/React.createElement(Box, {
117
+ position: "relative"
118
+ }, /*#__PURE__*/React.createElement(ValidationMessage, {
119
+ error: error,
120
+ validationId: validationId
121
+ }), error && /*#__PURE__*/React.createElement(ErrorBorder, {
122
+ warning: false
123
+ }), /*#__PURE__*/React.createElement(StyledHiddenFileInput, _extends({}, required && {
124
+ required
125
+ }, {
126
+ accept: accept,
127
+ "aria-invalid": !!error,
128
+ id: uniqueId,
129
+ ref: internalCallbackRef,
130
+ name: uniqueName,
131
+ onChange: onInputChange,
132
+ type: "file"
133
+ }, rest)), /*#__PURE__*/React.createElement(StyledFileInputPresentation, _extends({
134
+ isDraggedOver: isDraggedOver,
135
+ isDraggingFile: isDraggingFile,
136
+ error: error,
137
+ onDragLeave: onDragLeave,
138
+ onDragOver: onDragOver,
139
+ onDrop: onDrop
140
+ }, sizeProps), /*#__PURE__*/React.createElement(ButtonMinor, {
141
+ buttonType: "primary",
142
+ onClick: onSelectFileClick
143
+ }, textOnButton), /*#__PURE__*/React.createElement(Typography, {
144
+ m: 0
145
+ }, mainText))));
146
+ return /*#__PURE__*/React.createElement(InputBehaviour, null, /*#__PURE__*/React.createElement(FormField, _extends({
147
+ error: error,
148
+ label: label,
149
+ labelId: labelId,
150
+ id: uniqueId,
151
+ isRequired: required,
152
+ "data-component": "file-input",
153
+ "data-role": dataRole,
154
+ "data-element": dataElement,
155
+ validationRedesignOptIn: true // do not support old-style validation for File Input component
156
+ }, filterStyledSystemMarginProps(rest)), filesUploaded.length === 0 ? input : filesUploaded.map(props => /*#__PURE__*/React.createElement(StyledFileInputPresentation, _extends({
157
+ hasUploadStatus: true
158
+ }, sizeProps, {
159
+ key: props.filename
160
+ }), /*#__PURE__*/React.createElement(FileUploadStatus, props)))));
161
+ });
162
+ FileInput.propTypes = {
163
+ "accept": PropTypes.string,
164
+ "buttonText": PropTypes.string,
165
+ "data-component": PropTypes.string,
166
+ "data-element": PropTypes.string,
167
+ "data-role": PropTypes.string,
168
+ "dragAndDropText": PropTypes.string,
169
+ "error": PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
170
+ "id": PropTypes.string,
171
+ "inputHint": PropTypes.node,
172
+ "isVertical": PropTypes.bool,
173
+ "label": PropTypes.string,
174
+ "m": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.number, PropTypes.shape({
175
+ "__@toStringTag": PropTypes.string.isRequired,
176
+ "description": PropTypes.string,
177
+ "toString": PropTypes.func.isRequired,
178
+ "valueOf": PropTypes.func.isRequired
179
+ }), PropTypes.string])), PropTypes.number, PropTypes.object, PropTypes.shape({
180
+ "__@toStringTag": PropTypes.string.isRequired,
181
+ "description": PropTypes.string,
182
+ "toString": PropTypes.func.isRequired,
183
+ "valueOf": PropTypes.func.isRequired
184
+ }), PropTypes.string]),
185
+ "margin": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.number, PropTypes.shape({
186
+ "__@toStringTag": PropTypes.string.isRequired,
187
+ "description": PropTypes.string,
188
+ "toString": PropTypes.func.isRequired,
189
+ "valueOf": PropTypes.func.isRequired
190
+ }), PropTypes.string])), PropTypes.number, PropTypes.object, PropTypes.shape({
191
+ "__@toStringTag": PropTypes.string.isRequired,
192
+ "description": PropTypes.string,
193
+ "toString": PropTypes.func.isRequired,
194
+ "valueOf": PropTypes.func.isRequired
195
+ }), PropTypes.string]),
196
+ "marginBottom": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.number, PropTypes.shape({
197
+ "__@toStringTag": PropTypes.string.isRequired,
198
+ "description": PropTypes.string,
199
+ "toString": PropTypes.func.isRequired,
200
+ "valueOf": PropTypes.func.isRequired
201
+ }), PropTypes.string])), PropTypes.number, PropTypes.object, PropTypes.shape({
202
+ "__@toStringTag": PropTypes.string.isRequired,
203
+ "description": PropTypes.string,
204
+ "toString": PropTypes.func.isRequired,
205
+ "valueOf": PropTypes.func.isRequired
206
+ }), PropTypes.string]),
207
+ "marginLeft": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.number, PropTypes.shape({
208
+ "__@toStringTag": PropTypes.string.isRequired,
209
+ "description": PropTypes.string,
210
+ "toString": PropTypes.func.isRequired,
211
+ "valueOf": PropTypes.func.isRequired
212
+ }), PropTypes.string])), PropTypes.number, PropTypes.object, PropTypes.shape({
213
+ "__@toStringTag": PropTypes.string.isRequired,
214
+ "description": PropTypes.string,
215
+ "toString": PropTypes.func.isRequired,
216
+ "valueOf": PropTypes.func.isRequired
217
+ }), PropTypes.string]),
218
+ "marginRight": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.number, PropTypes.shape({
219
+ "__@toStringTag": PropTypes.string.isRequired,
220
+ "description": PropTypes.string,
221
+ "toString": PropTypes.func.isRequired,
222
+ "valueOf": PropTypes.func.isRequired
223
+ }), PropTypes.string])), PropTypes.number, PropTypes.object, PropTypes.shape({
224
+ "__@toStringTag": PropTypes.string.isRequired,
225
+ "description": PropTypes.string,
226
+ "toString": PropTypes.func.isRequired,
227
+ "valueOf": PropTypes.func.isRequired
228
+ }), PropTypes.string]),
229
+ "marginTop": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.number, PropTypes.shape({
230
+ "__@toStringTag": PropTypes.string.isRequired,
231
+ "description": PropTypes.string,
232
+ "toString": PropTypes.func.isRequired,
233
+ "valueOf": PropTypes.func.isRequired
234
+ }), PropTypes.string])), PropTypes.number, PropTypes.object, PropTypes.shape({
235
+ "__@toStringTag": PropTypes.string.isRequired,
236
+ "description": PropTypes.string,
237
+ "toString": PropTypes.func.isRequired,
238
+ "valueOf": PropTypes.func.isRequired
239
+ }), PropTypes.string]),
240
+ "marginX": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.number, PropTypes.shape({
241
+ "__@toStringTag": PropTypes.string.isRequired,
242
+ "description": PropTypes.string,
243
+ "toString": PropTypes.func.isRequired,
244
+ "valueOf": PropTypes.func.isRequired
245
+ }), PropTypes.string])), PropTypes.number, PropTypes.object, PropTypes.shape({
246
+ "__@toStringTag": PropTypes.string.isRequired,
247
+ "description": PropTypes.string,
248
+ "toString": PropTypes.func.isRequired,
249
+ "valueOf": PropTypes.func.isRequired
250
+ }), PropTypes.string]),
251
+ "marginY": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.number, PropTypes.shape({
252
+ "__@toStringTag": PropTypes.string.isRequired,
253
+ "description": PropTypes.string,
254
+ "toString": PropTypes.func.isRequired,
255
+ "valueOf": PropTypes.func.isRequired
256
+ }), PropTypes.string])), PropTypes.number, PropTypes.object, PropTypes.shape({
257
+ "__@toStringTag": PropTypes.string.isRequired,
258
+ "description": PropTypes.string,
259
+ "toString": PropTypes.func.isRequired,
260
+ "valueOf": PropTypes.func.isRequired
261
+ }), PropTypes.string]),
262
+ "maxHeight": PropTypes.string,
263
+ "maxWidth": PropTypes.string,
264
+ "mb": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.number, PropTypes.shape({
265
+ "__@toStringTag": PropTypes.string.isRequired,
266
+ "description": PropTypes.string,
267
+ "toString": PropTypes.func.isRequired,
268
+ "valueOf": PropTypes.func.isRequired
269
+ }), PropTypes.string])), PropTypes.number, PropTypes.object, PropTypes.shape({
270
+ "__@toStringTag": PropTypes.string.isRequired,
271
+ "description": PropTypes.string,
272
+ "toString": PropTypes.func.isRequired,
273
+ "valueOf": PropTypes.func.isRequired
274
+ }), PropTypes.string]),
275
+ "minHeight": PropTypes.string,
276
+ "minWidth": PropTypes.string,
277
+ "ml": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.number, PropTypes.shape({
278
+ "__@toStringTag": PropTypes.string.isRequired,
279
+ "description": PropTypes.string,
280
+ "toString": PropTypes.func.isRequired,
281
+ "valueOf": PropTypes.func.isRequired
282
+ }), PropTypes.string])), PropTypes.number, PropTypes.object, PropTypes.shape({
283
+ "__@toStringTag": PropTypes.string.isRequired,
284
+ "description": PropTypes.string,
285
+ "toString": PropTypes.func.isRequired,
286
+ "valueOf": PropTypes.func.isRequired
287
+ }), PropTypes.string]),
288
+ "mr": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.number, PropTypes.shape({
289
+ "__@toStringTag": PropTypes.string.isRequired,
290
+ "description": PropTypes.string,
291
+ "toString": PropTypes.func.isRequired,
292
+ "valueOf": PropTypes.func.isRequired
293
+ }), PropTypes.string])), PropTypes.number, PropTypes.object, PropTypes.shape({
294
+ "__@toStringTag": PropTypes.string.isRequired,
295
+ "description": PropTypes.string,
296
+ "toString": PropTypes.func.isRequired,
297
+ "valueOf": PropTypes.func.isRequired
298
+ }), PropTypes.string]),
299
+ "mt": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.number, PropTypes.shape({
300
+ "__@toStringTag": PropTypes.string.isRequired,
301
+ "description": PropTypes.string,
302
+ "toString": PropTypes.func.isRequired,
303
+ "valueOf": PropTypes.func.isRequired
304
+ }), PropTypes.string])), PropTypes.number, PropTypes.object, PropTypes.shape({
305
+ "__@toStringTag": PropTypes.string.isRequired,
306
+ "description": PropTypes.string,
307
+ "toString": PropTypes.func.isRequired,
308
+ "valueOf": PropTypes.func.isRequired
309
+ }), PropTypes.string]),
310
+ "mx": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.number, PropTypes.shape({
311
+ "__@toStringTag": PropTypes.string.isRequired,
312
+ "description": PropTypes.string,
313
+ "toString": PropTypes.func.isRequired,
314
+ "valueOf": PropTypes.func.isRequired
315
+ }), PropTypes.string])), PropTypes.number, PropTypes.object, PropTypes.shape({
316
+ "__@toStringTag": PropTypes.string.isRequired,
317
+ "description": PropTypes.string,
318
+ "toString": PropTypes.func.isRequired,
319
+ "valueOf": PropTypes.func.isRequired
320
+ }), PropTypes.string]),
321
+ "my": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.number, PropTypes.shape({
322
+ "__@toStringTag": PropTypes.string.isRequired,
323
+ "description": PropTypes.string,
324
+ "toString": PropTypes.func.isRequired,
325
+ "valueOf": PropTypes.func.isRequired
326
+ }), PropTypes.string])), PropTypes.number, PropTypes.object, PropTypes.shape({
327
+ "__@toStringTag": PropTypes.string.isRequired,
328
+ "description": PropTypes.string,
329
+ "toString": PropTypes.func.isRequired,
330
+ "valueOf": PropTypes.func.isRequired
331
+ }), PropTypes.string]),
332
+ "name": PropTypes.string,
333
+ "onChange": PropTypes.func.isRequired,
334
+ "required": PropTypes.bool,
335
+ "uploadStatus": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.object, PropTypes.shape({
336
+ "filename": PropTypes.string.isRequired,
337
+ "iconType": PropTypes.oneOf(["accessibility_web", "add", "admin", "alert_on", "alert", "analysis", "app_facebook", "app_instagram", "app_tiktok", "app_twitter", "app_youtube", "apps", "arrow_bottom_right_circle", "arrow_down", "arrow_left_boxed", "arrow_left_right_small", "arrow_left_small", "arrow_left", "arrow_right_small", "arrow_right", "arrow_top_left_circle", "arrow_up", "arrow", "arrows_left_right", "attach", "bank_with_card", "bank", "basket_with_squares", "basket", "bed", "bill_paid", "bin", "block_arrow_right", "blocked_square", "blocked", "bold", "box_arrow_left", "box_arrow_right", "boxed_shapes", "bulk_destroy", "bullet_list_dotted", "bullet_list_numbers", "bullet_list", "business", "calendar_pay_date", "calendar_today", "calendar", "call", "camera", "car_lock", "car_money", "car_repair", "card_view", "card_wallet", "caret_down", "caret_large_down", "caret_large_left", "caret_large_right", "caret_large_up", "caret_left", "caret_right", "caret_up", "cart", "cash", "chart_bar", "chart_line", "chart_pie", "chat_notes", "chat", "check_all", "check_none", "chevron_down_thick", "chevron_down", "chevron_left_thick", "chevron_left", "chevron_right_thick", "chevron_right", "chevron_up_thick", "chevron_up", "circle_with_dots", "circles_connection", "clock", "close", "cloud_co2", "coins", "collaborate", "computer_clock", "connect_off", "connect", "construction", "contact_card", "contacts", "copy", "create", "credit_card_slash", "credit_card", "cross_circle", "cross", "csv", "dashboard", "delete", "delivery", "disconnect", "disputed", "document_right_align", "document_tick", "document_vertical_lines", "download", "draft", "drag_vertical", "drag", "drill", "dropdown", "duplicate", "edit", "edited", "ellipsis_horizontal", "ellipsis_vertical", "email_switch", "email", "entry", "envelope_dollar", "envelope_euro", "error_square", "error", "euro", "expand", "factory", "favourite_lined", "favourite", "fax", "feedback", "file_excel", "file_generic", "file_image", "file_pdf", "file_word", "files_leaning", "filter_new", "filter", "fit_height", "fit_width", "flag", "folder", "form_refresh", "gift", "go", "graduation_hat", "graph", "grid", "hand_cash_coins", "hand_cash_note", "heart_pulse", "help", "hide", "home", "image", "in_progress", "in_transit", "individual", "info", "intranet", "italic", "job_seeked", "key", "laptop", "leaf", "ledger_arrow_left", "ledger_arrow_right", "ledger", "lightbulb_off", "lightbulb_on", "like_no", "like", "link_cloud", "link_on", "link", "list_view", "location", "locked", "logout", "lookup", "marker", "message", "microphone", "minus_large", "minus", "mobile", "money_bag", "none", "old_warning", "palm_tree", "pause_circle", "pause", "pdf", "people_switch", "people", "percentage_boxed", "person_info", "person_tick", "person", "petrol_pump", "phone", "piggy_bank", "plane", "play_circle", "play", "plus_large", "plus", "pound", "print", "progress", "progressed", "protect", "question_hollow", "question_mark", "question", "recruiting", "refresh_clock", "refresh", "remove", "sage_coin", "save", "scan", "search", "send", "services", "settings_old", "settings", "share", "shop", "sort_down", "sort_up", "spanner", "split_container", "split", "square_dot", "squares_nine", "stacked_boxes", "stacked_squares", "submitted", "support_online", "sync", "tag", "talk", "target_man", "target", "theatre_masks", "three_boxes", "tick_circle", "tick_thick", "tick", "true_tick", "u_turn_left", "u_turn_right", "undo", "unlocked", "upload", "uploaded", "video", "view", "volunteering", "warning", "website", "welfare"]),
338
+ "message": PropTypes.string,
339
+ "onAction": PropTypes.func.isRequired,
340
+ "progress": PropTypes.number,
341
+ "status": PropTypes.oneOf(["uploading"]).isRequired
342
+ })])), PropTypes.object, PropTypes.shape({
343
+ "filename": PropTypes.string.isRequired,
344
+ "iconType": PropTypes.oneOf(["accessibility_web", "add", "admin", "alert_on", "alert", "analysis", "app_facebook", "app_instagram", "app_tiktok", "app_twitter", "app_youtube", "apps", "arrow_bottom_right_circle", "arrow_down", "arrow_left_boxed", "arrow_left_right_small", "arrow_left_small", "arrow_left", "arrow_right_small", "arrow_right", "arrow_top_left_circle", "arrow_up", "arrow", "arrows_left_right", "attach", "bank_with_card", "bank", "basket_with_squares", "basket", "bed", "bill_paid", "bin", "block_arrow_right", "blocked_square", "blocked", "bold", "box_arrow_left", "box_arrow_right", "boxed_shapes", "bulk_destroy", "bullet_list_dotted", "bullet_list_numbers", "bullet_list", "business", "calendar_pay_date", "calendar_today", "calendar", "call", "camera", "car_lock", "car_money", "car_repair", "card_view", "card_wallet", "caret_down", "caret_large_down", "caret_large_left", "caret_large_right", "caret_large_up", "caret_left", "caret_right", "caret_up", "cart", "cash", "chart_bar", "chart_line", "chart_pie", "chat_notes", "chat", "check_all", "check_none", "chevron_down_thick", "chevron_down", "chevron_left_thick", "chevron_left", "chevron_right_thick", "chevron_right", "chevron_up_thick", "chevron_up", "circle_with_dots", "circles_connection", "clock", "close", "cloud_co2", "coins", "collaborate", "computer_clock", "connect_off", "connect", "construction", "contact_card", "contacts", "copy", "create", "credit_card_slash", "credit_card", "cross_circle", "cross", "csv", "dashboard", "delete", "delivery", "disconnect", "disputed", "document_right_align", "document_tick", "document_vertical_lines", "download", "draft", "drag_vertical", "drag", "drill", "dropdown", "duplicate", "edit", "edited", "ellipsis_horizontal", "ellipsis_vertical", "email_switch", "email", "entry", "envelope_dollar", "envelope_euro", "error_square", "error", "euro", "expand", "factory", "favourite_lined", "favourite", "fax", "feedback", "file_excel", "file_generic", "file_image", "file_pdf", "file_word", "files_leaning", "filter_new", "filter", "fit_height", "fit_width", "flag", "folder", "form_refresh", "gift", "go", "graduation_hat", "graph", "grid", "hand_cash_coins", "hand_cash_note", "heart_pulse", "help", "hide", "home", "image", "in_progress", "in_transit", "individual", "info", "intranet", "italic", "job_seeked", "key", "laptop", "leaf", "ledger_arrow_left", "ledger_arrow_right", "ledger", "lightbulb_off", "lightbulb_on", "like_no", "like", "link_cloud", "link_on", "link", "list_view", "location", "locked", "logout", "lookup", "marker", "message", "microphone", "minus_large", "minus", "mobile", "money_bag", "none", "old_warning", "palm_tree", "pause_circle", "pause", "pdf", "people_switch", "people", "percentage_boxed", "person_info", "person_tick", "person", "petrol_pump", "phone", "piggy_bank", "plane", "play_circle", "play", "plus_large", "plus", "pound", "print", "progress", "progressed", "protect", "question_hollow", "question_mark", "question", "recruiting", "refresh_clock", "refresh", "remove", "sage_coin", "save", "scan", "search", "send", "services", "settings_old", "settings", "share", "shop", "sort_down", "sort_up", "spanner", "split_container", "split", "square_dot", "squares_nine", "stacked_boxes", "stacked_squares", "submitted", "support_online", "sync", "tag", "talk", "target_man", "target", "theatre_masks", "three_boxes", "tick_circle", "tick_thick", "tick", "true_tick", "u_turn_left", "u_turn_right", "undo", "unlocked", "upload", "uploaded", "video", "view", "volunteering", "warning", "website", "welfare"]),
345
+ "message": PropTypes.string,
346
+ "onAction": PropTypes.func.isRequired,
347
+ "progress": PropTypes.number,
348
+ "status": PropTypes.oneOf(["uploading"]).isRequired
349
+ })])
350
+ };
351
+ export { FileInput };
352
+ FileInput.displayName = "FileInput";
353
+ export default FileInput;
@@ -0,0 +1,14 @@
1
+ import type { InputProps } from "../../__internal__/input";
2
+ import type { ValidationProps } from "../../__internal__/validations";
3
+ export declare const StyledHiddenFileInput: import("styled-components").StyledComponent<"input", any, InputProps, never>;
4
+ interface StyledFileInputPresentationProps extends Pick<ValidationProps, "error"> {
5
+ isDraggedOver?: boolean;
6
+ isDraggingFile?: boolean;
7
+ hasUploadStatus?: boolean;
8
+ maxHeight?: string;
9
+ maxWidth?: string;
10
+ minHeight?: string;
11
+ minWidth?: string;
12
+ }
13
+ export declare const StyledFileInputPresentation: import("styled-components").StyledComponent<"div", any, StyledFileInputPresentationProps, never>;
14
+ export {};
@@ -0,0 +1,58 @@
1
+ import styled, { css } from "styled-components";
2
+ import StyledTypography from "../typography/typography.style";
3
+ export const StyledHiddenFileInput = styled.input`
4
+ display: none;
5
+ `;
6
+ export const StyledFileInputPresentation = styled.div`
7
+ ${({
8
+ hasUploadStatus,
9
+ minWidth,
10
+ minHeight,
11
+ maxWidth,
12
+ maxHeight
13
+ }) => css`
14
+ min-width: ${minWidth};
15
+ min-height: ${minHeight};
16
+ max-width: ${maxWidth};
17
+ ${!hasUploadStatus && css`
18
+ padding: var(--spacing150);
19
+ max-height: ${maxHeight};
20
+ `}
21
+ `}
22
+
23
+ ${({
24
+ hasUploadStatus,
25
+ isDraggedOver,
26
+ isDraggingFile,
27
+ error
28
+ }) => {
29
+ const borderWidthToken = error || isDraggingFile ? "borderWidth200" : "borderWidth100";
30
+ let borderColorToken = "colorsUtilityMajor300";
31
+ let backgroundColorToken = "colorsUtilityYang100";
32
+ if (isDraggedOver) {
33
+ borderColorToken = "colorsUtilityMajor400";
34
+ backgroundColorToken = "colorsUtilityMajor100";
35
+ } else if (isDraggingFile) {
36
+ borderColorToken = "colorsUtilityMajor400";
37
+ }
38
+ if (error) {
39
+ borderColorToken = `colorsSemanticNegative${isDraggingFile ? 600 : 500}`;
40
+ }
41
+ return !hasUploadStatus && css`
42
+ display: flex;
43
+ flex-direction: column;
44
+ flex-wrap: wrap;
45
+ justify-content: center;
46
+ align-content: center;
47
+ align-items: center;
48
+ text-align: center;
49
+ gap: var(--spacing150);
50
+ border-radius: var(--borderRadius050);
51
+ border: var(--${borderWidthToken}) dashed var(--${borderColorToken});
52
+ background: var(--${backgroundColorToken});
53
+ ${StyledTypography} {
54
+ color: var(--colorsUtilityYin055);
55
+ }
56
+ `;
57
+ }}
58
+ `;
@@ -0,0 +1,3 @@
1
+ export { default } from "./file-input.component";
2
+ export type { FileInputProps } from "./file-input.component";
3
+ export type { FileUploadStatusProps } from "./__internal__/file-upload-status";
@@ -0,0 +1 @@
1
+ export { default } from "./file-input.component";
@@ -2,8 +2,9 @@ function _extends() { _extends = Object.assign ? Object.assign.bind() : function
2
2
  import React, { useEffect, useState, useCallback, useLayoutEffect, useRef, useMemo } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { flip, offset, size } from "@floating-ui/dom";
5
- import { useVirtualizer } from "@tanstack/react-virtual";
5
+ import { useVirtualizer, defaultRangeExtractor } from "@tanstack/react-virtual";
6
6
  import findLastIndex from "lodash/findLastIndex";
7
+ import usePrevious from "../../../hooks/__internal__/usePrevious";
7
8
  import useScrollBlock from "../../../hooks/__internal__/useScrollBlock";
8
9
  import useModalManager from "../../../hooks/__internal__/useModalManager";
9
10
  import { StyledSelectList, StyledSelectLoaderContainer, StyledSelectListTable, StyledSelectListTableHeader, StyledSelectListTableBody } from "./select-list.style";
@@ -60,7 +61,26 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
60
61
  allowScroll
61
62
  } = useScrollBlock();
62
63
  const actionButtonHeight = useRef(0);
64
+ const wasOpen = usePrevious(isOpen);
65
+
66
+ // ensure scroll-position goes back to the top whenever the list is (re)-opened. (On Safari, without this it remains at the bottom if it had been scrolled
67
+ // to the bottom before closing.)
68
+ useEffect(() => {
69
+ if (isOpen && !wasOpen) {
70
+ listContainerRef.current?.scrollTo(0, 0);
71
+ }
72
+ });
63
73
  const overscan = enableVirtualScroll ? virtualScrollOverscan : React.Children.count(children);
74
+
75
+ // need to use a custom rangeExtractor that ensure the currently-selected option, if any, always appears as an option returned from the virtualiser.
76
+ // This ensures that the aria-activedescendant value is always valid even when the currently-selected item is out of the visible range.
77
+ const rangeExtractor = range => {
78
+ const toRender = defaultRangeExtractor(range);
79
+ if (currentOptionsListIndex !== -1 && !toRender.includes(currentOptionsListIndex)) {
80
+ toRender.push(currentOptionsListIndex);
81
+ }
82
+ return toRender;
83
+ };
64
84
  const virtualizer = useVirtualizer({
65
85
  count: React.Children.count(children),
66
86
  getScrollElement: () => listContainerRef.current,
@@ -68,9 +88,21 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
68
88
  // value doesn't really seem to matter since we're dynamically measuring, but 40px is the height of a single-line option
69
89
  overscan,
70
90
  paddingStart: multiColumn ? TABLE_HEADER_HEIGHT : 0,
71
- scrollPaddingEnd: actionButtonHeight.current
91
+ scrollPaddingEnd: actionButtonHeight.current,
92
+ rangeExtractor
72
93
  });
73
94
  const items = virtualizer.getVirtualItems();
95
+
96
+ // getVirtualItems returns an empty array of items if the select list is currently closed - which is correct visually but
97
+ // we need to ensure that any currently-selected option is still in the DOM to avoid any accessibility issues.
98
+ // The isOpen prop will ensure that no options are visible regardless of what is in the items array.
99
+ if (items.length === 0 && currentOptionsListIndex !== -1) {
100
+ // only index property is required with the item not visible so the following type assertion, even though incorrect,
101
+ // should be OK
102
+ items.push({
103
+ index: currentOptionsListIndex
104
+ });
105
+ }
74
106
  const listHeight = virtualizer.getTotalSize();
75
107
  useEffect(() => {
76
108
  if (isOpen) {
@@ -119,11 +151,13 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
119
151
  measureElement(element);
120
152
  }
121
153
  };
122
- const renderedChildren = items.map(item => {
123
- const {
124
- index,
125
- start
126
- } = item;
154
+
155
+ // the rangeExtractor above can cause an undefined value to be appended to the return items.
156
+ // Easiest way to stop that crashing is just to filter it out.
157
+ const renderedChildren = items.filter(item => item !== undefined).map(({
158
+ index,
159
+ start
160
+ }) => {
127
161
  const child = childrenList[index];
128
162
  if (! /*#__PURE__*/React.isValidElement(child)) {
129
163
  return child;
@@ -67,6 +67,16 @@ const enGB = {
67
67
  }
68
68
  }
69
69
  },
70
+ fileInput: {
71
+ dragAndDrop: () => "or drag and drop your file",
72
+ selectFile: () => "Select file",
73
+ fileUploadStatus: () => "File upload status",
74
+ actions: {
75
+ cancel: () => "Cancel upload",
76
+ clear: () => "Clear",
77
+ delete: () => "Delete file"
78
+ }
79
+ },
70
80
  heading: {
71
81
  backLinkAriaLabel: () => "Back"
72
82
  },
@@ -50,6 +50,16 @@ interface Locale {
50
50
  formSummary: (errors: number, warnings: number, type: string) => [string, string] | null;
51
51
  };
52
52
  };
53
+ fileInput: {
54
+ dragAndDrop: () => string;
55
+ selectFile: () => string;
56
+ fileUploadStatus: () => string;
57
+ actions: {
58
+ cancel: () => string;
59
+ clear: () => string;
60
+ delete: () => string;
61
+ };
62
+ };
53
63
  heading: {
54
64
  backLinkAriaLabel: () => string;
55
65
  };
@@ -102,6 +102,16 @@ const plPL = {
102
102
  }
103
103
  }
104
104
  },
105
+ fileInput: {
106
+ dragAndDrop: () => "lub przeciągnij i upuść plik",
107
+ selectFile: () => "Wybierz pliki",
108
+ fileUploadStatus: () => "Status przesyłania plików",
109
+ actions: {
110
+ cancel: () => "Anuluj przesyłanie",
111
+ clear: () => "Wyczyść",
112
+ delete: () => "Usuń plik"
113
+ }
114
+ },
105
115
  heading: {
106
116
  backLinkAriaLabel: () => "Wstecz"
107
117
  },
@@ -1,5 +1,5 @@
1
1
  import { ReactWrapper, ShallowWrapper } from "enzyme";
2
2
  declare const elementsTagTest: (wrapper: ReactWrapper | ShallowWrapper, elements: string[]) => void;
3
3
  declare const rootTagTest: (rootNode: ReactWrapper | ShallowWrapper, comp: string, elem?: string, role?: string) => void;
4
- declare const rootTagTestRtl: (element: HTMLElement, comp: string, elem?: string, role?: string) => void;
4
+ declare const rootTagTestRtl: (rootNode: HTMLElement, comp: string, elem?: string, role?: string) => void;
5
5
  export { elementsTagTest, rootTagTest, rootTagTestRtl };
@@ -20,9 +20,11 @@ const rootTagTest = (rootNode, comp, elem, role) => {
20
20
  expect(rootNode.prop("data-role")).toEqual(role);
21
21
  };
22
22
  exports.rootTagTest = rootTagTest;
23
- const rootTagTestRtl = (element, comp, elem, role) => {
24
- expect(element.getAttribute("data-component")).toBe(comp);
25
- expect(element.getAttribute("data-element")).toBe(elem);
26
- expect(element.getAttribute("data-role")).toBe(role);
23
+ const rootTagTestRtl = (rootNode, comp, elem, role) => {
24
+ expect(rootNode).toHaveAttribute("data-component", comp);
25
+ expect(rootNode).toHaveAttribute("data-element", elem);
26
+ expect(rootNode).toHaveAttribute("data-role", role);
27
27
  };
28
+
29
+ // eslint-disable-next-line jest/no-export
28
30
  exports.rootTagTestRtl = rootTagTestRtl;
@@ -3,7 +3,9 @@
3
3
  var _jestFetchMock = require("jest-fetch-mock");
4
4
  var _mockMatchMedia = require("./mock-match-media");
5
5
  var _mockResizeObserver = _interopRequireDefault(require("./mock-resize-observer"));
6
+ var _mockElementScrollto = _interopRequireDefault(require("./mock-element-scrollto"));
6
7
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
7
8
  (0, _jestFetchMock.enableFetchMocks)();
8
9
  (0, _mockResizeObserver.default)();
9
- (0, _mockMatchMedia.setupMatchMediaMock)();
10
+ (0, _mockMatchMedia.setupMatchMediaMock)();
11
+ (0, _mockElementScrollto.default)();
@@ -0,0 +1,2 @@
1
+ declare const setupScrollToMock: () => void;
2
+ export default setupScrollToMock;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ const setupScrollToMock = () => {
8
+ // need to mock the `scrollTo` method, which is undefined in JSDOM. As we're not actually testing this behaviour, just make it
9
+ // do nothing.
10
+ HTMLElement.prototype.scrollTo = () => {};
11
+ };
12
+ var _default = exports.default = setupScrollToMock;
@@ -0,0 +1,3 @@
1
+ import type { ReactWrapper } from "enzyme";
2
+ export declare function simulateSelectTextboxEvent(container: ReactWrapper, eventType: string, ...eventArgs: any[]): void;
3
+ export declare function simulateDropdownEvent(container: ReactWrapper, eventType: string): void;