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

Feat: onboarding create join project #53

Merged
merged 20 commits into from
Dec 12, 2024

Conversation

cimigree
Copy link
Contributor

@cimigree cimigree commented Dec 9, 2024

related to #42

Description

  • Moves some files around
  • Adds bigger icons
  • Adds components for the repeated background for the Onboarding Screens
  • Updates existing screens to use the new OnboardingScreenLayout component
  • Updates the Onboarding Top Menu so a user cannot bypass the necessary steps
  • Updates the first Onboarding screen to be more responsive and styled better
  • Adds the Create project Screen
  • Adds the Join project screen

Notes

  • Many of the changed files are just moving files into folders and updating the imports and also updating the size of some icons
  • I added the Toggle for importing a config but I wasn't sure if that was actually part of the design or if that is done differently on desktop so I didn't add any logic for it
  • I was not sure when the Join Project screen should show up... If the logic was the same as on mobile (Like what is the idea? Someone will get to the Create or Join Project screen and someone else will be right there and then the invite will be sent?) The discovery will have to be there, right?
  • I was thinking we could add the logic later and just start with the UI/ screens for now

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' })}

@cimigree cimigree requested a review from ErikSin December 9, 2024 20:08
@ErikSin
Copy link
Contributor

ErikSin commented Dec 9, 2024

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 OnboardingScreenLayout

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 OnboardingScreenLayout is solely for stying, so there is no logic to be tested. Previously if we wanted to test the < input /> in the Device name screen, and how it interacted with the button, we would have to test the DeviceNamingSreen, and then test the button rendered inside the OnboardingScreenLayout

Comment on lines 125 to 139
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)
Copy link
Contributor

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

Copy link
Contributor Author

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.
@cimigree
Copy link
Contributor Author

cimigree commented Dec 10, 2024

@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.

@cimigree cimigree requested a review from ErikSin December 10, 2024 23:51
@cimigree cimigree mentioned this pull request Dec 11, 2024
Copy link
Contributor

@ErikSin ErikSin left a 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 />
Copy link
Contributor

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

Copy link
Contributor Author

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.

Comment on lines +98 to +104
const localError: boolean = checkForError(
value.trim(),
DEVICE_NAME_MAX_LENGTH_GRAPHEMES,
)
setDeviceName(value)
if (localError !== inputError) {
setInputError(localError)
Copy link
Contributor

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

Copy link
Contributor Author

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.

Comment on lines 206 to 229
<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>
Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Done.

Comment on lines 150 to 155
<OnboardingTopMenu
currentStep={3}
onBackPress={() =>
navigate({ to: '/Onboarding/CreateJoinProjectScreen' })
}
/>
Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Done.

Copy link
Contributor

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

Copy link
Contributor Author

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!

@cimigree cimigree requested a review from ErikSin December 12, 2024 16:46
Copy link
Contributor

@ErikSin ErikSin left a 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

Comment on lines 218 to 219
expanded={advancedSettingOpen}
onChange={(_, expanded) => setAdvancedSettingOpen(expanded)}
Copy link
Contributor

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.

@cimigree cimigree merged commit 45c576c into main Dec 12, 2024
2 checks passed
@cimigree cimigree deleted the feat/onboarding-create-join-project branch December 12, 2024 20:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants