-
Notifications
You must be signed in to change notification settings - Fork 0
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
Feat: onboarding create join project #53
Conversation
… screen. Makes create project screen.
… skip steps or go back.
I think we can simplify the onboarding screen and make it more testable and maintainable by exposing the children at the top level and avoiding such deep nesting. I would suggest: // this screen now controls whether the onBack can be pressed, and it does not have to be determined in OnboardingTopMenu
// Also the screen content can be fully determined by the parent. No need to pass props to the OnboardingScreenLayout and conditionally render things based on what screen it is
const SomeOnboardingScreen = () => {
function onBackPress(){
navigate(to next screen)
}
return(
<OnboardingScreenLayout
topMenu={<OnboardingTopMenu onBackPress={ionBackPress) currentStep=}/>}
>
{...screen content here}
</OnboardingScreenLayout>
)
}
const OnboardingScreenLayout = ({OnboardingTopMenu: ReactNode, children:ReactNode}) => {
return(
<Container>
<OnboardingTopMenu/>
<ContentWrapper>
<ContentBox>
{children}
</ContentBox>
</ContentWrapper>
</Container>
)
}
const OnboardingTopMenu = ({onBackPress?, currentStep}) => {
return (
<MenuContainer>
<GoBackButton disabled={!onBackPress} onPress={onBackPress}>
<Steps>
{[1, 2, 3].map(step => <Step active={currentStep === step}/>)}
</Steps>
</MenuContainer>
)
} This suggested architecture does make the component slightly less reusable as It is simply a layout wrapper and the parent determines the content completely. But any shared styling can still be shared, just pass it around to the children not encapsulated in the The first major thing that this does is it allows the OnboardingTopMenu to be completely agnostic and not reliant on the nav state. In the future if we change the steps, we do not need to refactor the OnboardingTopMenu to account for the changes in navigation. Since the parent just controls this we can simply add or take away a screen with no refactoring. Secondly we can test the screens in isolation. The |
const value = event.target.value | ||
const graphemeCount = countGraphemes(value.trim()) | ||
const byteLength = getUtf8ByteLength(value.trim()) | ||
let localError = false | ||
|
||
if ( | ||
graphemeCount > PROJECT_NAME_MAX_LENGTH_GRAPHEMES || | ||
byteLength > PROJECT_NAME_MAX_BYTES || | ||
value.trim().length === 0 | ||
) { | ||
localError = true | ||
} else { | ||
setProjectName(value) | ||
} | ||
setError(localError) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we write a test for this?
we could write a pure function that does not deal with react state
Something like:
const handleChange = (event: ChangeEvent<HTMLInputElement>) =>{
const value = event.target.value
const localError:boolean= checkForError(value)
if (!localError) setProjectName(value)
if (localError !== error) setError(localError)
}
then we could write a test for checkForError
which is not reliant on react state, and can be a pure function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I did this. I tried to anyway.
… logic out of naming screens. Fixes the onboarding top menu.
@ErikSin, There is still logic that needs to be added, but I think the component and screen structure might be closer to what you had in mind. I didn't make shareable components for the buttons or anything. Hope that is ok, but let me know if I should do that too. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Last things, can yo write tests for your function. Vitest should now be setup!
<OnboardingTopMenu | ||
currentStep={1} | ||
onBackPress={() => navigate({ to: '/Onboarding/DataPrivacy' })} | ||
/> | ||
<ContentBox> | ||
<PrivacyPolicy /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the privacy policy a nested component. It basically a container inside a container. it just seems like an unnecessary nesting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did that in the same way I did it on mobile because the Privacy Policy is used in a separate place within the settings and is almost exactly the same so that way it can be reused as a component there.
const localError: boolean = checkForError( | ||
value.trim(), | ||
DEVICE_NAME_MAX_LENGTH_GRAPHEMES, | ||
) | ||
setDeviceName(value) | ||
if (localError !== inputError) { | ||
setInputError(localError) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if there is an error, do we want to set the name? Or should we just set the device name when no error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am doing that because when there is a blank there (someone erases all of the letters), there is an error and we need to set the device name to a blank. If I don't set the device name in that case a user can never erase that last letter.
<div | ||
style={{ | ||
width: '100%', | ||
maxWidth: 400, | ||
margin: '0 auto', | ||
textAlign: 'center', | ||
}} | ||
> | ||
<div | ||
style={{ | ||
display: 'flex', | ||
justifyContent: 'space-between', | ||
alignItems: 'center', | ||
gap: 12, | ||
padding: '12px 0', | ||
cursor: 'pointer', | ||
}} | ||
onClick={() => setAdvancedSettingOpen(!advancedSettingOpen)} | ||
> | ||
<Text bold style={{ fontSize: '1.125rem' }}> | ||
{formatMessage(m.advancedProjectSettings)} | ||
</Text> | ||
{advancedSettingOpen ? <ChevronDown /> : <ChevronUp />} | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should us an accordian here. Accordian would give the animation, and doesn't require us to use css to hide
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. Done.
<OnboardingTopMenu | ||
currentStep={3} | ||
onBackPress={() => | ||
navigate({ to: '/Onboarding/CreateJoinProjectScreen' }) | ||
} | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should also disable the back button when setProjectNameMutation.isPending
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you write test for these functions? Vitest was just integrated into main, so you can rebase and just start wrtiting test. We are going to colocate are test files (which I will document). So if you can create a onboardingLogic.test.ts
file and test these function there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! That was fun! Much more fun than E2E tests!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job and nice tests! Just one small comment about uncontrolled vs controlled state. But otherwise good to merge
expanded={advancedSettingOpen} | ||
onChange={(_, expanded) => setAdvancedSettingOpen(expanded)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One small thing. Lets make this uncontrolled component. In otherwords, dont pass in the expanded
and onChange
props, and let it manage its own state. For maintainability, the less state the parent has to control the better.
related to #42
Description
Notes
so you can get to the screen to see it by changing the navigation in the CreateJoinProjectScreen
from:
onClick={() => navigate({ to: '/Onboarding/CreateProjectScreen' })}
to:
onClick={() => navigate({ to: '/Onboarding/JoinProjectScreen' })}