feat: Allow adding View elements to canvas#3133
feat: Allow adding View elements to canvas#3133mtrezza merged 6 commits intoparse-community:alphafrom
Conversation
|
🚀 Thanks for opening this pull request! We appreciate your effort in improving the project. Please let us know once your pull request is ready for review. |
📝 WalkthroughWalkthroughAdds a new "view" dashboard element: UI registration, a configuration modal (ViewConfigDialog), a data-rendering element (ViewElement), and CustomDashboard integrations for loading, saving, normalizing, fetching, and rendering view-based data. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant UI as CustomDashboard (UI)
participant Pref as ViewPreferencesManager
participant Server as Data Source / CloudFunction
User->>UI: Open "Add Element" -> choose "View"
UI->>Pref: loadAvailableViews()
Pref-->>UI: list of availableViews
UI->>User: show ViewConfigDialog (availableViews)
User->>UI: select view & save
UI->>Pref: (optional) persist selection / preference
UI->>Server: fetch view data (cloudFunction or aggregation query)
Server-->>UI: view results (possibly aggregated)
UI->>UI: normalizeViewResults(results)
UI->>User: render ViewElement with table data
User->>UI: click refresh or pointer item
UI->>Server: re-fetch or handle pointer interaction
Server-->>UI: updated data / pointer details
UI->>User: update ViewElement
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@src/dashboard/Data/CustomDashboard/elements/ViewElement.react.js`:
- Around line 203-217: Replace the ad-hoc regex checks in the 'Link' case of
ViewElement.react.js (the block that reads value.url into url and value.text
into text) with an allowlist URL-scheme check: create an isSafeUrl helper that
uses the URL constructor (with window.location.origin as base) and only permits
http: and https: protocols, treat any parse failure or non-allowed protocol as
unsafe, and if unsafe set url = '#'; keep the existing fallback for text but
also sanitize it similarly; ensure you reference the same variables (url,
value.url, text, value.text) and return the <a> element unchanged except for
using the validated url.
- Around line 89-159: The useMemo hooks (for columnWidths and tableWidth) are
called after early returns which breaks Rules of Hooks; move these hook calls to
the top of the component before any conditional returns and compute
displayColumns there as a memo too. Specifically, remove the existing
declarations of displayColumns, columnWidths, and tableWidth from below the
early returns and re-create them using useMemo (e.g., const displayColumns =
useMemo(() => columns || Object.keys(data?.[0] || {}).filter(k => k !== 'ACL'),
[columns, data]); const columnWidths = useMemo(() => { ...computeTextWidth... },
[data, displayColumns]); const tableWidth = useMemo(() =>
Object.values(columnWidths).reduce((s,w)=>s+w,0), [columnWidths]); ensure
computeTextWidth is used inside those memos and their dependency arrays include
data and columns/displayColumns.
🧹 Nitpick comments (1)
src/dashboard/Data/CustomDashboard/elements/ViewElement.scss (1)
16-16: Consider using SCSS variables for hardcoded colors.Several color values are hardcoded (e.g.,
#e3e3ea,#0e0e1a,#666666,#ef4444). For better theming consistency and maintainability, consider using SCSS variables fromglobals.scssif available.The styling is otherwise well-structured with good responsive patterns (sticky headers, ellipsis handling, hover states).
Also applies to: 23-23, 28-29, 71-72, 80-80, 101-101, 111-111
src/dashboard/Data/CustomDashboard/elements/ViewElement.react.js
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@src/dashboard/Data/CustomDashboard/elements/ViewElement.react.js`:
- Around line 220-225: In the 'Link' case where you set const url =
isSafeUrl(value.url) ? value.url : '#', remove the isSafeUrl(text) check so you
don't treat non-URL labels as invalid; change the text fallback to only trigger
when value.text is empty/whitespace (i.e. replace if (!text || text.trim() ===
'' || !isSafeUrl(text)) with if (!text || text.trim() === '' ) ), keeping the
href validation for value.url using isSafeUrl and preserving user-provided
labels in variables text and url.
- Around line 53-58: The code using canvas.getContext('2d') in computeTextWidth
may get a null context (e.g., JSDOM); add a null guard after const context =
canvas.getContext('2d') and handle the fallback by returning a safe default
width (e.g., the minimum 60px or compute a rough width based on character count)
instead of dereferencing context; update computeTextWidth._canvas assignment
logic to still set _canvas but avoid using context when it's null so no runtime
exception occurs.
- Around line 299-302: The refresh button rendered in ViewElement (the onRefresh
conditional that returns <button type="button" onClick={onRefresh}
className={styles.refreshButton}>) is icon-only and needs an accessible label;
add an aria-label (for example aria-label="Refresh" or a localized equivalent)
to the <button> (or a title attribute as fallback) so screen readers can
announce its purpose, keeping the Icon component and existing props unchanged.
🧹 Nitpick comments (1)
src/dashboard/Data/CustomDashboard/elements/ViewElement.react.js (1)
233-287: Consider allowlist-based validation for Image/Video URLsThe current regex checks can be bypassed (e.g.,
data:or encoded schemes). Consider reusingisSafeUrl(or an explicit allowlist includingblob:/data:if required) for consistent sanitization of media URLs.🔧 Example adjustment
case 'Image': { - const url = value.url; - if (!url || url.match(/javascript/i) || url.match(/<script/i)) { + const url = isSafeUrl(value.url) ? value.url : null; + if (!url) { return '-'; } ... } case 'Video': { - const url = value.url; - if (!url || url.match(/javascript/i) || url.match(/<script/i)) { + const url = isSafeUrl(value.url) ? value.url : null; + if (!url) { return '-'; } ... }
| if (typeof document !== 'undefined') { | ||
| const canvas = computeTextWidth._canvas || (computeTextWidth._canvas = document.createElement('canvas')); | ||
| const context = canvas.getContext('2d'); | ||
| context.font = '12px "Source Code Pro", "Courier New", monospace'; | ||
| const width = context.measureText(str).width + 32; // Add padding | ||
| return Math.max(width, 60); // Minimum 60px |
There was a problem hiding this comment.
Guard against missing canvas context
canvas.getContext('2d') can return null (e.g., JSDOM or unsupported environments), which will throw when setting context.font. Add a null guard with a safe fallback.
🔧 Suggested fix
if (typeof document !== 'undefined') {
const canvas = computeTextWidth._canvas || (computeTextWidth._canvas = document.createElement('canvas'));
const context = canvas.getContext('2d');
+ if (!context) {
+ return Math.max((str.length + 2) * 8, 60);
+ }
context.font = '12px "Source Code Pro", "Courier New", monospace';
const width = context.measureText(str).width + 32; // Add padding
return Math.max(width, 60); // Minimum 60px
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (typeof document !== 'undefined') { | |
| const canvas = computeTextWidth._canvas || (computeTextWidth._canvas = document.createElement('canvas')); | |
| const context = canvas.getContext('2d'); | |
| context.font = '12px "Source Code Pro", "Courier New", monospace'; | |
| const width = context.measureText(str).width + 32; // Add padding | |
| return Math.max(width, 60); // Minimum 60px | |
| if (typeof document !== 'undefined') { | |
| const canvas = computeTextWidth._canvas || (computeTextWidth._canvas = document.createElement('canvas')); | |
| const context = canvas.getContext('2d'); | |
| if (!context) { | |
| return Math.max((str.length + 2) * 8, 60); | |
| } | |
| context.font = '12px "Source Code Pro", "Courier New", monospace'; | |
| const width = context.measureText(str).width + 32; // Add padding | |
| return Math.max(width, 60); // Minimum 60px | |
| } |
🤖 Prompt for AI Agents
In `@src/dashboard/Data/CustomDashboard/elements/ViewElement.react.js` around
lines 53 - 58, The code using canvas.getContext('2d') in computeTextWidth may
get a null context (e.g., JSDOM); add a null guard after const context =
canvas.getContext('2d') and handle the fallback by returning a safe default
width (e.g., the minimum 60px or compute a rough width based on character count)
instead of dereferencing context; update computeTextWidth._canvas assignment
logic to still set _canvas but avoid using context when it's null so no runtime
exception occurs.
| case 'Link': { | ||
| const url = isSafeUrl(value.url) ? value.url : '#'; | ||
| let text = value.text; | ||
| if (!text || text.trim() === '' || !isSafeUrl(text)) { | ||
| text = 'Link'; | ||
| } |
There was a problem hiding this comment.
Don’t validate link text as a URL
Using isSafeUrl(text) forces non-URL labels to render as “Link”, which drops user-provided text. Only fall back when the text is empty.
🔧 Suggested fix
case 'Link': {
const url = isSafeUrl(value.url) ? value.url : '#';
let text = value.text;
- if (!text || text.trim() === '' || !isSafeUrl(text)) {
+ if (!text || text.trim() === '') {
text = 'Link';
}
return (🤖 Prompt for AI Agents
In `@src/dashboard/Data/CustomDashboard/elements/ViewElement.react.js` around
lines 220 - 225, In the 'Link' case where you set const url =
isSafeUrl(value.url) ? value.url : '#', remove the isSafeUrl(text) check so you
don't treat non-URL labels as invalid; change the text fallback to only trigger
when value.text is empty/whitespace (i.e. replace if (!text || text.trim() ===
'' || !isSafeUrl(text)) with if (!text || text.trim() === '' ) ), keeping the
href validation for value.url using isSafeUrl and preserving user-provided
labels in variables text and url.
| {onRefresh && ( | ||
| <button type="button" onClick={onRefresh} className={styles.refreshButton}> | ||
| <Icon name="refresh-solid" width={12} height={12} fill="#94a3b8" /> | ||
| </button> |
There was a problem hiding this comment.
Add an accessible label to the icon-only refresh button
The refresh button is icon-only, which is not screen-reader friendly. Add aria-label (or title) for accessibility.
🔧 Suggested fix
- {onRefresh && (
- <button type="button" onClick={onRefresh} className={styles.refreshButton}>
+ {onRefresh && (
+ <button
+ type="button"
+ onClick={onRefresh}
+ className={styles.refreshButton}
+ aria-label="Refresh view data"
+ >
<Icon name="refresh-solid" width={12} height={12} fill="#94a3b8" />
</button>
)}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {onRefresh && ( | |
| <button type="button" onClick={onRefresh} className={styles.refreshButton}> | |
| <Icon name="refresh-solid" width={12} height={12} fill="#94a3b8" /> | |
| </button> | |
| {onRefresh && ( | |
| <button | |
| type="button" | |
| onClick={onRefresh} | |
| className={styles.refreshButton} | |
| aria-label="Refresh view data" | |
| > | |
| <Icon name="refresh-solid" width={12} height={12} fill="#94a3b8" /> | |
| </button> | |
| )} |
🤖 Prompt for AI Agents
In `@src/dashboard/Data/CustomDashboard/elements/ViewElement.react.js` around
lines 299 - 302, The refresh button rendered in ViewElement (the onRefresh
conditional that returns <button type="button" onClick={onRefresh}
className={styles.refreshButton}>) is icon-only and needs an accessible label;
add an aria-label (for example aria-label="Refresh" or a localized equivalent)
to the <button> (or a title attribute as fallback) so screen readers can
announce its purpose, keeping the Icon component and existing props unchanged.
# [8.3.0-alpha.16](8.3.0-alpha.15...8.3.0-alpha.16) (2026-01-20) ### Features * Allow adding View elements to canvas ([#3133](#3133)) ([2f2ae9a](2f2ae9a))
|
🎉 This change has been released in version 8.3.0-alpha.16 |
# [8.3.0](8.2.0...8.3.0) (2026-02-05) ### Bug Fixes * Canvas graph element does not apply data filter option ([#3128](#3128)) ([00ff1aa](00ff1aa)) * Canvas is not expandable or scrollable beyond current view size ([#3140](#3140)) ([cb73122](cb73122)) * Cloud Config parameter modal cannot scroll when content scales beyond visible area ([#3124](#3124)) ([bb6de31](bb6de31)) * Context menu of header cell in info panel does not group related records by class name ([#3122](#3122)) ([230c1e2](230c1e2)) * Context menu sub-menu in data browser vertically misaligned when scrolling in parent menu ([#3120](#3120)) ([2acaa27](2acaa27)) * Dashboard crashes when adding Parse Pointer JSON to array field in data browser ([#3125](#3125)) ([70bf081](70bf081)) * Data browser graph requires value field even when calculated value is defined ([#3127](#3127)) ([53e4f2b](53e4f2b)) * Graph in canvas ignores date filter constraints ([#3137](#3137)) ([6d8b8e6](6d8b8e6)) * Group-by not using custom aggregation type for single-series graph ([#3159](#3159)) ([b8e8891](b8e8891)) * Incorrect percentage and average calculation for calculated values in graphs ([#3152](#3152)) ([14b4d48](14b4d48)) * Info panel auto-scrolling not pausing during UI interaction like text selection or context menu display ([#3165](#3165)) ([bfe4e8a](bfe4e8a)) * Name of calculated value in data browser graph allows leading and trailing spaces ([#3132](#3132)) ([9493b18](9493b18)) * Optimize data browser cell context menu grouping and readability ([#3141](#3141)) ([5c711e1](5c711e1)) * Security upgrade react-router and react-router-dom ([#3107](#3107)) ([b76b9d1](b76b9d1)) * Setting a keyboard shortcut to its default value creates an unnecessary dashboard config storage entry ([#3173](#3173)) ([4949053](4949053)) ### Features * Add auto-scrolling for info panels ([#3149](#3149)) ([3cd8197](3cd8197)) * Add canvas tree to sidebar instead of loading dialog and favorite functionality ([#3146](#3146)) ([e58dc82](e58dc82)) * Add cloning of element in canvas ([#3144](#3144)) ([dec06ad](dec06ad)) * Add context menu in data browser to add cell content or selected text to Cloud Config parameter ([#3123](#3123)) ([9bc5197](9bc5197)) * Add context menu in data browser to get related records from String and Number fields ([#3118](#3118)) ([824bebd](824bebd)) * Add context menu item to get related records for selected text in data browser cell ([#3142](#3142)) ([33d3595](33d3595)) * Add custom dashboard canvas with graphs and data tables ([#3126](#3126)) ([d45c27b](d45c27b)) * Add customizable styles for line and bar series in data browser graph ([#3131](#3131)) ([501dd4b](501dd4b)) * Add customizable Y-axis titles for data browser graphs ([#3130](#3130)) ([2946e64](2946e64)) * Add dialog to execute Cloud Job with parameters ([#3158](#3158)) ([da419e0](da419e0)) * Add expanded view for canvas elements graph, table, view ([#3156](#3156)) ([0ffd767](0ffd767)) * Add field `objectId` to get related records context menu in data browser ([#3161](#3161)) ([2847ccf](2847ccf)) * Add formula-based calculated value to data browser graph ([#3129](#3129)) ([7c5d1b3](7c5d1b3)) * Add keyboard shortcut for executing script on selected rows in data browser ([#3171](#3171)) ([75389ad](75389ad)) * Add more customization options for graphs in canvas ([#3134](#3134)) ([ddbd666](ddbd666)) * Add non-alphanumeric character detection in Cloud Config parameters ([#3143](#3143)) ([1594ec8](1594ec8)) * Add quick-remove button to array parameter in Cloud Config ([#3121](#3121)) ([b632074](b632074)) * Add support for server side storage of Cloud Config parameter history ([#3169](#3169)) ([964e540](964e540)) * Allow adding View elements to canvas ([#3133](#3133)) ([2f2ae9a](2f2ae9a)) * Allow to favorite canvas for quick-access via sidebar menu ([#3135](#3135)) ([1e3a3c7](1e3a3c7)) * Allow to pause auto-scroll of info panel by holding the Option key ([#3160](#3160)) ([db38f14](db38f14)) * Allow to use ESC key to cancel, Cmd + Enter key to submit, Tab key to navigate a modal dialog ([#3172](#3172)) ([debdc09](debdc09)) * Detect and warn about unprintable characters in Cloud Config parameter values ([#3119](#3119)) ([26c909c](26c909c)) * Improve usability and layout of canvas and elements ([#3148](#3148)) ([cd654dc](cd654dc)) * Optimize appearance of graph tick labels on x-axis for date values ([#3147](#3147)) ([a9edcaf](a9edcaf)) * Optimize canvas menu organization ([#3145](#3145)) ([1558826](1558826)) * Replace graph fields with graph series for more customization ([#3157](#3157)) ([51feba4](51feba4)) * Various improvements for auto-scrolling info panel ([#3151](#3151)) ([423d83a](423d83a))
New Pull Request Checklist
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.