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

Addon Onboarding: New design and flow based on Save from Controls #28327

Merged
merged 13 commits into from
Jun 28, 2024
Merged
Prev Previous commit
Next Next commit
Hide welcome modal more subtly
  • Loading branch information
ghengeveld committed Jun 19, 2024
commit d136973487b77407405cf13416adcb145f2544ab
26 changes: 16 additions & 10 deletions code/addons/onboarding/src/Onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { STORYBOOK_ADDON_ONBOARDING_STEPS } from './constants';
import { STORYBOOK_ADDON_ONBOARDING_CHANNEL } from './constants';

import { HighlightElement } from './components/HighlightElement/HighlightElement';
import { WelcomeModal } from './features/WelcomeModal/WelcomeModal';

const SpanHighlight = styled.span(({ theme }) => ({
display: 'inline-flex',
Expand Down Expand Up @@ -75,7 +76,7 @@ export default function Onboarding({ api }: { api: API }) {
[api]
);

const skipOnboarding = useCallback(() => {
const disableOnboarding = useCallback(() => {
// remove onboarding query parameter from current url
const url = new URL(window.location.href);
// @ts-expect-error (not strict)
Expand All @@ -87,13 +88,13 @@ export default function Onboarding({ api }: { api: API }) {
}, [api, setEnabled]);

const completeOnboarding = useCallback(() => {
selectStory('configure-your-project--docs');
api.emit(STORYBOOK_ADDON_ONBOARDING_CHANNEL, {
step: '6:FinishedOnboarding' satisfies StepKey,
type: 'telemetry',
});
skipOnboarding();
}, [api, selectStory, skipOnboarding]);
selectStory('configure-your-project--docs');
disableOnboarding();
}, [api, selectStory, disableOnboarding]);

useEffect(() => {
api.setQueryParams({ onboarding: 'true' });
Expand Down Expand Up @@ -128,16 +129,16 @@ export default function Onboarding({ api }: { api: API }) {
return api.on(SAVE_STORY_RESPONSE, ({ payload, success }) => {
if (!success) return;
setCreatedStory(payload);
setShowConfetti(true);
setStep('5:StoryCreated');
setTimeout(() => api.clearNotification('save-story-success'));
});
}, [api]);

useEffect(() => {
if (step === '1:Intro') setTimeout(() => setStep('2:Controls'), 3000);
if (step === '5:StoryCreated') setShowConfetti(true);
api.emit(STORYBOOK_ADDON_ONBOARDING_CHANNEL, { step, type: 'telemetry' });
}, [api, step]);
useEffect(
() => api.emit(STORYBOOK_ADDON_ONBOARDING_CHANNEL, { step, type: 'telemetry' }),
[api, step]
);

if (!enabled) {
return null;
Expand Down Expand Up @@ -229,10 +230,15 @@ export default function Onboarding({ api }: { api: API }) {
}}
/>
)}
<WelcomeModal
step={step}
onProceed={() => setStep('2:Controls')}
onSkip={disableOnboarding}
/>
<GuidedTour
step={step}
steps={steps}
onClose={skipOnboarding}
onClose={disableOnboarding}
onComplete={completeOnboarding}
/>
</ThemeProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import { ArrowRightIcon } from '@storybook/icons';
import { keyframes, styled } from '@storybook/theming';
import { Modal } from '@storybook/components';

export const ModalWrapper = styled(Modal)`
export const ModalWrapper = styled(Modal)<{ visible?: boolean }>`
background: white;
opacity: ${({ visible }) => (visible ? 1 : 0)};
transition: opacity 0.2s;
z-index: 10;
`;

export const ModalContentWrapper = styled.div`
Expand Down
72 changes: 42 additions & 30 deletions code/addons/onboarding/src/features/WelcomeModal/WelcomeModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { FC } from 'react';
import React from 'react';
import { styled } from '@storybook/theming';
import React, { useEffect, useState } from 'react';

import type { StepKey } from '../../Onboarding';
import { Button } from '../../components/Button/Button';
import { StorybookLogo } from './StorybookLogo';
import {
Expand All @@ -18,38 +19,49 @@ import {
} from './WelcomeModal.styled';

interface WelcomeModalProps {
step: StepKey;
onProceed: () => void;
skipOnboarding: () => void;
onSkip: () => void;
container?: HTMLElement;
}

export const WelcomeModal: FC<WelcomeModalProps> = ({ onProceed, skipOnboarding, container }) => {
export const WelcomeModal = ({ step, onProceed, onSkip, container }: WelcomeModalProps) => {
const [rendered, setRendered] = useState(true);
const [visible, setVisible] = useState(true);

useEffect(() => {
if (step !== '1:Intro') {
setVisible(false);
setTimeout(setRendered, 500, false);
}
}, [step]);

if (!rendered) return null;

return (
<div style={{ zIndex: 10 }}>
<ModalWrapper width={540} height={430} defaultOpen container={container}>
<ModalContentWrapper data-chromatic="ignore">
<TopContent>
<StorybookLogo />
<Title>Welcome to Storybook</Title>
<Description>
Storybook helps you develop UI components faster. Learn the basics in a few simple
steps.
</Description>
<Button style={{ marginTop: 4 }} onClick={onProceed}>
Start your 3 minute tour
</Button>
</TopContent>
<SkipButton onClick={skipOnboarding}>
Skip tour
<StyledIcon />
</SkipButton>
<Background>
<Circle1 />
<Circle2 />
<Circle3 />
</Background>
</ModalContentWrapper>
</ModalWrapper>
</div>
<ModalWrapper width={540} height={430} defaultOpen container={container} visible={visible}>
<ModalContentWrapper data-chromatic="ignore">
<TopContent>
<StorybookLogo />
<Title>Welcome to Storybook</Title>
<Description>
Storybook helps you develop UI components faster. Learn the basics in a few simple
steps.
</Description>
<Button style={{ marginTop: 4 }} onClick={onProceed}>
Start your 3 minute tour
</Button>
</TopContent>
<SkipButton onClick={onSkip}>
Skip tour
<StyledIcon />
</SkipButton>
<Background>
<Circle1 />
<Circle2 />
<Circle3 />
</Background>
</ModalContentWrapper>
</ModalWrapper>
);
};