From 1def576973f9fc902746c544dc7078bdbfb31660 Mon Sep 17 00:00:00 2001 From: ilkin-overleaf <100852799+ilkin-overleaf@users.noreply.github.com> Date: Fri, 29 Apr 2022 14:10:10 +0300 Subject: [PATCH] Merge pull request #7808 from overleaf/ii-settings-fixes-1 [SettingsPage] UI Fixes 1 GitOrigin-RevId: 0e0f605191218af4db70a801ff1e50b47f6e0b01 --- .../settings/components/emails-section.tsx | 14 +- .../settings/components/emails/actions.tsx | 4 +- .../components/emails/add-email-input.tsx | 38 +++- .../settings/components/emails/add-email.tsx | 38 ++-- .../components/emails/country-input.tsx | 16 +- .../components/emails/downshift-input.tsx | 17 +- .../emails/institution-and-role.tsx | 26 ++- .../resend-confirmation-email-button.tsx | 5 +- .../settings/components/emails/row.tsx | 4 +- .../settings/context/user-email-context.tsx | 3 +- .../stylesheets/vendor/select/select.css | 170 +++++++++--------- .../emails/emails-section-actions.test.tsx | 4 +- .../emails-section-add-new-email.test.tsx | 15 +- .../components/emails/emails-section.test.tsx | 2 +- 14 files changed, 221 insertions(+), 135 deletions(-) diff --git a/services/web/frontend/js/features/settings/components/emails-section.tsx b/services/web/frontend/js/features/settings/components/emails-section.tsx index cc010d38e5..9cfdc72d38 100644 --- a/services/web/frontend/js/features/settings/components/emails-section.tsx +++ b/services/web/frontend/js/features/settings/components/emails-section.tsx @@ -18,6 +18,7 @@ function EmailsSectionContent() { state: { data: userEmailsData }, isInitializing, isInitializingError, + isInitializingSuccess, } = useUserEmailsContext() const userEmails = Object.values(userEmailsData.byId) @@ -32,11 +33,13 @@ function EmailsSectionContent() {

-
+ <> {isInitializing ? ( -
- {t('loading')}... +
+
+ {t('loading')}... +
) : ( <> @@ -48,14 +51,14 @@ function EmailsSectionContent() { ))} )} - + {isInitializingSuccess && } {isInitializingError && ( {' '} {t('error_performing_request')} )} -
+ ) } @@ -73,7 +76,6 @@ function EmailsSection() { -
) } diff --git a/services/web/frontend/js/features/settings/components/emails/actions.tsx b/services/web/frontend/js/features/settings/components/emails/actions.tsx index ef67d3d440..d8ef82ca87 100644 --- a/services/web/frontend/js/features/settings/components/emails/actions.tsx +++ b/services/web/frontend/js/features/settings/components/emails/actions.tsx @@ -2,7 +2,6 @@ import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import MakePrimary from './actions/make-primary' import Remove from './actions/remove' -import Icon from '../../../../shared/components/icon' import useAsync from '../../../../shared/hooks/use-async' import { useUserEmailsContext } from '../../context/user-email-context' import { UserEmailData } from '../../../../../../types/user-email' @@ -51,8 +50,7 @@ function Actions({ userEmailData }: ActionsProps) { /> {(makePrimaryAsync.isError || deleteEmailAsync.isError) && (
- {' '} - {t('error_performing_request')} + {t('generic_something_went_wrong')}
)} diff --git a/services/web/frontend/js/features/settings/components/emails/add-email-input.tsx b/services/web/frontend/js/features/settings/components/emails/add-email-input.tsx index 6715ebd8d8..ddb5f0a32b 100644 --- a/services/web/frontend/js/features/settings/components/emails/add-email-input.tsx +++ b/services/web/frontend/js/features/settings/components/emails/add-email-input.tsx @@ -4,6 +4,7 @@ import { useCallback, useEffect, useState, + forwardRef, } from 'react' import { getJSON } from '../../../../infrastructure/fetch-json' import useAbortController from '../../../../shared/hooks/use-abort-controller' @@ -38,13 +39,14 @@ export function clearDomainCache() { type AddEmailInputProps = { onChange: (value: string, institution?: InstitutionInfo) => void + inputRef?: React.ForwardedRef } -export function AddEmailInput({ onChange }: AddEmailInputProps) { +function AddEmailInputBase({ onChange, inputRef }: AddEmailInputProps) { const { signal } = useAbortController() - const [suggestion, setSuggestion] = useState(null) - const [inputValue, setInputValue] = useState(null) + const [suggestion, setSuggestion] = useState(null) + const [inputValue, setInputValue] = useState(null) const [matchedInstitution, setMatchedInstitution] = useState(null) @@ -52,7 +54,10 @@ export function AddEmailInput({ onChange }: AddEmailInputProps) { if (inputValue == null) { return } - if (matchedInstitution && suggestion === inputValue) { + if ( + matchedInstitution && + inputValue.endsWith(matchedInstitution.hostname) + ) { onChange(inputValue, matchedInstitution) } else { onChange(inputValue) @@ -103,12 +108,23 @@ export function AddEmailInput({ onChange }: AddEmailInputProps) { const handleKeyDownEvent = useCallback( (event: KeyboardEvent) => { - if (event.key === 'Tab' || event.key === 'Enter') { + const setInputValueAndResetSuggestion = () => { + setInputValue(suggestion) + setSuggestion(null) + } + + if (event.key === 'Enter') { event.preventDefault() + if (suggestion) { - setInputValue(suggestion) + setInputValueAndResetSuggestion() } } + + if (event.key === 'Tab' && suggestion) { + event.preventDefault() + setInputValueAndResetSuggestion() + } }, [suggestion] ) @@ -129,7 +145,17 @@ export function AddEmailInput({ onChange }: AddEmailInputProps) { onKeyDown={handleKeyDownEvent} value={inputValue || ''} placeholder="e.g. johndoe@mit.edu" + ref={inputRef} />
) } + +const AddEmailInput = forwardRef< + HTMLInputElement, + Omit +>((props, ref) => ) + +AddEmailInput.displayName = 'AddEmailInput' + +export { AddEmailInput } diff --git a/services/web/frontend/js/features/settings/components/emails/add-email.tsx b/services/web/frontend/js/features/settings/components/emails/add-email.tsx index 906e65c034..41d138e7cd 100644 --- a/services/web/frontend/js/features/settings/components/emails/add-email.tsx +++ b/services/web/frontend/js/features/settings/components/emails/add-email.tsx @@ -1,6 +1,6 @@ -import { useState, useEffect } from 'react' +import { useState, useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' -import { Button, Row, Col } from 'react-bootstrap' +import { Button, Row, Col, Alert } from 'react-bootstrap' import Cell from './cell' import Icon from '../../../../shared/components/icon' import DownshiftInput from './downshift-input' @@ -39,6 +39,8 @@ function AddEmail() { const [isFormVisible, setIsFormVisible] = useState( () => window.location.hash === '#add-email' ) + const emailRef = useRef(null) + const countryRef = useRef(null) const [newEmail, setNewEmail] = useState('') const [newEmailMatchedInstitution, setNewEmailMatchedInstitution] = useState(null) @@ -53,7 +55,7 @@ function AddEmail() { const [isInstitutionFieldsVisible, setIsInstitutionFieldsVisible] = useState(false) const [isUniversityDirty, setIsUniversityDirty] = useState(false) - const { isLoading, isError, runAsync } = useAsync() + const { isLoading, isError, error, runAsync } = useAsync() const { runAsync: institutionRunAsync } = useAsync() const { state, @@ -65,6 +67,18 @@ function AddEmail() { setUserEmailsContextLoading(isLoading) }, [setUserEmailsContextLoading, isLoading]) + useEffect(() => { + if (isFormVisible && emailRef.current) { + emailRef.current?.focus() + } + }, [emailRef, isFormVisible]) + + useEffect(() => { + if (isInstitutionFieldsVisible && countryRef.current) { + countryRef.current?.focus() + } + }, [countryRef, isInstitutionFieldsVisible]) + useEffect(() => { if (university) { setIsUniversityDirty(true) @@ -194,7 +208,7 @@ function AddEmail() { - + @@ -211,7 +225,7 @@ function AddEmail() { {!ssoAvailable && ( <> - + {isInstitutionFieldsVisible ? ( <> @@ -219,6 +233,7 @@ function AddEmail() {
@@ -269,7 +284,7 @@ function AddEmail() { - + - {isError && ( -
- {' '} - {t('error_performing_request')} -
- )}
@@ -294,6 +303,11 @@ function AddEmail() { )} + {isError && ( + + {error.getUserFacingMessage()} + + )}
) } diff --git a/services/web/frontend/js/features/settings/components/emails/country-input.tsx b/services/web/frontend/js/features/settings/components/emails/country-input.tsx index 1c07869c1f..1c1dd05a3e 100644 --- a/services/web/frontend/js/features/settings/components/emails/country-input.tsx +++ b/services/web/frontend/js/features/settings/components/emails/country-input.tsx @@ -1,5 +1,5 @@ +import { useState, forwardRef } from 'react' import { useTranslation } from 'react-i18next' -import { useState } from 'react' import { useCombobox } from 'downshift' import classnames from 'classnames' import { defaults as countries } from '../../countries-list' @@ -7,11 +7,12 @@ import { CountryCode } from '../../../../../../types/country' type CountryInputProps = { setValue: React.Dispatch> + inputRef?: React.ForwardedRef } & React.InputHTMLAttributes const itemToString = (item: typeof countries[number] | null) => item?.name ?? '' -function CountryInput({ setValue }: CountryInputProps) { +function Downshift({ setValue, inputRef }: CountryInputProps) { const { t } = useTranslation() const [inputItems, setInputItems] = useState(() => countries) const [inputValue, setInputValue] = useState('') @@ -23,6 +24,7 @@ function CountryInput({ setValue }: CountryInputProps) { getInputProps, getComboboxProps, getItemProps, + highlightedIndex, openMenu, selectedItem, } = useCombobox({ @@ -66,6 +68,7 @@ function CountryInput({ setValue }: CountryInputProps) { openMenu() } }, + ref: inputRef, })} className="form-control" type="text" @@ -86,6 +89,8 @@ function CountryInput({ setValue }: CountryInputProps) {
@@ -99,4 +104,11 @@ function CountryInput({ setValue }: CountryInputProps) { ) } +const CountryInput = forwardRef< + HTMLInputElement, + Omit +>((props, ref) => ) + +CountryInput.displayName = 'CountryInput' + export default CountryInput diff --git a/services/web/frontend/js/features/settings/components/emails/downshift-input.tsx b/services/web/frontend/js/features/settings/components/emails/downshift-input.tsx index 0f29495f16..c18694cb27 100644 --- a/services/web/frontend/js/features/settings/components/emails/downshift-input.tsx +++ b/services/web/frontend/js/features/settings/components/emails/downshift-input.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react' +import { useState, useEffect, forwardRef } from 'react' import { useCombobox } from 'downshift' import classnames from 'classnames' @@ -7,6 +7,7 @@ type DownshiftInputProps = { inputValue: string label: string setValue: React.Dispatch> + inputRef?: React.ForwardedRef } & React.InputHTMLAttributes const filterItemsByInputValue = ( @@ -14,13 +15,14 @@ const filterItemsByInputValue = ( inputValue: DownshiftInputProps['inputValue'] ) => items.filter(item => item.toLowerCase().includes(inputValue.toLowerCase())) -function DownshiftInput({ +function Downshift({ items, inputValue, placeholder, label, setValue, disabled, + inputRef, }: DownshiftInputProps) { const [inputItems, setInputItems] = useState(items) @@ -35,6 +37,7 @@ function DownshiftInput({ getInputProps, getComboboxProps, getItemProps, + highlightedIndex, openMenu, selectedItem, } = useCombobox({ @@ -78,6 +81,7 @@ function DownshiftInput({ openMenu() } }, + ref: inputRef, })} className="form-control" type="text" @@ -98,6 +102,8 @@ function DownshiftInput({
@@ -111,4 +117,11 @@ function DownshiftInput({ ) } +const DownshiftInput = forwardRef< + HTMLInputElement, + Omit +>((props, ref) => ) + +DownshiftInput.displayName = 'DownshiftInput' + export default DownshiftInput diff --git a/services/web/frontend/js/features/settings/components/emails/institution-and-role.tsx b/services/web/frontend/js/features/settings/components/emails/institution-and-role.tsx index dc47b7b427..b6b920e952 100644 --- a/services/web/frontend/js/features/settings/components/emails/institution-and-role.tsx +++ b/services/web/frontend/js/features/settings/components/emails/institution-and-role.tsx @@ -1,11 +1,10 @@ -import { useState, useEffect } from 'react' +import { useState, useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' import { UserEmailData } from '../../../../../../types/user-email' import { Button } from 'react-bootstrap' import { isChangingAffiliation } from '../../utils/selectors' 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 { getJSON, postJSON } from '../../../../infrastructure/fetch-json' import { defaults as defaultRoles } from '../../roles' @@ -30,11 +29,22 @@ function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) { const [role, setRole] = useState(affiliation?.role || '') const [department, setDepartment] = useState(affiliation?.department || '') const [departments, setDepartments] = useState(defaultDepartments) + const roleRef = useRef(null) + const isChangingAffiliationInProgress = isChangingAffiliation( + state, + userEmailData.email + ) useEffect(() => { setUserEmailsContextLoading(isLoading) }, [setUserEmailsContextLoading, isLoading]) + useEffect(() => { + if (isChangingAffiliationInProgress && roleRef.current) { + roleRef.current?.focus() + } + }, [roleRef, isChangingAffiliationInProgress]) + const handleChangeAffiliation = () => { setEmailAffiliationBeingEdited(userEmailData.email) @@ -87,7 +97,7 @@ function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) { return ( <>
{affiliation.institution.name}
- {!isChangingAffiliation(state, userEmailData.email) ? ( + {!isChangingAffiliationInProgress ? (
{(affiliation.role || affiliation.department) && ( <> @@ -112,6 +122,7 @@ function InstitutionAndRole({ userEmailData }: InstitutionAndRoleProps) { placeholder={t('role')} label={t('role')} setValue={setRole} + ref={roleRef} /> {!isLoading && ( <> - {t('save_or_cancel-or')} + {t('save_or_cancel-or')}
{isError && ( - - {' '} - {t('error_performing_request')} - +
{t('generic_something_went_wrong')}
)} ) diff --git a/services/web/frontend/js/features/settings/components/emails/row.tsx b/services/web/frontend/js/features/settings/components/emails/row.tsx index 343db97e61..eb7b617f32 100644 --- a/services/web/frontend/js/features/settings/components/emails/row.tsx +++ b/services/web/frontend/js/features/settings/components/emails/row.tsx @@ -17,14 +17,14 @@ function EmailsRow({ userEmailData }: EmailsRowProps) { - + {userEmailData.affiliation?.institution && ( )} - + diff --git a/services/web/frontend/js/features/settings/context/user-email-context.tsx b/services/web/frontend/js/features/settings/context/user-email-context.tsx index 961ec2dc91..e42fe91f2a 100644 --- a/services/web/frontend/js/features/settings/context/user-email-context.tsx +++ b/services/web/frontend/js/features/settings/context/user-email-context.tsx @@ -196,7 +196,7 @@ const reducer = (state: State, action: Action) => { function useUserEmails() { const [state, unsafeDispatch] = useReducer(reducer, initialState) const dispatch = useSafeDispatch(unsafeDispatch) - const { data, isLoading, isError, runAsync } = useAsync() + const { data, isLoading, isError, isSuccess, runAsync } = useAsync() const getEmails = useCallback(() => { runAsync(getJSON('/user/emails?ensureAffiliation=true')) @@ -214,6 +214,7 @@ function useUserEmails() { return { state, isInitializing: isLoading && !data, + isInitializingSuccess: isSuccess, isInitializingError: isError, getEmails, setLoading: useCallback( diff --git a/services/web/frontend/stylesheets/vendor/select/select.css b/services/web/frontend/stylesheets/vendor/select/select.css index 0e713bb1b9..5298519bc3 100755 --- a/services/web/frontend/stylesheets/vendor/select/select.css +++ b/services/web/frontend/stylesheets/vendor/select/select.css @@ -26,7 +26,8 @@ } -.ui-select-choices-row:hover { +.ui-select-choices-row:hover, +.ui-select-choices-row--highlighted { background-color: #f5f5f5; } @@ -34,19 +35,19 @@ /* Mark invalid Select2 */ .ng-dirty.ng-invalid > a.select2-choice { - border-color: #D44950; + border-color: #D44950; } .select2-result-single { padding-left: 0; } -.select2-locked > .select2-search-choice-close{ - display:none; +.select2-locked > .select2-search-choice-close { + display: none; } -.select-locked > .ui-select-match-close{ - display:none; +.select-locked > .ui-select-match-close { + display: none; } body > .select2-container.open { @@ -56,46 +57,49 @@ body > .select2-container.open { /* Handle up direction Select2 */ .ui-select-container[theme="select2"].direction-up .ui-select-match, .ui-select-container.select2.direction-up .ui-select-match { - border-radius: 4px; /* FIXME hardcoded value :-/ */ - border-top-left-radius: 0; - border-top-right-radius: 0; + border-radius: 4px; /* FIXME hardcoded value :-/ */ + border-top-left-radius: 0; + border-top-right-radius: 0; } + .ui-select-container[theme="select2"].direction-up .ui-select-dropdown, .ui-select-container.select2.direction-up .ui-select-dropdown { - border-radius: 4px; /* FIXME hardcoded value :-/ */ - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; + border-radius: 4px; /* FIXME hardcoded value :-/ */ + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; - border-top-width: 1px; /* FIXME hardcoded value :-/ */ - border-top-style: solid; + border-top-width: 1px; /* FIXME hardcoded value :-/ */ + border-top-style: solid; - box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25); + box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25); - margin-top: -4px; /* FIXME hardcoded value :-/ */ + margin-top: -4px; /* FIXME hardcoded value :-/ */ } + .ui-select-container[theme="select2"].direction-up .ui-select-dropdown .select2-search, .ui-select-container.select2.direction-up .ui-select-dropdown .select2-search { - margin-top: 4px; /* FIXME hardcoded value :-/ */ + margin-top: 4px; /* FIXME hardcoded value :-/ */ } + .ui-select-container[theme="select2"].direction-up.select2-dropdown-open .ui-select-match, .ui-select-container.select2.direction-up.select2-dropdown-open .ui-select-match { - border-bottom-color: #5897fb; + border-bottom-color: #5897fb; } .ui-select-container[theme="select2"] .ui-select-dropdown .ui-select-search-hidden, -.ui-select-container[theme="select2"] .ui-select-dropdown .ui-select-search-hidden input{ - opacity: 0; - height: 0; - min-height: 0; - padding: 0; - margin: 0; - border:0; +.ui-select-container[theme="select2"] .ui-select-dropdown .ui-select-search-hidden input { + opacity: 0; + height: 0; + min-height: 0; + padding: 0; + margin: 0; + border: 0; } /* Selectize theme */ /* Helper class to show styles when focus */ -.selectize-input.selectize-focus{ +.selectize-input.selectize-focus { border-color: #007FBB !important; } @@ -116,23 +120,23 @@ body > .select2-container.open { /* Mark invalid Selectize */ .ng-dirty.ng-invalid > div.selectize-input { - border-color: #D44950; + border-color: #D44950; } /* Handle up direction Selectize */ .ui-select-container[theme="selectize"].direction-up .ui-select-dropdown { - box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25); - margin-top: -2px; /* FIXME hardcoded value :-/ */ + box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25); + margin-top: -2px; /* FIXME hardcoded value :-/ */ } -.ui-select-container[theme="selectize"] input.ui-select-search-hidden{ - opacity: 0; - height: 0; - min-height: 0; - padding: 0; - margin: 0; - border:0; - width: 0; +.ui-select-container[theme="selectize"] input.ui-select-search-hidden { + opacity: 0; + height: 0; + min-height: 0; + padding: 0; + margin: 0; + border: 0; + width: 0; } /* Bootstrap theme */ @@ -171,22 +175,23 @@ body > .select2-container.open { border-top-right-radius: 0; border-bottom-right-radius: 0; } + .input-group > .ui-select-bootstrap > input.ui-select-search.form-control.direction-up { border-radius: 4px !important; /* FIXME hardcoded value :-/ */ border-top-right-radius: 0 !important; border-bottom-right-radius: 0 !important; } -.ui-select-bootstrap .ui-select-search-hidden{ - opacity: 0; - height: 0; - min-height: 0; - padding: 0; - margin: 0; - border:0; +.ui-select-bootstrap .ui-select-search-hidden { + opacity: 0; + height: 0; + min-height: 0; + padding: 0; + margin: 0; + border: 0; } -.ui-select-bootstrap > .ui-select-match > .btn{ +.ui-select-bootstrap > .ui-select-match > .btn { /* Instead of center because of .btn */ text-align: left !important; } @@ -198,7 +203,7 @@ body > .select2-container.open { } /* See Scrollable Menu with Bootstrap 3 http://stackoverflow.com/questions/19227496 */ -.ui-select-bootstrap > .ui-select-choices ,.ui-select-bootstrap > .ui-select-no-choice { +.ui-select-bootstrap > .ui-select-choices, .ui-select-bootstrap > .ui-select-no-choice { width: 100%; height: auto; max-height: 200px; @@ -261,62 +266,64 @@ body > .ui-select-bootstrap.open { border-right: 1px solid #428bca; } -.ui-select-bootstrap .ui-select-choices-row>span { - cursor: pointer; - display: block; - padding: 3px 20px; - clear: both; - font-weight: 400; - line-height: 1.42857143; - color: #333; - white-space: nowrap; +.ui-select-bootstrap .ui-select-choices-row > span { + cursor: pointer; + display: block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #333; + white-space: nowrap; } -.ui-select-bootstrap .ui-select-choices-row>span:hover, .ui-select-bootstrap .ui-select-choices-row>span:focus { - text-decoration: none; - color: #262626; - background-color: #f5f5f5; +.ui-select-bootstrap .ui-select-choices-row > span:hover, .ui-select-bootstrap .ui-select-choices-row > span:focus { + text-decoration: none; + color: #262626; + background-color: #f5f5f5; } -.ui-select-bootstrap .ui-select-choices-row.active>span { - color: #fff; - text-decoration: none; - outline: 0; - background-color: #428bca; +.ui-select-bootstrap .ui-select-choices-row.active > span { + color: #fff; + text-decoration: none; + outline: 0; + background-color: #428bca; } -.ui-select-bootstrap .ui-select-choices-row.disabled>span, -.ui-select-bootstrap .ui-select-choices-row.active.disabled>span { - color: #777; - cursor: not-allowed; - background-color: #fff; +.ui-select-bootstrap .ui-select-choices-row.disabled > span, +.ui-select-bootstrap .ui-select-choices-row.active.disabled > span { + color: #777; + cursor: not-allowed; + background-color: #fff; } /* fix hide/show angular animation */ .ui-select-match.ng-hide-add, .ui-select-search.ng-hide-add { - display: none !important; + display: none !important; } /* Mark invalid Bootstrap */ .ui-select-bootstrap.ng-dirty.ng-invalid > button.btn.ui-select-match { - border-color: #D44950; + border-color: #D44950; } /* Handle up direction Bootstrap */ .ui-select-container[theme="bootstrap"].direction-up .ui-select-dropdown { - box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25); + box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25); } .ui-select-bootstrap .ui-select-match-text { - width: 100%; - padding-right: 1em; + width: 100%; + padding-right: 1em; } + .ui-select-bootstrap .ui-select-match-text span { - display: inline-block; - width: 100%; - overflow: hidden; + display: inline-block; + width: 100%; + overflow: hidden; } + .ui-select-bootstrap .ui-select-toggle > a.btn { position: absolute; height: 10px; @@ -326,10 +333,10 @@ body > .ui-select-bootstrap.open { /* Spinner */ .ui-select-refreshing.glyphicon { - position: absolute; - right: 0; - padding: 8px 27px; - } + position: absolute; + right: 0; + padding: 8px 27px; +} @-webkit-keyframes ui-select-spin { 0% { @@ -341,6 +348,7 @@ body > .ui-select-bootstrap.open { transform: rotate(359deg); } } + @keyframes ui-select-spin { 0% { -webkit-transform: rotate(0deg); diff --git a/services/web/test/frontend/features/settings/components/emails/emails-section-actions.test.tsx b/services/web/test/frontend/features/settings/components/emails/emails-section-actions.test.tsx index b7c4e5c79b..6a88d52ddc 100644 --- a/services/web/test/frontend/features/settings/components/emails/emails-section-actions.test.tsx +++ b/services/web/test/frontend/features/settings/components/emails/emails-section-actions.test.tsx @@ -65,7 +65,7 @@ describe('email actions - make primary', function () { screen.getByRole('button', { name: /sending/i }) ) - screen.getByText(/an error has occurred while performing your request/i) + screen.getByText(/sorry, something went wrong/i) screen.getByRole('button', { name: /make primary/i }) }) }) @@ -114,7 +114,7 @@ describe('email actions - delete', function () { screen.getByRole('button', { name: /deleting/i }) ) - screen.getByText(/an error has occurred while performing your request/i) + screen.getByText(/sorry, something went wrong/i) screen.getByRole('button', { name: /remove/i }) }) }) diff --git a/services/web/test/frontend/features/settings/components/emails/emails-section-add-new-email.test.tsx b/services/web/test/frontend/features/settings/components/emails/emails-section-add-new-email.test.tsx index f1371d9ff0..b906f4947a 100644 --- a/services/web/test/frontend/features/settings/components/emails/emails-section-add-new-email.test.tsx +++ b/services/web/test/frontend/features/settings/components/emails/emails-section-add-new-email.test.tsx @@ -60,6 +60,7 @@ describe('', function () { hasSamlFeature: true, samlInitPath: 'saml/init', }) + fetchMock.reset() }) afterEach(function () { @@ -67,10 +68,12 @@ describe('', function () { resetFetchMock() }) - it('renders "add another email" button', function () { + it('renders "add another email" button', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', []) render() + await fetchMock.flush(true) + screen.getByRole('button', { name: /add another email/i }) }) @@ -146,7 +149,7 @@ describe('', function () { resetFetchMock() fetchMock .get('/user/emails?ensureAffiliation=true', []) - .post('/user/emails', 500) + .post('/user/emails', 400) const addAnotherEmailBtn = screen.getByRole('button', { name: /add another email/i, @@ -170,18 +173,20 @@ describe('', function () { expect(submitBtn.disabled).to.be.true await screen.findByText( - /an error has occurred while performing your request/i + /Invalid Request. Please correct the data and try again./i ) expect(submitBtn).to.not.be.null expect(submitBtn.disabled).to.be.false }) it('can link email address to an existing SSO institution', async function () { - fetchMock.reset() fetchMock.get('/user/emails?ensureAffiliation=true', []) - fetchMock.get('express:/institutions/domains', institutionDomainData) render() + await fetchMock.flush(true) + fetchMock.reset() + fetchMock.get('express:/institutions/domains', institutionDomainData) + await userEvent.click( screen.getByRole('button', { name: /add another email/i, diff --git a/services/web/test/frontend/features/settings/components/emails/emails-section.test.tsx b/services/web/test/frontend/features/settings/components/emails/emails-section.test.tsx index b0dcbc01fb..fd4786a48b 100644 --- a/services/web/test/frontend/features/settings/components/emails/emails-section.test.tsx +++ b/services/web/test/frontend/features/settings/components/emails/emails-section.test.tsx @@ -198,7 +198,7 @@ describe('', function () { await waitForElementToBeRemoved(() => screen.getByText(/sending/i)) - screen.getByText(/an error has occurred while performing your request/i) + screen.getByText(/sorry, something went wrong/i) screen.getByRole('button', { name: /resend confirmation email/i }) }) })