Repository navigation
[SCAL-337048] feat(sdk): support per-action disabledActionReason - #711
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
Code Review
This pull request introduces support for a per-action disabledActionReason map (DisabledActionReasonMap) in the Visual Embed SDK, allowing developers to specify custom tooltip reasons for different disabled actions, with an optional GENERIC_REASON fallback. It includes validation utilities, warning logs for invalid types or keys, updated TypeScript types, and comprehensive unit tests. The feedback suggests a more robust implementation of the isPlainObject utility to prevent built-in object types like Map, Set, Date, and RegExp from bypassing the type check and being silently ignored.
commit: |
yinstardev
reviewed
Oct 23, 2026
jitendra-bhati-ts
force-pushed
the
SCAL-337048-per-action-disabled-reason
branch
from
October 23, 2026 07:06
aae4802 to
15a957a
Compare
yinstardev
previously approved these changes
Oct 23, 2026
shivam-kumar-ts
previously approved these changes
Oct 23, 2026
disabledActionReason accepted only one string, shown on every disabled action, so a host could not explain why each action was disabled. It now also accepts a map keyed by Action, with an optional GENERIC_REASON fallback. A string is sent unchanged. A map is cleaned before it is sent: keys that are not an Action or GENERIC_REASON, and values that are not strings, are dropped with a console warning, so a bad entry never fails the embed. What is left goes out JSON-encoded in the existing disableHint param; an empty result sends nothing, the same as leaving the option unset. GENERIC_REASON and DisabledActionReasonMap are exported from the main and React entry points.
…-337048) With excludeConfigFromURL, disableHint is held back from the iframe URL and sent in the APP_INIT embedParams instead. Add tests that a map arrives there as the cleaned object, without the unknown key, and that a string arrives unchanged. Also detect a plain object by its built-in type, so a Map, Date or other built-in gets the invalid-type warning, named by its real type, instead of being treated as an empty map.
…337048) Use lodash isPlainObject and isString, which the SDK already uses, instead of a hand-written type check. isPlainObject is false for arrays, Maps, Dates and other built-ins, so they still get the invalid-type warning. The warning no longer names the value's type.
…AL-337048) 1.53.1 is already released, so the map form ships in the next release. Use the <TBD> placeholder, which the release bump fills in, and point the disabledActionReason doc at DisabledActionReasonMap for the SDK version instead of repeating it.
…7048) A general name, so other action helpers can live in the same file.
jitendra-bhati-ts
dismissed stale reviews from shivam-kumar-ts and yinstardev
via
October 27, 2026 09:35
131cefd
jitendra-bhati-ts
force-pushed
the
SCAL-337048-per-action-disabled-reason
branch
from
October 27, 2026 09:35
15a957a to
131cefd
Compare
Contributor
|
this last few comment this will be the breakign changes for the 26.11 or previous version |
… (SCAL-337048) A per-action map can hold a reason for every action, which is too long for the iframe URL. Send the map in the APP_INIT payload's embedParams on every embed, the same place excludeConfigFromURL already sends it; the app merges those embedParams over the URL params, so it needs no change. The string form stays on the URL, so it keeps working on every cluster. Prerender and UpdateEmbedParams already carry the map as an object. Filter the map with lodash pickBy and walk the query params with lodash forEach, matching the lodash helpers the SDK already uses.
jitendra-bhati-ts
force-pushed
the
SCAL-337048-per-action-disabled-reason
branch
from
October 27, 2026 19:23
131cefd to
6784e5a
Compare
… (SCAL-337048) Map each param to a check on its value instead of hard-coding the object check in the loop, so a future param can be kept off the URL by adding one entry with its own condition. No behaviour change.
ruchI9897
reviewed
Oct 28, 2026
…y (SCAL-337048) GENERIC_REASON was the only const in types.ts. Type the map key as Action | 'GENERIC_REASON' instead, so customers write GENERIC_REASON as a plain key and TypeScript still checks it. The key on the wire is unchanged, so the merged app side needs nothing. The constant was never released.
shivam-kumar-ts
approved these changes
Oct 28, 2026
ruchI9897
approved these changes
Oct 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
disabledActionReasoncan now be a string (unchanged) or a map that gives each disabled action its own tooltip, with an optionalGENERIC_REASONfallback.Why
Today one string is shown on every disabled action. A host that disables Download for licensing and Save for read-only access can only show one generic message for both.
How
types.ts):disabledActionReason?: string | DisabledActionReasonMap; newDisabledActionReasonMaptype (Partial<Record<Action | 'GENERIC_REASON', string>>), exported from the main and/reactentry points.GENERIC_REASONis a plain key typed in the map, not an exported const, so customers writeGENERIC_REASON: '...'and TypeScript still checks it.utils/actions.ts, new; lodashisString,isPlainObject,pickBy,isEmpty): a string goes out unchanged. A map keeps only keys that are anActionorGENERIC_REASONwith a string value. Anything else is dropped with alogger.warn, and the embed never fails over it. The allow-list also blocks__proto__/constructorkeys. An empty map, or a map where every key was dropped, sends nothing, the same as leaving the option unset.ts-embed.ts): the map is never put on the iframe URL. A map can hold a reason for every action, which is too long for a URL, so it is sent in theAPP_INITpayload (embedParams.disableHint) on every embed, the same channelexcludeConfigFromURLalready uses.APP_INIT_PARAMSmaps each param to a check on its value (disableHint→isPlainObject), andgetUrlQueryParamsObjectmoves a param to the init data when its check passes. Another param can be kept off the URL by adding one entry with its own check. The string form stays on the URL, so it keeps working on every cluster.UpdateEmbedParamsalready send the whole config over postMessage, so they carry the map as an object without extra code.disabledActionReasondisableHint), unchangedAPP_INITpayload (embedParams.disableHint), never the URLVersion
The object form is supported from SDK
<TBD>(next release) | ThoughtSpot Cloud 26.12.0.cl. The string form is unchanged (SDK 1.6.0).Backward compatibility
The string form is unchanged, and all existing tests pass as they are. One behaviour change: a non-string, non-object value (number, array, boolean), which the type never allowed, is now dropped with a warning instead of being sent as-is.
App side (merged)
The tooltip is drawn by the ThoughtSpot app, not the SDK. The app side is merged in scaligent #72606 and #72825. The app needs no change for the
APP_INITtransport: it already mergesAPP_INITembedParamsover the URL params on every load. On a cluster without the app change, a map does not render correctly, which is why the object form needs ThoughtSpot Cloud 26.12.0.cl.SDK map on a cluster without app side changes: tooltip shows [object Object]. (Screenshots were taken while the map still went on the URL; the result is the same with the
APP_INITtransport.)Tests
actions.spec.tscovers the string form, full map, partial map with and withoutGENERIC_REASON, a map with onlyGENERIC_REASON, empty map, unknown keys, prototype keys, non-string values and invalid types.liveboard.spec.tschecks that a map is kept off the iframe URL, and that an empty map sends nodisableHint.ts-embed.spec.tschecks that the map arrives in theAPP_INITpayload with and withoutexcludeConfigFromURL(other params stay on the URL), that a string stays on the URL, and the cleaned map in theUpdateEmbedParamspayload.disableHint, and each disabled action shows its own reason.npm run tsc:check,npm run lint,npm run lint-versionsandnpm run test-sdkall pass (2207 passed, 4 skipped). Bundle size 32.69 kB of the 34 kB limit.Jira: https://thoughtspot.atlassian.net/browse/SCAL-337048