Skip to content

feat: Allow adding View elements to canvas#3133

Merged
mtrezza merged 6 commits intoparse-community:alphafrom
mtrezza:feat/canvas-view
Jan 20, 2026
Merged

feat: Allow adding View elements to canvas#3133
mtrezza merged 6 commits intoparse-community:alphafrom
mtrezza:feat/canvas-view

Conversation

@mtrezza
Copy link
Member

@mtrezza mtrezza commented Jan 20, 2026

New Pull Request Checklist

Summary by CodeRabbit

  • New Features
    • Added a "View" element type for dashboards to display data from saved views.
    • New configuration dialog to pick and title available views.
    • Loads available views for selection and saving to the dashboard.
    • Renders view data as interactive tables with dynamic column sizing and type-aware cell rendering (links, images, pointers, dates, etc.).
    • Includes refresh capability, loading/no-data states, and error handling.

✏️ Tip: You can customize this high-level summary in your review settings.

@parse-github-assistant
Copy link

parse-github-assistant bot commented Jan 20, 2026

🚀 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.

@coderabbitai
Copy link

coderabbitai bot commented Jan 20, 2026

📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
Element Type Registration
src/dashboard/Data/CustomDashboard/AddElementDialog.react.js
Adds a 'view' entry to the elementTypes array (icon, title, description) so "View" is selectable when adding elements.
Dashboard Core Integration
src/dashboard/Data/CustomDashboard/CustomDashboard.react.js
Integrates view elements into dashboard lifecycle: adds state (showViewDialog, availableViews), methods (loadAvailableViews, handleSaveView, normalizeViewResults), fetch/render/reload logic, and imports for ViewElement/ViewConfigDialog.
Configuration Dialog
src/dashboard/Data/CustomDashboard/elements/ViewConfigDialog.react.js
New ViewConfigDialog component: select an available view, optional title, validation, and emits normalized config (title, viewId, viewName, className, cloudFunction/query).
View Rendering Element
src/dashboard/Data/CustomDashboard/elements/ViewElement.react.js
New ViewElement component: table rendering of view results with dynamic column widths, per-type cell formatting (Pointer, Date, File, GeoPoint, Link, Image, Video), loading/error/no-data states, refresh handling, and pointer click plumbing.
Styling
src/dashboard/Data/CustomDashboard/elements/ViewElement.scss
New SCSS for ViewElement: table layout, sticky headers, state views (loading/error/empty), responsive ellipsis, and control styling.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is incomplete, missing critical sections from the template including Issue Description (with 'Closes' reference), Approach, and TODOs checklist. Add the missing sections: provide the issue reference in the 'Closes' field, describe the implementation approach, and complete the pre-merge TODOs checklist (tests, documentation, etc.).
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: Allow adding View elements to canvas' clearly and specifically summarizes the main change—adding View element support to the dashboard canvas.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@parseplatformorg
Copy link
Contributor

parseplatformorg commented Jan 20, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@mtrezza mtrezza changed the title feat: Add View element to canvas feat: Allow adding View elements to canvas Jan 20, 2026
@mtrezza
Copy link
Member Author

mtrezza commented Jan 20, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 from globals.scss if 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

@mtrezza mtrezza merged commit 2f2ae9a into parse-community:alpha Jan 20, 2026
11 of 12 checks passed
@mtrezza mtrezza deleted the feat/canvas-view branch January 20, 2026 14:39
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 URLs

The current regex checks can be bypassed (e.g., data: or encoded schemes). Consider reusing isSafeUrl (or an explicit allowlist including blob:/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 '-';
         }
         ...
       }

Comment on lines +53 to +58
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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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.

Comment on lines +220 to +225
case 'Link': {
const url = isSafeUrl(value.url) ? value.url : '#';
let text = value.text;
if (!text || text.trim() === '' || !isSafeUrl(text)) {
text = 'Link';
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +299 to +302
{onRefresh && (
<button type="button" onClick={onRefresh} className={styles.refreshButton}>
<Icon name="refresh-solid" width={12} height={12} fill="#94a3b8" />
</button>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
{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.

parseplatformorg pushed a commit that referenced this pull request Jan 20, 2026
# [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))
@parseplatformorg
Copy link
Contributor

🎉 This change has been released in version 8.3.0-alpha.16

@parseplatformorg parseplatformorg added the state:released-alpha Released as alpha version label Jan 20, 2026
parseplatformorg pushed a commit that referenced this pull request Feb 5, 2026
# [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))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state:released-alpha Released as alpha version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants