Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modal for copying JS script tag #3238

Merged
merged 7 commits into from
May 10, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ The types of changes are:
- Access and erasure support for Gorgias [#2444](https://github.com/ethyca/fides/pull/2444)
- Privacy Experience Bulk Create, Bulk Update, and Detail Endpoints [#3185](https://github.com/ethyca/fides/pull/3185)
- Initial privacy experience UI [#3186](https://github.com/ethyca/fides/pull/3186)
- A JavaScript modal to copy a script tag for `fides.js` [#3238](https://github.com/ethyca/fides/pull/3238)

### Changed

Expand Down
14 changes: 14 additions & 0 deletions clients/admin-ui/cypress/e2e/privacy-experiences.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,20 @@ describe("Privacy experiences", () => {
cy.getByTestId("empty-state");
});

it("can copy a JS script tag", () => {
cy.visit(PRIVACY_EXPERIENCE_ROUTE);
cy.getByTestId("js-tag-btn").click();
cy.getByTestId("copy-js-tag-modal");
// Have to use a "real click" in order for Cypress to properly inspect
// the window's clipboard https://github.com/cypress-io/cypress/issues/18198
cy.getByTestId("clipboard-btn").realClick();
cy.window().then((win) => {
win.navigator.clipboard.readText().then((text) => {
expect(text).to.contain("<script src=");
});
});
});

describe("table", () => {
beforeEach(() => {
cy.visit(PRIVACY_EXPERIENCE_ROUTE);
Expand Down
2 changes: 2 additions & 0 deletions clients/admin-ui/cypress/support/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

// Import commands.js using ES2015 syntax:
import "./commands";
// eslint-disable-next-line import/no-extraneous-dependencies
import "cypress-real-events";

import { stubHomePage, stubPlus, stubSystemCrud } from "./stubs";

Expand Down
2 changes: 1 addition & 1 deletion clients/admin-ui/cypress/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"jsx": "react",
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress", "node"],
"types": ["cypress", "node", "cypress-real-events"],
"baseUrl": "..",
"paths": {
"~/*": ["src/*"]
Expand Down
17 changes: 17 additions & 0 deletions clients/admin-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions clients/admin-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@
"cytoscape-klay": "^3.1.4",
"date-fns": "^2.29.3",
"date-fns-tz": "^2.0.0",
"file-saver": "^2.0.5",
"formik": "^2.2.9",
"i18n-iso-countries": "^7.5.0",
"immer": "^9.0.21",
"file-saver": "^2.0.5",
"js-yaml": "^4.1.0",
"lodash": "^4.17.21",
"msw": "^1.2.1",
Expand Down Expand Up @@ -70,16 +70,17 @@
"@types/cytoscape-klay": "^3.1.0",
"@types/file-saver": "^2.0.5",
"@types/js-yaml": "^4.0.5",
"@types/lodash": "^4.14.192",
"@types/node": "18.15.10",
"@types/react": "17.0.38",
"@types/react-cytoscapejs": "^1.2.2",
"@types/lodash": "^4.14.192",
"@types/react-table": "^7.7.14",
"@typescript-eslint/eslint-plugin": "^5.57.0",
"@typescript-eslint/parser": "^5.57.0",
"babel-jest": "^29.5.0",
"cross-env": "^7.0.3",
"cypress": "^12.8.1",
"cypress-real-events": "^1.7.6",
"eslint": "^8.36.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^17.0.0",
Expand Down
23 changes: 10 additions & 13 deletions clients/admin-ui/src/features/common/ClipboardButton.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Icon, Tooltip, useClipboard } from "@fidesui/react";
import { IconButton, Tooltip, useClipboard } from "@fidesui/react";
import React, { useState } from "react";

import { CopyIcon } from "./Icon";

enum TooltipText {
COPY = "Copy",
COPIED = "Copied",
COPIED = "Copied!",
}

const useClipboardButton = (copyText: string) => {
Expand Down Expand Up @@ -67,22 +69,17 @@ const ClipboardButton = ({ copyText }: ClipboardButtonProps) => {
setTooltipText(TooltipText.COPY);
}}
>
<Icon
cursor="pointer"
width={18}
height={18}
viewBox="0 0 18 18"
<IconButton
icon={<CopyIcon />}
color={iconColor}
onClick={handleMouseDown}
onMouseUp={handleMouseUp}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<path
fill="currentColor"
d="M15 3.75V0H10.625C9.58945 0 8.75 0.839453 8.75 1.875V13.125C8.75 14.1605 9.58945 15 10.625 15H18.125C19.1605 15 20 14.1605 20 13.125V5H16.2852C15.5625 5 15 4.4375 15 3.75ZM16.25 0V3.75H20L16.25 0ZM7.5 13.75V5H1.875C0.839453 5 0 5.83945 0 6.875V18.125C0 19.1605 0.839453 20 1.875 20H9.375C10.4105 20 11.25 19.1605 11.25 18.125V16.25H10C8.62109 16.25 7.5 15.1289 7.5 13.75Z"
/>
</Icon>
aria-label="copy"
variant="ghost"
data-testid="clipboard-btn"
/>
</Tooltip>
);
};
Expand Down
12 changes: 12 additions & 0 deletions clients/admin-ui/src/features/common/Icon/Copy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createIcon } from "@fidesui/react";

export const CopyIcon = createIcon({
displayName: "CopyIcon",
viewBox: "0 0 18 18",
path: (
<path
fill="currentColor"
d="M15 3.75V0H10.625C9.58945 0 8.75 0.839453 8.75 1.875V13.125C8.75 14.1605 9.58945 15 10.625 15H18.125C19.1605 15 20 14.1605 20 13.125V5H16.2852C15.5625 5 15 4.4375 15 3.75ZM16.25 0V3.75H20L16.25 0ZM7.5 13.75V5H1.875C0.839453 5 0 5.83945 0 6.875V18.125C0 19.1605 0.839453 20 1.875 20H9.375C10.4105 20 11.25 19.1605 11.25 18.125V16.25H10C8.62109 16.25 7.5 15.1289 7.5 13.75Z"
/>
),
});
1 change: 1 addition & 0 deletions clients/admin-ui/src/features/common/Icon/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { default as AWSLogoIcon } from "./AWSLogo";
export { CopyIcon } from "./Copy";
export { default as DataFlowScannerLogo } from "./DataFlowScannerLogo";
export { default as DownloadLightIcon } from "./DownloadLightIcon";
export { default as GearLightIcon } from "./GearLightIcon";
Expand Down
75 changes: 75 additions & 0 deletions clients/admin-ui/src/features/privacy-experience/JavaScriptTag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {
Button,
Code,
Modal,
ModalBody,
ModalContent,
ModalHeader,
ModalOverlay,
Stack,
Text,
useDisclosure,
} from "@fidesui/react";
import { useRef } from "react";

import ClipboardButton from "~/features/common/ClipboardButton";
import { CopyIcon } from "~/features/common/Icon";

const SCRIPT_TAG =
'<script src="https://{privacy-center-hostname-and-path}/fides.js"></script>';

const JavaScriptTag = () => {
const modal = useDisclosure();
const initialRef = useRef(null);

return (
<>
<Button
onClick={modal.onOpen}
variant="outline"
size="sm"
rightIcon={<CopyIcon />}
data-testid="js-tag-btn"
>
Get JavaScript tag
</Button>
<Modal
isOpen={modal.isOpen}
onClose={modal.onClose}
isCentered
size="xl"
initialFocusRef={initialRef}
>
<ModalOverlay />
<ModalContent data-testid="copy-js-tag-modal">
{/* Setting tabIndex and a ref makes this the initial modal focus.
This is helpful because otherwise the copy button receives the focus
which triggers unexpected tooltip behavior */}
<ModalHeader tabIndex={-1} ref={initialRef} pb={0}>
Copy JavaScript tag
</ModalHeader>
<ModalBody pt={3} pb={6}>
<Stack spacing={3}>
<Text>
Copy the code below and paste it onto every page of your
website, as high up in the &lt;head&gt; as possible. Replace the
bracketed component with your privacy center&apos;s hostname and
path.
</Text>
<Code display="flex" p={0}>
<Text p={4}>{SCRIPT_TAG}</Text>
<ClipboardButton copyText={SCRIPT_TAG} />
</Code>
<Text>
For more information about adding a JavaScript tag to your
website, visit our docs.
</Text>
</Stack>
</ModalBody>
</ModalContent>
</Modal>
</>
);
};

export default JavaScriptTag;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button, Flex, Spinner } from "@fidesui/react";
import { Box, Button, Flex, Spinner, Stack } from "@fidesui/react";
import { PRIVACY_EXPERIENCE_ROUTE, SYSTEM_ROUTE } from "common/nav/v2/routes";
import { useHasPermission } from "common/Restrict";
import { DateCell, FidesTable, MultiTagCell } from "common/table";
Expand All @@ -21,6 +21,8 @@ import {
} from "~/features/privacy-experience/privacy-experience.slice";
import { PrivacyExperienceResponse, ScopeRegistryEnum } from "~/types/api";

import JavaScriptTag from "./JavaScriptTag";

const PrivacyExperiencesTable = () => {
const router = useRouter();
// Subscribe to get all privacy experiences
Expand Down Expand Up @@ -91,11 +93,16 @@ const PrivacyExperiencesTable = () => {
);
}
return (
<FidesTable<PrivacyExperienceResponse>
columns={columns}
data={privacyExperiences}
onRowClick={userCanUpdate ? handleRowClick : undefined}
/>
<Stack spacing={3}>
<Box alignSelf="end">
<JavaScriptTag />
</Box>
<FidesTable<PrivacyExperienceResponse>
columns={columns}
data={privacyExperiences}
onRowClick={userCanUpdate ? handleRowClick : undefined}
/>
</Stack>
);
};

Expand Down