diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4204754b69..3e8c8b598f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,9 @@ The types of changes are:
### Added
- Added models for Privacy Center configuration (for plus users) [#4716](https://github.com/ethyca/fides/pull/4716)
+### Changed
+- Updated privacy notice & experience forms to hide translation UI when user doesn't have translation feature [#4728](https://github.com/ethyca/fides/pull/4728)
+
### Fixed
- Fixed responsive issues with the buttons on the integration screen [#4729](https://github.com/ethyca/fides/pull/4729)
diff --git a/clients/admin-ui/cypress/e2e/privacy-experiences.cy.ts b/clients/admin-ui/cypress/e2e/privacy-experiences.cy.ts
index 3cfacb3010..4b7e8c7dbc 100644
--- a/clients/admin-ui/cypress/e2e/privacy-experiences.cy.ts
+++ b/clients/admin-ui/cypress/e2e/privacy-experiences.cy.ts
@@ -1,4 +1,4 @@
-import { stubPlus } from "cypress/support/stubs";
+import { stubPlus, stubTranslationConfig } from "cypress/support/stubs";
import { PRIVACY_EXPERIENCE_ROUTE } from "~/features/common/nav/v2/routes";
import { RoleRegistryEnum } from "~/types/api";
@@ -192,4 +192,21 @@ describe("Privacy experiences", () => {
});
});
});
+
+ describe("translation interface", () => {
+ it("shows the language interface when translations are enabled", () => {
+ stubTranslationConfig(true);
+ cy.visit(`${PRIVACY_EXPERIENCE_ROUTE}/new`);
+ cy.wait("@getTranslationConfig");
+ cy.getByTestId("input-auto_detect_language").should("exist");
+ });
+
+ it("shows an edit button instead when translations are disabled", () => {
+ stubTranslationConfig(false);
+ cy.visit(`${PRIVACY_EXPERIENCE_ROUTE}/new`);
+ cy.wait("@getTranslationConfig");
+ cy.getByTestId("input-auto_detect_language").should("not.exist");
+ cy.getByTestId("edit-experience-btn").should("exist");
+ });
+ });
});
diff --git a/clients/admin-ui/cypress/e2e/privacy-notices.cy.ts b/clients/admin-ui/cypress/e2e/privacy-notices.cy.ts
index 9ba3dfd6e7..4cd7b21517 100644
--- a/clients/admin-ui/cypress/e2e/privacy-notices.cy.ts
+++ b/clients/admin-ui/cypress/e2e/privacy-notices.cy.ts
@@ -3,6 +3,7 @@ import {
stubPlus,
stubPrivacyNoticesCrud,
stubTaxonomyEntities,
+ stubTranslationConfig,
} from "cypress/support/stubs";
import { PRIVACY_NOTICES_ROUTE } from "~/features/common/nav/v2/routes";
@@ -307,6 +308,7 @@ describe("Privacy notices", () => {
});
it("can create a new privacy notice", () => {
+ stubTranslationConfig(true);
cy.visit(`${PRIVACY_NOTICES_ROUTE}/new`);
cy.getByTestId("new-privacy-notice-page");
const notice = {
@@ -382,4 +384,21 @@ describe("Privacy notices", () => {
cy.getByTestId("input-notice_key").clear().type("custom_key");
});
});
+
+ describe("translation interface", () => {
+ it("shows the translation interface when translations are enabled", () => {
+ stubLanguages();
+ stubTranslationConfig(true);
+ cy.visit(`${PRIVACY_NOTICES_ROUTE}/new`);
+ cy.wait("@getTranslationConfig");
+ cy.getByTestId("add-language-btn").should("exist");
+ });
+
+ it("doesn't show the translation interface when translations are disabled", () => {
+ stubTranslationConfig(false);
+ cy.visit(`${PRIVACY_NOTICES_ROUTE}/new`);
+ cy.wait("@getTranslationConfig");
+ cy.getByTestId("add-language-btn").should("not.exist");
+ });
+ });
});
diff --git a/clients/admin-ui/cypress/support/stubs.ts b/clients/admin-ui/cypress/support/stubs.ts
index f338ad12f6..d864b4fa64 100644
--- a/clients/admin-ui/cypress/support/stubs.ts
+++ b/clients/admin-ui/cypress/support/stubs.ts
@@ -247,3 +247,14 @@ export const stubSystemVendors = () => {
fixture: "systems/system-vendors.json",
}).as("getSystemVendors");
};
+
+export const stubTranslationConfig = (enabled: boolean) => {
+ cy.intercept("GET", "/api/v1/config*", {
+ body: {
+ consent: {
+ enable_translations: enabled,
+ enable_oob_translations: enabled,
+ },
+ },
+ }).as("getTranslationConfig");
+};
diff --git a/clients/admin-ui/src/features/privacy-experience/ConfigurePrivacyExperience.tsx b/clients/admin-ui/src/features/privacy-experience/ConfigurePrivacyExperience.tsx
index d370144c66..a54da83627 100644
--- a/clients/admin-ui/src/features/privacy-experience/ConfigurePrivacyExperience.tsx
+++ b/clients/admin-ui/src/features/privacy-experience/ConfigurePrivacyExperience.tsx
@@ -37,6 +37,7 @@ import {
import { PrivacyExperienceForm } from "~/features/privacy-experience/PrivacyExperienceForm";
import PrivacyExperienceTranslationForm from "~/features/privacy-experience/PrivacyExperienceTranslationForm";
import { selectAllPrivacyNotices } from "~/features/privacy-notices/privacy-notices.slice";
+import { useGetConfigurationSettingsQuery } from "~/features/privacy-requests";
import {
ComponentType,
ExperienceConfigCreate,
@@ -91,6 +92,11 @@ const ConfigurePrivacyExperience = ({
const router = useRouter();
+ const { data: appConfig } = useGetConfigurationSettingsQuery({
+ api_set: false,
+ });
+ const translationsEnabled = appConfig?.consent?.enable_translations;
+
const languagePage = useAppSelector(selectLanguagePage);
const languagePageSize = useAppSelector(selectLanguagePageSize);
useGetAllLanguagesQuery({ page: languagePage, size: languagePageSize });
@@ -184,12 +190,14 @@ const ConfigurePrivacyExperience = ({
{translationToEdit ? (
) : (
diff --git a/clients/admin-ui/src/features/privacy-experience/NewJavaScriptTag.tsx b/clients/admin-ui/src/features/privacy-experience/NewJavaScriptTag.tsx
index 11b0c21399..ff632d8a6d 100644
--- a/clients/admin-ui/src/features/privacy-experience/NewJavaScriptTag.tsx
+++ b/clients/admin-ui/src/features/privacy-experience/NewJavaScriptTag.tsx
@@ -43,7 +43,7 @@ const NewJavaScriptTag = ({ property }: Props) => {
const fidesJsScriptTag = useMemo(() => {
const script = FIDES_JS_SCRIPT_TEMPLATE.replace(
PROPERTY_UNIQUE_ID_TEMPLATE,
- property.id.toString()
+ property.id!.toString()
);
if (isFidesCloud && isSuccess && fidesCloudConfig?.privacy_center_url) {
script.replace(
diff --git a/clients/admin-ui/src/features/privacy-experience/PrivacyExperienceForm.tsx b/clients/admin-ui/src/features/privacy-experience/PrivacyExperienceForm.tsx
index 91ad8ab5e2..7b4f209ecd 100644
--- a/clients/admin-ui/src/features/privacy-experience/PrivacyExperienceForm.tsx
+++ b/clients/admin-ui/src/features/privacy-experience/PrivacyExperienceForm.tsx
@@ -1,4 +1,5 @@
import {
+ ArrowForwardIcon,
Box,
Button,
ButtonGroup,
@@ -81,10 +82,12 @@ export const PrivacyExperienceConfigColumnLayout = ({
export const PrivacyExperienceForm = ({
allPrivacyNotices,
+ translationsEnabled,
onSelectTranslation,
onCreateTranslation,
}: {
allPrivacyNotices: LimitedPrivacyNoticeResponseSchema[];
+ translationsEnabled?: boolean;
onSelectTranslation: (t: ExperienceTranslation) => void;
onCreateTranslation: (lang: SupportedLanguage) => ExperienceTranslation;
}) => {
@@ -241,34 +244,48 @@ export const PrivacyExperienceForm = ({
getItemLabel={(item) => PRIVACY_NOTICE_REGION_RECORD[item]}
draggable
/>
- setFieldValue("translations", newValues)}
- idField="language"
- canDeleteItem={(item) => !item.is_default}
- allItems={allLanguages
- .slice()
- .sort((a, b) => a.name.localeCompare(b.name))
- .map((lang) => ({
- language: lang.id as SupportedLanguage,
- is_default: false,
- }))}
- getItemLabel={getTranslationDisplayName}
- createNewValue={(opt) =>
- onCreateTranslation(opt.value as SupportedLanguage)
- }
- onRowClick={onSelectTranslation}
- selectOnAdd
- draggable
- />
-
+ {translationsEnabled ? (
+ <>
+ setFieldValue("translations", newValues)}
+ idField="language"
+ canDeleteItem={(item) => !item.is_default}
+ allItems={allLanguages
+ .slice()
+ .sort((a, b) => a.name.localeCompare(b.name))
+ .map((lang) => ({
+ language: lang.id as SupportedLanguage,
+ is_default: false,
+ }))}
+ getItemLabel={getTranslationDisplayName}
+ createNewValue={(opt) =>
+ onCreateTranslation(opt.value as SupportedLanguage)
+ }
+ onRowClick={onSelectTranslation}
+ selectOnAdd
+ draggable
+ />
+
+ >
+ ) : (
+ }
+ onClick={() => onSelectTranslation(values.translations![0])}
+ data-testid="edit-experience-btn"
+ >
+ Edit experience text
+
+ )}
);
};
diff --git a/clients/admin-ui/src/features/privacy-experience/PrivacyExperienceTranslationForm.tsx b/clients/admin-ui/src/features/privacy-experience/PrivacyExperienceTranslationForm.tsx
index 3efcf5e64a..1d95780cc1 100644
--- a/clients/admin-ui/src/features/privacy-experience/PrivacyExperienceTranslationForm.tsx
+++ b/clients/admin-ui/src/features/privacy-experience/PrivacyExperienceTranslationForm.tsx
@@ -40,10 +40,12 @@ export const OOBTranslationNotice = ({
const PrivacyExperienceTranslationForm = ({
translation,
+ translationsEnabled,
isOOB,
onReturnToMainForm,
}: {
translation: TranslationWithLanguageName;
+ translationsEnabled?: boolean;
isOOB?: boolean;
onReturnToMainForm: () => void;
}) => {
@@ -148,46 +150,57 @@ const PrivacyExperienceTranslationForm = ({
);
+ let unsavedChangesMessage;
+ if (!translationsEnabled) {
+ unsavedChangesMessage =
+ "You have unsaved changes to this experience text. Discard changes?";
+ } else {
+ unsavedChangesMessage = isEditing
+ ? "You have unsaved changes to this translation. Discard changes?"
+ : "This translation has not been added to your experience. Discard translation?";
+ }
+
return (
- {isEditing
- ? "You have unsaved changes to this translation. Discard changes?"
- : "This translation has not been added to your experience. Discard translation?"}
-
- }
+ title={translationsEnabled ? "Translation not saved" : "Text not saved"}
+ message={{unsavedChangesMessage}}
confirmButtonText="Discard"
handleConfirm={discardChanges}
/>
-
- Are you sure you want to update the default language of this
- experience?
-
- }
- handleConfirm={() => setNewDefaultTranslation(translationIndex)}
- />
- Edit {translation.name} translation
+ {translationsEnabled
+ ? `Edit ${translation.name} translation`
+ : "Edit experience text"}
{isOOB ? : null}
-
+ {translationsEnabled ? (
+ <>
+
+
+ Are you sure you want to update the default language of this
+ experience?
+
+ }
+ handleConfirm={() => setNewDefaultTranslation(translationIndex)}
+ />
+ >
+ ) : null}
+
(
+ <>
+
+
+ >
+);
+
const TranslationFormBlock = ({
index,
name,
@@ -51,17 +68,7 @@ const TranslationFormBlock = ({
Edit {name} translation
{isOOB ? : null}
-
-
+
);
@@ -111,6 +118,11 @@ const PrivacyNoticeTranslationForm = ({
const [translationIsOOB, setTranslationIsOOB] = useState(false);
const [tabIndex, setTabIndex] = useState(0);
+ const { data: appConfig } = useGetConfigurationSettingsQuery({
+ api_set: false,
+ });
+ const translationsEnabled = appConfig?.consent?.enable_translations;
+
const languagePage = useAppSelector(selectPage);
const languagePageSize = useAppSelector(selectPageSize);
useGetAllLanguagesQuery({ page: languagePage, size: languagePageSize });
@@ -161,6 +173,14 @@ const PrivacyNoticeTranslationForm = ({
const getLanguageDisplayName = (lang: SupportedLanguage) =>
allLanguages.find((language) => language.id === lang)?.name ?? lang;
+ if (!translationsEnabled) {
+ return (
+
+
+
+ );
+ }
+
return (
;
translations?: Array;
- properties?: Array;
+ properties?: Array;
};
diff --git a/clients/admin-ui/src/types/api/models/ExperienceConfigListViewResponse.ts b/clients/admin-ui/src/types/api/models/ExperienceConfigListViewResponse.ts
index 5611175ea6..69897ac1a6 100644
--- a/clients/admin-ui/src/types/api/models/ExperienceConfigListViewResponse.ts
+++ b/clients/admin-ui/src/types/api/models/ExperienceConfigListViewResponse.ts
@@ -3,7 +3,7 @@
/* eslint-disable */
import type { ComponentType } from "./ComponentType";
-import { MinimalProperty } from "./MinimalProperty";
+import type { MinimalProperty } from "./MinimalProperty";
import type { PrivacyNoticeRegion } from "./PrivacyNoticeRegion";
/**
@@ -13,8 +13,8 @@ export type ExperienceConfigListViewResponse = {
id: string;
name: string;
regions: Array;
- properties: Array;
component: ComponentType;
updated_at: string;
disabled: boolean;
+ properties: Array;
};
diff --git a/clients/admin-ui/src/types/api/models/ExperienceConfigResponse.ts b/clients/admin-ui/src/types/api/models/ExperienceConfigResponse.ts
index 44f6b16563..bfae9cbda1 100644
--- a/clients/admin-ui/src/types/api/models/ExperienceConfigResponse.ts
+++ b/clients/admin-ui/src/types/api/models/ExperienceConfigResponse.ts
@@ -4,6 +4,7 @@
import type { ComponentType } from "./ComponentType";
import type { ExperienceTranslationResponse } from "./ExperienceTranslationResponse";
+import type { MinimalProperty } from "./MinimalProperty";
import type { PrivacyNoticeRegion } from "./PrivacyNoticeRegion";
import type { PrivacyNoticeResponse } from "./PrivacyNoticeResponse";
@@ -24,4 +25,5 @@ export type ExperienceConfigResponse = {
component: ComponentType;
privacy_notices?: Array;
translations?: Array;
+ properties?: Array;
};
diff --git a/clients/admin-ui/src/types/api/models/ExperienceConfigResponseNoNotices.ts b/clients/admin-ui/src/types/api/models/ExperienceConfigResponseNoNotices.ts
index 3c7b2c7802..e2ab444c49 100644
--- a/clients/admin-ui/src/types/api/models/ExperienceConfigResponseNoNotices.ts
+++ b/clients/admin-ui/src/types/api/models/ExperienceConfigResponseNoNotices.ts
@@ -4,6 +4,7 @@
import type { ComponentType } from "./ComponentType";
import type { ExperienceTranslationResponse } from "./ExperienceTranslationResponse";
+import type { MinimalProperty } from "./MinimalProperty";
import type { PrivacyNoticeRegion } from "./PrivacyNoticeRegion";
import type { SupportedLanguage } from "./SupportedLanguage";
@@ -75,4 +76,5 @@ export type ExperienceConfigResponseNoNotices = {
updated_at: string;
component: ComponentType;
translations?: Array;
+ properties?: Array;
};
diff --git a/clients/admin-ui/src/types/api/models/ExperienceConfigUpdate.ts b/clients/admin-ui/src/types/api/models/ExperienceConfigUpdate.ts
index d8140abcc2..883d9a48a6 100644
--- a/clients/admin-ui/src/types/api/models/ExperienceConfigUpdate.ts
+++ b/clients/admin-ui/src/types/api/models/ExperienceConfigUpdate.ts
@@ -3,6 +3,7 @@
/* eslint-disable */
import type { ExperienceTranslation } from "./ExperienceTranslation";
+import type { MinimalProperty } from "./MinimalProperty";
import type { PrivacyNoticeRegion } from "./PrivacyNoticeRegion";
/**
@@ -24,4 +25,5 @@ export type ExperienceConfigUpdate = {
regions: Array;
translations: Array;
privacy_notice_ids: Array;
+ properties: Array;
};
diff --git a/clients/admin-ui/src/types/api/models/MinimalPrivacyExperience.ts b/clients/admin-ui/src/types/api/models/MinimalPrivacyExperience.ts
index 578ea938ec..e74f286db6 100644
--- a/clients/admin-ui/src/types/api/models/MinimalPrivacyExperience.ts
+++ b/clients/admin-ui/src/types/api/models/MinimalPrivacyExperience.ts
@@ -2,6 +2,10 @@
/* tslint:disable */
/* eslint-disable */
+/**
+ * Minimal representation of a privacy experience, contains enough information
+ * to select experiences by name in the UI and an ID to link the selections in the database.
+ */
export type MinimalPrivacyExperience = {
id: string;
name: string;
diff --git a/clients/admin-ui/src/types/api/models/MinimalProperty.ts b/clients/admin-ui/src/types/api/models/MinimalProperty.ts
index 3408a59a48..f8abf03740 100644
--- a/clients/admin-ui/src/types/api/models/MinimalProperty.ts
+++ b/clients/admin-ui/src/types/api/models/MinimalProperty.ts
@@ -2,6 +2,9 @@
/* tslint:disable */
/* eslint-disable */
+/**
+ * A base template for all other Fides Schemas to inherit from.
+ */
export type MinimalProperty = {
id: string;
name: string;
diff --git a/clients/admin-ui/src/types/api/models/PrivacyExperienceResponse.ts b/clients/admin-ui/src/types/api/models/PrivacyExperienceResponse.ts
index e0e4277317..c87cf354b5 100644
--- a/clients/admin-ui/src/types/api/models/PrivacyExperienceResponse.ts
+++ b/clients/admin-ui/src/types/api/models/PrivacyExperienceResponse.ts
@@ -23,7 +23,8 @@ import type { TCFVendorRelationships } from "./TCFVendorRelationships";
* Notices are extracted from the shared Experience Config and placed at the top-level here
* for backwards compatibility, and to reduce nesting due to notice translations.
*
- * Additionally, the notices on the ExperienceConfig are further filtered.
+ * Additionally, the notices may be a subset of the notices attached to the ExperienceConfig
+ * due to filtering
*/
export type PrivacyExperienceResponse = {
id: string;
diff --git a/clients/admin-ui/src/types/api/models/PrivacyNoticeResponse.ts b/clients/admin-ui/src/types/api/models/PrivacyNoticeResponse.ts
index 1bac778bbb..b708418194 100644
--- a/clients/admin-ui/src/types/api/models/PrivacyNoticeResponse.ts
+++ b/clients/admin-ui/src/types/api/models/PrivacyNoticeResponse.ts
@@ -13,7 +13,7 @@ import type { UserConsentPreference } from "./UserConsentPreference";
/**
* An API representation of a PrivacyNotice used for response payloads
*
- * Overrides fields from PriavcyNotice schema to indicate which ones
+ * Overrides fields from PrivacyNotice schema to indicate which ones
* are guaranteed to be supplied
*/
export type PrivacyNoticeResponse = {
diff --git a/clients/admin-ui/src/types/api/models/Property.ts b/clients/admin-ui/src/types/api/models/Property.ts
index 8b8b46bbbe..45ac8823ec 100644
--- a/clients/admin-ui/src/types/api/models/Property.ts
+++ b/clients/admin-ui/src/types/api/models/Property.ts
@@ -2,12 +2,15 @@
/* tslint:disable */
/* eslint-disable */
-import { MinimalPrivacyExperience } from "./MinimalPrivacyExperience";
-import { PropertyType } from "./PropertyType";
+import type { MinimalPrivacyExperience } from "./MinimalPrivacyExperience";
+import type { PropertyType } from "./PropertyType";
+/**
+ * A base template for all other Fides Schemas to inherit from.
+ */
export type Property = {
- id: string;
name: string;
type: PropertyType;
+ id?: string;
experiences: Array;
};
diff --git a/clients/admin-ui/src/types/api/models/SupportedLanguage.ts b/clients/admin-ui/src/types/api/models/SupportedLanguage.ts
index 47ca71346b..d9f326e78e 100644
--- a/clients/admin-ui/src/types/api/models/SupportedLanguage.ts
+++ b/clients/admin-ui/src/types/api/models/SupportedLanguage.ts
@@ -16,6 +16,7 @@ export enum SupportedLanguage {
EL = "el",
EN = "en",
ES = "es",
+ ES_MX = "es-MX",
ET = "et",
EU = "eu",
FI = "fi",