Skip to content

Commit 8e3a953

Browse files
committed
sync onboarding options to global state
1 parent ab459a6 commit 8e3a953

File tree

3 files changed

+78
-43
lines changed

3 files changed

+78
-43
lines changed

src/components/codeContext.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import Cookies from 'js-cookie';
55

66
import {isLocalStorageAvailable} from 'sentry-docs/utils';
77

8+
import {OnboardingOptionType} from './onboarding';
9+
810
type ProjectCodeKeywords = {
911
API_URL: string;
1012
DSN: string;
@@ -99,12 +101,14 @@ type CodeSelection = {
99101
type CodeContextType = {
100102
codeKeywords: CodeKeywords;
101103
isLoading: boolean;
104+
onboardingOptions: OnboardingOptionType[];
102105
sharedKeywordSelection: [
103106
Record<string, number>,
104107
React.Dispatch<Record<string, number>>,
105108
];
106109
storedCodeSelection: SelectedCodeTabs;
107110
updateCodeSelection: (selection: CodeSelection) => void;
111+
updateOnboardingOptions: (options: OnboardingOptionType[]) => void;
108112
};
109113

110114
export const CodeContext = createContext<CodeContextType | null>(null);
@@ -297,6 +301,7 @@ export function CodeContextProvider({children}: {children: React.ReactNode}) {
297301
const [codeKeywords, setCodeKeywords] = useState(cachedCodeKeywords ?? DEFAULTS);
298302
const [isLoading, setIsLoading] = useState<boolean>(cachedCodeKeywords ? false : true);
299303
const [storedCodeSelection, setStoredCodeSelection] = useState<SelectedCodeTabs>({});
304+
const [onboardingOptions, setOnboardingOptions] = useState<OnboardingOptionType[]>([]);
300305

301306
// populate state using localstorage
302307
useEffect(() => {
@@ -342,6 +347,8 @@ export function CodeContextProvider({children}: {children: React.ReactNode}) {
342347
updateCodeSelection,
343348
sharedKeywordSelection,
344349
isLoading,
350+
onboardingOptions,
351+
updateOnboardingOptions: options => setOnboardingOptions(options),
345352
};
346353

347354
return <CodeContext.Provider value={result}>{children}</CodeContext.Provider>;

src/components/codeTabs.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import styled from '@emotion/styled';
1414
import {CodeBlockProps} from './codeBlock';
1515
import {CodeContext} from './codeContext';
1616
import {KEYWORDS_REGEX, ORG_AUTH_TOKEN_REGEX} from './codeKeywords';
17+
import {updateElementsVisibilityForOptions} from './onboarding';
1718
import {SignInNote} from './signInNote';
1819

1920
// human readable versions of names
@@ -99,6 +100,12 @@ export function CodeTabs({children}: CodeTabProps) {
99100
}
100101
}, [codeContext?.storedCodeSelection, groupId, possibleChoices]);
101102

103+
// react to possible changes in options when switching tabs
104+
useEffect(() => {
105+
updateElementsVisibilityForOptions(codeContext?.onboardingOptions || [], false);
106+
// eslint-disable-next-line react-hooks/exhaustive-deps
107+
}, [selectedTabIndex]);
108+
102109
const buttons = possibleChoices.map((choice, idx) => (
103110
<TabButton
104111
key={idx}

src/components/onboarding/index.tsx

Lines changed: 64 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
'use client';
22

3-
import {ReactNode, useEffect, useReducer, useState} from 'react';
3+
import {ReactNode, useContext, useEffect, useReducer, useState} from 'react';
44
import {QuestionMarkCircledIcon} from '@radix-ui/react-icons';
55
import * as Tooltip from '@radix-ui/react-tooltip';
66
import {Button, Checkbox, Theme} from '@radix-ui/themes';
77

88
import styles from './styles.module.scss';
99

10+
import {CodeContext} from '../codeContext';
11+
1012
const optionDetails: Record<
1113
OptionId,
1214
{
@@ -74,7 +76,7 @@ const OPTION_IDS = [
7476

7577
type OptionId = (typeof OPTION_IDS)[number];
7678

77-
type OnboardingOptionType = {
79+
export type OnboardingOptionType = {
7880
/**
7981
* Unique identifier for the option, will control the visibility
8082
* of `<OnboardingOption optionId="this_id"` /> somewhere on the page
@@ -119,12 +121,65 @@ export function OnboardingOption({
119121
);
120122
}
121123

124+
/**
125+
* Updates DOM elements' visibility based on selected onboarding options
126+
*/
127+
export function updateElementsVisibilityForOptions(
128+
options: OnboardingOptionType[],
129+
touchedOptions: boolean
130+
) {
131+
options.forEach(option => {
132+
if (option.disabled) {
133+
return;
134+
}
135+
const targetElements = document.querySelectorAll<HTMLDivElement>(
136+
`[data-onboarding-option="${option.id}"]`
137+
);
138+
139+
targetElements.forEach(el => {
140+
const hiddenForThisOption = el.dataset.hideForThisOption === 'true';
141+
if (hiddenForThisOption) {
142+
el.classList.toggle('hidden', option.checked);
143+
} else {
144+
el.classList.toggle('hidden', !option.checked);
145+
}
146+
// only animate things when user has interacted with the options
147+
if (touchedOptions) {
148+
if (el.classList.contains('code-line')) {
149+
el.classList.toggle('animate-line', option.checked);
150+
}
151+
// animate content, account for inverted logic for hiding
152+
else {
153+
el.classList.toggle(
154+
'animate-content',
155+
hiddenForThisOption ? !option.checked : option.checked
156+
);
157+
}
158+
}
159+
});
160+
if (option.checked && optionDetails[option.id].deps?.length) {
161+
const dependenciesSelector = optionDetails[option.id].deps!.map(
162+
dep => `[data-onboarding-option="${dep}"]`
163+
);
164+
const dependencies = document.querySelectorAll<HTMLDivElement>(
165+
dependenciesSelector.join(', ')
166+
);
167+
168+
dependencies.forEach(dep => {
169+
dep.classList.remove('hidden');
170+
});
171+
}
172+
});
173+
}
174+
122175
export function OnboardingOptionButtons({
123176
options: initialOptions,
124177
}: {
125178
// convenience to allow passing option ids as strings when no additional config is required
126179
options: (OnboardingOptionType | OptionId)[];
127180
}) {
181+
const codeContext = useContext(CodeContext);
182+
128183
const normalizedOptions = initialOptions.map(option => {
129184
if (typeof option === 'string') {
130185
return {id: option, disabled: option === 'error-monitoring'};
@@ -182,49 +237,15 @@ export function OnboardingOptionButtons({
182237
});
183238
});
184239
}
240+
241+
// sync local state to global
185242
useEffect(() => {
186-
options.forEach(option => {
187-
if (option.disabled) {
188-
return;
189-
}
190-
const targetElements = document.querySelectorAll<HTMLDivElement>(
191-
`[data-onboarding-option="${option.id}"]`
192-
);
193-
targetElements.forEach(el => {
194-
const hiddenForThisOption = el.dataset.hideForThisOption === 'true';
195-
if (hiddenForThisOption) {
196-
el.classList.toggle('hidden', option.checked);
197-
} else {
198-
el.classList.toggle('hidden', !option.checked);
199-
}
200-
// only animate things when user has interacted with the options
201-
if (touchedOptions) {
202-
if (el.classList.contains('code-line')) {
203-
el.classList.toggle('animate-line', option.checked);
204-
}
205-
// animate content, account for inverted logic for hiding
206-
else {
207-
el.classList.toggle(
208-
'animate-content',
209-
hiddenForThisOption ? !option.checked : option.checked
210-
);
211-
}
212-
}
213-
});
214-
if (option.checked && optionDetails[option.id].deps?.length) {
215-
const dependenciesSelecor = optionDetails[option.id].deps!.map(
216-
dep => `[data-onboarding-option="${dep}"]`
217-
);
218-
const dependencies = document.querySelectorAll<HTMLDivElement>(
219-
dependenciesSelecor.join(', ')
220-
);
243+
codeContext?.updateOnboardingOptions(options);
244+
}, [options, codeContext]);
221245

222-
dependencies.forEach(dep => {
223-
dep.classList.remove('hidden');
224-
});
225-
}
226-
});
227-
}, [options, touchedOptions]);
246+
useEffect(() => {
247+
updateElementsVisibilityForOptions(options, touchedOptions);
248+
}, [options, touchOptions, touchedOptions]);
228249

229250
return (
230251
<div className="onboarding-options flex flex-wrap gap-3 py-2 bg-[var(--white)] dark:bg-[var(--gray-1)] sticky top-[80px] z-[4] shadow-[var(--shadow-6)] transition">

0 commit comments

Comments
 (0)