Skip to content

Commit

Permalink
OOB translations in admin UI (#4718)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpople committed Mar 15, 2024
1 parent 526deb0 commit 97dde00
Show file tree
Hide file tree
Showing 13 changed files with 146 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ The types of changes are:
- Removed properties beta flag [#4710](https://github.com/ethyca/fides/pull/4710)
- Add acknowledge button label to default Experience English form [#4714](https://github.com/ethyca/fides/pull/4714)
- Update FidesJS to support localizing CMP UI with configurable, non-English default locales [#4720](https://github.com/ethyca/fides/pull/4720)
- Add loading of template translations for notices and experiences [#4718](https://github.com/ethyca/fides/pull/4718)

### Changed
- Moved location-targeting from Notices to Experiences [#4576](https://github.com/ethyca/fides/pull/4576)
Expand Down
14 changes: 11 additions & 3 deletions clients/admin-ui/src/features/common/ScrollableList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const ScrollableListItem = <T extends unknown>({
direction="row"
gap={2}
maxH={maxH}
w="full"
px={2}
align="center"
role="group"
Expand Down Expand Up @@ -66,12 +67,18 @@ const ScrollableListItem = <T extends unknown>({
onRowClick(item);
}
}}
overflow="clip"
>
<Text fontSize="sm" userSelect="none">
<Text
fontSize="sm"
userSelect="none"
textOverflow="ellipsis"
whiteSpace="nowrap"
overflow="hidden"
>
{label}
</Text>
</Flex>

{onDeleteItem ? (
<IconButton
aria-label="Delete"
Expand Down Expand Up @@ -222,7 +229,8 @@ const ScrollableList = <T extends unknown>({
borderColor: "gray.200",
borderRadius: "md",
w: "full",
overflowY: "hidden",
maxH: "8.5rem",
overflowY: "auto",
} as ChakraProps;

const innerList = draggable ? (
Expand Down
2 changes: 2 additions & 0 deletions clients/admin-ui/src/features/common/api.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ export const baseApi = createApi({
"Organization",
"Plus",
"Privacy Experience Configs",
"Experience Config Translations",
"Privacy Notices",
"Privacy Notice Translations",
"Property",
"Purpose",
"System",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
ExperienceConfigCreate,
ExperienceConfigResponse,
ExperienceTranslation,
SupportedLanguage,
} from "~/types/api";
import { isErrorResult } from "~/types/errors";

Expand All @@ -65,7 +66,6 @@ const translationSchema = (requirePreferencesLink: boolean) =>
const validationSchema = Yup.object().shape({
name: Yup.string().required().label("Experience name"),
component: Yup.string().required().label("Experience type"),
// translations: Yup.array().of(translationSchema),
translations: Yup.array().when("component", {
is: (value: ComponentType) =>
value === ComponentType.BANNER_AND_MODAL ||
Expand All @@ -77,8 +77,10 @@ const validationSchema = Yup.object().shape({

const ConfigurePrivacyExperience = ({
passedInExperience,
passedInTranslations,
}: {
passedInExperience?: ExperienceConfigResponse;
passedInTranslations?: ExperienceTranslation[];
}) => {
const [postExperienceConfigMutation] = usePostExperienceConfigMutation();
const [patchExperienceConfigMutation] = usePatchExperienceConfigMutation();
Expand Down Expand Up @@ -136,13 +138,35 @@ const ConfigurePrivacyExperience = ({
TranslationWithLanguageName | undefined
>(undefined);

const handleNewTranslationSelected = (translation: ExperienceTranslation) => {
const [usingOOBValues, setUsingOOBValues] = useState<boolean>(false);

const handleTranslationSelected = (translation: ExperienceTranslation) => {
setTranslationToEdit({
...translation,
name: findLanguageDisplayName(translation, allLanguages),
});
};

const handleCreateNewTranslation = (language: SupportedLanguage) => {
const availableTranslation = passedInTranslations?.find(
(t) => t.language === language
);
if (availableTranslation) {
setUsingOOBValues(true);
}
return (
availableTranslation ?? {
language,
is_default: false,
}
);
};

const handleExitTranslationForm = () => {
setTranslationToEdit(undefined);
setUsingOOBValues(false);
};

return (
<Formik
initialValues={initialValues as ExperienceConfigCreate}
Expand All @@ -160,12 +184,14 @@ const ConfigurePrivacyExperience = ({
{translationToEdit ? (
<PrivacyExperienceTranslationForm
translation={translationToEdit}
onReturnToMainForm={() => setTranslationToEdit(undefined)}
isOOB={usingOOBValues}
onReturnToMainForm={handleExitTranslationForm}
/>
) : (
<PrivacyExperienceForm
allPrivacyNotices={allPrivacyNotices}
onSelectTranslation={handleNewTranslationSelected}
onSelectTranslation={handleTranslationSelected}
onCreateTranslation={handleCreateNewTranslation}
/>
)}
<Flex direction="column" w="75%" bgColor="gray.50" overflowY="hidden">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const PrivacyExperienceConfigColumnLayout = ({
children: React.ReactNode;
}) => (
<Flex direction="column" minH="full" w="25%" borderRight="1px solid #DEE5EE">
<Flex direction="column" h="full" overflowY="scroll" px={4}>
<Flex direction="column" h="full" overflowY="auto" px={4}>
<Flex direction="column" gap={4} w="full" pb={4}>
{children}
</Flex>
Expand All @@ -82,9 +82,11 @@ export const PrivacyExperienceConfigColumnLayout = ({
export const PrivacyExperienceForm = ({
allPrivacyNotices,
onSelectTranslation,
onCreateTranslation,
}: {
allPrivacyNotices: LimitedPrivacyNoticeResponseSchema[];
onSelectTranslation: (t: ExperienceTranslation) => void;
onCreateTranslation: (lang: SupportedLanguage) => ExperienceTranslation;
}) => {
const router = useRouter();

Expand Down Expand Up @@ -254,10 +256,9 @@ export const PrivacyExperienceForm = ({
is_default: false,
}))}
getItemLabel={getTranslationDisplayName}
createNewValue={(opt) => ({
language: opt.value as SupportedLanguage,
is_default: false,
})}
createNewValue={(opt) =>
onCreateTranslation(opt.value as SupportedLanguage)
}
onRowClick={onSelectTranslation}
selectOnAdd
draggable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
CustomTextArea,
CustomTextInput,
} from "~/features/common/form/inputs";
import InfoBox from "~/features/common/InfoBox";
import WarningModal from "~/features/common/modals/WarningModal";
import { BackButtonNonLink } from "~/features/common/nav/v2/BackButton";
import {
Expand All @@ -27,11 +28,23 @@ import {
ExperienceTranslation,
} from "~/types/api";

export const OOBTranslationNotice = ({
languageName,
}: {
languageName: string;
}) => (
<InfoBox
text={`This is a default translation provided by Fides. If you've modified the default English language text, these translations will not match, so verify any changes with a native ${languageName} speaker before using.`}
/>
);

const PrivacyExperienceTranslationForm = ({
translation,
isOOB,
onReturnToMainForm,
}: {
translation: TranslationWithLanguageName;
isOOB?: boolean;
onReturnToMainForm: () => void;
}) => {
const { values, setFieldValue, errors, touched, setTouched } =
Expand All @@ -42,7 +55,7 @@ const PrivacyExperienceTranslationForm = ({
const { name, ...rest } = translation;
return rest as ExperienceTranslation;
}, [translation]);
const isEditing = !!initialTranslation.title;
const isEditing = !!initialTranslation.title && !isOOB;

const formConfig = getTranslationFormFields(values.component);

Expand Down Expand Up @@ -90,7 +103,7 @@ const PrivacyExperienceTranslationForm = ({
};

const handleLeaveForm = () => {
if (translationIsTouched || !initialTranslation.title) {
if (translationIsTouched || isOOB) {
onOpenUnsavedChanges();
} else {
onReturnToMainForm();
Expand Down Expand Up @@ -126,7 +139,7 @@ const PrivacyExperienceTranslationForm = ({
</Button>
<Button
colorScheme="primary"
isDisabled={!translationIsTouched || !!errors.translations}
isDisabled={(!translationIsTouched && !isOOB) || !!errors.translations}
data-testid="save-btn"
onClick={handleSaveTranslation}
>
Expand All @@ -144,7 +157,9 @@ const PrivacyExperienceTranslationForm = ({
title="Translation not saved"
message={
<Text>
You have unsaved changes to this translation. Discard changes?
{isEditing
? "You have unsaved changes to this translation. Discard changes?"
: "This translation has not been added to your experience. Discard translation?"}
</Text>
}
confirmButtonText="Discard"
Expand All @@ -163,8 +178,9 @@ const PrivacyExperienceTranslationForm = ({
handleConfirm={() => setNewDefaultTranslation(translationIndex)}
/>
<Heading fontSize="md" fontWeight="semibold">
{translation.name}
Edit {translation.name} translation
</Heading>
{isOOB ? <OOBTranslationNotice languageName={translation.name} /> : null}
<CustomSwitch
name={`translations.${translationIndex}.is_default`}
id={`translations.${translationIndex}.is_default`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const defaultTranslations: ExperienceTranslationCreate[] = [
accept_button_label: "Accept",
reject_button_label: "Reject",
save_button_label: "Save",
acknowledge_button_label: "Ok",
acknowledge_button_label: "OK",
privacy_preferences_link_label: "Privacy Preferences",
},
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ExperienceConfigListViewResponse,
ExperienceConfigResponse,
ExperienceConfigUpdate,
ExperienceTranslation,
Page_ExperienceConfigListViewResponse_,
PrivacyNoticeRegion,
} from "~/types/api";
Expand Down Expand Up @@ -105,6 +106,15 @@ const privacyExperienceConfigApi = baseApi.injectEndpoints({
{ type: "Privacy Experience Configs", id: arg },
],
}),
getAvailableConfigTranslations: build.query<
Array<ExperienceTranslation>,
string
>({
query: (id) => ({
url: `experience-config/${id}/available_translations`,
}),
providesTags: () => ["Experience Config Translations"],
}),
postExperienceConfig: build.mutation<
ExperienceConfigResponse,
ExperienceConfigCreate
Expand All @@ -124,6 +134,7 @@ export const {
usePatchExperienceConfigMutation,
useLimitedPatchExperienceConfigMutation,
useGetExperienceConfigByIdQuery,
useGetAvailableConfigTranslationsQuery,
usePostExperienceConfigMutation,
} = privacyExperienceConfigApi;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
} from "~/features/data-use/data-use.slice";
import PrivacyNoticeTranslationForm from "~/features/privacy-notices/PrivacyNoticeTranslationForm";
import {
NoticeTranslation,
PrivacyNoticeCreation,
PrivacyNoticeRegion,
PrivacyNoticeResponseWithRegions,
Expand Down Expand Up @@ -98,8 +99,10 @@ const PrivacyNoticeLocationDisplay = ({

const PrivacyNoticeForm = ({
privacyNotice: passedInPrivacyNotice,
availableTranslations,
}: {
privacyNotice?: PrivacyNoticeResponseWithRegions;
availableTranslations?: NoticeTranslation[];
}) => {
const router = useRouter();
const toast = useToast();
Expand Down Expand Up @@ -191,7 +194,9 @@ const PrivacyNoticeForm = ({
variant="stacked"
/>
</FormSection>
<PrivacyNoticeTranslationForm />
<PrivacyNoticeTranslationForm
availableTranslations={availableTranslations}
/>
</Stack>
<ButtonGroup size="sm" spacing={2}>
<Button
Expand Down
Loading

0 comments on commit 97dde00

Please sign in to comment.