Step Handler
Concept
The Step Handler provides powerful hooks that execute after each step of the import process, giving you complete control over the data flow and user experience. Unlike its predecessor (Data Handler) which runs before the header and the review steps, the Step Handler runs after each step is completed, allowing you to validate, modify, or block progression based on your requirements.
| Description | The Step Handler is an advanced feature that provides hooks after each step of the import process. It enables you to access the raw uploaded and parsed files, validate data, add cell-specific errors/warnings/infos, and control the import flow with customizable alert and block modals. With the Step Handler , you can implement complex validation logic, throw pre-submission errors, access column and option mappings, and provide custom user feedback at any stage of the import process. Step Handler replaces the Data Handler and provides more granular control with hooks for upload, header selection, mapping, and review steps. |
| Available hooks |
|
| Control functions |
|
Basic syntax
Here is an example of the Step Handler's syntax:
stepHandler={{
uploadStep: async ({parsedData, rawData, updateData, alert, block}) => {
// Hook runs after file upload and parsing
},
headerStep: async ({data, updateData, alert, block}) => {
// Hook runs after header selection confirmation
},
mappingStep: async ({data, tdm, updateData, alert, block, logs}) => {
// Hook runs after mapping confirmation
},
reviewStep: async ({data, updateData, block, logs}) => {
// Hook runs when user clicks "Complete import" in the review step
}
}}
uploadStep()
| Description | This hook runs after the user has uploaded one or multiple files and the files have been parsed. It provides access to both the raw uploaded files and their parsed data, allowing validation and transformation before proceeding to the next step. |
| Parameters |
|
| Run event | The uploadStep function is executed immediately after file upload and parsing is complete |
Parameters
parsedData
Array of sheet objects containing the parsed data and metadata for each uploaded file/sheet.
Sheet object properties:
- data: A 2D array representing the content of the sheet
- fileName: The name of the uploaded file
- fileSize: The size of the file in megabytes (MB)
- fileType: The file extension (e.g., "xlsx", "csv", "xls")
- sheetName: The name of the sheet (for Excel files) or defaults to the
fileNamefor single-sheet files
Example:
[
{
"data": [
[
"company",
"order_id",
"customer_email",
"order_instructions",
], ...
],
"fileName": "test-file.xlsx",
"fileSize": 0.0067,
"fileType": "xlsx",
"sheetName": "test-sheet-1"
}, ...
]
rawData
Array of raw file objects containing the unprocessed files exactly as they were uploaded. This gives you access to the original files before any parsing or processing.
updateData
Function to modify the data structure before proceeding to the next step.
Return format:
updateData([
{
fileName: "Customers",
sheetName: "Internal", // optional
data: [
// supports 2D array and array of row objects
["id", "name"],
["12345", "Jason"],
],
},
{
fileName: "Products",
data: [
// supports 2D array and array of row objects
{ id: "12", quantity: 24 },
{ id: "13", quantity: 88 },
],
},
]);
alert
Function to show a warning modal with proceed/cancel options.
Parameters:
- title: Title of the warning modal
- description: Description text explaining the warning
- cancelButton: Text for the cancel button
- proceedButton: Text for the proceed button
block
Function to show an error modal that blocks progression.
Parameters:
- title: Text for the modal title
- description: Text explaining the error
- closeButton: Text for the close button
Implementation Example
stepHandler={{
uploadStep: async ({parsedData, rawData, updateData, alert, block}) => {
// Access raw uploaded files
console.log('Raw files:', rawData);
// Process parsed data
const processedData = parsedData.map(file => ({
fileName: file.fileName,
sheetName: file.sheetName,
data: file.data.filter(row => row.length > 0) // Remove empty rows
}));
// Check for issues and potentially block
if (processedData.some(file => file.data.length === 0)) {
block({
title: "Empty File Detected",
description: "One or more files contain no data. Please upload valid files.",
closeButton: "Close"
});
return;
}
// Update data for next step
updateData(processedData);
}
}}
Interaction with other features
- allowManualInput: When the user clicks the "Manual Entry" button,
uploadStep()is not triggered, and the user is forwarded to the review step. - automaticHeaderDetection: When
automaticHeaderDetection === true, it does not affectuploadStep().uploadStep()is triggered after parsing. If it finishes withoutalert()orblock()being called, the user is forwarded to the next step (sheet or header selection). - Contextual Engine:
uploadStep()runs after file upload, before the Contextual Engine is applied or the modal to use the Contextual Engine is displayed.
headerStep()
| Description | This hook runs after the user has confirmed their header selection. It allows validation of the selected headers and provides the ability to modify the data structure before proceeding to either the mapping step or join step. |
| Parameters |
|
| Run event | The headerStep function is executed when the user confirms their header selection |
Parameters
data
Array of sheet objects containing the data after header selection. It can contain one or multiple sheet objects depending on how many sheets have been selected inside the sheet selection. Each sheet object extends the structure of parsedData from uploadStep with additional properties that describe the transformations and decisions made during header selection.
Sheet object properties:
- data: A 2D array representing the content of the sheet, where the first row contains the headers
- fileName: The name of the uploaded file
- fileSize: The size of the file in megabytes (MB)
- fileType: The file extension (e.g., "xlsx", "csv", "xls")
- sheetName: The name of the sheet (for Excel files) or defaults to the
fileNamefor single-sheet files - transposed: A boolean indicating whether the user transposed the sheet during header selection. This field is always present regardless of whether
transposeis enabled. When the feature is not enabled, the value is alwaysfalse. - headerRowIndexes: An array of 0-based row indexes indicating which row(s) were selected as the header. For a single header row, this is a single-element array (e.g.,
[0]). For merged header rows, it contains all merged row indexes in order (e.g.,[0, 1]). If the sheet was transposed, the indexes refer to the transposed layout. - addedColumns: An array of objects describing columns added during header selection via metadata cell selection or the "Add column" action. When no columns were added, this is an empty array
[]. This field is always present regardless of whethermetadataSelectionis enabled. Each entry contains:- columnName (
string): The name the user entered for the column - value (
string): The value applied to every row - source (
string): How the column was created. One of"metadata_cell","custom_value","file_name", or"sheet_name"
- columnName (
Example:
[
{
"data": [
[
"company",
"order_id",
"customer_email",
"order_instructions",
], ...
],
"fileName": "test-file.xlsx",
"fileSize": 0.0067,
"fileType": "xlsx",
"sheetName": "test-sheet-1",
"transposed": false,
"headerRowIndexes": [0],
"addedColumns": []
}, ...
]
Example with merged headers and added columns:
[
{
"data": [
[
"2024 - Revenue",
"2024 - Costs",
"2024 - Profit",
"Region",
],
["50000", "30000", "20000", "EU"],
["80000", "45000", "35000", "EU"],
],
"fileName": "financials.xlsx",
"fileSize": 0.0045,
"fileType": "xlsx",
"sheetName": "Q1 Report",
"transposed": false,
"headerRowIndexes": [0, 1],
"addedColumns": [
{
"columnName": "Region",
"value": "EU",
"source": "custom_value"
}
]
}, ...
]
updateData
Function to modify the data structure before proceeding to the next step.
Return formats:
- Single 2D array: Forwards the user to the mapping step
- Single array of row objects: Forwards the user to the mapping step
- Array with single sheet object: Forwards the user to the mapping step
- Array with multiple sheet objects: Forwards the user to the join step when multipleFileUpload is activated
Example:
// Single 2D array
updateData([
["id", "name"],
["12345", "Jason"],
]);
// Single array of row objects
updateData([
{ id: "12", quantity: 24 },
{ id: "13", quantity: 88 },
]);
// Array with single sheet object
updateData([
{
fileName: "Sheet1",
data: [
["header1", "header2"],
["value1", "value2"],
],
},
]);
// Array with multiple sheet objects
updateData([
{
fileName: "Sheet1",
data: [
["header1", "header2"],
["value1", "value2"],
],
},
{
fileName: "Sheet2",
data: [
["header3", "header4"],
["value3", "value4"],
],
},
]);
alert
Function to show a warning modal with proceed/cancel options.
Parameters:
- title: Title of the warning modal
- description: Description text explaining the warning
- cancelButton: Text for the cancel button
- proceedButton: Text for the proceed button
block
Function to show an error modal that blocks progression.
Parameters:
- title: Text for the modal title
- description: Text explaining the error
- closeButton: Text for the close button
Implementation example
headerStep: async ({ data, updateData, alert, block }) => {
// Validate required headers
const requiredHeaders = ["id", "email", "name"];
const headers = data[0].data[0];
const missingHeaders = requiredHeaders.filter((h) => !headers.includes(h));
if (missingHeaders.length > 0) {
alert({
title: "Missing required headers",
description: `The following headers are required: ${missingHeaders.join(", ")}`,
cancelButton: "Fix headers",
proceedButton: "Continue anyway",
});
}
// Remove empty columns
const cleanData = data.map((sheet) => ({
...sheet,
data: sheet.data.map((row) => row.filter((_, colIndex) => sheet.data.some((r) => r[colIndex]?.trim()))),
}));
updateData(cleanData);
};
Interaction with other features
- automaticHeaderDetection: When the user clicks the "Manual Entry" button,
headerStep()is not triggered, and the user is forwarded to the review step. - multipleFileUpload: If multiple sheets are returned via
updateData()but multiple file upload is not activated, only the first sheet object is used for the mapping step. - allowManualInput: When
automaticHeaderDetection === true,headerStep()is triggered at the end of the automated header selection. Ifblock()oralert()is called, the corresponding modal is shown. Closing the modal returns the user to the previous step. Continuing forwards the user to the next step. - Contextual Engine: If the Contextual Engine is triggered in the header selection step (single sheet import),
headerStep()does not run. If the Contextual Engine is triggered in the join step (multiple sheets import),headerStep()runs at the end of header selection/before going to the join step.
mappingStep()
| Description | This hook runs when the user confirms their column mappings and all required mappings are complete. It provides access to the mapped data and the Target Data Model (TDM), allowing validation and modification of both the schema and data. |
| Parameters |
|
| Run event | The mappingStep function is executed after the user confirms their column mappings and all required mappings are complete |
Parameters
data
Array of row objects containing the mapped values using the Target Data Model (TDM) keys.
Example:
[
{
id: "12345",
name: "Jason Miller",
email: "[email protected]"
}, ...
]
tdm
Target Data Model object for modifying the schema.
Available methods:
- addColumn(): Add a new column to the schema
tdm.addColumn({
key: "column_key",
label: "column_label",
columnType: "string",
validations: [{ validate: "required" }],
hidden: false,
disabled: false,
}); - removeColumn(): Remove a column from the schema via its
keytdm.removeColumn("column_key");
updateData
Function to update data and add cell-specific error/warning/info messages.
Data formats:
-
Simple data: Array of row objects without validation messages
updateData([
{ id: "12345", name: "Jason" },
{ id: "67890", name: "Max" },
]); -
Data with validation: Array of row objects with value and info properties
updateData([
{
id: {
value: "12345",
info: [
{
message: "Invalid ID format",
level: "error",
},
],
},
},
]);infoThe
messagefield insideinfosupports HTML content when used inmappingStep(). This allows you to render rich text and interactive elements such as links, lists, paragraphs, and other common HTML tags inside the corresponding popovers.
alert
Function to show a warning modal with proceed/cancel options.
Parameters:
- title: Title of the warning modal
- description: Description text explaining the warning
- cancelButton: Text for the cancel button
- proceedButton: Text for the proceed button
block
Function to show an error modal that blocks progression.
Parameters:
- title: Text for the modal title
- description: Text explaining the error
- closeButton: Text for the close button
logs
Object containing details about the column and option mappings, the custom-added columns and options during the mapping step.
Properties:
- mappings: Array of mapping objects containing relation between input and target columns, including option mappings for category columns
- columns: Object containing added columns and options via
allowCustomColumnsandallowCustomOptionsAdvanced
Example:
{
"mappings": [
{
"sourceColumn": "Company Identification Number", // input column
"targetColumn": "company_code" // matched Target Data Model column
},
{
"sourceColumn": "Status",
"targetColumn": "deal_status",
"options": [ // when this is a category column (columnType === "category", "currency_code", "country_code_alpha_2" or "country_code_alpha_3"
{
"sourceValue": "In progress", // input value
"targetOptions": ["Ongoing", ...] // matched Target Data Model dropdown option (can contain multiple strings when isMultiSelect === true)
},
{
"sourceValue": "TBD",
"targetOptions": [] // when being unmatched
}
]
},
{
"sourceColumn": "",
"targetColumn": "address"
}, ...
],
"columns": {
"addedColumns": [
{
"label": "Revenue",
"key": "revenue",
"columnType": "currency_eur"
}
],
"addedOptions": [
{
"columnKey": "deal_status",
"dropdownOptions": [
{
"label": "Done",
"value": "done",
"type": "string"
}
]
}
]
}
}
Implementation example
mappingStep: async ({ data, tdm, updateData, alert, block, logs }) => {
// Check mapping completeness
const unmappedRequired = logs.mappings.filter((m) => !m.sourceColumn).map((m) => m.targetColumn);
if (unmappedRequired.length > 0) {
block({
title: "Unmapped Columns",
description: `Every column needs to be mapped. Please map the following columns: ${unmappedRequired.join(", ")}`,
closeButton: "Fix mappings",
});
return;
}
// Add custom validation column
tdm.addColumn({
key: "status",
label: "Validation status",
columnType: "string",
hidden: true,
});
// Validate email format
const validatedData = data.map((row) => {
const validatedRow = { ...row };
if (row.email && !row.email.includes("@")) {
validatedRow.email = {
value: row.email,
info: [
{
message: "Invalid email format",
level: "error",
},
],
};
validatedRow.status = "Invalid";
}
return validatedRow;
});
updateData(validatedData);
};