Skip to content

feat(dashboards): add text widget handling in widget builder state functions#110577

Merged
nikkikapadia merged 14 commits intomasterfrom
nikki/text-widget/widget-builder-state
Mar 16, 2026
Merged

feat(dashboards): add text widget handling in widget builder state functions#110577
nikkikapadia merged 14 commits intomasterfrom
nikki/text-widget/widget-builder-state

Conversation

@nikkikapadia
Copy link
Member

@nikkikapadia nikkikapadia commented Mar 12, 2026

This PR is just dealing with all the widget builder state setting, retrieving and converting (from state to widget and vice versa). This is not really hooked up to anything within the widget builder yet but that's coming in my next PR.

Couple of things to note:

  1. I've made the textContent state variable just a regular state instead of the query params state. We don't want to send large descriptions into the url params as we could hit url length limits and that would suck.
  2. To compensate for ^that I've added in a session storage key which the initial data can be read from (this would be specifically for editing a widget and I will bake this into the editWidget functions in later PRs). The only downside to this is that when we copy and paste the url of the widget builder the text would not get copied with it.

I am open to other opinions on how to do this! Another thought I had was using a hash and compression function however sentry doesn't have one in the app already and would require further discussion with engineering peeps to see which library would be best for this. In my opinion that may be a little too much hassle for this relatively small feature. Nonetheless i'm all ears!

Closes DAIN-1320

@github-actions github-actions bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Mar 12, 2026
@nikkikapadia nikkikapadia marked this pull request as ready for review March 13, 2026 14:00
@nikkikapadia nikkikapadia requested a review from a team as a code owner March 13, 2026 14:00
Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: textContent leaks into query params via spread operator
    • I excluded textContent from the spread in convertBuilderStateToStateQueryParams and added a regression test asserting it is not present in query params.

Create PR

Or push these changes by commenting:

@cursor push e5dfe4a601
Preview (e5dfe4a601)
diff --git a/static/app/views/dashboards/widgetBuilder/utils/convertBuilderStateToStateQueryParams.spec.tsx b/static/app/views/dashboards/widgetBuilder/utils/convertBuilderStateToStateQueryParams.spec.tsx
--- a/static/app/views/dashboards/widgetBuilder/utils/convertBuilderStateToStateQueryParams.spec.tsx
+++ b/static/app/views/dashboards/widgetBuilder/utils/convertBuilderStateToStateQueryParams.spec.tsx
@@ -56,6 +56,18 @@
     expect(queryParams.selectedAggregate).toBe(0);
   });
 
