Skip to content

Commit

Permalink
feat: add target configs for multiple platforms, add deep links and r…
Browse files Browse the repository at this point in the history
…efactor social connection list (#125)

* feat: add multiple target configs
feat: add deep links
refactor: unify connectionOptions instead of social/web3

* fix

* add defaultMobileConfig

* add required tests in targetConfig from feedback

* fix connection spec tests, rename: social->primary, web3->secondary, secondary->extra, and add tests from feedback

* use `isSocialLogin` instead of `isMagicConnection`
  • Loading branch information
kuruk-mm authored Nov 21, 2024
1 parent ab0651b commit 6b711ac
Show file tree
Hide file tree
Showing 9 changed files with 357 additions and 168 deletions.
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', () => {
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 && (
<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

0 comments on commit 6b711ac

Please sign in to comment.