Hi all, excited to share the new and improved Cascader component! This new version brings Cascader up to the level of our modern components, with features including:
- The intuitive GUI from Navigation and Checkbox Group to manually create and organize options list
- Dynamically map existing data to options, as with Select and Checkbox Group
- A new dropdown look and user experience, consistent with the Select component
- New label options (customizable caption, color, icon) to match Select option customization
- Support for numeric values alongside string for options
If you are looking to upgrade, some key changes from the existing Cascader are:
- The
value
of the new Cascader component is the value of the selected option. You can still access the path of values leading to the selected option via the new propertyvaluePath
.
- Like other modern Retool components,
value
s for options need to be unique.- If you use the one click upgrade for Cascaders, duplicate values will have parent values appended to make them unique (i.e. option with duplicate value
Cost
nested under optionShipping
, would have it’s value changed toShipping-Cost
). (Starting in v3.145 on cloud)
- If you use the one click upgrade for Cascaders, duplicate values will have parent values appended to make them unique (i.e. option with duplicate value
- The format for dynamically mapping option data is different from the existing Cascader. Like Checkbox Group and Navigation, the data source is expected to be an array of objects with a property that points to its parent. Cascader nests options by parent value.
- Below are two code snippets to help transform existing Cascader data (either
string[][]
or{ value: string, label?: string, children: Option[] }[]
) to be compatible with the new component{ value: string, label?: string, parentValue?: string }[]
.
- Below are two code snippets to help transform existing Cascader data (either
We have launched this feature to 100% of cloud users, and hope to launch it to the next on-prem version. Please share any feedback or questions regarding upgrading you have in this thread!
Happy Building!
// Migrates string[][] to { value: string, label?: string, parentValue?: string }[]
function migrateSimpleStructureToCascader2(structure) {
let options = []
const keyToValueMap = new Map()
const uniqueValues = new Set()
for (const leafNodePath of structure) {
for (let i = 0; i < leafNodePath.length; i++) {
const key = leafNodePath.slice(0, i + 1).join('-')
if (keyToValueMap.has(key)) continue
// If the value is not unique, we use the key
const originalValue = leafNodePath[i]
const isNotUniqueValue = uniqueValues.has(originalValue)
const value = isNotUniqueValue ? key : originalValue
// Track the option to its potentially changed value
keyToValueMap.set(key, value)
const parentKey = i === 0 ? undefined : leafNodePath.slice(0, i).join('-')
const parentValue = parentKey ? keyToValueMap.get(parentKey) : undefined
const option = {
value,
label: isNotUniqueValue ? originalValue : undefined,
parentValue,
}
options.push(option)
uniqueValues.add(value)
}
}
return options;
}
// Migrates { value: string, label?: string, children: { value: string, label?: string }[] }[] to { value: string, label?: string, parentValue?: string }[]
function migrateComplexStructureToCascader2(structure)
{
let options = []
const uniqueValues = new Set()
function recursivelyCreateOptions(
originalOption,
{
options, // Array to add the new option to
uniqueValues, // Set to keep track of unique values
parentValue,
},
) {
let value = originalOption.value
if (value === undefined) return { options, uniqueValues }
// If the value is not unique, we make it unique by appending the parent key
if (uniqueValues.has(value)) {
value = `${parentValue}-${value}`
}
// Use explicit label if it exists. Otherwise, if we altered the value to make it unique, use the original value as the label
let label = originalOption.label
if (!label && originalOption.value !== value) {
label = originalOption.value
}
const option = {
value,
label,
parentValue,
}
options.push(option)
uniqueValues.add(value)
// Create options for children, this can be recursive
if (originalOption.children) {
for (const child of originalOption.children) {
const recursiveResult = recursivelyCreateOptions(child, { options, uniqueValues, parentValue: value })
options = recursiveResult.options
uniqueValues = recursiveResult.uniqueValues
}
}
return { options, uniqueValues }
}
for (const oldOption of structure) {
options = recursivelyCreateOptions(oldOption, { options, uniqueValues }).options
}
return options
}