Skip to content

Commit ee84d54

Browse files
PelayoFelguerosoCopilot
andcommitted
Centralize Tour components and functions
Co-authored-by: Copilot <copilot@github.com>
1 parent eba3a70 commit ee84d54

5 files changed

Lines changed: 352 additions & 357 deletions

File tree

apps/website/screens/theme-generator/ThemeGeneratorConfigPage.tsx

Lines changed: 6 additions & 330 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,13 @@
1-
import { useMemo, useRef, useState, useEffect } from "react";
2-
import { useRouter } from "next/router";
3-
import {
4-
DxcButton,
5-
DxcContainer,
6-
DxcDialog,
7-
DxcFlex,
8-
DxcHeading,
9-
DxcParagraph,
10-
DxcWizard,
11-
} from "@dxc-technology/halstack-react";
1+
import { useMemo, useRef, useState } from "react";
2+
import { DxcContainer, DxcFlex, DxcWizard } from "@dxc-technology/halstack-react";
123
import StepHeading from "./components/StepHeading";
134
import BottomButtons from "./components/BottomButtons";
145
import ThemeGeneratorPreviewPage from "./ThemeGeneratorPreviewPage";
156
import { BrandingDetails } from "./steps/BrandingDetails";
167
import { generateTokens, handleExport } from "./utils";
178
import { Colors, FileData, Step } from "./types";
189
import ReviewDetails from "./steps/ReviewDetails";
19-
import { ACTIONS, EVENTS, Joyride, STATUS, type EventData, type Step as JoyrideStep } from "react-joyride";
20-
import TourPopover from "./components/TourPopover";
21-
import Code from "@/common/Code";
10+
import Tour from "./components/Tour/Tour";
2211

