Skip to content

Commit

Permalink
feat: world permissions (#3073)
Browse files Browse the repository at this point in the history
* feat: world permissions saga flow

* feat: adapt auth

* test: worlds api and fixes

* feat: component WorldPermissionsModal and sagas for complete flow

* refactor: world permission access tab

* feat: collaborators tab.

* fix: remove unused import

* feat: translations and ui updates

* style: ui adjustments

* style: modal title

* feat: translations. refactor: css to module

* refactor: onGetWorldPermissions

* feat: remove unnecessary props

* fix: Update decentraland-dapps to allow fetching multiple profiles

* fix: Add tests for the WorldPermissionsHeader and refactor it

* fix: Refactor and add tests for the WorldPermissionsAddUserForm

* refactor: WorldPermissionsModal

* feat: WorldPermissionsAddUserForm is loading and i18n

* test: WorldPermissionsAddUserFrom

* refactor: better get getCatalystProfiles performance

* refactor: WorldPermissionsAvatarWithInfo

* test: WorldPermissionsAvatarWithInfo

* test: getResumedAddress

* fix: Use decentraland-dapps to get profiles

* fix: Fix disabled button

* fix: Remove comment

* fix: Refactor loading condition

* feat: Add WorldPermissionsCollaboratorsItem tests

* test: world get permissions

* style: toggle public style

* feat: WorldPermissionsAccessItem tests

* test: world post permissions

* fix: Loading text uncentered

* fix: Can't add user in access form

* test: world put permissions

* test: world delete permissions

* test: change descriptions labels

* fix: Test

* fix: remove collaborators

* test: world permission reducer

* feat: typo

---------

Co-authored-by: Lautaro Petaccio <1120791+LautaroPetaccio@users.noreply.github.com>
Co-authored-by: Lautaro Petaccio <lausuper@gmail.com>
  • Loading branch information
3 people authored Apr 30, 2024
1 parent a3d920f commit 55616cd
Show file tree
Hide file tree
Showing 63 changed files with 3,044 additions and 94 deletions.
21 changes: 16 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"decentraland-builder-scripts": "^0.24.0",
"decentraland-connect": "^6.3.1",
"decentraland-crypto-fetch": "^2.0.1",
"decentraland-dapps": "^19.5.3",
"decentraland-dapps": "^19.7.0",
"decentraland-ecs": "6.12.4-7784644013.commit-f770b3e",
"decentraland-experiments": "^1.0.2",
"decentraland-transactions": "^2.6.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
.addUserWrapper {
margin-top: 16px;
}

.addUserButtonContainer :global(button) {
width: 100%;
}

.addUserFormContainer {
display: flex;
justify-content: space-between;
border-radius: 10px;
background: #1b1a1f;
padding: 24px;
width: 100%;
}

.addUserFormContainer :global(.dcl.field.full.address) {
flex: 1;
margin-right: 16px;
}

.addUserFormContainer :global(.dcl.field.full:not(.error) p.message) {
display: none;
}

.addUserFormContainer :global(button:nth-child(2)) {
margin-right: 16px;
}

.addUserFormContainer :global(button) {
width: 155px;
height: 52px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react'
import { Icon } from 'decentraland-ui'
import { Button } from 'decentraland-ui/dist/components/Button/Button'
import { t } from 'decentraland-dapps/dist/modules/translation/utils'

import { Field } from 'decentraland-ui/dist/components/Field/Field'
import { WorldPermissionNames } from 'lib/api/worlds'

import styles from './WorldPermissionsAddUserForm.module.css'
import { Props } from './WorldPermissionsAddUserForm.types'

export const WORLD_PERMISSIONS_ADD_USER_FORM_SHOW_FORM_BUTTON_DATA_TEST_ID = 'world-permissions-add-user-form-show-form-button-data-test-id'
export const WORLD_PERMISSIONS_ADD_USER_FORM_FIELD_DATA_TEST_ID = 'world-permissions-add-user-form-field-data-test-id'
export const WORLD_PERMISSIONS_ADD_USER_FORM_CHANGE_PERMISSION_BUTTON_DATA_TEST_ID =
'world-permissions-add-user-form-change-permission-button-data-test-id'

export const WorldPermissionsAddUserForm = React.memo((props: Props) => {
const {
showAddUserForm,
newAddress,
isLoading,
isLoadingNewUser,
addButtonLabel,
error,
onShowAddUserForm,
onNewAddressChange,
onUserPermissionListChange
} = props

return (
<div className={styles.addUserWrapper}>
{!showAddUserForm ? (
<div className={styles.addUserButtonContainer}>
<Button
data-testid={WORLD_PERMISSIONS_ADD_USER_FORM_SHOW_FORM_BUTTON_DATA_TEST_ID}
onClick={onShowAddUserForm}
loading={isLoading}
>
<Icon name="plus" />
{addButtonLabel}
</Button>
</div>
) : (
<div className={styles.addUserFormContainer}>
<Field
data-testid={WORLD_PERMISSIONS_ADD_USER_FORM_FIELD_DATA_TEST_ID}
placeholder="0x..."
value={newAddress}
onChange={onNewAddressChange}
kind="full"
loading={isLoadingNewUser}
disabled={isLoadingNewUser}
error={error}
message={error ? t('world_permissions_modal.invalid_address') : ''}
/>
<Button
data-testid={WORLD_PERMISSIONS_ADD_USER_FORM_CHANGE_PERMISSION_BUTTON_DATA_TEST_ID}
onClick={e =>
newAddress === ''
? onShowAddUserForm(e, {})
: onUserPermissionListChange(e, { walletAddress: newAddress, worldPermissionName: WorldPermissionNames.Access })
}
loading={isLoadingNewUser}
disabled={isLoadingNewUser || error}
>
{newAddress === '' ? t('world_permissions_modal.button_cancel_label') : t('world_permissions_modal.button_add_label')}
</Button>
</div>
)}
</div>
)
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type Props = {
showAddUserForm: boolean
newAddress: string
isLoading: boolean
isLoadingNewUser: boolean
addButtonLabel: string
error: boolean
onShowAddUserForm: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>, data: any) => void
onNewAddressChange: (e: React.ChangeEvent<HTMLInputElement>, data: any) => void
onUserPermissionListChange: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>, data: any) => void
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { render, fireEvent } from '@testing-library/react'
import { WorldPermissionNames } from 'lib/api/worlds'
import {
WORLD_PERMISSIONS_ADD_USER_FORM_CHANGE_PERMISSION_BUTTON_DATA_TEST_ID,
WORLD_PERMISSIONS_ADD_USER_FORM_FIELD_DATA_TEST_ID,
WORLD_PERMISSIONS_ADD_USER_FORM_SHOW_FORM_BUTTON_DATA_TEST_ID,
WorldPermissionsAddUserForm
} from './WorldPermissionsAddUserForm'
import { Props } from './WorldPermissionsAddUserForm.types'

const renderWorldPermissionsAddUserForm = (props: Partial<Props> = {}) =>
render(
<WorldPermissionsAddUserForm
showAddUserForm={false}
newAddress={'aNewAddress'}
isLoading={false}
isLoadingNewUser={false}
addButtonLabel={'aButtonLabel'}
error={false}
onShowAddUserForm={() => undefined}
onNewAddressChange={() => undefined}
onUserPermissionListChange={() => undefined}
{...props}
/>
)

describe("when rendering the Worlds Permissions Add User Form it's loading", () => {
it('should not render the description', () => {
const { queryByTestId } = renderWorldPermissionsAddUserForm({ isLoading: true })
expect(queryByTestId(WORLD_PERMISSIONS_ADD_USER_FORM_SHOW_FORM_BUTTON_DATA_TEST_ID)).toBeInTheDocument()
console.log(queryByTestId(WORLD_PERMISSIONS_ADD_USER_FORM_SHOW_FORM_BUTTON_DATA_TEST_ID))
expect(queryByTestId(WORLD_PERMISSIONS_ADD_USER_FORM_SHOW_FORM_BUTTON_DATA_TEST_ID)).toHaveClass('loading')
})
})

describe('when rendering the Worlds Permissions Add User Form', () => {
let renderedComponent: ReturnType<typeof renderWorldPermissionsAddUserForm>
const buttonLabel = 'aButtonLabel'

describe('and the prop to show the add user form is set to false', () => {
let onShowAddUserForm: jest.Mock

beforeEach(() => {
onShowAddUserForm = jest.fn()
renderedComponent = renderWorldPermissionsAddUserForm({ showAddUserForm: false, addButtonLabel: buttonLabel, onShowAddUserForm })
})

it('should render a button to show the form', () => {
const { getByTestId } = renderedComponent
const showFormButton = getByTestId(WORLD_PERMISSIONS_ADD_USER_FORM_SHOW_FORM_BUTTON_DATA_TEST_ID)

expect(showFormButton).toBeInTheDocument()
expect(showFormButton.textContent).toEqual(buttonLabel)
})

describe('when clicking the button to show the form', () => {
beforeEach(() => {
const { getByTestId } = renderedComponent
const showFormButton = getByTestId(WORLD_PERMISSIONS_ADD_USER_FORM_SHOW_FORM_BUTTON_DATA_TEST_ID)
fireEvent.click(showFormButton)
})

it('should call the onShowAddUserForm method prop', () => {
expect(onShowAddUserForm).toHaveBeenCalled()
})
})
})

describe('and the prop to show the add user form is set to true', () => {
let showAddUserForm: boolean

beforeEach(() => {
showAddUserForm = true
})

describe('and the address field is changed', () => {
let onNewAddressChange: jest.Mock

beforeEach(() => {
onNewAddressChange = jest.fn()
renderedComponent = renderWorldPermissionsAddUserForm({ showAddUserForm, onNewAddressChange })
const { getByTestId } = renderedComponent
const showFormButton = getByTestId(WORLD_PERMISSIONS_ADD_USER_FORM_FIELD_DATA_TEST_ID).children[0]
fireEvent.change(showFormButton, { target: { value: '0x00' } })
})

it('should call the onNewAddressChange method prop', () => {
expect(onNewAddressChange).toHaveBeenCalled()
})
})

describe("and there's an error", () => {
let error: boolean

beforeEach(() => {
error = true
renderedComponent = renderWorldPermissionsAddUserForm({ showAddUserForm, error })
})

it('should render the the button as disabled', () => {
const { getByTestId } = renderedComponent
const changePermissionButton = getByTestId(WORLD_PERMISSIONS_ADD_USER_FORM_CHANGE_PERMISSION_BUTTON_DATA_TEST_ID)

expect(changePermissionButton).toHaveAttribute('disabled')
})
})

describe('and a new user is being loaded', () => {
let isLoadingNewUser: boolean

beforeEach(() => {
isLoadingNewUser = true
renderedComponent = renderWorldPermissionsAddUserForm({ showAddUserForm, isLoadingNewUser })
})

it('should render disabled the field and the button and set them as loading', () => {
const { getByTestId, debug } = renderedComponent
const changePermissionButton = getByTestId(WORLD_PERMISSIONS_ADD_USER_FORM_CHANGE_PERMISSION_BUTTON_DATA_TEST_ID)
const addressField = getByTestId(WORLD_PERMISSIONS_ADD_USER_FORM_FIELD_DATA_TEST_ID)
debug()

expect(addressField).toHaveClass('disabled')
expect(addressField).toHaveClass('loading')
expect(changePermissionButton).toHaveAttribute('disabled')
expect(changePermissionButton).toHaveClass('loading')
})
})

describe('and clicking the button to change the permission', () => {
let newAddress: string

describe('when the newAddress prop is empty', () => {
let onShowAddUserForm: jest.Mock

beforeEach(() => {
newAddress = ''
onShowAddUserForm = jest.fn()
renderedComponent = renderWorldPermissionsAddUserForm({ showAddUserForm, newAddress, onShowAddUserForm })
const { getByTestId } = renderedComponent
const changePermissionButton = getByTestId(WORLD_PERMISSIONS_ADD_USER_FORM_CHANGE_PERMISSION_BUTTON_DATA_TEST_ID)
fireEvent.click(changePermissionButton)
})

it('should call the onShowAddUserForm method prop', () => {
expect(onShowAddUserForm).toHaveBeenCalled()
})
})

describe('when the new address prop is set', () => {
let onUserPermissionListChange: jest.Mock

beforeEach(() => {
newAddress = 'aNewAddress'
onUserPermissionListChange = jest.fn()
renderedComponent = renderWorldPermissionsAddUserForm({ showAddUserForm, newAddress, onUserPermissionListChange })
const { getByTestId } = renderedComponent
const changePermissionButton = getByTestId(WORLD_PERMISSIONS_ADD_USER_FORM_CHANGE_PERMISSION_BUTTON_DATA_TEST_ID)
fireEvent.click(changePermissionButton)
})

it('should call the onUserPermissionListChange method prop', () => {
expect(onUserPermissionListChange).toHaveBeenCalledWith(expect.anything(), {
walletAddress: newAddress,
worldPermissionName: WorldPermissionNames.Access
})
})
})
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './WorldPermissionsAddUserForm.types'
export * from './WorldPermissionsAddUserForm'
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { connect } from 'react-redux'
import { getProfileOfAddress, isLoadingProfile } from 'decentraland-dapps/dist/modules/profile/selectors'
import { RootState } from 'modules/common/types'
import { MapStateProps, OwnProps } from './WorldPermissionsAvatarWithInfo.types'
import { WorldPermissionsAvatarWithInfo } from './WorldPermissionsAvatarWithInfo'

const mapState = (state: RootState, ownProps: OwnProps): MapStateProps => ({
profileAvatar: getProfileOfAddress(state, ownProps.walletAddress)?.avatars[0],
isLoading: isLoadingProfile(state, ownProps.walletAddress) || ownProps.isLoading
})

export default connect(mapState)(WorldPermissionsAvatarWithInfo)
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.avatar {
display: flex;
align-items: center;
width: 100%;
}

.avatar .avatarface {
margin-right: 12px;
}

.avatar .loadingText:global(.dui-loading-text) {
margin-bottom: 0;
margin-left: 10px;
}

.avatar .paragraph {
color: #a09ba8;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: normal;
cursor: pointer;
}

.avatar .paragraph .span {
color: #fcfcfc;
font-size: 16px;
font-style: normal;
font-weight: 600;
line-height: normal;
margin-right: 8px;
}
Loading

0 comments on commit 55616cd

Please sign in to comment.