Skip to content

Commit

Permalink
Merge pull request #416 from adobe/clickEnhancements
Browse files Browse the repository at this point in the history
New settings to manage improved click collection
  • Loading branch information
jonsnyder authored Jun 26, 2024
2 parents 15dc1fa + 9c4d5e3 commit 310ce78
Show file tree
Hide file tree
Showing 7 changed files with 269 additions and 59 deletions.
24 changes: 24 additions & 0 deletions scripts/helpers/createExtensionManifest.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,30 @@ const createExtensionManifest = ({ version }) => {
clickCollectionEnabled: {
type: "boolean",
},
clickCollection: {
type: "object",
properties: {
internalLinkEnabled: {
type: "boolean",
},
externalLinkEnabled: {
type: "boolean",
},
downloadLinkEnabled: {
type: "boolean",
},
sessionStorageEnabled: {
type: "boolean",
},
eventGroupingEnabled: {
type: "boolean",
},
filterClickDetails: {
type: "string",
minLength: 1,
},
},
},
downloadLinkQualifier: {
type: "string",
minLength: 1,
Expand Down
3 changes: 3 additions & 0 deletions src/view/components/codeField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const CodeField = ({
description,
language,
placeholder,
beta,
}) => {
const [{ value }, { touched, error }, { setValue, setTouched }] =
useField(name);
Expand Down Expand Up @@ -68,6 +69,7 @@ const CodeField = ({
description={description}
error={touched && error ? error : undefined}
onPress={onPress}
beta={beta}
/>
);
};
Expand All @@ -79,6 +81,7 @@ CodeField.propTypes = {
buttonLabelSuffix: PropTypes.string,
name: PropTypes.string.isRequired,
description: PropTypes.node,
beta: PropTypes.bool,
language: PropTypes.string,
placeholder: PropTypes.string,
};
Expand Down
4 changes: 4 additions & 0 deletions src/view/components/codePreview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
import CodeIcon from "@spectrum-icons/workflow/Code";
import FieldDescriptionAndError from "./fieldDescriptionAndError";
import "./codePreview.styl";
import BetaBadge from "./betaBadge";

const CodePreview = ({
"data-test-id": dataTestId,
Expand All @@ -32,6 +33,7 @@ const CodePreview = ({
description,
error,
onPress,
beta,
}) => {
return (
// To get this element to shrink to its contents, we had to use
Expand All @@ -42,6 +44,7 @@ const CodePreview = ({
// textarea sizing when there is a label on it.
<View position="relative" alignSelf="flex-start">
<LabeledValue label={label} aria-label={ariaLabel} />
{beta && <BetaBadge />}
<FieldDescriptionAndError description={description} error={error}>
<TextArea
width="size-5000"
Expand Down Expand Up @@ -73,6 +76,7 @@ CodePreview.propTypes = {
description: PropTypes.node,
error: PropTypes.string,
onPress: PropTypes.func.isRequired,
beta: PropTypes.bool,
};

export default CodePreview;
204 changes: 171 additions & 33 deletions src/view/configuration/dataCollectionSection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,20 @@ import copyPropertiesIfValueDifferentThanDefault from "./utils/copyPropertiesIfV
import copyPropertiesWithDefaultFallback from "./utils/copyPropertiesWithDefaultFallback";
import FormElementContainer from "../components/formElementContainer";
import FieldDescriptionAndError from "../components/fieldDescriptionAndError";
import BetaBadge from "../components/betaBadge";
import Alert from "../components/alert";

const CONTEXT_GRANULARITY = {
ALL: "all",
SPECIFIC: "specific",
};

const EVENT_GROUPING = {
NONE: "none",
SESSION_STORAGE: "sessionStorage",
MEMORY: "memory",
};

const contextOptions = [
{
label: "Web (information about the current page)",
Expand Down Expand Up @@ -68,38 +76,78 @@ const contextOptions = [
},
];

const clickCollectionIsEnabled = (instanceValues) => {
return (
instanceValues.clickCollection.internalLinkEnabled ||
instanceValues.clickCollection.externalLinkEnabled ||
instanceValues.clickCollection.downloadLinkEnabled
);
};

export const bridge = {
getInstanceDefaults: () => ({
onBeforeEventSend: "",
onBeforeLinkClickSend: "",
clickCollectionEnabled: true,
downloadLinkQualifier:
"\\.(exe|zip|wav|mp3|mov|mpg|avi|wmv|pdf|doc|docx|xls|xlsx|ppt|pptx)$",
contextGranularity: CONTEXT_GRANULARITY.ALL,
context: contextOptions
.filter((option) => option.default)
.map((option) => option.value),
}),
getInstanceDefaults: () => {
const defaults = {
onBeforeEventSend: "",
onBeforeLinkClickSend: "",
clickCollectionEnabled: true,
clickCollection: {
internalLinkEnabled: true,
externalLinkEnabled: true,
downloadLinkEnabled: true,
sessionStorageEnabled: false,
eventGroupingEnabled: false,
filterClickDetails: "",
},
downloadLinkQualifier:
"\\.(exe|zip|wav|mp3|mov|mpg|avi|wmv|pdf|doc|docx|xls|xlsx|ppt|pptx)$",
contextGranularity: CONTEXT_GRANULARITY.ALL,
context: contextOptions
.filter((option) => option.default)
.map((option) => option.value),
eventGrouping: EVENT_GROUPING.NONE,
};
return defaults;
},
getInitialInstanceValues: ({ instanceSettings }) => {
const instanceValues = {};

const propertyKeysToCopy = [
"onBeforeEventSend",
"onBeforeLinkClickSend",
"clickCollectionEnabled",
"clickCollection",
"downloadLinkQualifier",
"context",
];

copyPropertiesWithDefaultFallback({
toObj: instanceValues,
fromObj: instanceSettings,
defaultsObj: bridge.getInstanceDefaults(),
keys: [
"onBeforeEventSend",
"onBeforeLinkClickSend",
"clickCollectionEnabled",
"downloadLinkQualifier",
"context",
],
keys: propertyKeysToCopy,
});

if (instanceSettings.clickCollectionEnabled === false) {
instanceValues.clickCollection.internalLinkEnabled = false;
instanceValues.clickCollection.externalLinkEnabled = false;
instanceValues.clickCollection.downloadLinkEnabled = false;
}

instanceValues.contextGranularity = instanceSettings.context
? CONTEXT_GRANULARITY.SPECIFIC
: CONTEXT_GRANULARITY.ALL;

if (
instanceValues.clickCollection.eventGroupingEnabled &&
instanceValues.clickCollection.sessionStorageEnabled
) {
instanceValues.eventGrouping = EVENT_GROUPING.SESSION_STORAGE;
} else if (instanceValues.clickCollection.eventGroupingEnabled) {
instanceValues.eventGrouping = EVENT_GROUPING.MEMORY;
} else {
instanceValues.eventGrouping = EVENT_GROUPING.NONE;
}

return instanceValues;
},
getInstanceSettings: ({ instanceValues }) => {
Expand All @@ -109,22 +157,32 @@ export const bridge = {
"onBeforeLinkClickSend",
"clickCollectionEnabled",
];

if (instanceValues.clickCollectionEnabled) {
if (clickCollectionIsEnabled(instanceValues)) {
instanceValues.clickCollectionEnabled = true;
propertyKeysToCopy.push("downloadLinkQualifier");
propertyKeysToCopy.push("clickCollection");
instanceValues.clickCollection.eventGroupingEnabled = false;
instanceValues.clickCollection.sessionStorageEnabled = false;
if (instanceValues.clickCollection.internalLinkEnabled) {
if (instanceValues.eventGrouping === EVENT_GROUPING.SESSION_STORAGE) {
instanceValues.clickCollection.eventGroupingEnabled = true;
instanceValues.clickCollection.sessionStorageEnabled = true;
} else if (instanceValues.eventGrouping === EVENT_GROUPING.MEMORY) {
instanceValues.clickCollection.eventGroupingEnabled = true;
}
}
} else {
instanceValues.clickCollectionEnabled = false;
}

copyPropertiesIfValueDifferentThanDefault({
toObj: instanceSettings,
fromObj: instanceValues,
defaultsObj: bridge.getInstanceDefaults(),
keys: propertyKeysToCopy,
});

if (instanceValues.contextGranularity === CONTEXT_GRANULARITY.SPECIFIC) {
instanceSettings.context = instanceValues.context;
}

return instanceSettings;
},
instanceValidationSchema: object().shape({
Expand Down Expand Up @@ -170,14 +228,65 @@ const DataCollectionSection = ({ instanceFieldName }) => {
/>
<div>
<FormikCheckbox
data-test-id="clickCollectionEnabledField"
name={`${instanceFieldName}.clickCollectionEnabled`}
description="Indicates whether data associated with clicks on navigational links, download links, or personalized content should be automatically collected."
data-test-id="internalLinkEnabledField"
name={`${instanceFieldName}.clickCollection.internalLinkEnabled`}
description="Collect data associated with clicks on links within the current domain."
width="size-5000"
>
Enable click data collection
Collect internal link clicks
</FormikCheckbox>
{instanceValues.clickCollectionEnabled && (
{instanceValues.clickCollection.internalLinkEnabled && (
<FieldSubset>
<FormikRadioGroup
label="Event grouping options:"
name={`${instanceFieldName}.eventGrouping`}
>
<Radio
data-test-id="eventGroupingNoneField"
value={EVENT_GROUPING.NONE}
width="size-5000"
>
No event grouping: Internal link click events are sent
immediately.
</Radio>
<Radio
data-test-id="eventGroupingSessionStorageField"
value={EVENT_GROUPING.SESSION_STORAGE}
width="size-5000"
>
Event grouping using session storage: Keep internal link click
data in session storage until the next page view event
(Recommended). <BetaBadge />
</Radio>
<Radio
data-test-id="eventGroupingMemoryField"
value={EVENT_GROUPING.MEMORY}
width="size-5000"
>
Event grouping using local object: Keep internal link click
data in a local object until the next page view event.
Applicable for single-page applications. <BetaBadge />
</Radio>
</FormikRadioGroup>
</FieldSubset>
)}
<FormikCheckbox
data-test-id="externalLinkEnabledField"
name={`${instanceFieldName}.clickCollection.externalLinkEnabled`}
description="Collect data associated with clicks on links to external domains."
width="size-5000"
>
Collect external link clicks
</FormikCheckbox>
<FormikCheckbox
data-test-id="downloadLinkEnabledField"
name={`${instanceFieldName}.clickCollection.downloadLinkEnabled`}
description="Collect data assocated with clicks on links that qualify as download links."
width="size-5000"
>
Collect download link clicks
</FormikCheckbox>
{instanceValues.clickCollection.downloadLinkEnabled && (
<FieldSubset>
<Flex gap="size-100">
<FormikTextField
Expand Down Expand Up @@ -211,21 +320,50 @@ const DataCollectionSection = ({ instanceFieldName }) => {
defaultValue={instanceDefaults.downloadLinkQualifier}
/>
</Flex>
</FieldSubset>
)}
{clickCollectionIsEnabled(instanceValues) && (
<Flex gap="size-100">
<CodeField
data-test-id="filterClickDetailsEditButton"
label="Filter click properties"
buttonLabelSuffix="filter click properties callback code"
name={`${instanceFieldName}.clickCollection.filterClickDetails`}
description="Callback function to evaluate and modify click-related properties before collection."
language="javascript"
placeholder={
"// Use this custom code block to adjust or filter click data. You can use the following variables:\n// content.clickedElement: The DOM element that was clicked\n// content.pageName: The page name when the click happened\n// content.linkName: The name of the clicked link\n// content.linkRegion: The region of the clicked link\n// content.linkType: The type of link (typically exit, download, or other)\n// content.linkUrl: The destination URL of the clicked link\n// Return false to omit link data."
}
beta
/>
</Flex>
)}
{/* onBeforeLinkClickSend is deprecated so if it has not been defined we don't show it */}
{clickCollectionIsEnabled(instanceValues) &&
instanceValues.onBeforeLinkClickSend && (
<Flex gap="size-100">
<CodeField
data-test-id="onBeforeLinkClickSendEditButton"
label="On before link click send callback"
label="On before link click send callback (deprecated, use filter click properties instead)"
buttonLabelSuffix="on before link click event send callback code"
name={`${instanceFieldName}.onBeforeLinkClickSend`}
description='Callback function for modifying data before each link click event is sent to the server. A variable named "content" will be available for use within your custom code. Filter by "content.clickedElement" or modify "content.xdm" or "content.data" as needed to transform data before it is sent to the server. Return false if you want to cancel the link tracking for this element.'
description="Callback function to modify the event payload when a link is clicked."
language="javascript"
placeholder={
'// Filter by "content.clickedElement".\n// Modify content.xdm or content.data as necessary. There is no need to wrap the\n// code in a function or return a value. For example:\n// content.xdm.web.webPageDetails.name = "Checkout";'
"// Use this custom code block to adjust or filter the payload sent to Adobe. You can use the following variables:\n// content.clickedElement: The DOM element that was clicked\n// content.xdm: The XDM payload for the event\n// content.data: The data object payload for the event\n// Return false to abort sending data."
}
/>
</Flex>
</FieldSubset>
)}
)}
{instanceValues.onBeforeLinkClickSend &&
instanceValues.clickCollection.filterClickDetails && (
<Alert variant="notice" title="Warning">
Both filter-click-properties and on-before-link-click-send
callbacks have been defined. On-before-link-click-send has been
deprecated and will not be used if filter-click-properties is
available.
</Alert>
)}
</div>
<div>
<FormikRadioGroup
Expand Down
2 changes: 1 addition & 1 deletion test/functional/helpers/adobeIOClientCredentials.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ if (clientSecret && (privateKeyPath || privateKeyContents)) {
} catch (e) {
// eslint-disable-next-line no-console
console.error(
`Failed to read private key at ${privateKeyPath}. Please ensure the value provided in the ${PRIVATE_KEY_FILE_ENV_VAR_NAME} environment variable is correct.`,
`${e}\nFailed to read private key at ${privateKeyPath}. Please ensure the value provided in the ${PRIVATE_KEY_FILE_ENV_VAR_NAME} environment variable is correct.`,
);
}

Expand Down
Loading

0 comments on commit 310ce78

Please sign in to comment.