Is it possible to get support for the <summary> tag in the new record calculation field?
Epcot Center
Epcot is a theme park at Walt Disney World Resort featuring exciting attractions, international pavilions, award-winning fireworks and seasonal special events.
<details>
<summary>Epcot Center</summary>
<p>Epcot is a theme park at Walt Disney World Resort featuring exciting attractions, international pavilions, award-winning fireworks and seasonal special events.</p>
</details>
…
Use case one - an email that is sent to the client on status change. This calc field has the email body for reference. No need to see it all the time so it’s collapsed by default.
Use case two - instructions/notes/resources at the top of an item. For a portfolio item, we have AI image prompts that can be used to make mockups. Having this list collapsed and out of the way until needed is ideal.
Use case three - email template with mailto button for pre populating and email in the users native mail client. Again, collapsed by default.
wow. I hadn’t even thought about ways this might be useful. But now that it’s available, I’m starting to think of lots of ideas to apply it.
In the past we’ve used a calc field to pull details of related tasks, but just truncate it to the 5 most recent. This could unlock more data in that calc field if needed.
But there’s also new ways to connect that data with the new relationship field options…so I have to decide which approach is better for that.
Question: can you build in an expand/collapse all toggle for a summary field like this?
I’ll give you some code from our EOS L10 dashboard.
We pull in related items with their tasks attached. So, all incomplete related items, and their incomplete tasks. Color change if they are past due. Also includes assignment and details from the task.
I’ll be at a computer in less than an hour. I’ll grab a video and the code for you.
let lastRefreshed = @Last Refreshed || '';
let item = @Title;
let itemLink = 'https://tapeapp.com/[your workspace]/(workspaces/dashboard/apps/l10-issues-items//main-modal:record/' + @Record ID;
let checklist = @Checklist;
// If no checklist items, return empty string
if (!checklist || checklist.length === 0) {
'';
} else {
// Check if all tasks are missing due dates
let allTasksMissingDueDates = checklist.every(task => !task.dueAt);
if (allTasksMissingDueDates) {
'';
} else {
// User ID to name mapping
const userNames = {
1234: "John",
1235: "Not John",
};
// Function to format last refreshed date
function formatLastRefreshed(dateString) {
if (!dateString) return '';
let date = new Date(dateString);
// Timezone
let monthFormatter = new Intl.DateTimeFormat('en-US', {timeZone: 'America/Detroit', month: '2-digit'});
let dayFormatter = new Intl.DateTimeFormat('en-US', {timeZone: 'America/Detroit', day: '2-digit'});
let yearFormatter = new Intl.DateTimeFormat('en-US', {timeZone: 'America/Detroit', year: 'numeric'});
let timeFormatter = new Intl.DateTimeFormat('en-US', {timeZone: 'America/Detroit', hour: '2-digit', minute: '2-digit', hour12: true});
let month = monthFormatter.format(date);
let day = dayFormatter.format(date);
let year = yearFormatter.format(date);
let time = timeFormatter.format(date);
return month + '/' + day + '/' + year + ' ' + time;
}
// Format date
function formatDate(dateString) {
if (!dateString) return '';
let date = new Date(dateString);
let month = (date.getMonth() + 1).toString().padStart(2, '0');
let day = date.getDate().toString().padStart(2, '0');
let year = date.getFullYear();
return month + '-' + day + '-' + year;
}
// Check if date is past due
function isPastDue(dateString) {
if (!dateString) return false;
let dueDate = new Date(dateString);
let today = new Date();
today.setHours(0, 0, 0, 0); // Reset time to start of day
return dueDate < today;
}
// Sort all items by due date
let sortedItems = checklist.sort((a, b) => {
if (!a.dueAt && !b.dueAt) return 0;
if (!a.dueAt) return 1; // Items without dates go to end
if (!b.dueAt) return -1;
return new Date(a.dueAt) - new Date(b.dueAt);
});
let html = '<div style="font-family: Arial, sans-serif; margin: 10px 0;">';
html += '<details style="border: 1px solid #ddd; border-radius: 4px;" open>';
html += '<summary style="background: black; color: white; padding: 10px 15px; cursor: pointer; user-select: none; font-weight: bold; list-style: none; position: relative;">';
html += '<span style="display: inline-block; width: 0; height: 0; border-left: 6px solid transparent; border-right: 6px solid transparent; border-top: 8px solid white; margin-right: 10px; transition: transform 0.2s;"></span>';
html += '<a href="' + itemLink + '" target="_blank" style="color: white; text-decoration: none;">' + item + '</a>';
html += '<span style="position: absolute; right: 15px; top: 50%; transform: translateY(-50%); font-size: 0.8em; font-weight: 300;">' + formatLastRefreshed(lastRefreshed) + '</span>';
html += '</summary>';
html += '<div style="background: white; padding: 15px;">';
html += '<table border="1" style="border-collapse: collapse; width: 100%; table-layout: fixed;">';
html += '<tr><th style="width: 20%;">Assigned To</th><th style="width: 60%;">Task</th><th style="width: 20%;">Due Date</th></tr>';
for (let i = 0; i < sortedItems.length; i++) {
let assignedUser = sortedItems[i].assigneeUserId === null ? 'Unassigned' :
(userNames[sortedItems[i].assigneeUserId] || 'Unknown');
let formattedDate = formatDate(sortedItems[i].dueAt);
let dateStyle = isPastDue(sortedItems[i].dueAt) ? 'color: red;' : '';
// Style for completed items
let rowStyle = sortedItems[i].completed ? 'text-decoration: line-through; color: #999;' : '';
html += '<tr>';
html += '<td style="' + rowStyle + '">' + assignedUser + '</td>';
html += '<td style="' + rowStyle + '">' + sortedItems[i].title + '</td>';
html += '<td style="' + rowStyle + ' ' + dateStyle + '">' + formattedDate + '</td>';
html += '</tr>';
}
html += '</table>';
html += '</div>';
html += '</details>';
html += '</div>';
html;
}
}
Parent: list of related items
let allTables = @All of Checklist Table;
// Filter out empty strings and join without commas
let nonEmptyTables = allTables.filter(table => table && table.trim() !== '');
nonEmptyTables.join('');