2312
const steps = [
2413
{
@@ -46,152 +35,9 @@ const wizardSteps = steps.map(({ label, description }) => ({
4635
description,
4736
}));
4837

49-
const firstStepTour: JoyrideStep[] = [
50-
{
51-
title: "The 3-Step Journey",
52-
content: (
53-
<>
54-
To build your theme, we'll guide you through 3 simple steps:
55-
<br />
56-
<br />
57-
1. Configure: Define brand colors and assets
58-
<br />
59-
2. Preview: See your styles applied
60-
<br />
61-
3. Export: Review and download your theme
62-
</>
63-
),
64-
target: "#wizard-tour",
65-
placement: "bottom",
66-
skipBeacon: true,
67-
},
68-
{
69-
title: "Define theme colors",
70-
content: (
71-
<>
72-
Set your theme colors, including core colors for your brand and semantic colors for system feedback.
73-
<br />
74-
<br />
75-
You can update this anytime.
76-
</>
77-
),
78-
placement: "bottom",
79-
target: "#colors-tour",
80-
skipBeacon: true,
81-
},
82-
{
83-
title: "Upload assets",
84-
content: (
85-
<>
86-
Upload branding assets such as logo, footer logos, and favicon.
87-
<br />
88-
<br />
89-
Applied across your theme for brand consistency.
90-
</>
91-
),
92-
placement: "top",
93-
target: "#assets-tour",
94-
skipBeacon: true,
95-
arrowSpacing: 162,
96-
},
97-
];
98-
99-
const secondStepTour: JoyrideStep[] = [
100-
{
101-
title: "Preview theme",
102-
content: (
103-
<>
104-
Use this toggle to preview your theme across different views. Components show individual UI elements, while
105-
Layouts display predefined page structures. Use this to check color usage, consistency, and readability across
106-
your theme.
107-
</>
108-
),
109-
target: "#toggle-tour",
110-
placement: "right",
111-
skipBeacon: true,
112-
},
113-
{
114-
title: "Filter your preview",
115-
content: (
116-
<>
117-
Choose specific component groups to preview, such as Forms or Feedback.
118-
<br />
119-
<br />
120-
You can select one or multiple groups to see how your theme styles apply across different scenarios.
121-
</>
122-
),
123-
placement: "left",
124-
target: "#select-preview-tour",
125-
skipBeacon: true,
126-
},
127-
{
128-
title: "Preview area",
129-
content: (
130-
<>
131-
In this area, you can preview both Components and Layout examples.
132-
<br />
133-
<br />
134-
Use the selector to add them to your canvas. If it gets too crowded, just click <Code>Clear</Code> to reset the
135-
view.
136-
</>
137-
),
138-
placement: "top",
139-
target: "#preview-area-tour",
140-
skipBeacon: true,
141-
arrowSpacing: 162,
142-
},
143-
];
144-
145-
const thirdStepTour: JoyrideStep[] = [
146-
{
147-
title: "Review theme",
148-
content: (
149-
<>
150-
Review your colors, branding, and overall configuration. Validate your setup before finalizing.
151-
<br />
152-
<br />
153-
Helps confirm your theme is ready for export.
154-
</>
155-
),
156-
target: "#review-tour",
157-
placement: "top",
158-
skipBeacon: true,
159-
},
160-
{
161-
title: "Copy configuration",
162-
content: (
163-
<>
164-
Copy your theme configuration as JSON.
165-
<br /> Use it for quick integration.
166-
<br />
167-
<br />
168-
Allow faster implementation without downloading a file.
169-
</>
170-
),
171-
placement: "left",
172-
target: "#copy-configuration-tour",
173-
skipBeacon: true,
174-
},
175-
{
176-
title: "Export theme",
177-
content: (
178-
<>
179-
Export your theme as a file and use it across your applications. Your theme is ready to be applied across
180-
projects.
181-
<br />
182-
<br />
183-
You're all set! If you'd like to go through the tour again, click the restart button.
184-
</>
185-
),
186-
placement: "left-start",
187-
target: "#export-tour",
188-
skipBeacon: true,
189-
},
190-
];
191-
19238
const ThemeGeneratorConfigPage = () => {
193-
const router = useRouter();
19439
const [currentStep, setCurrentStep] = useState<Step>(0);
40+
const [tourStepIndex, setTourStepIndex] = useState(0);
19541
const [colors, setColors] = useState<Colors>({
19642
primary: "#5F249F",
19743
secondary: "#0067B3",
@@ -210,26 +56,6 @@ const ThemeGeneratorConfigPage = () => {
21056
});
21157
const [tokens, setTokens] = useState<Record<string, string>>({});
21258
const lastGeneratedColorsRef = useRef<string>("");
213-
const [isDialogVisible, setDialogVisible] = useState(false);
214-
const [runTour, setRunTour] = useState(false);
215-
const [tourStepIndex, setTourStepIndex] = useState(0);
216-
const isTourActiveRef = useRef(false);
217-
const completedToursRef = useRef<Set<Step>>(new Set());
218-
219-
useEffect(() => {
220-
if (router.query.tour === "true") {
221-
setDialogVisible(true);
222-
// Remove the tour parameter from the URL without reloading
223-
void router.replace("/theme-generator/configuration", undefined, { shallow: true });
224-
}
225-
}, [router.query.tour]);
226-
227-
useEffect(() => {
228-
if (isTourActiveRef.current && !completedToursRef.current.has(currentStep)) {
229-
setTourStepIndex(0);
230-
setRunTour(true);
231-
}
232-
}, [currentStep]);
23359

23460
const themeJson = useMemo(() => {
23561
const themeObject = {
@@ -273,64 +99,12 @@ const ThemeGeneratorConfigPage = () => {
27399
setCurrentStep(step);
274100
};
275101

276-
const handleStartTour = () => {
277-
if (isDialogVisible) {
278-
setDialogVisible(false);
279-
}
280-
isTourActiveRef.current = true;
281-
setRunTour(true);
282-
};
283-
284-
const handleFinishTour = () => {
285-
completedToursRef.current.add(currentStep);
286-
if (currentStep === 2) {
287-
isTourActiveRef.current = false;
288-
}
289-
setRunTour(false);
290-
};
291-
292-
const handleRestartTour = () => {
293-
completedToursRef.current.delete(currentStep);
294-
setTourStepIndex(0);
295-
setRunTour(false);
296-
setTimeout(() => {
297-
setRunTour(true);
298-
}, 100);
299-
};
300-
301-
const handleTourEvent = ({ action, status, index, type }: EventData) => {
302-
if (status === STATUS.FINISHED || status === STATUS.SKIPPED || status === STATUS.PAUSED) {
303-
handleFinishTour();
304-
return;
305-
}
306-
307-
if (action === ACTIONS.CLOSE) {
308-
handleFinishTour();
309-
return;
310-
}
311-
312-
// Update index when step changes
313-
if (type === EVENTS.STEP_AFTER) {
314-
if (action === ACTIONS.NEXT) {
315-
setTourStepIndex(index + 1);
316-
} else if (action === ACTIONS.PREV) {
317-
setTourStepIndex(index - 1);
318-
}
319-
}
320-
};
321-
322102
const renderStepContent = () => {
323103
switch (currentStep) {
324104
case 0:
325105
return <BrandingDetails colors={colors} onColorsChange={setColors} logos={logos} onLogosChange={setLogos} />;
326106
case 1:
327-
return (
328-
<ThemeGeneratorPreviewPage
329-
tokens={tokens}
330-
logos={logos}
331-
showDefaultComponents={runTour && tourStepIndex === 2}
332-
/>
333-
);
107+
return <ThemeGeneratorPreviewPage tokens={tokens} logos={logos} showDefaultComponents={tourStepIndex === 2} />;
334108
case 2:
335109
return <ReviewDetails tokens={tokens} logos={logos} themeJson={themeJson} />;
336110
}
@@ -344,77 +118,7 @@ const ThemeGeneratorConfigPage = () => {
344118
padding={{ top: "var(--spacing-padding-xl)" }}
345119
background={{ color: "var(--color-bg-neutral-lighter)" }}
346120
>
347-
{currentStep === 0 && (
348-
<Joyride
349-
continuous
350-
run={runTour}
351-
steps={firstStepTour}
352-
stepIndex={tourStepIndex}
353-
tooltipComponent={(tooltipProps) => (
354-
<TourPopover {...tooltipProps} onFinish={handleFinishTour} onRestart={handleRestartTour} />
355-
)}
356-
onEvent={handleTourEvent}
357-
options={{
358-
overlayClickAction: false,
359-
hideOverlay: false,
360-
blockTargetInteraction: false,
361-
spotlightPadding: 4,
362-
overlayColor: "var(--color-bg-alpha-strong)",
363-
zIndex: 10000,
364-
arrowBase: 12,
365-
arrowSize: 6,
366-
offset: 0,
367-
}}
368-
/>
369-
)}
370-
371-
{currentStep === 1 && (
372-
<Joyride
373-
continuous
374-
run={runTour}
375-
steps={secondStepTour}
376-
stepIndex={tourStepIndex}
377-
tooltipComponent={(tooltipProps) => (
378-
<TourPopover {...tooltipProps} onFinish={handleFinishTour} onRestart={handleRestartTour} />
379-
)}
380-
onEvent={handleTourEvent}
381-
options={{
382-
overlayClickAction: false,
383-
hideOverlay: false,
384-
blockTargetInteraction: false,
385-
spotlightPadding: 4,
386-
overlayColor: "var(--color-bg-alpha-strong)",
387-
zIndex: 10000,
388-
arrowBase: 12,
389-
arrowSize: 6,
390-
offset: 0,
391-
}}
392-
/>
393-
)}
394-
395-
{currentStep === 2 && (
396-
<Joyride
397-
continuous
398-
run={runTour}
399-
steps={thirdStepTour}
400-
stepIndex={tourStepIndex}
401-
tooltipComponent={(tooltipProps) => (
402-
<TourPopover {...tooltipProps} onFinish={handleFinishTour} onRestart={handleRestartTour} />
403-
)}
404-
onEvent={handleTourEvent}
405-
options={{
406-
overlayClickAction: false,
407-
hideOverlay: false,
408-
blockTargetInteraction: false,
409-
spotlightPadding: 4,
410-
overlayColor: "var(--color-bg-alpha-strong)",
411-
zIndex: 10000,
412-
arrowBase: 12,
413-
arrowSize: 6,
414-
offset: 0,
415-
}}
416-
/>
417-
)}
121+
<Tour currentStep={currentStep} onTourStepIndexChange={setTourStepIndex} />
418122

419123
<DxcFlex direction="column" gap="var(--spacing-gap-xl)" fullHeight>
420124
<DxcContainer width="80%" maxWidth="1112px" margin={{ left: "auto", right: "auto" }}>
@@ -449,34 +153,6 @@ const ThemeGeneratorConfigPage = () => {
449153
/>
450154
</DxcFlex>
451155
</DxcContainer>
452-
{isDialogVisible && (
453-
<DxcDialog onCloseClick={() => setDialogVisible(false)}>
454-
<DxcContainer padding="var(--spacing-padding-l)" width="648px">
455-
<DxcFlex direction="column" gap="var(--spacing-gap-s)" alignItems="start">
456-
<DxcHeading level={3} text="Welcome to Halstack Theme Generator" />
457-
<DxcParagraph>
458-
This guided tour walk you through the 3 simple steps to set up your Halstack Theme. We’ll start with
459-
branding, then preview in real components and product layouts and finally export your theme.
460-
</DxcParagraph>
461-
<DxcFlex gap="var(--spacing-gap-s)" justifyContent="end" alignSelf="stretch">
462-
<DxcButton
463-
label="Skip"
464-
mode="tertiary"
465-
size={{ height: "medium" }}
466-
onClick={() => setDialogVisible(false)}
467-
/>
468-
<DxcButton
469-
label="Get Started!"
470-
icon="filled_play_arrow"
471-
iconPosition="after"
472-
size={{ height: "medium" }}
473-
onClick={handleStartTour}
474-
/>
475-
</DxcFlex>
476-
</DxcFlex>
477-
</DxcContainer>
478-
</DxcDialog>
479-
)}
480156
</>
481157
);
482158
};

0 commit comments

Comments
 (0)