diff --git a/apps/website/hooks/useCopyToClipboard.tsx b/apps/website/hooks/useCopyToClipboard.tsx
new file mode 100644
index 0000000000..27d6fe3256
--- /dev/null
+++ b/apps/website/hooks/useCopyToClipboard.tsx
@@ -0,0 +1,20 @@
+import { useToast } from "@dxc-technology/halstack-react";
+
+const useCopyToClipboard = () => {
+ const toast = useToast();
+
+ const handleCopy = (text: string) => {
+ navigator.clipboard
+ .writeText(text)
+ .then(() => {
+ toast.success({ message: "Code copied to the clipboard." });
+ })
+ .catch(() => {
+ toast.warning({ message: "Failed to copy the text to the clipboard." });
+ });
+ };
+
+ return handleCopy;
+};
+
+export default useCopyToClipboard;
diff --git a/apps/website/pages/_app.tsx b/apps/website/pages/_app.tsx
index 5f63224d90..942240c825 100644
--- a/apps/website/pages/_app.tsx
+++ b/apps/website/pages/_app.tsx
@@ -2,7 +2,7 @@ import { ReactElement, ReactNode, useMemo, useState } from "react";
import type { NextPage } from "next";
import type { AppProps } from "next/app";
import Head from "next/head";
-import { DxcApplicationLayout, DxcLink, DxcToastsQueue } from "@dxc-technology/halstack-react";
+import { DxcApplicationLayout, DxcInset, DxcLink, DxcToastsQueue } from "@dxc-technology/halstack-react";
import MainContent from "@/common/MainContent";
import { useRouter } from "next/router";
import { LinkDetails, LinksSectionDetails, LinksSections } from "@/common/pagesList";
@@ -116,11 +116,13 @@ export default function App({ Component, pageProps, emotionCache = clientSideEmo
appTitle="Theme Generator"
sideContent={
!currentPath.includes("/theme-generator/user-guide") && (
-
-
- User guide
-
-
+
+
+
+ User guide
+
+
+
)
}
/>
@@ -137,7 +139,7 @@ export default function App({ Component, pageProps, emotionCache = clientSideEmo
/>
)
}
- footer={isThemeGenerator && }
+ footer={isThemeGenerator ? : undefined}
>
diff --git a/apps/website/screens/common/example/Example.tsx b/apps/website/screens/common/example/Example.tsx
index 23970f0030..afdc75dd42 100644
--- a/apps/website/screens/common/example/Example.tsx
+++ b/apps/website/screens/common/example/Example.tsx
@@ -2,7 +2,8 @@ import { useState } from "react";
import styled from "@emotion/styled";
import { LiveProvider, LiveEditor, LiveError, LivePreview } from "react-live";
import theme from "./liveEditorTheme";
-import { DxcButton, DxcFlex, useToast } from "@dxc-technology/halstack-react";
+import { DxcButton, DxcFlex } from "@dxc-technology/halstack-react";
+import useCopyToClipboard from "hooks/useCopyToClipboard";
const StyledPreview = styled.div`
background-color: var(--color-bg-neutral-lightest);
@@ -49,24 +50,13 @@ type ExamplePropTypes = {
};
//
const Example = ({ actionsVisible = true, defaultIsVisible = false, example }: ExamplePropTypes) => {
- const toast = useToast();
const [isCodeVisible, changeIsCodeVisible] = useState(defaultIsVisible);
const [liveCode, setLiveCode] = useState(example.code);
const handleCodeOnClick = () => {
changeIsCodeVisible(!isCodeVisible);
};
-
- const handleCopy = () => {
- navigator.clipboard
- .writeText(liveCode)
- .then(() => {
- toast.success({ message: "Code copied to the clipboard." });
- })
- .catch(() => {
- toast.warning({ message: "Failed to copy the text to the clipboard." });
- });
- };
+ const handleCopy = useCopyToClipboard();
return (
@@ -79,7 +69,16 @@ const Example = ({ actionsVisible = true, defaultIsVisible = false, example }: E
{actionsVisible && (
- {isCodeVisible && }
+ {isCodeVisible && (
+ {
+ handleCopy(liveCode);
+ }}
+ />
+ )}
{
case 1:
return <>>;
case 2:
- return <>>;
+ return ;
}
};
diff --git a/apps/website/screens/theme-generator/components/BottomButtons.tsx b/apps/website/screens/theme-generator/components/BottomButtons.tsx
index be00b1aca2..7a2974d19b 100644
--- a/apps/website/screens/theme-generator/components/BottomButtons.tsx
+++ b/apps/website/screens/theme-generator/components/BottomButtons.tsx
@@ -15,7 +15,7 @@ const BottomButtons = ({
}) => (
diff --git a/apps/website/screens/theme-generator/components/branding/ColorCard.tsx b/apps/website/screens/theme-generator/components/branding/ColorCard.tsx
index 5ed50535ae..c056ddcbbc 100644
--- a/apps/website/screens/theme-generator/components/branding/ColorCard.tsx
+++ b/apps/website/screens/theme-generator/components/branding/ColorCard.tsx
@@ -1,8 +1,8 @@
import { useState } from "react";
-import { DxcContainer, DxcFlex, DxcPopover, DxcTextInput, useToast } from "@dxc-technology/halstack-react";
+import { DxcContainer, DxcFlex, DxcPopover, DxcTextInput } from "@dxc-technology/halstack-react";
import styled from "@emotion/styled";
import { SketchPicker } from "react-color";
-import { copyToClipboard } from "../../utils";
+import useCopyToClipboard from "hooks/useCopyToClipboard";
const ColorBox = styled.button<{ color: string }>`
aspect-ratio: 1;
@@ -36,7 +36,6 @@ export const ColorCard = ({
const [isOpen, setIsOpen] = useState(false);
const [inputValue, setInputValue] = useState(color);
const [error, setError] = useState();
- const toast = useToast();
const handleBlur = ({ value, error }: { value: string; error?: string }) => {
setError(error);
@@ -45,15 +44,7 @@ export const ColorCard = ({
}
};
- const handleCopy = (value: string) => {
- copyToClipboard(value)
- .then(() => {
- toast.success({ message: "Copied to clipboard" });
- })
- .catch(() => {
- toast.warning({ message: "Failed to copy to clipboard" });
- });
- };
+ const handleCopy = useCopyToClipboard();
return (
{
+ return (
+
+
+ {label}
+
+
+ {logo ? (
+
+ ) : (
+ No asset selected.
+ )}
+
+
+
+
+ );
+};
+
+const ReviewBrandingAssets = ({ logos }: { logos: Logos }) => {
+ const brandingAssets = useMemo(() => {
+ return [
+ { label: "Main logo", logo: logos.mainLogo?.[0]?.preview },
+ { label: "Footer logo", logo: logos.footerLogo?.[0]?.preview },
+ { label: "Reduced footer logo", logo: logos.footerReducedLogo?.[0]?.preview },
+ { label: "Favicon", logo: logos.favicon?.[0]?.preview },
+ ];
+ }, [logos]);
+
+ return (
+
+ {brandingAssets.some((asset) => asset.logo) ? (
+ brandingAssets.map((asset) => )
+ ) : (
+ No branding assets selected.
+ )}
+
+ );
+};
+
+export default ReviewBrandingAssets;
diff --git a/apps/website/screens/theme-generator/components/review/ReviewSectionContainer.tsx b/apps/website/screens/theme-generator/components/review/ReviewSectionContainer.tsx
new file mode 100644
index 0000000000..a15502c24b
--- /dev/null
+++ b/apps/website/screens/theme-generator/components/review/ReviewSectionContainer.tsx
@@ -0,0 +1,22 @@
+import { DxcContainer, DxcFlex } from "@dxc-technology/halstack-react";
+
+const ReviewSectionContainer = ({ title, children }: { title: React.ReactNode; children: React.ReactNode }) => {
+ return (
+
+
+ {title}
+ {children}
+
+
+ );
+};
+
+export default ReviewSectionContainer;
diff --git a/apps/website/screens/theme-generator/components/review/ReviewTokensGrid.tsx b/apps/website/screens/theme-generator/components/review/ReviewTokensGrid.tsx
new file mode 100644
index 0000000000..639f622448
--- /dev/null
+++ b/apps/website/screens/theme-generator/components/review/ReviewTokensGrid.tsx
@@ -0,0 +1,60 @@
+import { DxcFlex, DxcGrid, DxcTypography } from "@dxc-technology/halstack-react";
+import { Tokens } from "../../types";
+import React, { useMemo } from "react";
+import { divideColorTokens, SHADE_VALUES } from "../../utils";
+
+const ReviewTokensGrid = ({ generatedTokens }: { generatedTokens: Tokens }) => {
+ const tokenGroups = useMemo(() => divideColorTokens(generatedTokens), [generatedTokens]);
+ return (
+
+ {SHADE_VALUES.map((value, index) =>
+ index === 0 ? (
+
+
+ {value}
+
+
+ ) : (
+
+ {value}
+
+ )
+ )}
+ {Object.entries(tokenGroups).map(([group, colors]) => (
+
+
+
+ {group.charAt(0).toUpperCase() + group.slice(1)}
+
+
+ {colors.map((color: string, index: number) => (
+
+ ))}
+
+ ))}
+
+ );
+};
+
+export default ReviewTokensGrid;
diff --git a/apps/website/screens/theme-generator/components/review/ReviewTokensList.tsx b/apps/website/screens/theme-generator/components/review/ReviewTokensList.tsx
new file mode 100644
index 0000000000..59a414aea1
--- /dev/null
+++ b/apps/website/screens/theme-generator/components/review/ReviewTokensList.tsx
@@ -0,0 +1,13 @@
+import { DxcContainer, DxcTypography } from "@dxc-technology/halstack-react";
+
+const ReviewTokensList = ({ themeJson }: { themeJson: string }) => {
+ return (
+
+
+ {themeJson}
+
+
+ );
+};
+
+export default ReviewTokensList;
diff --git a/apps/website/screens/theme-generator/steps/ReviewDetails.tsx b/apps/website/screens/theme-generator/steps/ReviewDetails.tsx
new file mode 100644
index 0000000000..0089da9614
--- /dev/null
+++ b/apps/website/screens/theme-generator/steps/ReviewDetails.tsx
@@ -0,0 +1,63 @@
+import { DxcButton, DxcFlex, DxcGrid, DxcTypography } from "@dxc-technology/halstack-react";
+import { Logos, Tokens } from "../types";
+import ReviewTokensGrid from "../components/review/ReviewTokensGrid";
+import ReviewTokensList from "../components/review/ReviewTokensList";
+import ReviewBrandingAssets from "../components/review/ReviewBrandingAssets";
+import useCopyToClipboard from "hooks/useCopyToClipboard";
+import { useMemo } from "react";
+import ReviewSectionContainer from "../components/review/ReviewSectionContainer";
+
+const ReviewDetails = ({ generatedTokens, logos }: { generatedTokens: Tokens; logos: Logos }) => {
+ const themeJson = useMemo(() => {
+ const themeObject = {
+ tokens: generatedTokens,
+ logos: {
+ mainLogo: "",
+ footerLogo: "",
+ footerReducedLogo: "",
+ favicon: "",
+ },
+ };
+ return JSON.stringify(themeObject, null, 2);
+ }, [generatedTokens]);
+
+ const handleCopy = useCopyToClipboard();
+
+ return (
+ <>
+
+
+ Color palette & theme
+
+ handleCopy(themeJson)}
+ />
+
+ }
+ >
+
+
+
+
+
+
+
+
+ Branding assets
+
+ }
+ >
+
+
+ >
+ );
+};
+
+export default ReviewDetails;
diff --git a/apps/website/screens/theme-generator/types.ts b/apps/website/screens/theme-generator/types.ts
index 0a444a9f18..0b82e23756 100644
--- a/apps/website/screens/theme-generator/types.ts
+++ b/apps/website/screens/theme-generator/types.ts
@@ -5,6 +5,7 @@ export type Tokens = Record;
export type BaseColors = Record;
export type Step = 0 | 1 | 2;
+
export type Colors = {
primary: CssColor;
secondary: CssColor;
@@ -15,22 +16,38 @@ export type Colors = {
error: CssColor;
warning: CssColor;
};
+
+export type TokenGroups = {
+ primary: string[];
+ secondary: string[];
+ tertiary: string[];
+ semantic01: string[];
+ semantic02: string[];
+ semantic03: string[];
+ semantic04: string[];
+ neutral: string[];
+ alpha: string[];
+};
+
export type FileData = {
error?: string;
file: File;
preview?: string;
};
+
export type Logos = {
mainLogo: FileData[];
footerLogo: FileData[];
footerReducedLogo: FileData[];
favicon: FileData[];
};
+
export type Field = {
id: string;
label: string;
helperText: string;
};
+
export type BrandingDetailsSection = {
id: string;
title: string;
diff --git a/apps/website/screens/theme-generator/utils.ts b/apps/website/screens/theme-generator/utils.ts
index 25cf9062a2..943e55293e 100644
--- a/apps/website/screens/theme-generator/utils.ts
+++ b/apps/website/screens/theme-generator/utils.ts
@@ -1,5 +1,5 @@
import { Color, BackgroundColor, Theme, type CssColor } from "@adobe/leonardo-contrast-colors";
-import { BaseColors, Tokens } from "./types";
+import { BaseColors, TokenGroups, Tokens } from "./types";
/**
* Contrast ratios for generating color shades
@@ -103,6 +103,29 @@ export const handleExport = (tokens: Tokens) => {
downloadAnchorNode.remove();
};
-export const copyToClipboard = (value: string) => {
- return navigator.clipboard.writeText(value);
+export const divideColorTokens = (tokens: Tokens) => {
+ const groups: TokenGroups = {
+ primary: [],
+ secondary: [],
+ tertiary: [],
+ semantic01: [],
+ semantic02: [],
+ semantic03: [],
+ semantic04: [],
+ neutral: [],
+ alpha: [],
+ };
+
+ for (const [key, value] of Object.entries(tokens)) {
+ // divides tokens names into 3 sections
+ const sections = key.match(/^--color-([a-z0-9]+)-(.+)$/);
+ if (sections) {
+ // the second section indicates the group (primary, secondary, etc.)
+ const group = sections[1];
+ if (group && group in groups) {
+ groups[group as keyof TokenGroups].push(value);
+ }
+ }
+ }
+ return groups;
};