зеркало из
1
0
Форкнуть 0

Add application insights and cookie banner to Storybook (#155)

This commit is contained in:
James Burnside 2021-04-23 14:27:08 -07:00 коммит произвёл GitHub
Родитель 430c147a54
Коммит a874dcb3b6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 2332 добавлений и 1931 удалений

1
.github/workflows/ci.yml поставляемый
Просмотреть файл

@ -75,6 +75,7 @@ jobs:
working-directory: ./packages/storybook working-directory: ./packages/storybook
env: env:
GH_TOKEN: ${{ github.actor }}:${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}
TELEMETRY_INSTRUMENTATION_KEY: ${{ secrets.TELEMETRY_INSTRUMENTATION_KEY }}
# Create a GitHub issue if the CI failed when running on the `main` branch # Create a GitHub issue if the CI failed when running on the `main` branch
- name: Create issue if main branch CI failed - name: Create issue if main branch CI failed

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -29,7 +29,8 @@ module.exports = {
// Note: This triggers babel to retranspile all package dependency files during webpack's compilation step. // Note: This triggers babel to retranspile all package dependency files during webpack's compilation step.
config.resolve.alias = { config.resolve.alias = {
...(config.resolve.alias || {}), ...(config.resolve.alias || {}),
"@azure/communication-ui": path.resolve(__dirname, "../../communication-ui/src") "@azure/communication-ui": path.resolve(__dirname, "../../communication-ui/src"),
"@azure/acs-chat-selector": path.resolve(__dirname, "../../acs-chat-selector/src")
} }
return config; return config;

Просмотреть файл

@ -0,0 +1,25 @@
<script src="https://wcpstatic.microsoft.com/mscc/lib/v2/wcp-consent.js"></script>
<script>
window.addEventListener('load', () => {
const cookieBannerId = 'cookie-banner';
const cookieBannerContainer = document.createElement('div');
cookieBannerContainer.id = cookieBannerId;
document.body.insertBefore(cookieBannerContainer, document.body.firstChild);
function onConsentChanged(categoryPreferences) {
window.cookieConsentChanged && window.cookieConsentChanged();
}
function initCallback(err, _siteConsent) {
if (!err) {
siteConsent = _siteConsent; // siteConsent is used to get the current consent
window.cookieConsentChanged && window.cookieConsentChanged(); // call callback now cookie library has finished initializing
} else {
console.log(`Error initializing WcpConsent: ${err}`);
}
}
window.WcpConsent && WcpConsent.init('en-US', cookieBannerId, initCallback, onConsentChanged, WcpConsent.themes.light);
});
</script>

Просмотреть файл

@ -1,5 +1,8 @@
import { create } from '@storybook/theming'; import { create } from '@storybook/theming';
import { addons } from '@storybook/addons'; import { addons } from '@storybook/addons';
import { initTelemetry } from './telemetry';
initTelemetry();
addons.setConfig({ addons.setConfig({
theme: create({ theme: create({

Просмотреть файл

@ -0,0 +1,67 @@
import { analyticsCookieConsentObtained } from "./telemetry";
describe('storybook telemetry util tests', () => {
beforeEach(() => {
// reset cookie consent window variables
(window as any).siteConsent = undefined;
})
describe ('test analyticsCookieConsentObtained', () => {
test('analyticsCookieConsentObtained returns false if telemetry library is not initialized', () => {
// set cookie consent library to uninitialized
(window as any).siteConsent = undefined;
const result = analyticsCookieConsentObtained();
expect(result).toEqual(false);
});
test('analyticsCookieConsentObtained returns false if telemetry library is initialized but consent has not been obtained', () => {
// set cookie consent library to uninitialized
(window as any).siteConsent = {
isConsentRequired: true,
getConsent: () => ({
Required: true,
Analytics: false
})
};
const result = analyticsCookieConsentObtained();
expect(result).toEqual(false);
});
test('analyticsCookieConsentObtained returns true if telemetry library is initialized and consent has been obtained', () => {
// set cookie consent library to uninitialized
(window as any).siteConsent = {
isConsentRequired: true,
getConsent: () => ({
Required: true,
Analytics: true
})
};
const result = analyticsCookieConsentObtained();
expect(result).toEqual(true);
});
test('analyticsCookieConsentObtained returns true if telemetry library is initialized but consent is not required', () => {
// set cookie consent library to uninitialized
(window as any).siteConsent = {
isConsentRequired: false,
getConsent: () => ({
Required: false,
Analytics: false
})
};
const result = analyticsCookieConsentObtained();
expect(result).toEqual(true);
});
})
});

Просмотреть файл

@ -0,0 +1,68 @@
import { ApplicationInsights } from '@microsoft/applicationinsights-web'
import { ReactPlugin } from '@microsoft/applicationinsights-react-js';
import { createBrowserHistory } from "history";
/**
* Check if we have the necessary cookie consent to allow the app insights library to make use of cookies
*/
export const analyticsCookieConsentObtained = (): boolean => {
return !!(window as any).siteConsent && // has telemetry library been initialized
(!(window as any).siteConsent.isConsentRequired || // check if we need collect consent in this region
(window as any).siteConsent.getConsent().Analytics); // check if we have consent to collect analytics telemetry
}
/**
* Start telemetry collection and watch for cookie consent changes.
*/
export const initTelemetry = () => {
const appInsightsInstance = startTelemetry(analyticsCookieConsentObtained());
if (appInsightsInstance) {
createCookieChangedCallback(appInsightsInstance);
}
}
/**
* Setup the window.cookieConsentChanged that is called when the cookie banner's onConsentChanged is called.
* @param applicationInsightsInstance application instance that enables or disables appInsight's cookie manager
*/
const createCookieChangedCallback = (applicationInsightsInstance: ApplicationInsights) => {
(window as any).cookieConsentChanged = () => {
const analyticsCookieConsent = analyticsCookieConsentObtained();
applicationInsightsInstance.getCookieMgr().setEnabled(analyticsCookieConsent);
}
}
/**
* Start app insights tracking telemetry
* @param cookieConsent do we have consent to collect cookies for analytics purposes
* @returns the created instance of the application insights library
*/
const startTelemetry = (cookieConsent: boolean): ApplicationInsights | undefined => {
const instrumentationKey = process.env.TELEMETRY_INSTRUMENTATION_KEY;
if (!instrumentationKey) {
console.warn('No telemetry instrumentationKey provided. Telemetry collection is disabled.')
return;
}
// Initialize telemetry react plugin
const browserHistory = createBrowserHistory({ window });
var reactPlugin = new ReactPlugin();
// Initialize and start collecting telemetry
const appInsights = new ApplicationInsights({
config: {
disableCookiesUsage: !cookieConsent,
instrumentationKey,
enableAutoRouteTracking: true,
extensions: [reactPlugin],
extensionConfig: {
[reactPlugin.identifier]: { history: browserHistory }
}
}
});
appInsights.loadAppInsights();
return appInsights;
}

Просмотреть файл

@ -105,10 +105,10 @@ module.exports = {
// restoreMocks: false, // restoreMocks: false,
// The root directory that Jest should scan for tests and modules within // The root directory that Jest should scan for tests and modules within
rootDir: './stories', rootDir: '.',
// A list of paths to directories that Jest should use to search for files in // A list of paths to directories that Jest should use to search for files in
// roots: ['stories'], roots: ['stories', '.storybook'],
// Allows you to use a custom runner instead of Jest's default test runner // Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner", // runner: "jest-runner",
@ -161,7 +161,7 @@ module.exports = {
// A map from regular expressions to paths to transformers // A map from regular expressions to paths to transformers
transform: { transform: {
'^.+\\.(ts)x?$': 'ts-jest', '^.+\\.(ts)x?$': 'ts-jest',
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '<rootDir>/../jest/fileTransform.js' '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '<rootDir>/jest/fileTransform.js'
}, },
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation

Просмотреть файл

@ -30,11 +30,14 @@
"@fluentui/react-icons-northstar": "^0.51.2", "@fluentui/react-icons-northstar": "^0.51.2",
"@fluentui/react-northstar": "^0.51.2", "@fluentui/react-northstar": "^0.51.2",
"@fluentui/react-theme-provider": "^0.18.0", "@fluentui/react-theme-provider": "^0.18.0",
"classnames": "^2.2.6", "@microsoft/applicationinsights-react-js": "~3.0.5",
"react-aria-live": "^2.0.5", "@microsoft/applicationinsights-web": "~2.6.1",
"react-linkify": "^1.0.0-alpha",
"@uifabric/react-hooks": "~7.13.11", "@uifabric/react-hooks": "~7.13.11",
"copy-to-clipboard": "~3.3.1" "classnames": "^2.2.6",
"copy-to-clipboard": "~3.3.1",
"history": "~5.0.0",
"react-aria-live": "^2.0.5",
"react-linkify": "^1.0.0-alpha"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.13.1", "react": "^16.13.1",
@ -46,16 +49,16 @@
"@mdx-js/react": "^1.6.22", "@mdx-js/react": "^1.6.22",
"@microsoft/api-documenter": "~7.12.11", "@microsoft/api-documenter": "~7.12.11",
"@microsoft/api-extractor": "~7.13.2", "@microsoft/api-extractor": "~7.13.2",
"@storybook/addon-actions": "^6.1.1", "@storybook/addon-actions": "~6.1.1",
"@storybook/addon-docs": "^6.1.18", "@storybook/addon-docs": "~6.1.18",
"@storybook/addon-essentials": "^6.1.1", "@storybook/addon-essentials": "~6.1.1",
"@storybook/addon-knobs": "^6.1.9", "@storybook/addon-knobs": "~6.1.9",
"@storybook/addon-links": "^6.1.1", "@storybook/addon-links": "~6.1.1",
"@storybook/addon-storyshots": "^6.1.6", "@storybook/addon-storyshots": "~6.1.6",
"@storybook/node-logger": "^6.1.1", "@storybook/node-logger": "~6.1.1",
"@storybook/react": "^6.1.1", "@storybook/react": "~6.1.1",
"@storybook/storybook-deployer": "^2.8.7", "@storybook/storybook-deployer": "~2.8.7",
"@storybook/theming": "^6.1.10", "@storybook/theming": "~6.1.10",
"@testing-library/jest-dom": "^5.11.4", "@testing-library/jest-dom": "^5.11.4",
"@testing-library/react-hooks": "^3.4.2", "@testing-library/react-hooks": "^3.4.2",
"@types/classnames": "^2.2.11", "@types/classnames": "^2.2.11",

Просмотреть файл

@ -0,0 +1,17 @@
import React from 'react';
import { Meta } from '@storybook/react/types-6-0';
import { PrimaryButton } from '@fluentui/react';
function openManageCookiesModal(): void {
(window as any).parent.siteConsent?.manageConsent();
}
export const ManageCookies: () => JSX.Element = () => {
const manageCookiesRequired = (window as any).parent.siteConsent?.isConsentRequired;
const buttonText = manageCookiesRequired ? 'Manage Cookies' : 'Manage Cookies unavailable';
return <PrimaryButton text={buttonText} onClick={openManageCookiesModal} disabled={!manageCookiesRequired} />;
};
export default {
title: `Settings/Manage Cookies`
} as Meta;

Просмотреть файл

@ -1000,6 +1000,59 @@ exports[`storybook snapshot tests Storyshots Examples/Themes Teams Theme Compone
</div> </div>
`; `;
exports[`storybook snapshot tests Storyshots Settings/Manage Cookies Manage Cookies 1`] = `
<div
className="css-75 root-0 body-1"
>
<div
className="css-75"
data-uses-unhanded-props={true}
dir="ltr"
>
<div
style={
Object {
"alignItems": "center",
"display": "flex",
"height": "100vh",
"justifyContent": "center",
}
}
>
<button
aria-disabled={true}
className="ms-Button ms-Button--primary is-disabled root-2"
data-is-focusable={false}
disabled={true}
onClick={[Function]}
onKeyDown={[Function]}
onKeyPress={[Function]}
onKeyUp={[Function]}
onMouseDown={[Function]}
onMouseUp={[Function]}
type="button"
>
<span
className="ms-Button-flexContainer flexContainer-3"
data-automationid="splitbuttonprimary"
>
<span
className="ms-Button-textContainer textContainer-4"
>
<span
className="ms-Button-label label-6"
id="id__0"
>
Manage Cookies unavailable
</span>
</span>
</span>
</button>
</div>
</div>
</div>
`;
exports[`storybook snapshot tests Storyshots UI Components/GridLayout Grid Layout Component 1`] = ` exports[`storybook snapshot tests Storyshots UI Components/GridLayout Grid Layout Component 1`] = `
<div <div
className="css-75 root-0 body-1" className="css-75 root-0 body-1"

Просмотреть файл

@ -25,6 +25,6 @@
"isolatedModules": true, "isolatedModules": true,
"moduleResolution": "node" "moduleResolution": "node"
}, },
"include": ["stories/**/*"], "include": ["stories/**/*", "./.storybook/**/*.test.ts"],
"exclude": ["dist", "node_modules"] "exclude": ["dist", "node_modules"]
} }