+  it('does not include text content in query params', () => {
+    const mockState: WidgetBuilderState = {
+      textContent: 'This should not be in URL params',
+      description: 'Description',
+    };
+
+    const queryParams = convertBuilderStateToStateQueryParams(mockState);
+
+    expect(queryParams).not.toHaveProperty('textContent');
+    expect(queryParams.description).toBe('Description');
+  });
+
   it('applies the thresholds to the query params', () => {
     const mockState: WidgetBuilderState = {
       query: ['transaction.duration:>100'],

diff --git a/static/app/views/dashboards/widgetBuilder/utils/convertBuilderStateToStateQueryParams.ts b/static/app/views/dashboards/widgetBuilder/utils/convertBuilderStateToStateQueryParams.ts
--- a/static/app/views/dashboards/widgetBuilder/utils/convertBuilderStateToStateQueryParams.ts
+++ b/static/app/views/dashboards/widgetBuilder/utils/convertBuilderStateToStateQueryParams.ts
@@ -11,7 +11,15 @@
 export function convertBuilderStateToStateQueryParams(
   state: WidgetBuilderState
 ): WidgetBuilderStateQueryParams {
-  const {fields, yAxis, sort, thresholds, traceMetric, ...rest} = state;
+  const {
+    fields,
+    yAxis,
+    sort,
+    thresholds,
+    traceMetric,
+    textContent: _textContent,
+    ...rest
+  } = state;
   return {
     ...rest,
     field: serializeFields(fields ?? []),

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

@linear-code
Copy link

linear-code bot commented Mar 13, 2026

@nikkikapadia nikkikapadia marked this pull request as draft March 13, 2026 18:08
Copy link
Member

@gggritso gggritso left a comment

Choose a reason for hiding this comment

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

Cool! Just a few nits, looks good overall

decoder: decodeScalar,
deserializer: getAxisRange,
});
const [textContent, setTextContent] = useState<string | undefined>(() => {
Copy link
Member

Choose a reason for hiding this comment

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

👀 is it possible to use useLocalStorageState for this?

@nikkikapadia nikkikapadia marked this pull request as ready for review March 16, 2026 14:48
@nikkikapadia nikkikapadia requested a review from gggritso March 16, 2026 14:49
setDescription(action.payload.description, options);
setTextContent(undefined);
}

Copy link
Contributor

Choose a reason for hiding this comment

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

SET_STATE doesn't reset fields/sorts for text widget payload

Low Severity

The SET_STATE action for text widgets sets textContent and clears description, but doesn't explicitly reset data-oriented fields like fields, yAxis, sort, limit, thresholds, traceMetric, or axisRange when they're absent from the payload. The setFields and setYAxis calls are guarded by if (action.payload.field) / if (action.payload.yAxis), so if a text widget payload somehow omits those keys (rather than providing empty arrays), stale values from the previous widget type could persist. Current callers via convertWidgetToBuilderState always include field: [], so this works today, but the SET_DISPLAY_TYPE TEXT branch is more thorough in its cleanup by contrast.

Fix in Cursor Fix in Web

Copy link
Member Author

Choose a reason for hiding this comment

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

usually the only times we use SET_STATE is for populating the state from caching and widget library. All of those have widget's that were already pre-defined a certain way so the same level of cleanup isn't necessary

Copy link
Member

@gggritso gggritso left a comment

Choose a reason for hiding this comment

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

LGTM! I have one nit about function naming, but you can merge past it if you want 👍

Comment on lines +85 to +89
/**
* Converts a widget to SET_STATE params, including textContent for text widgets.
* Use this when dispatching SET_STATE actions. This will carry all necessary information
* needed for text widgets in addition to all other widgets.
*/
Copy link
Member

Choose a reason for hiding this comment

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

I appreciate this comment, but I'm still a bit confused about the distinction between convertWidgetToBuilderState and convertWidgetToBuilderStateParams. If I'm reading this right, it looks like the former is used to take a current widget configuration and convert it into the format in the builder state. The latter is for … turning a widget into a URL query? If that's right, would be worth updating the comments to explain the difference, and maybe even rename the latter function to convertWidgetToQueryParams or something similar to indicate what it's doing

Copy link
Member Author

Choose a reason for hiding this comment

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

ya good point it could be confusing, i'll rename and update description before merging 👍

Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

});
const [textContent, setTextContent, _removeTextContent] = useSessionStorage<
string | undefined
>(SESSION_STORAGE_CONTENT_KEY_MAP.textContent, undefined);
Copy link
Contributor

Choose a reason for hiding this comment

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

Stores "undefined" string instead of removing session storage key

Low Severity

The third return value from useSessionStorage (_removeTextContent) is a proper removeItem function that deletes the key from session storage, but it's deliberately unused. Instead, setTextContent(undefined) is called in multiple places, which runs JSON.stringify(undefined) (returns JS undefined), causing sessionStorage.setItem to store the string "undefined". This only works because readStorageValue has a backward-compatibility check (value === 'undefined') with a comment explicitly stating "This should no longer happen." Using the provided remove function instead of setTextContent(undefined) would properly clean up the storage key.

Additional Locations (1)
Fix in Cursor Fix in Web

Copy link
Member Author

Choose a reason for hiding this comment

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

i don't want to remove the key until i close the widget builder

@nikkikapadia nikkikapadia merged commit f861662 into master Mar 16, 2026
63 checks passed
@nikkikapadia nikkikapadia deleted the nikki/text-widget/widget-builder-state branch March 16, 2026 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants