Merge pull request #7786 from overleaf/ii-departments-override

Override default departments

GitOrigin-RevId: 23061bc8c083bb8099ca62bd0cdb3c796e49979d
This commit is contained in:
ilkin-overleaf 2022-04-29 14:09:53 +03:00 committed by Copybot
parent 35e0d83343
commit 85f731110c
6 changed files with 114 additions and 16 deletions

View file

@ -9,8 +9,8 @@ import { AddEmailInput, InstitutionInfo } from './add-email-input'
import useAsync from '../../../../shared/hooks/use-async'
import { useUserEmailsContext } from '../../context/user-email-context'
import { getJSON, postJSON } from '../../../../infrastructure/fetch-json'
import { defaults as roles } from '../../roles'
import { defaults as departments } from '../../departments'
import { defaults as defaultRoles } from '../../roles'
import { defaults as defaultDepartments } from '../../departments'
import { University } from '../../../../../../types/university'
import { CountryCode } from '../../../../../../types/country'
import { ExposedSettings } from '../../../../../../types/exposed-settings'
@ -49,6 +49,7 @@ function AddEmail() {
const [university, setUniversity] = useState('')
const [role, setRole] = useState('')
const [department, setDepartment] = useState('')
const [departments, setDepartments] = useState(defaultDepartments)
const [isInstitutionFieldsVisible, setIsInstitutionFieldsVisible] =
useState(false)
const [isUniversityDirty, setIsUniversityDirty] = useState(false)
@ -70,6 +71,18 @@ function AddEmail() {
}
}, [setIsUniversityDirty, university])
useEffect(() => {
const selectedKnownUniversity = countryCode
? universities[countryCode]?.find(({ name }) => name === university)
: undefined
if (selectedKnownUniversity && selectedKnownUniversity.departments.length) {
setDepartments(selectedKnownUniversity.departments)
} else {
setDepartments(defaultDepartments)
}
}, [countryCode, universities, university])
// Fetch country institution
useEffect(() => {
// Skip if country not selected or universities for
@ -222,7 +235,7 @@ function AddEmail() {
<>
<div className="form-group mb-2">
<DownshiftInput
items={roles}
items={defaultRoles}
inputValue={role}
placeholder={t('role')}
label={t('role')}

View file

@ -7,9 +7,10 @@ import { useUserEmailsContext } from '../../context/user-email-context'
import DownshiftInput from './downshift-input'
import Icon from '../../../../shared/components/icon'
import useAsync from '../../../../shared/hooks/use-async'
import { postJSON } from '../../../../infrastructure/fetch-json'
import { defaults as roles } from '../../roles'
import { defaults as departments } from '../../departments'
import { getJSON, postJSON } from '../../../../infrastructure/fetch-json'
import { defaults as defaultRoles } from '../../roles'
import { defaults as defaultDepartments } from '../../departments'
import { University } from '../../../../../../types/university'
type InstitutionAndRoleProps = {
userEmailData: UserEmailData
@ -18,6 +19,7 @@ type InstitutionAndRoleProps = {
function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) {
const { t } = useTranslation()
const { isLoading, isError, runAsync } = useAsync()
const changeAffiliationAsync = useAsync()
const { affiliation } = userEmailData
const {
state,
@ -27,6 +29,7 @@ function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) {
} = useUserEmailsContext()
const [role, setRole] = useState(affiliation?.role || '')
const [department, setDepartment] = useState(affiliation?.department || '')
const [departments, setDepartments] = useState(defaultDepartments)
useEffect(() => {
setUserEmailsContextLoading(isLoading)
@ -34,6 +37,23 @@ function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) {
const handleChangeAffiliation = () => {
setEmailAffiliationBeingEdited(userEmailData.email)
if (!affiliation?.institution.id) {
return
}
changeAffiliationAsync
.runAsync<University>(
getJSON(`/institutions/list/${affiliation.institution.id}`)
)
.then(data => {
if (data.departments.length) {
setDepartments(data.departments)
}
})
.catch(() => {
setDepartments(defaultDepartments)
})
}
const handleCancelAffiliationChange = () => {
@ -87,7 +107,7 @@ function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) {
<div className="affiliation-change-container small">
<form onSubmit={handleSubmit}>
<DownshiftInput
items={roles}
items={defaultRoles}
inputValue={role}
placeholder={t('role')}
label={t('role')}

View file

@ -5,6 +5,7 @@ const fakeUsersData = [
affiliation: {
institution: {
confirmed: true,
id: 1,
name: 'Overleaf',
},
licence: 'pro_plus',
@ -22,6 +23,7 @@ const fakeUsersData = [
affiliation: {
institution: {
confirmed: true,
id: 2,
name: 'Overleaf',
},
licence: 'pro_plus',
@ -38,8 +40,27 @@ const fakeUsersData = [
]
const fakeInstitutions = [
{ id: 9326, name: 'Unknown', country_code: 'al', departments: [] },
{
id: 9326,
name: 'Unknown',
country_code: 'al',
departments: ['New department'],
},
]
const fakeInstitution = {
id: 123,
name: 'test',
country_code: 'de',
departments: [],
team_id: null,
}
const bazFakeInstitution = {
id: 2,
name: 'Baz',
country_code: 'de',
departments: ['Custom department 1', 'Custom department 2'],
team_id: null,
}
const fakeInstitutionDomain = [
{
@ -56,7 +77,11 @@ const fakeInstitutionDomain = [
export function defaultSetupMocks(fetchMock) {
fetchMock
.get(/\/user\/emails/, fakeUsersData, { delay: MOCK_DELAY })
.get(/\/institutions\/list/, fakeInstitutions, { delay: MOCK_DELAY })
.get(/\/institutions\/list\/2/, bazFakeInstitution, { delay: MOCK_DELAY })
.get(/\/institutions\/list\/\d+/, fakeInstitution, { delay: MOCK_DELAY })
.get(/\/institutions\/list\?country_code=.*/, fakeInstitutions, {
delay: MOCK_DELAY,
})
.get(/\/institutions\/domains/, fakeInstitutionDomain)
.post(/\/user\/emails\/*/, 200, {
delay: MOCK_DELAY,

View file

@ -196,8 +196,9 @@ describe('<EmailsSection />', function () {
await screen.findByRole('link', { name: 'Link Accounts and Add Email' })
})
it('adds new email address with existing institution', async function () {
it('adds new email address with existing institution and custom departments', async function () {
const country = 'Germany'
const customDepartment = 'Custom department'
fetchMock.get('/user/emails?ensureAffiliation=true', [])
render(<EmailsSection />)
@ -225,7 +226,7 @@ describe('<EmailsSection />', function () {
id: userEmailData.affiliation.institution.id,
name: userEmailData.affiliation.institution.name,
country_code: 'de',
departments: [],
departments: [customDepartment],
},
])
@ -252,10 +253,19 @@ describe('<EmailsSection />', function () {
const roleInput = screen.getByRole('textbox', { name: /role/i })
await userEvent.type(roleInput, userEmailData.affiliation.role)
const departmentInput = screen.getByRole('textbox', { name: /department/i })
await userEvent.type(departmentInput, userEmailData.affiliation.department)
await userEvent.click(departmentInput)
await userEvent.click(screen.getByText(customDepartment))
const userEmailDataCopy = {
...userEmailData,
affiliation: {
...userEmailData.affiliation,
department: customDepartment,
},
}
fetchMock
.get('/user/emails?ensureAffiliation=true', [userEmailData])
.get('/user/emails?ensureAffiliation=true', [userEmailDataCopy])
.post(/\/user\/emails/, 200)
await userEvent.click(
@ -272,13 +282,13 @@ describe('<EmailsSection />', function () {
id: userEmailData.affiliation?.institution.id,
},
role: userEmailData.affiliation?.role,
department: userEmailData.affiliation?.department,
department: customDepartment,
})
screen.getByText(userEmailData.email)
screen.getByText(userEmailData.affiliation.institution.name)
screen.getByText(userEmailData.affiliation.role, { exact: false })
screen.getByText(userEmailData.affiliation.department, { exact: false })
screen.getByText(customDepartment, { exact: false })
})
it('adds new email address without existing institution', async function () {

View file

@ -111,9 +111,39 @@ describe('user role and institution', function () {
.to.not.exist
})
it('fetches institution data and replaces departments dropdown on add/change', async function () {
const userEmailData = userData1
fetchMock.get('/user/emails?ensureAffiliation=true', [userEmailData])
render(<EmailsSection />)
await fetchMock.flush(true)
fetchMock.reset()
const fakeDepartment = 'Fake department'
const institution = userEmailData.affiliation.institution
fetchMock.get(`/institutions/list/${institution.id}`, {
id: institution.id,
name: institution.name,
country_code: 'de',
departments: [fakeDepartment],
})
fireEvent.click(
screen.getByRole('button', { name: /add role and department/i })
)
await fetchMock.flush(true)
fetchMock.reset()
fireEvent.click(screen.getByRole('textbox', { name: /department/i }))
screen.getByText(fakeDepartment)
})
it('adds new role and department', async function () {
fetchMock
.get('/user/emails?ensureAffiliation=true', [userData1])
.get(/\/institutions\/list/, { departments: [] })
.post('/user/emails/endorse', 200)
render(<EmailsSection />)

View file

@ -4,5 +4,5 @@ export type University = {
id: number
name: string
country_code: CountryCode
departments: unknown[]
departments: string[]
}