feat: support ability for consumers to add custom props / HTML attributes defined via configuration to components #723
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.
Description
Context
Open edX instances (e.g., 2U) rely on third-party tools for logging and analytics (e.g., Datadog, Hotjar). Some of these tools end up ingesting PII into things such as session replays, etc. that should otherwise be masked.
Example vendor-specific approaches to masking UI elements from their ingested data:
data-dd-privacy="allow" | "mask" | "hidden" | "mask-user-input"
.data-hj-suppress
anddata-hj-allow
..fs-mask
,.fs-unmask
,.fs-exclude
, etc. class names.Currently, such attributes for Hotjar are hardcoded throughout the platform (search results), despite not all instances of Open edX using Hotjar. While these attributes are fairly benign, the current approach does not support extending the masking of PII to other vendors (e.g., Datadog).
Related, there are other vendor-specific HTML attributes and class names unrelated to PII this approach would also be applicable to. As such, the following implementation is generic to support "custom props" on annotated elements, regardless of whether it's for PII masking or otherwise.
Solution
Introduces a React hook (
useComponentPropOverrides
) and a Higher-Order-Component (withComponentPropOverrides
) as a mechanism to allow consumers to add one or more custom props (e.g., to mask PII within session replays) to any component that accepts prop spreading (e.g.,...props
).Configuration
May be configured by
env.config.js
or the MFE runtime configuration withcomponentPropOverrides
(open to other config property names):Usage
Consider the following JSX containing PII (i.e., username):
useComponentPropOverrides
(hook)withComponentPropOverrides
(HOC)By using
useComponentPropOverrides
(hook) orwithComponentPropOverrides
(HOC) to mask the username from third-party tools with the abovecomponentPropOverrides
configuration, the resulting HTML would render as the following:It does this by attempting to find a match between the component's specified selector and the
componentPropOverrides.targets.*
defined via configuration. If the specified selector matches one of the selectors defined via configuration, any configured props and their values be returned.Special cases
By default, components supporting configurable prop overrides only works with the prop
className
and any prop prefixed withdata-
(e.g.,data-dd-privacy
). Any other configuration prop name is ignored and not applied during rendering.In certain cases, components may opt-in to supporting overrides of explicitly named prop names (e.g.,
onClick
). By making the special cases like function handlers orstyle
opt-in within the MFE's base code, it provides opportunity to discuss/review which prop overrides are officially supported in any given component beyond the defaultdata-*
attributes orclassName
prop.className
Custom class name(s) will be concatenated with any existing
className
prop values that might already be passed to the component.style
Custom
style
properties will be shallow merged with any existingstyle
properties that might already exist for the component. If the style properties overlap, the customstyle
's value takes precedence.Functions (e.g.,
onClick
)If a custom prop is defined as a function (i.e., supported when
customComponentProps
is defined viaenv.config
) but the component itself already has anonClick
function handler, the component'sonClick
function will be executed first before calling the customonClick
handler. Both functions otherwise receive the same functionsargs
.Merge checklist:
frontend-platform
. This can be done by runningnpm start
and opening http://localhost:8080.module.config.js
file infrontend-build
.fix
,feat
) and is appropriate for your code change. Consider whether your code is a breaking change, and modify your commit accordingly.Post merge: