From 0af0da7477cbaee9f481a4917b6fd4cafdae500d Mon Sep 17 00:00:00 2001 From: Camila Sosa Morales Date: Thu, 9 Feb 2023 10:21:48 -0500 Subject: [PATCH] feat: mint step 1 UI layout connect with GitHub (#113) * feat: add gh steps * fix: fix import as change the component name * chore: add context for mint view * fix: fix styles for dropdown * chore: add dropdown on github components * styles: fix styles for github repository config card * fix: apply PR review comments * style: reuse no results component --- ui/src/components/core/combobox/combobox.tsx | 36 +-- ui/src/components/core/combobox/dropdown.tsx | 11 +- ui/src/components/core/icon/icon-library.tsx | 1 - ui/src/components/core/separator.styles.ts | 7 + ui/src/theme/foundations/radii.ts | 2 + ui/src/theme/foundations/spacing.ts | 10 +- ui/src/views/index.ts | 2 + .../mint/github-step/github-connect-step.tsx | 53 ++++ .../github-step/github-repo-configuration.tsx | 130 ++++++++++ .../github-repository-selection.tsx | 166 +++++++++++++ ui/src/views/mint/github-step/github-step.tsx | 20 ++ ui/src/views/mint/github-step/index.ts | 1 + ui/src/views/mint/mint-step.tsx | 49 ++++ ui/src/views/mint/mint-stepper.tsx | 231 ++++++------------ ui/src/views/mint/mint.context.tsx | 62 +++++ 15 files changed, 602 insertions(+), 179 deletions(-) create mode 100644 ui/src/components/core/separator.styles.ts create mode 100644 ui/src/views/mint/github-step/github-connect-step.tsx create mode 100644 ui/src/views/mint/github-step/github-repo-configuration.tsx create mode 100644 ui/src/views/mint/github-step/github-repository-selection.tsx create mode 100644 ui/src/views/mint/github-step/github-step.tsx create mode 100644 ui/src/views/mint/github-step/index.ts create mode 100644 ui/src/views/mint/mint-step.tsx create mode 100644 ui/src/views/mint/mint.context.tsx diff --git a/ui/src/components/core/combobox/combobox.tsx b/ui/src/components/core/combobox/combobox.tsx index 8cde662..4f40be7 100644 --- a/ui/src/components/core/combobox/combobox.tsx +++ b/ui/src/components/core/combobox/combobox.tsx @@ -14,7 +14,7 @@ const ComboboxInput = ({ handleInputChange, handleInputClick, }: ComboboxInputProps) => ( -
+
selectedValue.label} @@ -43,7 +43,6 @@ type ComboboxOptionProps = { const ComboboxOption = ({ option }: ComboboxOptionProps) => ( `relative cursor-default select-none py-2 px-3.5 text-slate11 rounded-xl mb-2 text-sm ${ @@ -65,8 +64,10 @@ const ComboboxOption = ({ option }: ComboboxOptionProps) => ( ); -const NoResults = () => ( -
+export const NoResults = ({ css }: { css?: string }) => ( +
Nothing found.
); @@ -125,15 +126,14 @@ export const Combobox: React.FC = ({ onChange={handleComboboxChange} > {({ open }) => ( - <> -
- - -
+
+ + + = ({ leaveTo="opacity-0" afterLeave={handleLeaveTransition} > - + {filteredItems.length === 0 && query !== '' ? ( ) : ( - filteredItems.map((option) => { - return ; + filteredItems.map((option: ComboboxItem) => { + return ; }) )} - +
)} ); diff --git a/ui/src/components/core/combobox/dropdown.tsx b/ui/src/components/core/combobox/dropdown.tsx index 78071f2..b96d187 100644 --- a/ui/src/components/core/combobox/dropdown.tsx +++ b/ui/src/components/core/combobox/dropdown.tsx @@ -39,8 +39,12 @@ const DropdownButton = ({ selectedValue, open }: DropdownButtonProps) => ( open ? 'border-b-0 rounded-t-xl bg-black border-slate6' : 'rounded-xl' }`} > - - {selectedValue ? selectedValue.label : 'Select'} + + {selectedValue && selectedValue.label ? selectedValue.label : 'Select'} @@ -67,6 +71,7 @@ export const Dropdown: React.FC = ({ const handleDropdownChange = (option: DropdownItem) => { onChange(option); }; + return ( {({ open }) => ( @@ -80,7 +85,7 @@ export const Dropdown: React.FC = ({ > {items.map((option: DropdownItem) => ( - + ))} diff --git a/ui/src/components/core/icon/icon-library.tsx b/ui/src/components/core/icon/icon-library.tsx index 2fa3ba1..a4b658d 100644 --- a/ui/src/components/core/icon/icon-library.tsx +++ b/ui/src/components/core/icon/icon-library.tsx @@ -6,7 +6,6 @@ import { AiOutlineDown } from '@react-icons/all-files/ai/AiOutlineDown'; import { BiSearch } from '@react-icons/all-files/bi/BiSearch'; import { IoCloudUploadSharp } from '@react-icons/all-files/io5/IoCloudUploadSharp'; import { MetamaskIcon, EthereumIcon } from './custom'; -import { BiSearch } from '@react-icons/all-files/bi/BiSearch'; export const IconLibrary = Object.freeze({ back: IoArrowBackCircleSharp, diff --git a/ui/src/components/core/separator.styles.ts b/ui/src/components/core/separator.styles.ts new file mode 100644 index 0000000..6a7ad86 --- /dev/null +++ b/ui/src/components/core/separator.styles.ts @@ -0,0 +1,7 @@ +import { dripStitches } from '@/theme'; + +const { styled } = dripStitches; + +export const Separator = styled('hr', { + borderTop: '1px solid $slate6', +}); diff --git a/ui/src/theme/foundations/radii.ts b/ui/src/theme/foundations/radii.ts index 7a2f72a..d0a885f 100644 --- a/ui/src/theme/foundations/radii.ts +++ b/ui/src/theme/foundations/radii.ts @@ -4,5 +4,7 @@ export const radii = { md: '0.5rem', // 8px lg: '0.75rem', // 12px xl: '1rem', // 16px + xlh: '1.25rem', // 20px + '2xl': '1.5rem', // 24px full: '9999px', }; diff --git a/ui/src/theme/foundations/spacing.ts b/ui/src/theme/foundations/spacing.ts index cb592d0..11d2deb 100644 --- a/ui/src/theme/foundations/spacing.ts +++ b/ui/src/theme/foundations/spacing.ts @@ -9,8 +9,8 @@ export const spacing = { '3h': '0.875rem', // 14px 4: '1rem', // 16px 5: '1.25rem', // 20px - 6: '1.5rem', - 7: '1.75rem', + 6: '1.5rem', // 24px + 7: '1.75rem', // 28px 8: '2rem', 9: '2.25rem', 10: '2.5rem', @@ -28,18 +28,24 @@ export const spacing = { 36: '9rem', 40: '10rem', 44: '11rem', + 46: '11.5rem', // 184px + '46h': '11.625rem', // 186px 48: '12rem', 52: '13rem', 56: '14rem', + 59: '14.9375rem', // 239px 60: '15rem', 62: '15.5rem', 64: '16rem', 72: '18rem', 80: '20rem', + 95: '23rem', + '95h': '23.375rem', // 374px 96: '24rem', 100: '25rem', 102: '25.5rem', // 408px 104: '26rem', // 416px 106: '26.5rem', // 424px + '107h': '26.625rem', // 426px 128: '32rem', // 512px }; diff --git a/ui/src/views/index.ts b/ui/src/views/index.ts index e405a73..75899f1 100644 --- a/ui/src/views/index.ts +++ b/ui/src/views/index.ts @@ -1 +1,3 @@ export * from './home'; +export * from './mint'; +export * from './svg-test'; diff --git a/ui/src/views/mint/github-step/github-connect-step.tsx b/ui/src/views/mint/github-step/github-connect-step.tsx new file mode 100644 index 0000000..1133d7c --- /dev/null +++ b/ui/src/views/mint/github-step/github-connect-step.tsx @@ -0,0 +1,53 @@ +import { Button, Card, Grid, Icon, IconButton } from '@/components'; +import { Mint } from '../mint.context'; + +export const GithubConnect: React.FC = () => { + const { setGithubStep } = Mint.useContext(); + + const handleNextStep = () => { + //TODO when we integrate GH login, we'll need to set the step to 2 after login + setGithubStep(2); + }; + return ( + + } + /> + } + /> + + + + + + After connecting your GitHub, your repositories will show here. + + + + + + ); +}; diff --git a/ui/src/views/mint/github-step/github-repo-configuration.tsx b/ui/src/views/mint/github-step/github-repo-configuration.tsx new file mode 100644 index 0000000..b95657e --- /dev/null +++ b/ui/src/views/mint/github-step/github-repo-configuration.tsx @@ -0,0 +1,130 @@ +import { + Button, + Card, + Dropdown, + DropdownItem, + Form, + Grid, + Icon, + IconButton, + Stepper, +} from '@/components'; +import React, { useState } from 'react'; +import { Mint } from '../mint.context'; +import { RepoRow } from './github-repository-selection'; + +//TODO remove once it's integrated with GH login +const branches: DropdownItem[] = [ + { + label: 'master', + value: 'master', + }, + { + label: 'develop', + value: 'develop', + }, + { + label: 'feature/branch', + value: 'feature/branch', + }, +]; + +export const GithubRepoConfiguration: React.FC = () => { + const { + repositoryName, + branchName, + commitHash, + setGithubStep, + setRepositoryConfig, + } = Mint.useContext(); + const { nextStep } = Stepper.useContext(); + const [branchSelected, setBranchSelected] = useState(branchName); + const [commitHashSelected, setCommitHashSelected] = useState(commitHash); + + const handlePrevStepClick = () => { + setGithubStep(2); + setRepositoryConfig('', ''); + }; + + const handleBranchChange = (dorpdownOption: DropdownItem) => { + //TODO we'll have to check the data that GH API returns + setBranchSelected(dorpdownOption.value); + }; + + const handleCommitHashChange = (e: React.ChangeEvent) => { + setCommitHashSelected(e.target.value); + }; + + const handleContinueClick = () => { + setRepositoryConfig(branchSelected, commitHashSelected); + nextStep(); + }; + + return ( + + } + css={{ mr: '$2' }} + onClick={handlePrevStepClick} + /> + } + rightIcon={ + } + /> + } + /> + + + + Use for NFA + + } + /> + + Git Branch + + + + Git Commit + + + + + + + ); +}; diff --git a/ui/src/views/mint/github-step/github-repository-selection.tsx b/ui/src/views/mint/github-step/github-repository-selection.tsx new file mode 100644 index 0000000..1756497 --- /dev/null +++ b/ui/src/views/mint/github-step/github-repository-selection.tsx @@ -0,0 +1,166 @@ +import { + Button, + Card, + Combobox, + ComboboxItem, + Flex, + Grid, + Icon, + IconButton, + NoResults, +} from '@/components'; +import { Input } from '@/components/core/input'; +import { Separator } from '@/components/core/separator.styles'; +import React, { forwardRef, useRef, useState } from 'react'; +import { Mint } from '../mint.context'; + +//TODO remove once it's integrated with GH login +const repos = [ + 'DyDx', + 'Testing', + 'Hello World', + 'Portofolio', + 'NFA', + 'NFT', + 'NFTs', +]; + +//TODO remove once it's integrated with GH login +const users: ComboboxItem[] = [ + { label: 'DyDx', value: 'DyDx', icon: 'github' }, + { label: 'Testing', value: 'Testing', icon: 'github' }, + { label: 'Hello World', value: 'Hello World', icon: 'github' }, + { label: 'Portofolio', value: 'Portofolio', icon: 'github' }, + { label: 'NFA', value: 'NFA', icon: 'github' }, + { label: 'NFT', value: 'NFT', icon: 'github' }, + { label: 'NFTs', value: 'NFTs', icon: 'github' }, +]; + +type RepoRowProps = { + repo: string; + button: React.ReactNode; +} & React.ComponentProps; + +export const RepoRow = forwardRef( + ({ repo, button, ...props }, ref) => ( + + + + {repo} + + {button} + + ) +); + +export const GithubRepositoryConnection: React.FC = () => { + const [searchValue, setSearchValue] = useState(''); + const [selectedUser, setSelectedUser] = useState(); + const { setGithubStep, setRepositoryName, setRepositoryConfig } = + Mint.useContext(); + + const timeOutRef = useRef(); + + const handleSearchChange = (event: React.ChangeEvent) => { + event.stopPropagation(); + timeOutRef.current && clearTimeout(timeOutRef.current); + timeOutRef.current = setTimeout(() => { + setSearchValue(event.target.value); + }, 500); + }; + + const handlePrevStepClick = () => { + setGithubStep(1); + }; + + const handleSelectRepo = (repo: string) => { + setRepositoryName(repo); + setGithubStep(3); + setRepositoryConfig('', ''); + }; + + const filteredRepositories = + searchValue === '' + ? repos + : repos.filter( + (item) => item.toUpperCase().indexOf(searchValue.toUpperCase()) != -1 + ); + + return ( + + } + css={{ mr: '$2' }} + onClick={handlePrevStepClick} + /> + } + rightIcon={ + } + /> + } + /> + + + + + + + + {filteredRepositories.length > 0 ? ( + filteredRepositories.map((repo, index, { length }) => ( + + handleSelectRepo(repo)} + > + Use for NFA + + } + /> + {index < length - 1 && } + + )) + ) : ( + + )} + + + + + ); +}; diff --git a/ui/src/views/mint/github-step/github-step.tsx b/ui/src/views/mint/github-step/github-step.tsx new file mode 100644 index 0000000..7cf3a2d --- /dev/null +++ b/ui/src/views/mint/github-step/github-step.tsx @@ -0,0 +1,20 @@ +import { Stepper } from '@/components'; +import { useState } from 'react'; +import { Mint } from '../mint.context'; +import { GithubConnect } from './github-connect-step'; +import { GithubRepoConfiguration } from './github-repo-configuration'; +import { GithubRepositoryConnection } from './github-repository-selection'; + +export const GithubStep = () => { + const { githubStep } = Mint.useContext(); + + switch (githubStep) { + case 1: + default: + return ; + case 2: + return ; + case 3: + return ; + } +}; diff --git a/ui/src/views/mint/github-step/index.ts b/ui/src/views/mint/github-step/index.ts new file mode 100644 index 0000000..20757a0 --- /dev/null +++ b/ui/src/views/mint/github-step/index.ts @@ -0,0 +1 @@ +export * from './github-step'; diff --git a/ui/src/views/mint/mint-step.tsx b/ui/src/views/mint/mint-step.tsx new file mode 100644 index 0000000..8650e57 --- /dev/null +++ b/ui/src/views/mint/mint-step.tsx @@ -0,0 +1,49 @@ +import { Flex, Stepper } from '@/components'; + +type StepperIndicatorContainerProps = { + children: React.ReactNode; +}; + +const StepperIndicatorContainer = ({ + children, +}: StepperIndicatorContainerProps) => { + return ( + + {children} + + ); +}; + +type MintStepContainerProps = { + children: React.ReactNode; +}; + +const Container = ({ children }: MintStepContainerProps) => ( + + {children} + +); + +type MintStepProps = { + children: React.ReactNode; + header: string; +}; + +export const MintStep: React.FC = ({ children, header }) => { + return ( + + + +

{header}

+
+ {children} +
+ ); +}; diff --git a/ui/src/views/mint/mint-stepper.tsx b/ui/src/views/mint/mint-stepper.tsx index 78f8ca2..50a8f4a 100644 --- a/ui/src/views/mint/mint-stepper.tsx +++ b/ui/src/views/mint/mint-stepper.tsx @@ -1,164 +1,85 @@ -import { Button, Flex, IconButton, Icon, Stepper } from '@/components'; +import { IconButton, Icon, Stepper, Card } from '@/components'; +import { GithubStep } from './github-step'; +import { MintStep } from './mint-step'; +import { Mint } from './mint.context'; + +//TODO remove after mint flow is done +const Heading = ({ title }: { title: string }) => { + const { prevStep } = Stepper.useContext(); -// TODO remove after flow integration -const StepperButton: React.FC = () => { - const { nextStep, prevStep, setStep } = Stepper.useContext(); return ( - - - - - + } + css={{ mr: '$2' }} + onClick={prevStep} + /> + } + rightIcon={ + } + /> + } + /> ); }; -const CardHeading = ({ title }: { title: string }) => { - const { currentStep, prevStep } = Stepper.useContext(); +export const MintStepper = () => { return ( - - - {currentStep > 0 && ( - } - onClick={prevStep} - css={{ mr: '$2' }} - /> - )} + + + + + + + + -

{title}

-
- } - /> -
+ + + + + + Step 2 + + + + + + + + + + + Step 3 + + + + + + + + + + + Step 4 + + + + + + + + {/* TODO remove buttons when finish to integrate all the flow */} + {/* */} + ); }; - -type CardProps = { - children: React.ReactNode; - title: string; -}; - -// TODO create card component for all the project and then remove this -const Card = ({ children, title }: CardProps) => ( - // TODO style with stitches -
- - {children} -
-); - -const Heading = ({ children }: { children: React.ReactNode }) => ( - // TODO style with stitches or we can use tailwind -

{children}

-); - -type StepperIndicatorContainerProps = { - children: React.ReactNode; -}; - -const StepperIndicatorContainer = ({ - children, -}: StepperIndicatorContainerProps) => { - return ( - - {children} - - ); -}; - -type MintStepContainerProps = { - children: React.ReactNode; -}; - -const MintStepContainer = ({ children }: MintStepContainerProps) => ( - - {children} - -); - -export const MintStepper = () => ( - - - - - - - Connect your Ethereum Wallet to mint an NFA - - {/* TODO create component to handle the wallet connection */} - - Step 1 - - - - - - - - Connect GitHub and select repository - - {/* TODO create component to handle the github connection */} - - Step 2 - - - - - - - - Finalize a few key things for your DyDx NFA - - {/* TODO create component to handle the NFA details */} - - Step 3 - - - - - - - - Review your DyDx NFA and mint it on Polygon - - {/* TODO create component to handle the NFA mint */} - - Step 4 - - - - - {/* TODO remove buttons when finish to integrate all the flow */} - - -); diff --git a/ui/src/views/mint/mint.context.tsx b/ui/src/views/mint/mint.context.tsx new file mode 100644 index 0000000..b59b094 --- /dev/null +++ b/ui/src/views/mint/mint.context.tsx @@ -0,0 +1,62 @@ +import { createContext } from '@/utils'; +import { useState } from 'react'; + +export type MintContext = { + repositoryName: string; + branchName: string; + commitHash: string; + githubStep: number; + setGithubStep: (step: number) => void; + setRepositoryName: (repo: string) => void; + setRepositoryConfig: (branch: string, hash: string) => void; +}; + +const [MintProvider, useContext] = createContext({ + name: 'Mint.Context', + hookName: 'Mint.useContext', + providerName: 'Mint.Provider', +}); + +export abstract class Mint { + static readonly useContext = useContext; + + static readonly Provider: React.FC = ({ children }) => { + const [repositoryName, setRepositoryName] = useState(''); + const [branchName, setBranchName] = useState(''); + const [commitHash, setCommitHash] = useState(''); + const [githubStep, setGithubStepContext] = useState(1); + + const setGithubStep = (step: number): void => { + if (step > 0 && step <= 3) { + setGithubStepContext(step); + } + }; + + const setRepositoryConfig = (branch: string, hash: string) => { + setBranchName(branch); + setCommitHash(hash); + }; + + return ( + + {children} + + ); + }; +} + +export namespace Mint { + export type ProviderProps = { + children: React.ReactNode; + }; +}