pixel-react-excel-sheet 1.0.31 → 1.0.32

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 (69) hide show
  1. package/lib/components/Charts/DashboardDonutChart/types.d.ts +1 -0
  2. package/lib/components/ConditionalDropdown/types.d.ts +1 -1
  3. package/lib/components/ConnectingBranch/ConnectingBranch.d.ts +2 -1
  4. package/lib/components/ConnectingBranch/data.d.ts +19 -33
  5. package/lib/components/ConnectingBranch/types.d.ts +10 -0
  6. package/lib/components/Excel/ExcelFile/ExcelFileComponents/actions.d.ts +7 -1
  7. package/lib/components/FileDropzone/types.d.ts +12 -4
  8. package/lib/components/MachineInputField/MachineInputField.d.ts +4 -1
  9. package/lib/components/Select/types.d.ts +1 -1
  10. package/lib/components/TextArea/Textarea.d.ts +1 -1
  11. package/lib/components/TextArea/Types.d.ts +1 -0
  12. package/lib/components/ThemeProvider/types.d.ts +1 -1
  13. package/lib/index.d.ts +88 -66
  14. package/lib/index.esm.js +175 -103
  15. package/lib/index.esm.js.map +1 -1
  16. package/lib/index.js +175 -103
  17. package/lib/index.js.map +1 -1
  18. package/lib/tsconfig.tsbuildinfo +1 -1
  19. package/lib/utils/validateFile/validateFile.d.ts +2 -0
  20. package/package.json +2 -2
  21. package/src/assets/Themes/BaseTheme.scss +1 -0
  22. package/src/assets/Themes/BlueTheme.scss +279 -0
  23. package/src/assets/Themes/DarkTheme.scss +1 -0
  24. package/src/assets/Themes/Theme.scss +5 -0
  25. package/src/components/AppHeader/AppHeader.tsx +4 -2
  26. package/src/components/Charts/DashboardDonutChart/DashboardDonutChart.stories.tsx +1 -0
  27. package/src/components/Charts/DashboardDonutChart/DashboardDonutChart.tsx +3 -1
  28. package/src/components/Charts/DashboardDonutChart/types.ts +1 -0
  29. package/src/components/ConditionalDropdown/ConditionalDropdown.stories.tsx +1 -1
  30. package/src/components/ConditionalDropdown/types.ts +1 -1
  31. package/src/components/ConnectingBranch/BranchComponents/MachineInstances.tsx +94 -79
  32. package/src/components/ConnectingBranch/ConnectingBranch.scss +31 -45
  33. package/src/components/ConnectingBranch/ConnectingBranch.stories.tsx +33 -1
  34. package/src/components/ConnectingBranch/ConnectingBranch.tsx +36 -23
  35. package/src/components/ConnectingBranch/{data.ts → data.tsx} +106 -17
  36. package/src/components/ConnectingBranch/types.ts +21 -0
  37. package/src/components/Excel/ExcelFile/ExcelFile.tsx +1 -1
  38. package/src/components/Excel/ExcelFile/ExcelFileComponents/ActiveCell.tsx +7 -5
  39. package/src/components/Excel/ExcelFile/ExcelFileComponents/FloatingRect.tsx +6 -1
  40. package/src/components/Excel/ExcelFile/ExcelFileComponents/Spreadsheet.scss +30 -11
  41. package/src/components/Excel/ExcelFile/ExcelFileComponents/Spreadsheet.tsx +65 -19
  42. package/src/components/Excel/ExcelFile/ExcelFileComponents/actions.ts +14 -0
  43. package/src/components/Excel/ExcelFile/ExcelFileComponents/reducer.ts +16 -0
  44. package/src/components/Excel/ExcelToolBar/ExcelToolBar.tsx +6 -5
  45. package/src/components/FileDropzone/Dropzone.tsx +3 -0
  46. package/src/components/FileDropzone/FileDropzone.scss +18 -0
  47. package/src/components/FileDropzone/FileDropzone.stories.tsx +75 -7
  48. package/src/components/FileDropzone/FileDropzone.tsx +2 -0
  49. package/src/components/FileDropzone/RadioFilePreview.tsx +7 -3
  50. package/src/components/FileDropzone/types.ts +13 -4
  51. package/src/components/Icon/iconList.ts +2 -2
  52. package/src/components/InputWithDropdown/InputWithDropdown.tsx +1 -1
  53. package/src/components/MachineInputField/MachineInputField.tsx +67 -70
  54. package/src/components/Search/Search.tsx +3 -1
  55. package/src/components/Select/types.ts +1 -1
  56. package/src/components/SequentialConnectingBranch/components/Branches/Branches.scss +1 -0
  57. package/src/components/SequentialConnectingBranch/components/Branches/Branches.tsx +2 -8
  58. package/src/components/TextArea/Textarea.tsx +2 -0
  59. package/src/components/TextArea/Types.ts +3 -0
  60. package/src/components/ThemeProvider/types.ts +1 -1
  61. package/src/utils/validateFile/validateFile.stories.tsx +49 -0
  62. package/src/utils/validateFile/validateFile.ts +39 -0
  63. package/src/components/SequentialConnectingBranch/components/AddBrowserModal/AddBrowserModal.scss +0 -51
  64. package/src/components/SequentialConnectingBranch/components/AddBrowserModal/AddBrowserModal.tsx +0 -107
  65. package/src/components/SequentialConnectingBranch/components/AddBrowserModal/types.ts +0 -5
  66. package/src/components/SequentialConnectingBranch/components/DatasetListModal/DatasetListModal.scss +0 -31
  67. package/src/components/SequentialConnectingBranch/components/DatasetListModal/DatasetListModal.tsx +0 -85
  68. package/src/components/SequentialConnectingBranch/components/DatasetListModal/types.ts +0 -4
  69. /package/src/assets/icons/{impactList.svg → impact_list.svg} +0 -0
@@ -5,8 +5,10 @@ import Toaster from '../Toast';
5
5
  import { useState } from 'react';
6
6
  import { RadioOption } from './types';
7
7
  import Drawer from '../Drawer/Drawer';
8
- import Typography from '../Typography';
8
+ import ConditionalDropdown from '../ConditionalDropdown/ConditionalDropdown';
9
9
  import './FileDropzone.scss';
10
+ import { DynamicObj } from '../CreateVariable/types';
11
+ import Button from '../Button';
10
12
 
11
13
  const meta: Meta<typeof FileDropzone> = {
12
14
  title: 'Components/FileDropzone',
@@ -107,8 +109,12 @@ export const WithRadioButton: Story = {
107
109
 
108
110
  const [selectedRadioOption, setSelectedRadioOption] =
109
111
  useState<RadioOption>();
110
- const [selectedFile, setSelectedFile] = useState<File | null>(null);
111
-
112
+ const [selectedFile, setSelectedFile] = useState<File | DynamicObj | null>(
113
+ null
114
+ );
115
+ const [testDataSelectedFile, setTestDataSelectedFile] = useState<
116
+ DynamicObj | File | null
117
+ >(null);
112
118
 
113
119
  const drawerArgs = {
114
120
  primaryButtonProps: {
@@ -156,6 +162,42 @@ export const WithRadioButton: Story = {
156
162
  }, 2000);
157
163
  };
158
164
 
165
+ const testData = [
166
+ {
167
+ _id: '1',
168
+ name: 'File1.txt',
169
+ actualPath: '/documents/File1.txt',
170
+ searchKey: 'file1',
171
+ parentId: 'root',
172
+ },
173
+ {
174
+ _id: '2',
175
+ name: 'File2.doc',
176
+ actualPath: '/documents/File2.doc',
177
+ searchKey: 'file2',
178
+ parentId: 'root',
179
+ },
180
+ {
181
+ _id: '3',
182
+ name: 'Image1.png',
183
+ actualPath: '/images/Image1.png',
184
+ searchKey: 'image1',
185
+ parentId: 'folder1',
186
+ },
187
+ {
188
+ _id: '4',
189
+ name: 'Presentation.ppt',
190
+ actualPath: '/presentations/Presentation.ppt',
191
+ searchKey: 'presentation',
192
+ parentId: 'folder2',
193
+ },
194
+ ];
195
+
196
+ const handleSaveButton = () => {
197
+ setSelectedFile(testDataSelectedFile);
198
+ setShowModal(false);
199
+ };
200
+
159
201
  return (
160
202
  <>
161
203
  <FileDropzone
@@ -176,6 +218,7 @@ export const WithRadioButton: Story = {
176
218
  selectedFile={selectedFile}
177
219
  handleFileChange={handleFileChange}
178
220
  handleRemoveFile={handleRemoveFile}
221
+ setShowDrawer={setShowModal}
179
222
  />
180
223
  {showToaster && (
181
224
  <Toaster
@@ -185,18 +228,43 @@ export const WithRadioButton: Story = {
185
228
  toastMessage={'Max 5 files can be uploaded'}
186
229
  />
187
230
  )}
188
- {selectedRadioOption?.value === 'Local File' &&
231
+ {selectedRadioOption?.value === 'Test Data' && (
189
232
  <Drawer
190
233
  {...drawerArgs}
191
234
  isOpen={showModal}
192
235
  onClose={() => setShowModal(false)}
193
- isFooterRequired={true}
236
+ isFooterRequired={false}
194
237
  _isExpanded={false}
195
238
  size="small"
239
+
196
240
  >
197
- <Typography>Upload TestData Files Here</Typography>
241
+ <ConditionalDropdown
242
+ label="Select Path Using #"
243
+ placeholder="Enter # to search files"
244
+ isHash
245
+ dataFiles={testData}
246
+ dropdownWidth="auto"
247
+ setHashInputValue={setTestDataSelectedFile}
248
+ />
249
+ <div className="footer_basic_button">
250
+ <Button
251
+ type="button"
252
+ variant="secondary"
253
+ size="small"
254
+ onClick={() => setShowModal(false)}
255
+ label={'Cancel'}
256
+ />
257
+
258
+ <Button
259
+ type={'button'}
260
+ variant="primary"
261
+ size="small"
262
+ label={'Save'}
263
+ onClick={handleSaveButton}
264
+ />
265
+ </div>
198
266
  </Drawer>
199
- }
267
+ )}
200
268
  </>
201
269
  );
202
270
  },
@@ -38,6 +38,7 @@ const FileDropzone: React.FC<FileDropzoneProps> = ({
38
38
  handleRemoveFile,
39
39
  isApiResponseError = false,
40
40
  isDisable = false,
41
+ setShowDrawer,
41
42
  }) => {
42
43
  const fileInputRef = useRef<HTMLInputElement | null>(null);
43
44
  const {
@@ -131,6 +132,7 @@ const FileDropzone: React.FC<FileDropzoneProps> = ({
131
132
  handleRemoveFile={handleRemoveFile}
132
133
  setSelectedFile={setSelectedFile}
133
134
  isDisable={isDisable}
135
+ setShowDrawer={setShowDrawer}
134
136
  />
135
137
 
136
138
  {isWebServiceFileDropZone && (
@@ -8,14 +8,18 @@ import { useRef } from 'react';
8
8
  const RadioFilePreview: React.FC<RadioFilePreviewProps> = ({
9
9
  selectedFile,
10
10
  onFileRemoveClick,
11
- onFileReplaceClick
11
+ onFileReplaceClick,
12
+ selectedRadioOption,
13
+ setShowDrawer,
12
14
  }) => {
13
15
  const fileInputRef = useRef<HTMLInputElement | null>(null);
14
16
 
15
17
  const handleReplaceClick = () => {
16
- if (fileInputRef.current) {
17
- fileInputRef.current.click();
18
+ if (selectedRadioOption?.value === 'Test Data') {
19
+ setShowDrawer?.(true);
20
+ return;
18
21
  }
22
+ fileInputRef.current?.click();
19
23
  };
20
24
 
21
25
  const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
@@ -1,4 +1,5 @@
1
1
  import { ReactNode } from 'react';
2
+ import { DynamicObj } from '../CreateVariable/types';
2
3
  export interface RadioOption {
3
4
  label: string;
4
5
  value: string;
@@ -93,12 +94,12 @@ export interface FileDropzoneProps {
93
94
  /**
94
95
  * Its the File Name of File Selected from Local File.
95
96
  **/
96
- setSelectedFile?: (file: File | null) => void;
97
+ setSelectedFile?: (file: File | DynamicObj | null) => void;
97
98
 
98
99
  /**
99
100
  * Its the File Name of File Selected from Local File.
100
101
  **/
101
- selectedFile?: File | null;
102
+ selectedFile?: File | DynamicObj | null;
102
103
 
103
104
  /**
104
105
  * Its the function which updates the Selected file from Local Directory.
@@ -120,6 +121,11 @@ export interface FileDropzoneProps {
120
121
  **/
121
122
  isDisable?: boolean;
122
123
 
124
+ /**
125
+ * Its the boolean value setted when the replace is clicked for TestData radio option .
126
+ **/
127
+ setShowDrawer?: (value: boolean | ((prevState: boolean) => boolean)) => void;
128
+
123
129
  }
124
130
  export interface FileState {
125
131
  accepted: File[];
@@ -176,11 +182,12 @@ export interface DroppableProps {
176
182
  selectedRadioOption?: Option,
177
183
  radioOptions?: RadioOption[],
178
184
  handleOptionChange?: (option: RadioOption)=> void,
179
- selectedFile?: File | null,
180
- setSelectedFile?: (file: File | null) => void;
185
+ selectedFile?: File | DynamicObj | null,
186
+ setSelectedFile?: (file: File | DynamicObj | null) => void;
181
187
  handleFileChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
182
188
  handleRemoveFile?: () => void;
183
189
  isDisable?: boolean;
190
+ setShowDrawer?: (value: boolean | ((prevState: boolean) => boolean)) => void;
184
191
  }
185
192
 
186
193
  export interface FilePreviewProps {
@@ -196,4 +203,6 @@ export interface RadioFilePreviewProps {
196
203
  onFileRemoveClick?: () => void;
197
204
  onFileReplaceClick?: (file: File | null) => void;
198
205
  setSelectedFile?: (file: File | null) => void;
206
+ selectedRadioOption?: Option,
207
+ setShowDrawer?: (value: boolean | ((prevState: boolean) => boolean)) => void
199
208
  }
@@ -45,7 +45,7 @@ import ManualLocator from '../../assets/icons/manual_locator.svg?react';
45
45
  import LicenseIcon from '../../assets/icons/active_license_icon.svg?react';
46
46
  import DeleteIcon from '../../assets/icons/delete.svg?react';
47
47
  import DetailsIcon from '../../assets/icons/details.svg?react';
48
- import ImpactListIcon from '../../assets/icons/impactList.svg?react';
48
+ import ImpactListIcon from '../../assets/icons/impact_list.svg?react';
49
49
  import Export from '../../assets/icons/export.svg?react';
50
50
  import FormatePainter from '../../assets/icons/formate_painter.svg?react';
51
51
  import Bold from '../../assets/icons/bold.svg?react';
@@ -295,7 +295,7 @@ Components['run_icon'] = RunIcon;
295
295
  Components['license'] = LicenseIcon;
296
296
  Components['delete'] = DeleteIcon;
297
297
  Components['details'] = DetailsIcon;
298
- Components['impactList'] = ImpactListIcon;
298
+ Components['impact_list'] = ImpactListIcon;
299
299
  Components['beautify_icon'] = BeautifyIcon;
300
300
  Components['add_variable_icon'] = AddVariable;
301
301
  Components['replace_file'] = ReplaceFile;
@@ -18,7 +18,7 @@ const InputWithDropdown = forwardRef<HTMLInputElement, InputWithDropdownProps>(
18
18
  error,
19
19
  helperText,
20
20
  optionsList,
21
- selectedOption,
21
+ selectedOption = { label: '', value: '' },
22
22
  autoComplete = 'off',
23
23
  onDropdownChangeHandler = () => {},
24
24
  onInputChangeHandler,
@@ -1,4 +1,3 @@
1
- import { forwardRef } from 'react';
2
1
  import './MachineInputField.scss';
3
2
  import Typography from '../Typography';
4
3
  import Icon from '../Icon';
@@ -6,81 +5,79 @@ import { MachineInputFieldProps, MachineType } from './types';
6
5
  import classNames from 'classnames';
7
6
  import { truncateText } from '../../utils/truncateText/truncateText';
8
7
 
9
- const MachineInputField = forwardRef<HTMLDivElement, MachineInputFieldProps>(
10
- ({
11
- width = '',
12
- options = [],
13
- runCount = 0,
14
- className = '',
15
- contentReverse = false,
16
- onClick = () => {},
17
- modalId = '',
18
- }) => {
19
- const getIcon: Record<MachineType['type'], string> = {
20
- Local: 'local',
21
- Browserstack: 'browserstack_icon',
22
- SauceLabs: 'sause_lab',
23
- LambdaTest: 'lambda_icon',
24
- mac: 'mac_icon',
25
- android: 'android_icon',
26
- linux: 'linux',
27
- 'Google Chrome': 'chrome_icon',
28
- 'Mozilla Firefox': 'fire_fox',
29
- 'Microsoft Edge': 'edge',
30
- 'Internet Explorer': 'internet_explorer',
31
- Safari: 'safari_icon',
32
- Opera: 'opera',
33
- windows: 'windows',
34
- };
8
+ const MachineInputField = ({
9
+ width = '',
10
+ options = [],
11
+ runCount = 0,
12
+ className = '',
13
+ contentReverse = false,
14
+ onClick = () => {},
15
+ modalId = '',
16
+ }: MachineInputFieldProps) => {
17
+ const getIcon: Record<MachineType['type'], string> = {
18
+ local: 'local',
19
+ browserstack: 'browserstack_icon',
20
+ sauceLabs: 'sause_lab',
21
+ lambdaTest: 'lambda_icon',
22
+ mac: 'mac_icon',
23
+ android: 'android_icon',
24
+ linux: 'linux',
25
+ 'google chrome': 'chrome_icon',
26
+ 'mozilla firefox': 'fire_fox',
27
+ 'microsoft edge': 'edge',
28
+ 'internet explorer': 'internet_explorer',
29
+ safari: 'safari_icon',
30
+ opera: 'opera',
31
+ windows: 'windows',
32
+ };
35
33
 
36
- return (
37
- <div
38
- ref={modalId}
39
- style={{ width: width }}
40
- className={classNames('ff-machine-input-field-wrapper', className)}
41
- onClick={() => onClick()}
34
+ return (
35
+ <div
36
+ id={modalId}
37
+ style={{ width: width }}
38
+ className={classNames('ff-machine-input-field-wrapper', className)}
39
+ onClick={() => onClick()}
40
+ >
41
+ <Typography
42
+ as="span"
43
+ color="var(--ff-machine-input-field-text-color)"
44
+ fontSize={8}
45
+ textAlign="center"
46
+ className="ff-machine-input-label"
42
47
  >
43
- <Typography
44
- as="span"
45
- color="var(--ff-machine-input-field-text-color)"
46
- fontSize={8}
47
- textAlign="center"
48
- className="ff-machine-input-label"
49
- >
50
- Run {runCount}
51
- </Typography>
48
+ Run {runCount}
49
+ </Typography>
52
50
 
53
- <div
54
- className={classNames('ff-machine-input-field', {
55
- 'ff-machine-input-field-reverse': contentReverse,
56
- })}
57
- >
58
- {options.map(({ label, type }) => (
59
- <div
60
- key={type}
61
- className={classNames('ff-machine-icon-text-wrapper', {
62
- 'ff-machine-icon-text-wrapper-reverse': contentReverse,
51
+ <div
52
+ className={classNames('ff-machine-input-field', {
53
+ 'ff-machine-input-field-reverse': contentReverse,
54
+ })}
55
+ >
56
+ {options.map(({ label, type }) => (
57
+ <div
58
+ key={type}
59
+ className={classNames('ff-machine-icon-text-wrapper', {
60
+ 'ff-machine-icon-text-wrapper-reverse': contentReverse,
61
+ })}
62
+ >
63
+ <Icon
64
+ name={getIcon[type.toLowerCase()] || 'local'}
65
+ className="ff-machine-icon"
66
+ />
67
+ <Typography
68
+ className={classNames('ff-machine-text', {
69
+ 'ff-machine-text-reverse': contentReverse,
63
70
  })}
71
+ color="var(--ff-machine-input-field-text-color)"
64
72
  >
65
- <Icon
66
- name={getIcon[type] || 'local'}
67
- className="ff-machine-icon"
68
- />
69
- <Typography
70
- className={classNames('ff-machine-text', {
71
- 'ff-machine-text-reverse': contentReverse,
72
- })}
73
- color="var(--ff-machine-input-field-text-color)"
74
- >
75
- {truncateText(label, 15)}
76
- </Typography>
77
- </div>
78
- ))}
79
- </div>
73
+ {truncateText(label, 15)}
74
+ </Typography>
75
+ </div>
76
+ ))}
80
77
  </div>
81
- );
82
- }
83
- );
78
+ </div>
79
+ );
80
+ };
84
81
 
85
82
  MachineInputField.displayName = 'MachineInputField';
86
83
 
@@ -57,7 +57,9 @@ const Search = ({
57
57
  };
58
58
  const handleChange = (data: string) => {
59
59
  setSearchValue(data);
60
- onSearch(data);
60
+ if (data === '') {
61
+ onSearch('');
62
+ }
61
63
  };
62
64
 
63
65
  return (
@@ -17,7 +17,7 @@ export interface SelectProps {
17
17
  /*
18
18
  * Selected option for the select dropdown
19
19
  */
20
- selectedOption?: Option;
20
+ selectedOption: Option;
21
21
 
22
22
  /*
23
23
  * onChange callback function for handling selected option changes
@@ -74,6 +74,7 @@
74
74
 
75
75
  .ff-connecting-text {
76
76
  margin-right: 8px;
77
+ cursor: pointer;
77
78
  }
78
79
 
79
80
  .ff-connecting-delete-icon {
@@ -72,12 +72,6 @@ const Branches = ({
72
72
  machineInfo: { osVersion, iconName },
73
73
  deviceInfo,
74
74
  } = machineInstance as ExecutionContext;
75
- // Helper to handle device info safely
76
- const getDeviceOption = (platform: string, defaultType: string = '', index = 0) =>
77
- deviceInfo?.[index]?.platform?.toLowerCase().includes(platform ?? '')
78
- ? defaultType
79
- : undefined;
80
-
81
75
  const getEnvironment = (environment: string = '') => {
82
76
  if (environment.toLowerCase().includes('browserstack'))
83
77
  return 'Browserstack';
@@ -110,11 +104,11 @@ const Branches = ({
110
104
  const mobileOptions = [
111
105
  {
112
106
  label: deviceInfo?.[0]?.name,
113
- type: getDeviceOption('android', 'android_icon', 0),
107
+ type: 'android',
114
108
  },
115
109
  {
116
110
  label: deviceInfo?.[1]?.name,
117
- type: getDeviceOption('ios', 'mac_icon', 1),
111
+ type: 'mac',
118
112
  },
119
113
  ];
120
114
 
@@ -17,6 +17,7 @@ const Textarea = ({
17
17
  onChange,
18
18
  onBlur,
19
19
  onFocus,
20
+ onPaste,
20
21
  rows = 4,
21
22
  cols = 40,
22
23
  resize = false,
@@ -65,6 +66,7 @@ const Textarea = ({
65
66
  onChange={onChange}
66
67
  onBlur={onBlur}
67
68
  onFocus={onFocus}
69
+ onPaste={onPaste}
68
70
  required={required}
69
71
  rows={rows}
70
72
  cols={cols}
@@ -49,6 +49,9 @@ export interface TextareaProps {
49
49
  onBlur?: (event: React.FocusEvent<HTMLTextAreaElement>) => void;
50
50
 
51
51
  onFocus?: (event: React.FocusEvent<HTMLTextAreaElement>) => void;
52
+
53
+ onPaste?: (event: React.ClipboardEvent<HTMLTextAreaElement>) => void;
54
+
52
55
  /**
53
56
  * id to select the textarea field uniquely
54
57
  */
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from "react";
2
2
 
3
- export type Theme = 'ff-light-theme' | 'ff-dark-theme' | 'ff-grey-theme';
3
+ export type Theme = 'ff-light-theme' | 'ff-dark-theme' | 'ff-grey-theme' | 'ff-blue-theme';
4
4
 
5
5
  export interface ThemeContextType {
6
6
  currentTheme: Theme;
@@ -0,0 +1,49 @@
1
+ import React, { useState } from 'react';
2
+ import {
3
+ validateFileExtension,
4
+ validateZipFileExtension,
5
+ } from './validateFile';
6
+
7
+ export default {
8
+ title: 'Utils/validateFile',
9
+ };
10
+
11
+ const allowedFileTypes = {
12
+ '.txt': 'text/plain',
13
+ '.zip': 'application/zip',
14
+ '.jpg': 'image/jpeg',
15
+ };
16
+
17
+ export const ValidateFileExtension = () => {
18
+ const [result, setResult] = useState<string | boolean>('');
19
+
20
+ const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
21
+ const isValid = validateFileExtension(event, allowedFileTypes);
22
+ setResult(isValid ? 'Valid file type' : 'Invalid file type');
23
+ };
24
+
25
+ return (
26
+ <div>
27
+ <h3>Validate File Extension</h3>
28
+ <input type="file" onChange={handleFileChange} />
29
+ <p>Result: {result.toString()}</p>
30
+ </div>
31
+ );
32
+ };
33
+
34
+ export const ValidateZipFileExtension = () => {
35
+ const [result, setResult] = useState<string | boolean>('');
36
+
37
+ const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
38
+ const isValid = validateZipFileExtension(event);
39
+ setResult(isValid ? 'Valid ZIP file' : 'Invalid ZIP file');
40
+ };
41
+
42
+ return (
43
+ <div>
44
+ <h3>Validate ZIP File Extension</h3>
45
+ <input type="file" onChange={handleFileChange} />
46
+ <p>Result: {result.toString()}</p>
47
+ </div>
48
+ );
49
+ };
@@ -0,0 +1,39 @@
1
+ export const validateFileExtension = (
2
+ file: File | React.ChangeEvent<HTMLInputElement>,
3
+ allowedFileTypes: Record<string, string>
4
+ ) => {
5
+ const selectedFile =
6
+ (file as React.ChangeEvent<HTMLInputElement>).target?.files?.[0] ||
7
+ (file as File);
8
+ const fileExtension =
9
+ '.' + selectedFile?.name?.split('.')?.pop()?.toLowerCase();
10
+ const fileType = selectedFile?.type;
11
+ if (
12
+ ['.ipa', '.y4m', '.yml', '.md', '.pem', '.properties'].includes(
13
+ fileExtension
14
+ ) &&
15
+ Object.keys(allowedFileTypes)?.includes(fileExtension)
16
+ ) {
17
+ return true;
18
+ }
19
+ return (
20
+ allowedFileTypes[fileExtension] &&
21
+ fileType === allowedFileTypes[fileExtension]
22
+ );
23
+ };
24
+
25
+ export const validateZipFileExtension = (
26
+ file: File | React.ChangeEvent<HTMLInputElement>
27
+ ) => {
28
+ const selectedFile =
29
+ (file as React.ChangeEvent<HTMLInputElement>).target?.files?.[0] ||
30
+ (file as File);
31
+ // List of valid MIME types
32
+ const validMimeTypes = ['application/zip', 'application/x-zip-compressed'];
33
+ // Validating the file extension and MIME type
34
+ return (
35
+ selectedFile &&
36
+ validMimeTypes?.includes(selectedFile?.type) &&
37
+ selectedFile?.name?.toLowerCase()?.endsWith('.zip')
38
+ );
39
+ };
@@ -1,51 +0,0 @@
1
- .ff-connecting-datalist-modal-header-wrapper,
2
- .ff-connecting-modal-child-wrapper {
3
- width: 534px;
4
- padding: 0 4px;
5
- }
6
-
7
- .ff-connecting-datalist-modal-header-wrapper {
8
- border-bottom: 1px solid var(--ff-connecting-branch-modal-border);
9
- margin-bottom: 4px;
10
- }
11
-
12
- .ff-connecting-modal-child-wrapper {
13
- .ff-branch-select-input-wrapper {
14
- display: flex;
15
- align-items: center;
16
- gap: 8px;
17
-
18
- .ff-select-browser {
19
- width: 263px;
20
- }
21
-
22
- .ff-branch-input-wrapper {
23
- display: flex;
24
- align-items: flex-end;
25
- height: 38px;
26
-
27
- .ff-input-browser {
28
- width: 245px;
29
- }
30
- }
31
- }
32
- }
33
-
34
- .ff-connecting-modal-footer-wrapper {
35
- display: flex;
36
- align-items: center;
37
- justify-content: space-between;
38
- padding: 0 8px;
39
- margin-top: 8px;
40
- border-top: 1px solid var(--ff-connecting-branch-modal-border);
41
-
42
- .ff-branch-toggle-wrapper,
43
- .ff-datalist-button-wrapper {
44
- display: flex;
45
- gap: 8px;
46
- }
47
-
48
- .ff-branch-toggle-wrapper {
49
- gap: 4px;
50
- }
51
- }