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: add target configs for multiple platforms, add deep links and refactor social connection list #125

Merged
merged 6 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 67 additions & 68 deletions src/components/Connection/Connection.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { act, fireEvent, render } from '@testing-library/react'
import { Connection } from './Connection'
import {
SHOW_MORE_BUTTON_TEST_ID,
SOCIAL_PRIMARY_TEST_ID,
SOCIAL_SECONDARY_TEST_ID,
WEB3_PRIMARY_TEST_ID,
WEB3_SECONDARY_TEST_ID
} from './constants'
import { EXTRA_TEST_ID, PRIMARY_TEST_ID, SECONDARY_TEST_ID, SHOW_MORE_BUTTON_TEST_ID } from './constants'
import { ConnectionOptionType, ConnectionProps } from './Connection.types'

function renderConnection(props: Partial<ConnectionProps>) {
Expand Down Expand Up @@ -36,50 +30,39 @@ function renderConnection(props: Partial<ConnectionProps>) {
let screen: ReturnType<typeof renderConnection>

describe('when rendering the component', () => {
let socialOptions: ConnectionProps['socialOptions'] | undefined
let web3Options: ConnectionProps['web3Options'] | undefined
let connectionOptions: ConnectionProps['connectionOptions']
let onConnect: jest.Mock

describe('and there are no social options', () => {
beforeEach(() => {
socialOptions = undefined
screen = renderConnection({ socialOptions })
})

it('should not render the primary social option', () => {
const { queryByTestId } = screen
expect(queryByTestId(SOCIAL_PRIMARY_TEST_ID)).not.toBeInTheDocument()
})
})

describe('and there are social options', () => {
describe('and there are primary, secondary and extra options', () => {
beforeEach(() => {
onConnect = jest.fn()
socialOptions = {
connectionOptions = {
primary: ConnectionOptionType.GOOGLE,
secondary: [ConnectionOptionType.APPLE, ConnectionOptionType.X, ConnectionOptionType.DISCORD]
secondary: ConnectionOptionType.APPLE,
extraOptions: [ConnectionOptionType.X, ConnectionOptionType.DISCORD]
}
screen = renderConnection({ socialOptions, onConnect })
screen = renderConnection({ connectionOptions, onConnect })
})

it('should render the primary social option', () => {
it('should render the primary and secondary option', () => {
const { getByTestId } = screen
expect(getByTestId(SOCIAL_PRIMARY_TEST_ID)).toBeInTheDocument()
expect(getByTestId(PRIMARY_TEST_ID)).toBeInTheDocument()
expect(getByTestId(SECONDARY_TEST_ID)).toBeInTheDocument()
})

it('should call the onConnect method prop when clicking the button', () => {
const { getByTestId } = screen
fireEvent.click(getByTestId(`${SOCIAL_PRIMARY_TEST_ID}-${ConnectionOptionType.GOOGLE}-button`))
fireEvent.click(getByTestId(`${PRIMARY_TEST_ID}-${ConnectionOptionType.GOOGLE}-button`))
expect(onConnect).toHaveBeenCalledWith(ConnectionOptionType.GOOGLE)
})

it('should render all the secondary options', () => {
it('should render all the extra options', () => {
const { getByTestId } = screen
act(() => {
fireEvent.click(getByTestId(SHOW_MORE_BUTTON_TEST_ID))
})
socialOptions?.secondary.forEach(option => {
expect(getByTestId(`${SOCIAL_SECONDARY_TEST_ID}-${option}-button`)).toBeInTheDocument()
connectionOptions?.extraOptions?.forEach(option => {
expect(getByTestId(`${EXTRA_TEST_ID}-${option}-button`)).toBeInTheDocument()
})
})

Expand All @@ -88,25 +71,13 @@ describe('when rendering the component', () => {
act(() => {
fireEvent.click(getByTestId(SHOW_MORE_BUTTON_TEST_ID))
})
socialOptions?.secondary.forEach(option => {
fireEvent.click(getByTestId(`${SOCIAL_SECONDARY_TEST_ID}-${option}-button`))
connectionOptions?.extraOptions?.forEach(option => {
fireEvent.click(getByTestId(`${EXTRA_TEST_ID}-${option}-button`))
expect(onConnect).toHaveBeenCalledWith(option)
})
})
})

describe('and there are no primary web3 options', () => {
kuruk-mm marked this conversation as resolved.
Show resolved Hide resolved
beforeEach(() => {
socialOptions = undefined
screen = renderConnection({ socialOptions })
})

it('should not render the primary web3 option', () => {
const { queryByTestId } = screen
expect(queryByTestId(WEB3_PRIMARY_TEST_ID)).not.toBeInTheDocument()
})
})

describe('and the user has metamask installed', () => {
let oldEthereum: typeof window.ethereum

Expand All @@ -116,12 +87,12 @@ describe('when rendering the component', () => {
...window.ethereum,
isMetaMask: true
}
web3Options = {
connectionOptions = {
primary: ConnectionOptionType.METAMASK,
secondary: [ConnectionOptionType.FORTMATIC, ConnectionOptionType.WALLET_CONNECT, ConnectionOptionType.COINBASE]
extraOptions: [ConnectionOptionType.FORTMATIC, ConnectionOptionType.WALLET_CONNECT, ConnectionOptionType.COINBASE]
}
onConnect = jest.fn()
screen = renderConnection({ web3Options, onConnect })
screen = renderConnection({ connectionOptions, onConnect })
})

afterEach(() => {
Expand All @@ -130,7 +101,7 @@ describe('when rendering the component', () => {

it('should call the onConnect method prop when clicking the button', () => {
const { getByTestId } = screen
fireEvent.click(getByTestId(`${WEB3_PRIMARY_TEST_ID}-${ConnectionOptionType.METAMASK}-button`))
fireEvent.click(getByTestId(`${PRIMARY_TEST_ID}-${ConnectionOptionType.METAMASK}-button`))
expect(onConnect).toHaveBeenCalledWith(ConnectionOptionType.METAMASK)
})
})
Expand All @@ -141,12 +112,12 @@ describe('when rendering the component', () => {
beforeEach(() => {
oldEthereum = window.ethereum
window.ethereum = undefined
web3Options = {
connectionOptions = {
primary: ConnectionOptionType.METAMASK,
secondary: [ConnectionOptionType.FORTMATIC, ConnectionOptionType.WALLET_CONNECT, ConnectionOptionType.COINBASE]
extraOptions: [ConnectionOptionType.FORTMATIC, ConnectionOptionType.WALLET_CONNECT, ConnectionOptionType.COINBASE]
}
onConnect = jest.fn()
screen = renderConnection({ web3Options, onConnect })
screen = renderConnection({ connectionOptions, onConnect })
})

afterEach(() => {
Expand All @@ -155,33 +126,38 @@ describe('when rendering the component', () => {

it('should not call the onConnect method prop when clicking the button', () => {
const { getByTestId } = screen
fireEvent.click(getByTestId(`${WEB3_PRIMARY_TEST_ID}-${ConnectionOptionType.METAMASK}-button`))
fireEvent.click(getByTestId(`${PRIMARY_TEST_ID}-${ConnectionOptionType.METAMASK}-button`))
expect(onConnect).not.toHaveBeenCalled()
})
})

describe('and there are web3 options', () => {
describe('and there are is not secondary options', () => {
beforeEach(() => {
onConnect = jest.fn()
web3Options = {
connectionOptions = {
primary: ConnectionOptionType.METAMASK,
secondary: [ConnectionOptionType.FORTMATIC, ConnectionOptionType.WALLET_CONNECT, ConnectionOptionType.COINBASE]
extraOptions: [ConnectionOptionType.FORTMATIC, ConnectionOptionType.WALLET_CONNECT, ConnectionOptionType.COINBASE]
}
screen = renderConnection({ web3Options, onConnect })
screen = renderConnection({ connectionOptions, onConnect })
})

it('should render the primary social option', () => {
it('should render the primary option', () => {
const { getByTestId } = screen
expect(getByTestId(WEB3_PRIMARY_TEST_ID)).toBeInTheDocument()
expect(getByTestId(PRIMARY_TEST_ID)).toBeInTheDocument()
})

it('should not render the secondary', () => {
const { queryByTestId } = screen
expect(queryByTestId(SECONDARY_TEST_ID)).not.toBeInTheDocument()
})

it('should render all the secondary options', () => {
it('should render all the extra options', () => {
const { getByTestId } = screen
act(() => {
fireEvent.click(getByTestId(SHOW_MORE_BUTTON_TEST_ID))
})
web3Options?.secondary.forEach(option => {
expect(getByTestId(`${WEB3_SECONDARY_TEST_ID}-${option}-button`)).toBeInTheDocument()
connectionOptions?.extraOptions?.forEach(option => {
expect(getByTestId(`${EXTRA_TEST_ID}-${option}-button`)).toBeInTheDocument()
})
})

Expand All @@ -190,18 +166,41 @@ describe('when rendering the component', () => {
act(() => {
fireEvent.click(getByTestId(SHOW_MORE_BUTTON_TEST_ID))
})
web3Options?.secondary.forEach(option => {
fireEvent.click(getByTestId(`${WEB3_SECONDARY_TEST_ID}-${option}-button`))
connectionOptions?.extraOptions?.forEach(option => {
fireEvent.click(getByTestId(`${EXTRA_TEST_ID}-${option}-button`))
expect(onConnect).toHaveBeenCalledWith(option)
})
})
})

describe('and there are no web3 nor social secondary options', () => {
describe('and there are primary, but not secondary nor extra options', () => {
beforeEach(() => {
connectionOptions = {
primary: ConnectionOptionType.METAMASK
}
screen = renderConnection({ connectionOptions })
})

it('should render the primary', () => {
const { queryByTestId } = screen
expect(queryByTestId(PRIMARY_TEST_ID)).toBeInTheDocument()
})

it('should not render the secondary', () => {
const { queryByTestId } = screen
expect(queryByTestId(SECONDARY_TEST_ID)).not.toBeInTheDocument()
})

it('should not render the more options button', () => {
const { queryByTestId } = screen
expect(queryByTestId(SHOW_MORE_BUTTON_TEST_ID)).not.toBeInTheDocument()
})
})

describe('and there are no primary nor secondary nor extra options', () => {
beforeEach(() => {
socialOptions = undefined
web3Options = undefined
screen = renderConnection({ socialOptions, web3Options })
connectionOptions = undefined
screen = renderConnection({ connectionOptions })
})

it('should not render the more options button', () => {
Expand Down
108 changes: 47 additions & 61 deletions src/components/Connection/Connection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import Icon from 'semantic-ui-react/dist/commonjs/elements/Icon/Icon'
import { Button } from 'decentraland-ui/dist/components/Button/Button'
import logoSrc from '../../assets/images/logo.svg'
import { ConnectionOption } from './ConnectionOption'
import { EXTRA_TEST_ID, PRIMARY_TEST_ID, SECONDARY_TEST_ID, SHOW_MORE_BUTTON_TEST_ID } from './constants'
import {
SHOW_MORE_BUTTON_TEST_ID,
SOCIAL_PRIMARY_TEST_ID,
SOCIAL_SECONDARY_TEST_ID,
WEB3_PRIMARY_TEST_ID,
WEB3_SECONDARY_TEST_ID
} from './constants'
import { ConnectionOptionType, ConnectionProps, MetamaskEthereumWindow, connectionOptionTitles } from './Connection.types'
ConnectionOptionType,
ConnectionProps,
MetamaskEthereumWindow,
connectionOptionTitles,
} from './Connection.types'
import styles from './Connection.module.css'
import { isSocialLogin } from '../Pages/LoginPage/utils'

const Primary = ({
message,
Expand Down Expand Up @@ -91,65 +91,60 @@ const defaultProps = {
}

export const Connection = (props: ConnectionProps): JSX.Element => {
const { i18n = defaultProps.i18n, onConnect, onLearnMore, socialOptions, web3Options, className, loadingOption } = props
const { i18n = defaultProps.i18n, onConnect, onLearnMore, connectionOptions, className, loadingOption } = props

const [showMore, setShowMore] = useState(false)
const handleShowMore = useCallback(() => {
setShowMore(!showMore)
}, [showMore])

const isMetamaskAvailable = (window.ethereum as MetamaskEthereumWindow)?.isMetaMask
const hasSocialSecondaryOptions = socialOptions && socialOptions.secondary && socialOptions.secondary.length > 0
const hasWeb3SecondaryOptions = web3Options && web3Options.secondary && web3Options.secondary.length > 0
const hasExtraOptions = connectionOptions?.extraOptions && connectionOptions.extraOptions.length > 0

const renderPrimary = (option: ConnectionOptionType, testId: string) => (
<Primary
onConnect={onConnect}
testId={testId}
option={option}
loadingOption={loadingOption}
error={
!isMetamaskAvailable && option === ConnectionOptionType.METAMASK
? 'You need to install the MetaMask Browser Extension to proceed. Please install it and try again.'
: undefined
}
message={
isSocialLogin(option) ? (
<>
{i18n.socialMessage(<div className={styles.primaryMagic} role="img" aria-label="Magic" />)}
<span className={styles.primaryLearnMore} role="button" onClick={() => onLearnMore(option)}>
Learn More
</span>
</>
) : (
i18n.web3Message(element => (
<span className={styles.primaryLearnMore} role="button" onClick={() => onLearnMore(option)}>
{element}
</span>
))
)
}
>
<>{isSocialLogin(option) ? i18n.accessWith(option) : i18n.connectWith(option)}</>
</Primary>
)

return (
<div className={classNames(className, styles.connection)}>
<img className={styles.dclLogo} src={logoSrc} alt="Decentraland logo" />
<div>
<h1 className={styles.title}>{i18n.title}</h1>
<h2 className={styles.subtitle}>{i18n.subtitle}</h2>
{socialOptions ? (
<Primary
onConnect={onConnect}
testId={SOCIAL_PRIMARY_TEST_ID}
option={socialOptions?.primary}
loadingOption={loadingOption}
message={
<>
{i18n.socialMessage(<div className={styles.primaryMagic} role="img" aria-label="Magic" />)}
<span className={styles.primaryLearnMore} role="button" onClick={() => onLearnMore(socialOptions?.primary)}>
Learn More
</span>
</>
}
>
<>{i18n.accessWith(socialOptions?.primary)}</>
</Primary>
) : null}
{web3Options ? (
<Primary
onConnect={onConnect}
testId={WEB3_PRIMARY_TEST_ID}
option={web3Options?.primary}
loadingOption={loadingOption}
error={
!isMetamaskAvailable && web3Options?.primary === ConnectionOptionType.METAMASK
? 'You need to install the MetaMask Browser Extension to proceed. Please install it and try again.'
: undefined
}
message={i18n.web3Message(element => (
<span className={styles.primaryLearnMore} role="button" onClick={() => onLearnMore(web3Options.primary)}>
{element}
</span>
))}
>
<>{i18n.connectWith(web3Options?.primary)}</>
</Primary>
) : null}
{connectionOptions && renderPrimary(connectionOptions.primary, PRIMARY_TEST_ID)}
{connectionOptions?.secondary && renderPrimary(connectionOptions.secondary, SECONDARY_TEST_ID)}
</div>

<div className={styles.showMore}>
{(hasWeb3SecondaryOptions || hasSocialSecondaryOptions) && (
{hasExtraOptions && (
<Button
data-testid={SHOW_MORE_BUTTON_TEST_ID}
basic
Expand All @@ -162,24 +157,15 @@ export const Connection = (props: ConnectionProps): JSX.Element => {
<Icon name={showMore ? 'chevron up' : 'chevron down'} />
</Button>
)}
{showMore && hasSocialSecondaryOptions && (
{showMore && hasExtraOptions && connectionOptions.extraOptions && (
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
{showMore && hasExtraOptions && connectionOptions.extraOptions && (
{showMore && hasExtraOptions && (

Copy link
Member Author

@kuruk-mm kuruk-mm Nov 20, 2024

Choose a reason for hiding this comment

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

I know that I'm already considering it in hasExtraOptions, but the compiler doesn't realize and it throws an error

npm run build, removing the connectionOptions.extraOptions &&:

src/components/Connection/Connection.tsx:163:13 - error TS2322: Type 'ConnectionOptionType[] | undefined' is not assignable to type 'ConnectionOptionType[]'.
  Type 'undefined' is not assignable to type 'ConnectionOptionType[]'.

163             options={connectionOptions.extraOptions}
                ~~~~~~~

  src/components/Connection/Connection.tsx:58:3
    58   options: ConnectionOptionType[]
         ~~~~~~~
    The expected type comes from property 'options' which is declared here on type 'IntrinsicAttributes & { options: ConnectionOptionType[]; tooltipDirection: "top center" | "bottom center"; testId?: string | undefined; loadingOption?: ConnectionOptionType | undefined; onConnect: (wallet: ConnectionOptionType) => unknown; }'


Found 1 error in src/components/Connection/Connection.tsx:163

<Secondary
testId={SOCIAL_SECONDARY_TEST_ID}
options={socialOptions.secondary}
testId={EXTRA_TEST_ID}
options={connectionOptions.extraOptions}
onConnect={onConnect}
tooltipDirection="top center"
loadingOption={loadingOption}
/>
)}
{showMore && hasWeb3SecondaryOptions && (
<Secondary
testId={WEB3_SECONDARY_TEST_ID}
options={web3Options.secondary}
onConnect={onConnect}
tooltipDirection="bottom center"
loadingOption={loadingOption}
/>
)}
</div>
</div>
)
Expand Down
Loading
Loading