Ver Fonte

Replace placeholders with labels (#26811)

* Replace placeholders for labels

* Remove redundant aria-label and update labels with `.visually-hidden`

* Change "Invite more members" to a label

* Fix helper text for group members and fix responsiveness

* Change error message to an error notification

* Use label and helper text instead of a placeholder

* Remove redundant label and use the placeholder text instead for the admin user searchbar

* Extract translations

GitOrigin-RevId: a504fda9779da82920b57c7b8aad38a8b027d09a
Rebeka Dekany há 1 ano atrás
pai
commit
74533fc401
47 ficheiros alterados com 244 adições e 256 exclusões
  1. 1 1
      server-ce/test/filestore-migration.spec.ts
  2. 4 2
      server-ce/test/helpers/project.ts
  3. 3 1
      server-ce/test/history.spec.ts
  4. 6 12
      services/web/app/views/admin/index.pug
  5. 6 4
      services/web/app/views/user/login.pug
  6. 0 1
      services/web/app/views/user/passwordReset.pug
  7. 1 3
      services/web/app/views/user/reconfirm.pug
  8. 4 4
      services/web/frontend/extracted-translations.json
  9. 1 1
      services/web/frontend/js/features/chat/components/message-input.tsx
  10. 0 1
      services/web/frontend/js/features/clone-project-modal/components/clone-project-modal-content.tsx
  11. 15 9
      services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-create-folder.tsx
  12. 22 24
      services/web/frontend/js/features/group-management/components/group-members.tsx
  13. 14 12
      services/web/frontend/js/features/group-management/components/managers-table.tsx
  14. 3 2
      services/web/frontend/js/features/history/components/change-list/add-label-modal.tsx
  15. 4 3
      services/web/frontend/js/features/project-list/components/modals/edit-tag-modal.tsx
  16. 4 3
      services/web/frontend/js/features/project-list/components/modals/manage-tag-modal.tsx
  17. 0 1
      services/web/frontend/js/features/project-list/components/modals/rename-project-modal.tsx
  18. 11 7
      services/web/frontend/js/features/project-list/components/new-project-button/modal-content-new-project-form.tsx
  19. 0 2
      services/web/frontend/js/features/settings/components/leave/modal-form.tsx
  20. 4 2
      services/web/frontend/js/features/share-project-modal/components/add-collaborators.tsx
  21. 7 12
      services/web/frontend/js/features/share-project-modal/components/select-collaborators.tsx
  22. 0 1
      services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-url-source.tsx
  23. 18 63
      services/web/frontend/stories/ui/form/form-input.stories.tsx
  24. 0 4
      services/web/frontend/stylesheets/pages/editor/tags-input.scss
  25. 10 7
      services/web/locales/en.json
  26. 27 16
      services/web/modules/user-activate/frontend/js/components/register-form.jsx
  27. 13 6
      services/web/modules/user-activate/frontend/js/components/user-activate-register.jsx
  28. 2 2
      services/web/modules/user-activate/test/frontend/js/components/register-form.test.jsx
  29. 5 5
      services/web/modules/user-activate/test/frontend/js/components/user-activate-register.test.jsx
  30. 3 3
      services/web/test/frontend/components/editor-left-menu/editor-left-menu.spec.tsx
  31. 1 1
      services/web/test/frontend/features/chat/components/message-input.test.tsx
  32. 1 1
      services/web/test/frontend/features/clone-project-modal/components/clone-project-modal.test.tsx
  33. 2 2
      services/web/test/frontend/features/editor-left-menu/components/actions-copy-project.test.tsx
  34. 8 4
      services/web/test/frontend/features/group-management/components/group-managers.spec.tsx
  35. 4 4
      services/web/test/frontend/features/group-management/components/group-members.spec.tsx
  36. 8 4
      services/web/test/frontend/features/group-management/components/institution-managers.spec.tsx
  37. 2 2
      services/web/test/frontend/features/group-management/components/managed-group-members.spec.tsx
  38. 8 4
      services/web/test/frontend/features/group-management/components/publisher-managers.spec.tsx
  39. 1 1
      services/web/test/frontend/features/history/components/change-list.spec.tsx
  40. 2 2
      services/web/test/frontend/features/project-list/components/new-project-button.test.tsx
  41. 4 4
      services/web/test/frontend/features/project-list/components/new-project-button/modal-content-new-project-form.test.tsx
  42. 4 2
      services/web/test/frontend/features/project-list/components/project-list-root.test.tsx
  43. 1 1
      services/web/test/frontend/features/project-list/components/sidebar/tags-list.test.tsx
  44. 1 1
      services/web/test/frontend/features/project-list/components/table/cells/action-buttons/copy-project-button.test.tsx
  45. 1 1
      services/web/test/frontend/features/project-list/components/table/cells/action-buttons/rename-project-button.test.tsx
  46. 2 2
      services/web/test/frontend/features/project-list/components/table/project-tools/rename-project-modal.test.tsx
  47. 6 6
      services/web/test/frontend/features/share-project-modal/components/share-project-modal.test.tsx

+ 1 - 1
server-ce/test/filestore-migration.spec.ts

@@ -106,7 +106,7 @@ describe('filestore migration', function () {
         name: /Create First Project|New Project/,
       }).click()
       cy.findByRole('link', { name: 'Example Project' }).click()
-      cy.findByPlaceholderText('Project Name').type(projectName)
+      cy.findByLabelText(/Project name/i).type(projectName)
       cy.findByRole('button', { name: 'Create' }).click()
       cy.url()
         .should('match', /\/project\/[a-fA-F0-9]{24}/)

+ 4 - 2
server-ce/test/helpers/project.ts

@@ -106,8 +106,10 @@ function shareProjectByEmail(
   openProjectByName(projectName)
   cy.findByText('Share').click()
   cy.findByRole('dialog').within(() => {
-    cy.findByLabelText('Add people', { selector: 'input' }).type(`${email},`)
-    cy.findByLabelText('Add people', { selector: 'input' })
+    cy.findByLabelText('Add email address', { selector: 'input' }).type(
+      `${email},`
+    )
+    cy.findByLabelText('Add email address', { selector: 'input' })
       .parents('form')
       .within(() => {
         cy.findByTestId('add-collaborator-select')

+ 3 - 1
server-ce/test/history.spec.ts

@@ -21,7 +21,9 @@ describe('History', function () {
         cy.findByText('Label this version').click()
       })
     cy.findByRole('dialog').within(() => {
-      cy.get('input[placeholder="New label name"]').type(`${name}{enter}`)
+      cy.findByLabelText(/New label name/i)
+        .as('input')
+        .type(`${name}{enter}`)
     })
   }
 

+ 6 - 12
services/web/app/views/admin/index.pug

@@ -28,11 +28,10 @@ block content
 										form(method='post' action='/admin/messages')
 											input(name='_csrf' type='hidden' value=csrfToken)
 											.form-group
-												label.form-label(for='content')
-												input.form-control(
+												label.form-label(for='system-message') Message
+												input#system-message.form-control(
 													name='content'
 													type='text'
-													placeholder='Message…'
 													required
 												)
 											button.btn.btn-primary(type='submit') Post Message
@@ -79,11 +78,11 @@ block content
 												form.col-xs-6(method='post' action='/admin/flushProjectToTpds')
 													input(name='_csrf' type='hidden' value=csrfToken)
 													.form-group
-														label.form-label(for='project_id') project_id
+														label.form-label(for='project-id') project_id
 														input.form-control(
 															name='project_id'
+															id='project-id'
 															type='text'
-															placeholder='project_id'
 															required
 														)
 													.form-group
@@ -94,12 +93,7 @@ block content
 												form.col-xs-6(method='post' action='/admin/pollDropboxForUser')
 													input(name='_csrf' type='hidden' value=csrfToken)
 													.form-group
-														label.form-label(for='user_id') user_id
-														input.form-control(
-															name='user_id'
-															type='text'
-															placeholder='user_id'
-															required
-														)
+														label.form-label(for='user-id') user_id
+														input.form-control(name='user_id' id='user-id' type='text' required)
 													.form-group
 														button.btn-primary.btn(type='submit') Poll

+ 6 - 4
services/web/app/views/user/login.pug

@@ -22,19 +22,21 @@ block content
 								+customFormMessageNewStyle('password-compromised')
 									| !{translate('password_compromised_try_again_or_use_known_device_or_reset', {}, [{name: 'a', attrs: {href: 'https://haveibeenpwned.com/passwords', rel: 'noopener noreferrer', target: '_blank'}}, {name: 'a', attrs: {href: '/user/password/reset', target: '_blank'}}])}.
 								.form-group
-									input.form-control(
+									label(for='email') #{translate("email")}
+									input#email.form-control(
 										name='email'
 										type='email'
 										required
-										placeholder='email@example.com'
 										autofocus='true'
+										autocomplete='username'
 									)
 								.form-group
-									input.form-control(
+									label(for='password') #{translate("password")}
+									input#password.form-control(
 										name='password'
 										type='password'
+										autocomplete='current-password'
 										required
-										placeholder='********'
 									)
 								.actions
 									button.btn-primary.btn(type='submit' data-ol-disabled-inflight)

+ 0 - 1
services/web/app/views/user/passwordReset.pug

@@ -56,7 +56,6 @@ block content
 						label.form-label(for='email') #{translate("email")}
 						input#email.form-control(
 							name='email'
-							aria-label='email'
 							type='email'
 							required
 							autocomplete='username'

+ 1 - 3
services/web/app/views/user/reconfirm.pug

@@ -45,11 +45,9 @@ block content
 					input(name='_csrf' type='hidden' value=csrfToken)
 					.form-group.mb-3
 						label.form-label(for='email') #{translate("please_enter_email")}
-						input.form-control(
+						input#email.form-control(
 							name='email'
-							aria-label='email'
 							type='email'
-							placeholder='email@example.com'
 							required
 							autofocus
 							value=email

+ 4 - 4
services/web/frontend/extracted-translations.json

@@ -82,12 +82,11 @@
   "add_files": "",
   "add_more_collaborators": "",
   "add_more_licenses_to_my_plan": "",
-  "add_more_managers": "",
+  "add_more_manager_emails": "",
   "add_new_email": "",
   "add_on": "",
   "add_ons": "",
   "add_or_remove_project_from_tag": "",
-  "add_people": "",
   "add_role_and_department": "",
   "add_to_dictionary": "",
   "add_to_tag": "",
@@ -491,6 +490,7 @@
   "edit_figure": "",
   "edit_sso_configuration": "",
   "edit_tag": "",
+  "edit_tag_name": "",
   "edit_your_custom_dictionary": "",
   "editing": "",
   "editing_captions": "",
@@ -536,7 +536,6 @@
   "end_of_document": "",
   "ensure_recover_account": "",
   "enter_any_size_including_units_or_valid_latex_command": "",
-  "enter_image_url": "",
   "enter_the_code": "",
   "enter_the_confirmation_code": "",
   "enter_the_number_of_licenses_youd_like_to_add_to_see_the_cost_breakdown": "",
@@ -604,6 +603,7 @@
   "fixed_width_wrap_text": "",
   "fold_line": "",
   "folder_location": "",
+  "folder_name": "",
   "following_paths_conflict": "",
   "font_family": "",
   "font_size": "",
@@ -1019,6 +1019,7 @@
   "manage_publisher_managers": "",
   "manage_sessions": "",
   "manage_subscription": "",
+  "manage_tag": "",
   "manage_your_ai_assist_add_on": "",
   "managed": "",
   "managed_user_accounts": "",
@@ -1096,7 +1097,6 @@
   "new_navigation_introducing_left_hand_side_rail_and_top_menus": "",
   "new_password": "",
   "new_project": "",
-  "new_project_name": "",
   "new_subscription_will_be_billed_immediately": "",
   "new_tag": "",
   "new_tag_name": "",

+ 1 - 1
services/web/frontend/js/features/chat/components/message-input.tsx

@@ -27,7 +27,7 @@ function MessageInput({ resetUnreadMessages, sendMessage }: MessageInputProps) {
   return (
     <form className="new-message">
       <label htmlFor="chat-input" className="visually-hidden">
-        {t('your_message_to_collaborators')}
+        {`${t('your_message_to_collaborators')}…`}
       </label>
       <textarea
         id="chat-input"

+ 0 - 1
services/web/frontend/js/features/clone-project-modal/components/clone-project-modal-content.tsx

@@ -99,7 +99,6 @@ export default function CloneProjectModalContent({
             <OLFormLabel>{t('new_name')}</OLFormLabel>
             <OLFormControl
               type="text"
-              placeholder={t('new_project_name')}
               required
               value={clonedProjectName}
               onChange={event => setClonedProjectName(event.target.value)}

+ 15 - 9
services/web/frontend/js/features/file-tree/components/modals/file-tree-modal-create-folder.tsx

@@ -11,6 +11,9 @@ import OLModal, {
   OLModalTitle,
 } from '@/shared/components/ol/ol-modal'
 import OLButton from '@/shared/components/ol/ol-button'
+import OLFormControl from '@/shared/components/ol/ol-form-control'
+import OLFormLabel from '@/shared/components/ol/ol-form-label'
+import OLFormGroup from '@/shared/components/ol/ol-form-group'
 
 function FileTreeModalCreateFolder() {
   const { t } = useTranslation()
@@ -116,6 +119,7 @@ function InputName({
   handleCreateFolder: () => void
 }) {
   const { autoFocusedRef } = useRefWithAutoFocus<HTMLInputElement>()
+  const { t } = useTranslation()
 
   function handleFocus(ev: React.FocusEvent<HTMLInputElement>) {
     ev.target.setSelectionRange(0, -1)
@@ -133,15 +137,17 @@ function InputName({
   }
 
   return (
-    <input
-      ref={autoFocusedRef}
-      className="form-control"
-      type="text"
-      value={name}
-      onKeyDown={handleKeyDown}
-      onChange={handleChange}
-      onFocus={handleFocus}
-    />
+    <OLFormGroup controlId="folder-name">
+      <OLFormLabel>{t('folder_name')}</OLFormLabel>
+      <OLFormControl
+        type="text"
+        ref={autoFocusedRef}
+        value={name}
+        onKeyDown={handleKeyDown}
+        onChange={handleChange}
+        onFocus={handleFocus}
+      />
+    </OLFormGroup>
   )
 }
 

+ 22 - 24
services/web/frontend/js/features/group-management/components/group-members.tsx

@@ -14,6 +14,7 @@ import OLButton from '@/shared/components/ol/ol-button'
 import OLFormControl from '@/shared/components/ol/ol-form-control'
 import OLFormText from '@/shared/components/ol/ol-form-text'
 import OLNotification from '@/shared/components/ol/ol-notification'
+import OLFormLabel from '@/shared/components/ol/ol-form-label'
 
 export default function GroupMembers() {
   const { isReady } = useWaitForI18n()
@@ -142,7 +143,6 @@ export default function GroupMembers() {
                 className="add-more-members-form"
                 data-testid="add-more-members-form"
               >
-                <p className="small">{t('invite_more_members')}</p>
                 {memberAdded && (
                   <OLNotification
                     content={t('members_added')}
@@ -152,37 +152,35 @@ export default function GroupMembers() {
                 )}
                 <ErrorAlert error={inviteError} />
                 <form onSubmit={onAddMembersSubmit}>
-                  <OLRow>
-                    <OLCol xs={6}>
+                  <OLRow className="align-items-center">
+                    <OLCol lg={8}>
+                      <OLFormLabel htmlFor="add-members-emails">
+                        {t('invite_more_members')}
+                      </OLFormLabel>
                       <OLFormControl
+                        id="add-members-emails"
                         type="input"
-                        placeholder="jane@example.com, joe@example.com"
-                        aria-describedby="add-members-description"
                         value={emailString}
                         onChange={handleEmailsChange}
-                        data-testid="add-members-input"
+                        aria-describedby="invite-more-members-help-text"
                       />
-                    </OLCol>
-                    <OLCol xs={4}>
-                      <OLButton
-                        variant="primary"
-                        onClick={onAddMembersSubmit}
-                        isLoading={inviteMemberLoading}
-                        loadingLabel={t('inviting')}
-                      >
-                        {t('invite')}
-                      </OLButton>
-                    </OLCol>
-                    <OLCol xs={2}>
-                      <a href={paths.exportMembers}>{t('export_csv')}</a>
-                    </OLCol>
-                  </OLRow>
-                  <OLRow>
-                    <OLCol xs={8}>
-                      <OLFormText>
+                      <OLFormText id="invite-more-members-help-text">
                         {t('add_comma_separated_emails_help')}
                       </OLFormText>
                     </OLCol>
+                    <OLCol lg={4} className="mt-3 mt-lg-0">
+                      <div className="align-items-center d-flex flex-column flex-lg-row gap-3 text-center">
+                        <OLButton
+                          variant="primary"
+                          onClick={onAddMembersSubmit}
+                          isLoading={inviteMemberLoading}
+                          loadingLabel={t('inviting')}
+                        >
+                          {t('invite')}
+                        </OLButton>
+                        <a href={paths.exportMembers}>{t('export_csv')}</a>
+                      </div>
+                    </OLCol>
                   </OLRow>
                 </form>
               </div>

+ 14 - 12
services/web/frontend/js/features/group-management/components/managers-table.tsx

@@ -18,6 +18,7 @@ import OLFormText from '@/shared/components/ol/ol-form-text'
 import OLTable from '@/shared/components/ol/ol-table'
 import OLTooltip from '@/shared/components/ol/ol-tooltip'
 import OLFormCheckbox from '@/shared/components/ol/ol-form-checkbox'
+import OLFormLabel from '@/shared/components/ol/ol-form-label'
 
 type ManagersPaths = {
   addMember: string
@@ -238,20 +239,28 @@ export function ManagersTable({
               <>
                 <hr />
                 <div>
-                  <p className="small">{t('add_more_managers')}</p>
                   <ErrorAlert error={inviteError} />
                   <form onSubmit={addManagers} data-testid="add-members-form">
                     <OLRow>
-                      <OLCol xs={6}>
+                      <OLCol lg={8}>
+                        <OLFormLabel htmlFor="add-manager-emails">
+                          {t('add_more_manager_emails')}
+                        </OLFormLabel>
                         <OLFormControl
+                          id="add-manager-emails"
                           type="input"
-                          placeholder="jane@example.com, joe@example.com"
-                          aria-describedby="add-members-description"
                           value={emailString}
                           onChange={handleEmailsChange}
+                          aria-describedby="invite-more-manager-help-text"
                         />
+                        <OLFormText id="invite-more-manager-help-text">
+                          {t('add_comma_separated_emails_help')}
+                        </OLFormText>
                       </OLCol>
-                      <OLCol xs={4}>
+                      <OLCol
+                        lg={2}
+                        className="mt-3 mt-lg-0 d-flex align-items-center d-flex flex-column flex-lg-row"
+                      >
                         <OLButton
                           variant="primary"
                           onClick={addManagers}
@@ -261,13 +270,6 @@ export function ManagersTable({
                         </OLButton>
                       </OLCol>
                     </OLRow>
-                    <OLRow>
-                      <OLCol xs={8}>
-                        <OLFormText>
-                          {t('add_comma_separated_emails_help')}
-                        </OLFormText>
-                      </OLCol>
-                    </OLRow>
                   </form>
                 </div>
               </>

+ 3 - 2
services/web/frontend/js/features/history/components/change-list/add-label-modal.tsx

@@ -19,6 +19,7 @@ import { Label } from '../../services/types/label'
 import { useRefWithAutoFocus } from '../../../../shared/hooks/use-ref-with-auto-focus'
 import { debugConsole } from '@/utils/debugging'
 import OLFormControl from '@/shared/components/ol/ol-form-control'
+import OLFormLabel from '@/shared/components/ol/ol-form-label'
 
 type AddLabelModalProps = {
   show: boolean
@@ -91,11 +92,11 @@ function AddLabelModal({ show, setShow, version }: AddLabelModalProps) {
       <OLForm onSubmit={handleSubmit}>
         <OLModalBody>
           {isError && <ModalError error={responseError} />}
-          <OLFormGroup>
+          <OLFormGroup controlId="add-label-modal">
+            <OLFormLabel>{t('history_new_label_name')}</OLFormLabel>
             <OLFormControl
               ref={autoFocusedRef}
               type="text"
-              placeholder={t('history_new_label_name')}
               required
               value={comment}
               onChange={(e: React.ChangeEvent<HTMLInputElement>) =>

+ 4 - 3
services/web/frontend/js/features/project-list/components/modals/edit-tag-modal.tsx

@@ -20,6 +20,7 @@ import OLFormGroup from '@/shared/components/ol/ol-form-group'
 import OLFormLabel from '@/shared/components/ol/ol-form-label'
 import OLButton from '@/shared/components/ol/ol-button'
 import Notification from '@/shared/components/notification'
+import OLFormControl from '@/shared/components/ol/ol-form-control'
 
 type EditTagModalProps = {
   id: string
@@ -93,12 +94,12 @@ export function EditTagModal({ id, tag, onEdit, onClose }: EditTagModalProps) {
 
       <OLModalBody>
         <OLForm onSubmit={handleSubmit}>
-          <OLFormGroup>
-            <input
+          <OLFormGroup controlId="edit-tag-modal">
+            <OLFormLabel>{t('edit_tag_name')}</OLFormLabel>
+            <OLFormControl
               ref={autoFocusedRef}
               className="form-control"
               type="text"
-              placeholder="Tag Name"
               name="new-tag-name"
               value={newTagName === undefined ? (tag.name ?? '') : newTagName}
               required

+ 4 - 3
services/web/frontend/js/features/project-list/components/modals/manage-tag-modal.tsx

@@ -19,6 +19,7 @@ import OLFormGroup from '@/shared/components/ol/ol-form-group'
 import OLFormLabel from '@/shared/components/ol/ol-form-label'
 import OLButton from '@/shared/components/ol/ol-button'
 import Notification from '@/shared/components/notification'
+import OLFormControl from '@/shared/components/ol/ol-form-control'
 
 type ManageTagModalProps = {
   id: string
@@ -94,12 +95,12 @@ export function ManageTagModal({
 
       <OLModalBody>
         <OLForm onSubmit={handleSubmit}>
-          <OLFormGroup>
-            <input
+          <OLFormGroup controlId="manage-tag-modal">
+            <OLFormLabel>{t('manage_tag')}</OLFormLabel>
+            <OLFormControl
               ref={autoFocusedRef}
               className="form-control"
               type="text"
-              placeholder="Tag Name"
               name="new-tag-name"
               value={newTagName === undefined ? (tag.name ?? '') : newTagName}
               required

+ 0 - 1
services/web/frontend/js/features/project-list/components/modals/rename-project-modal.tsx

@@ -121,7 +121,6 @@ function RenameProjectModal({
             <OLFormLabel>{t('new_name')}</OLFormLabel>
             <OLFormControl
               type="text"
-              placeholder={t('project_name')}
               required
               value={newProjectName}
               onChange={handleOnChange}

+ 11 - 7
services/web/frontend/js/features/project-list/components/new-project-button/modal-content-new-project-form.tsx

@@ -17,6 +17,8 @@ import {
 import OLFormControl from '@/shared/components/ol/ol-form-control'
 import OLButton from '@/shared/components/ol/ol-button'
 import OLForm from '@/shared/components/ol/ol-form'
+import OLFormLabel from '@/shared/components/ol/ol-form-label'
+import OLFormGroup from '@/shared/components/ol/ol-form-group'
 
 type NewProjectData = {
   project_id: string
@@ -86,13 +88,15 @@ function ModalContentNewProjectForm({ onCancel, template = 'none' }: Props) {
           </div>
         )}
         <OLForm onSubmit={handleSubmit}>
-          <OLFormControl
-            type="text"
-            ref={autoFocusedRef}
-            placeholder={t('project_name')}
-            onChange={handleChangeName}
-            value={projectName}
-          />
+          <OLFormGroup controlId="project-name">
+            <OLFormLabel>{t('project_name')}</OLFormLabel>
+            <OLFormControl
+              type="text"
+              ref={autoFocusedRef}
+              onChange={handleChangeName}
+              value={projectName}
+            />
+          </OLFormGroup>
         </OLForm>
       </OLModalBody>
 

+ 0 - 2
services/web/frontend/js/features/settings/components/leave/modal-form.tsx

@@ -77,7 +77,6 @@ function LeaveModalForm({
         <OLFormLabel>{t('email')}</OLFormLabel>
         <OLFormControl
           type="text"
-          placeholder={t('email')}
           required
           value={email}
           onChange={handleEmailChange}
@@ -87,7 +86,6 @@ function LeaveModalForm({
         <OLFormLabel>{t('password')}</OLFormLabel>
         <OLFormControl
           type="password"
-          placeholder={t('password')}
           required
           value={password}
           onChange={handlePasswordChange}

+ 4 - 2
services/web/frontend/js/features/share-project-modal/components/add-collaborators.tsx

@@ -14,6 +14,7 @@ import OLFormGroup from '@/shared/components/ol/ol-form-group'
 import { Select } from '@/shared/components/select'
 import OLButton from '@/shared/components/ol/ol-button'
 import { PermissionsLevel } from '@/features/ide-react/types/permissions'
+import OLFormText from '@/shared/components/ol/ol-form-text'
 
 export default function AddCollaborators({ readOnly }: { readOnly?: boolean }) {
   const [privileges, setPrivileges] = useState<PermissionsLevel>('readAndWrite')
@@ -211,11 +212,12 @@ export default function AddCollaborators({ readOnly }: { readOnly?: boolean }) {
         <SelectCollaborators
           loading={!nonMemberContacts}
           options={nonMemberContacts || []}
-          placeholder="Email, comma separated"
           multipleSelectionProps={multipleSelectionProps}
         />
+        <OLFormText id="add-collaborator-help-text">
+          {t('add_comma_separated_emails_help')}
+        </OLFormText>
       </OLFormGroup>
-
       <OLFormGroup>
         <div className="float-end add-collaborator-controls">
           <Select

+ 7 - 12
services/web/frontend/js/features/share-project-modal/components/select-collaborators.tsx

@@ -9,6 +9,7 @@ import Tag from '@/shared/components/tag'
 import { DropdownItem } from '@/shared/components/dropdown/dropdown-menu'
 import { Spinner } from 'react-bootstrap'
 import { Contact } from '../utils/types'
+import OLFormLabel from '@/shared/components/ol/ol-form-label'
 
 export type ContactItem = {
   email: string
@@ -25,12 +26,10 @@ const matchAllSpaces =
 export default function SelectCollaborators({
   loading,
   options,
-  placeholder,
   multipleSelectionProps,
 }: {
   loading: boolean
   options: Contact[]
-  placeholder: string
   multipleSelectionProps: UseMultipleSelectionReturnValue<ContactItem>
 }) {
   const { t } = useTranslation()
@@ -166,20 +165,18 @@ export default function SelectCollaborators({
   return (
     <div className="tags-input tags-new">
       {/* eslint-disable-next-line jsx-a11y/label-has-for */}
-      <label className="small" {...getLabelProps()}>
-        <strong>
-          {t('add_people')}
-          &nbsp;
-        </strong>
+      <OLFormLabel className="small" {...getLabelProps()}>
+        {t('add_email_address')}
         {loading && (
           <Spinner
             animation="border"
             aria-hidden="true"
             size="sm"
             role="status"
+            className="ms-2"
           />
         )}
-      </label>
+      </OLFormLabel>
 
       <div className="host">
         {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
@@ -197,16 +194,14 @@ export default function SelectCollaborators({
 
           <input
             data-testid="collaborator-email-input"
+            aria-describedby="add-collaborator-help-text"
             {...getInputProps(
               getDropdownProps({
                 className: classnames('input', {
                   'invalid-tag': !isValidInput,
                 }),
                 type: 'email',
-                placeholder,
-                size: inputValue.length
-                  ? inputValue.length + 5
-                  : placeholder.length,
+                size: inputValue.length ? inputValue.length + 5 : 5,
                 ref: inputRef,
                 // preventKeyAction: showDropdown,
                 onBlur: () => {

+ 0 - 1
services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-url-source.tsx

@@ -79,7 +79,6 @@ export const FigureModalUrlSource: FC = () => {
         <OLFormLabel>{t('image_url')}</OLFormLabel>
         <OLFormControl
           type="text"
-          placeholder={t('enter_image_url')}
           value={url}
           onChange={e => {
             setUrl(e.target.value)

+ 18 - 63
services/web/frontend/stories/ui/form/form-input.stories.tsx

@@ -88,37 +88,20 @@ export const Error: Story = {
     return (
       <>
         <FormGroup controlId="id-1">
-          <Form.Label>Label</Form.Label>
-          <FormControl
-            placeholder="Placeholder"
-            defaultValue="Large input"
-            size="lg"
-            isInvalid
-            {...args}
-          />
+          <Form.Label>Large input label</Form.Label>
+          <FormControl size="lg" isInvalid {...args} />
           <FormFeedback type="invalid">Error</FormFeedback>
         </FormGroup>
         <hr />
         <FormGroup controlId="id-2">
-          <Form.Label>Label</Form.Label>
-          <FormControl
-            placeholder="Placeholder"
-            defaultValue="Regular input"
-            isInvalid
-            {...args}
-          />
+          <Form.Label>Regular input label</Form.Label>
+          <FormControl isInvalid {...args} />
           <FormFeedback type="invalid">Error</FormFeedback>
         </FormGroup>
         <hr />
         <FormGroup controlId="id-3">
-          <Form.Label>Label</Form.Label>
-          <FormControl
-            placeholder="Placeholder"
-            defaultValue="Small input"
-            size="sm"
-            isInvalid
-            {...args}
-          />
+          <Form.Label>Small input label</Form.Label>
+          <FormControl size="sm" isInvalid {...args} />
           <FormFeedback type="invalid">Error</FormFeedback>
         </FormGroup>
       </>
@@ -131,34 +114,20 @@ export const Warning: Story = {
     return (
       <>
         <FormGroup controlId="id-1">
-          <Form.Label>Label</Form.Label>
-          <FormControl
-            placeholder="Placeholder"
-            defaultValue="Large input"
-            size="lg"
-            {...args}
-          />
+          <Form.Label>Large input label</Form.Label>
+          <FormControl size="lg" {...args} />
           <FormText type="warning">Warning</FormText>
         </FormGroup>
         <hr />
         <FormGroup controlId="id-2">
-          <Form.Label>Label</Form.Label>
-          <FormControl
-            placeholder="Placeholder"
-            defaultValue="Regular input"
-            {...args}
-          />
+          <Form.Label>Regular input label</Form.Label>
+          <FormControl {...args} />
           <FormText type="warning">Warning</FormText>
         </FormGroup>
         <hr />
         <FormGroup controlId="id-3">
-          <Form.Label>Label</Form.Label>
-          <FormControl
-            placeholder="Placeholder"
-            defaultValue="Small input"
-            size="sm"
-            {...args}
-          />
+          <Form.Label>Small input label</Form.Label>
+          <FormControl size="sm" {...args} />
           <FormText type="warning">Warning</FormText>
         </FormGroup>
       </>
@@ -171,34 +140,20 @@ export const Success: Story = {
     return (
       <>
         <FormGroup controlId="id-1">
-          <Form.Label>Label</Form.Label>
-          <FormControl
-            placeholder="Placeholder"
-            defaultValue="Large input"
-            size="lg"
-            {...args}
-          />
+          <Form.Label>Large input label</Form.Label>
+          <FormControl size="lg" {...args} />
           <FormText type="success">Success</FormText>
         </FormGroup>
         <hr />
         <FormGroup controlId="id-2">
-          <Form.Label>Label</Form.Label>
-          <FormControl
-            placeholder="Placeholder"
-            defaultValue="Regular input"
-            {...args}
-          />
+          <Form.Label>Regular input label</Form.Label>
+          <FormControl {...args} />
           <FormText type="success">Success</FormText>
         </FormGroup>
         <hr />
         <FormGroup controlId="id-3">
-          <Form.Label>Label</Form.Label>
-          <FormControl
-            placeholder="Placeholder"
-            defaultValue="Small input"
-            size="sm"
-            {...args}
-          />
+          <Form.Label>Small input label</Form.Label>
+          <FormControl size="sm" {...args} />
           <FormText type="success">Success</FormText>
         </FormGroup>
       </>

+ 0 - 4
services/web/frontend/stylesheets/pages/editor/tags-input.scss

@@ -4,10 +4,6 @@
   box-sizing: border-box;
 }
 
-.tags-input label.small {
-  font-weight: normal;
-}
-
 .tags-input .host {
   position: relative;
   height: 100%;

+ 10 - 7
services/web/locales/en.json

@@ -96,14 +96,13 @@
   "add_manager_user_not_found": "This email address is not registered in Overleaf. Please ask the person you are trying to add as a manager to create an Overleaf account if they have not already done so. When adding a manager, please use one of the email addresses that are associated with their account.",
   "add_more_collaborators": "Add more collaborators",
   "add_more_licenses_to_my_plan": "Add more licenses to my plan",
-  "add_more_managers": "Add more managers",
+  "add_more_manager_emails": "Add more manager emails",
   "add_new_email": "Add new email",
   "add_on": "Add-on",
   "add_ons": "Add-ons",
   "add_ons_for_any_plan": "Add-ons for any plan",
   "add_ons_for_any_plan_subheading": "Buy add-ons for any Overleaf plan (including the free plan) to unlock additional features.",
   "add_or_remove_project_from_tag": "Add or remove project from tag __tagName__",
-  "add_people": "Add people",
   "add_role_and_department": "Add role and department",
   "add_to_dictionary": "Add to dictionary",
   "add_to_tag": "Add to tag",
@@ -634,7 +633,8 @@
   "edit_dictionary_remove": "Remove from dictionary",
   "edit_figure": "Edit figure",
   "edit_sso_configuration": "Edit SSO Configuration",
-  "edit_tag": "Edit Tag",
+  "edit_tag": "Edit tag",
+  "edit_tag_name": "Edit tag name",
   "edit_your_custom_dictionary": "Edit your custom dictionary",
   "editing": "Editing",
   "editing_and_collaboration": "Editing and collaboration",
@@ -690,11 +690,12 @@
   "end_of_document": "End of document",
   "ensure_recover_account": "This will ensure that it can be used to recover your __appName__ account in case you lose access to your primary email address.",
   "enter_any_size_including_units_or_valid_latex_command": "Enter any size (including units) or valid LaTeX command",
-  "enter_image_url": "Enter image URL",
   "enter_the_code": "Enter the 6-digit code sent to __email__.",
   "enter_the_confirmation_code": "Enter the 6-digit confirmation code sent to __email__.",
   "enter_the_number_of_licenses_youd_like_to_add_to_see_the_cost_breakdown": "Enter the number of licenses you’d like to add to see the cost breakdown.",
+  "enter_your_email": "Enter your email",
   "enter_your_email_address_below_and_we_will_send_you_a_link_to_reset_your_password": "Enter your email address below, and we will send you a link to reset your password",
+  "enter_your_password": "Enter your password",
   "equation_generator": "Equation Generator",
   "equation_preview": "Equation preview",
   "error": "Error",
@@ -784,6 +785,7 @@
   "flexible_plans_for_everyone": "Flexible plans for everyone—from individual students and researchers, to large businesses and universities.",
   "fold_line": "Fold line",
   "folder_location": "Folder location",
+  "folder_name": "Folder name",
   "folders": "Folders",
   "following_paths_conflict": "The following files and folders conflict with the same path",
   "font_family": "Font Family",
@@ -1038,6 +1040,7 @@
   "import_from_github": "Import from GitHub",
   "import_idp_metadata": "Import IdP metadata",
   "import_to_sharelatex": "Import to __appName__",
+  "important_message": "Important message",
   "imported_from_another_project_at_date": "Imported from <0>Another project</0>/__sourceEntityPathHTML__, at __formattedDate__ __relativeDate__",
   "imported_from_external_provider_at_date": "Imported from <0>__shortenedUrlHTML__</0> at __formattedDate__ __relativeDate__",
   "imported_from_mendeley_at_date": "Imported from Mendeley at __formattedDate__ __relativeDate__",
@@ -1327,6 +1330,7 @@
   "manage_publisher_managers": "Manage publisher managers",
   "manage_sessions": "Manage Your Sessions",
   "manage_subscription": "Manage subscription",
+  "manage_tag": "Manage tag",
   "manage_your_ai_assist_add_on": "Manage your AI Assist add-on",
   "managed": "Managed",
   "managed_user_accounts": "Managed user accounts",
@@ -1419,11 +1423,10 @@
   "new_folder": "New folder",
   "new_look_and_feel": "New look and feel",
   "new_look_and_placement_of_the_settings": "New look and placement of the settings",
-  "new_name": "New Name",
+  "new_name": "New name",
   "new_navigation_introducing_left_hand_side_rail_and_top_menus": "New navigation - introducing left-hand side rail and top menus",
   "new_password": "New password",
   "new_project": "New project",
-  "new_project_name": "New project name",
   "new_snippet_project": "Untitled",
   "new_subscription_will_be_billed_immediately": "Your new subscription will be billed immediately to your current payment method.",
   "new_tag": "New tag",
@@ -1735,7 +1738,7 @@
   "project_history_labels": "Project history and labels",
   "project_last_published_at": "Your project was last published at",
   "project_linked_to": "This project is linked to",
-  "project_name": "Project Name",
+  "project_name": "Project name",
   "project_not_linked_to_github": "This project is not linked to a GitHub repository. You can create a repository for it in GitHub:",
   "project_ownership_transfer_confirmation_1": "Are you sure you want to make <0>__user__</0> the owner of <1>__project__</1>?",
   "project_ownership_transfer_confirmation_2": "This action cannot be undone. The new owner will be notified and will be able to change project access settings (including removing your own access).",

+ 27 - 16
services/web/modules/user-activate/frontend/js/components/register-form.jsx

@@ -1,10 +1,14 @@
 import { useState } from 'react'
+import { useTranslation } from 'react-i18next'
 import PropTypes from 'prop-types'
 import { postJSON } from '@/infrastructure/fetch-json'
 import OLButton from '@/shared/components/ol/ol-button'
 import OLForm from '@/shared/components/ol/ol-form'
 import OLFormControl from '@/shared/components/ol/ol-form-control'
-
+import OLFormLabel from '@/shared/components/ol/ol-form-label'
+import OLFormText from '@/shared/components/ol/ol-form-text'
+import OLCol from '@/shared/components/ol/ol-col'
+import OLRow from '@/shared/components/ol/ol-row'
 function RegisterForm({
   setRegistrationSuccess,
   setEmails,
@@ -12,6 +16,8 @@ function RegisterForm({
   setFailedEmails,
 }) {
   const [isLoading, setIsLoading] = useState(false)
+  const { t } = useTranslation()
+
   function handleRegister(event) {
     event.preventDefault()
     const formData = new FormData(event.target)
@@ -53,25 +59,30 @@ function RegisterForm({
 
   return (
     <OLForm onSubmit={handleRegister}>
-      <div className="d-flex gap-2 flex-wrap">
-        <div className="flex-grow-1 max-width">
+      <OLRow>
+        <OLCol lg={8}>
+          <OLFormLabel htmlFor="register-new-user-email">
+            Emails to register new users
+          </OLFormLabel>
           <OLFormControl
-            className="form-control"
+            id="register-new-user-email"
             name="email"
             type="text"
-            placeholder="jane@example.com, joe@example.com"
-            aria-label="emails to register"
-            aria-describedby="input-details"
+            aria-describedby="register-new-user-email-helper"
           />
-          <p id="input-details" className="visually-hidden">
-            Enter the emails you would like to register and separate them using
-            commas
-          </p>
-        </div>
-        <OLButton type="submit" className="ms-auto" isLoading={isLoading}>
-          Register
-        </OLButton>
-      </div>
+          <OLFormText id="register-new-user-email-helper">
+            {t('add_comma_separated_emails_help')}
+          </OLFormText>
+        </OLCol>
+        <OLCol
+          lg={4}
+          className="mt-3 mt-lg-0 d-flex align-items-center flex-column flex-lg-row"
+        >
+          <OLButton type="submit" isLoading={isLoading}>
+            Register
+          </OLButton>
+        </OLCol>
+      </OLRow>
     </OLForm>
   )
 }

+ 13 - 6
services/web/modules/user-activate/frontend/js/components/user-activate-register.jsx

@@ -4,6 +4,7 @@ import RegisterForm from './register-form'
 import OLRow from '@/shared/components/ol/ol-row'
 import OLCol from '@/shared/components/ol/ol-col'
 import OLCard from '@/shared/components/ol/ol-card'
+import Notification from '@/shared/components/notification'
 
 function UserActivateRegister() {
   const [emails, setEmails] = useState([])
@@ -16,7 +17,7 @@ function UserActivateRegister() {
       <OLCol>
         <OLCard>
           <div className="page-header">
-            <h1>Register New Users</h1>
+            <h1>Register new users</h1>
           </div>
           <RegisterForm
             setRegistrationSuccess={setRegistrationSuccess}
@@ -42,11 +43,17 @@ function UserActivateRegister() {
 
 function UserActivateError({ failedEmails }) {
   return (
-    <div className="row-spaced text-danger">
-      <p>Sorry, an error occured, failed to register these emails.</p>
-      {failedEmails.map(email => (
-        <p key={email}>{email}</p>
-      ))}
+    <div className="row-spaced">
+      <Notification
+        type="error"
+        content="Sorry, an error occured, failed to register these email:"
+        className="mb-3"
+      />
+      <ul>
+        {failedEmails.map(email => (
+          <li key={email}>{email}</li>
+        ))}
+      </ul>
     </div>
   )
 }

+ 2 - 2
services/web/modules/user-activate/test/frontend/js/components/register-form.test.jsx

@@ -25,7 +25,7 @@ describe('RegisterForm', function () {
         setFailedEmails={setFailedEmailsStub}
       />
     )
-    await screen.findByLabelText('emails to register')
+    await screen.findByLabelText('Emails to register new users')
     screen.getByRole('button', { name: /register/i })
   })
 
@@ -53,7 +53,7 @@ describe('RegisterForm', function () {
         setFailedEmails={setFailedEmailsStub}
       />
     )
-    const registerInput = screen.getByLabelText('emails to register')
+    const registerInput = screen.getByLabelText('Emails to register new users')
     const registerButton = screen.getByRole('button', { name: /register/i })
     fireEvent.change(registerInput, { target: { value: email } })
     fireEvent.click(registerButton)

+ 5 - 5
services/web/modules/user-activate/test/frontend/js/components/user-activate-register.test.jsx

@@ -19,7 +19,7 @@ describe('UserActivateRegister', function () {
     }
     fetchMock.get('/user/tpds/queues', TPDS_SYNCED)
     const registerMock = fetchMock.post('/admin/register', endPointResponse)
-    const registerInput = screen.getByLabelText('emails to register')
+    const registerInput = screen.getByLabelText('Emails to register new users')
     const registerButton = screen.getByRole('button', { name: /register/i })
 
     fireEvent.change(registerInput, { target: { value: email } })
@@ -41,7 +41,7 @@ describe('UserActivateRegister', function () {
     }
     fetchMock.get('/user/tpds/queues', TPDS_SYNCED)
     const registerMock = fetchMock.post('/admin/register', endPointResponse)
-    const registerInput = screen.getByLabelText('emails to register')
+    const registerInput = screen.getByLabelText('Emails to register new users')
     const registerButton = screen.getByRole('button', { name: /register/i })
 
     fireEvent.change(registerInput, { target: { value: email } })
@@ -76,7 +76,7 @@ describe('UserActivateRegister', function () {
       if (body.email === 'abc@gmail.com') return endPointResponse1
       else if (body.email === 'def@gmail.com') return endPointResponse2
     })
-    const registerInput = screen.getByLabelText('emails to register')
+    const registerInput = screen.getByLabelText('Emails to register new users')
     const registerButton = screen.getByRole('button', { name: /register/i })
 
     fireEvent.change(registerInput, { target: { value: email } })
@@ -102,7 +102,7 @@ describe('UserActivateRegister', function () {
       if (body.email === 'abc@') return endPointResponse1
       else if (body.email === 'def@') return endPointResponse2
     })
-    const registerInput = screen.getByLabelText('emails to register')
+    const registerInput = screen.getByLabelText('Emails to register new users')
     const registerButton = screen.getByRole('button', { name: /register/i })
 
     fireEvent.change(registerInput, { target: { value: email } })
@@ -133,7 +133,7 @@ describe('UserActivateRegister', function () {
       else if (body.email === 'def@gmail.com') return endPointResponse2
       else return 500
     })
-    const registerInput = screen.getByLabelText('emails to register')
+    const registerInput = screen.getByLabelText('Emails to register new users')
     const registerButton = screen.getByRole('button', { name: /register/i })
 
     fireEvent.change(registerInput, { target: { value: email } })

+ 3 - 3
services/web/test/frontend/components/editor-left-menu/editor-left-menu.spec.tsx

@@ -166,9 +166,9 @@ describe('<EditorLeftMenu />', function () {
         cy.findByRole('button', { name: 'Cancel' }).click()
         cy.findByRole('button', { name: 'Copy project' }).click()
 
-        cy.findByLabelText('New Name').focus()
-        cy.findByLabelText('New Name').clear()
-        cy.findByLabelText('New Name').type('Project Renamed')
+        cy.findByLabelText(/New name/i).focus()
+        cy.findByLabelText(/New name/i).clear()
+        cy.findByLabelText(/New name/i).type('Project Renamed')
         cy.get('#clone-project-form-name[value="Project Renamed"')
       })
 

+ 1 - 1
services/web/test/frontend/features/chat/components/message-input.test.tsx

@@ -21,7 +21,7 @@ describe('<MessageInput />', function () {
       />
     )
 
-    screen.getByLabelText('Send a message to your collaborators')
+    screen.getByLabelText('Send a message to your collaborators…')
   })
 
   it('sends a message after typing and hitting enter', function () {

+ 1 - 1
services/web/test/frontend/features/clone-project-modal/components/clone-project-modal.test.tsx

@@ -68,7 +68,7 @@ describe('<EditorCloneProjectModalWrapper />', function () {
     })
     expect(submitButton.disabled).to.be.false
 
-    const input = await screen.getByLabelText('New Name')
+    const input = await screen.getByLabelText(/New name/i)
 
     fireEvent.change(input, {
       target: { value: '' },

+ 2 - 2
services/web/test/frontend/features/editor-left-menu/components/actions-copy-project.test.tsx

@@ -23,7 +23,7 @@ describe('<ActionsCopyProject />', function () {
 
     fireEvent.click(screen.getByRole('button', { name: 'Copy project' }))
 
-    screen.getByPlaceholderText('New project name')
+    screen.getByLabelText(/New name/i)
   })
 
   it('loads the project page when submitted', async function () {
@@ -38,7 +38,7 @@ describe('<ActionsCopyProject />', function () {
 
     fireEvent.click(screen.getByRole('button', { name: 'Copy project' }))
 
-    const input = screen.getByPlaceholderText('New project name')
+    const input = screen.getByLabelText(/New name/i)
     fireEvent.change(input, { target: { value: 'New project' } })
 
     const button = screen.getByRole('button', { name: 'Copy' })

+ 8 - 4
services/web/test/frontend/features/group-management/components/group-managers.spec.tsx

@@ -68,8 +68,10 @@ describe('group managers', function () {
     })
 
     cy.findByTestId('add-members-form').within(() => {
-      cy.findByRole('textbox').type('someone.else@test.com')
-      cy.findByRole('button').click()
+      cy.findByLabelText(/Add more manager emails/i).type(
+        'someone.else@test.com'
+      )
+      cy.findByRole('button', { name: /add/i }).click()
     })
 
     cy.findByTestId('managed-entities-table')
@@ -94,8 +96,10 @@ describe('group managers', function () {
     })
 
     cy.findByTestId('add-members-form').within(() => {
-      cy.findByRole('textbox').type('someone.else@test.com')
-      cy.findByRole('button').click()
+      cy.findByLabelText(/Add more manager emails/i).type(
+        'someone.else@test.com'
+      )
+      cy.findByRole('button', { name: /add/i }).click()
     })
     cy.findByRole('alert').should('contain.text', 'Error: User already added')
   })

+ 4 - 4
services/web/test/frontend/features/group-management/components/group-members.spec.tsx

@@ -92,7 +92,7 @@ describe('GroupMembers', function () {
         },
       })
 
-      cy.findByTestId('add-members-input').type('someone.else@test.com')
+      cy.findByLabelText('Invite more members').type('someone.else@test.com')
       cy.findByRole('button', { name: /invite/i }).click()
 
       cy.findByTestId('managed-entities-table')
@@ -119,7 +119,7 @@ describe('GroupMembers', function () {
         },
       })
 
-      cy.findByTestId('add-members-input').type('someone.else@test.com')
+      cy.findByLabelText('Invite more members').type('someone.else@test.com')
       cy.findByRole('button', { name: /invite/i }).click()
       cy.findByRole('alert').contains('Error: User already added')
     })
@@ -297,7 +297,7 @@ describe('GroupMembers', function () {
         },
       })
 
-      cy.findByTestId('add-members-input').type('someone.else@test.com')
+      cy.findByLabelText('Invite more members').type('someone.else@test.com')
       cy.findByRole('button', { name: /invite/i }).click()
 
       cy.findByTestId('managed-entities-table')
@@ -326,7 +326,7 @@ describe('GroupMembers', function () {
         },
       })
 
-      cy.findByTestId('add-members-input').type('someone.else@test.com')
+      cy.findByLabelText('Invite more members').type('someone.else@test.com')
       cy.findByRole('button', { name: /invite/i }).click()
       cy.findByRole('alert').contains('Error: User already added')
     })

+ 8 - 4
services/web/test/frontend/features/group-management/components/institution-managers.spec.tsx

@@ -68,8 +68,10 @@ describe('institution managers', function () {
     })
 
     cy.findByTestId('add-members-form').within(() => {
-      cy.findByRole('textbox').type('someone.else@test.com')
-      cy.findByRole('button').click()
+      cy.findByLabelText(/Add more manager emails/i).type(
+        'someone.else@test.com'
+      )
+      cy.findByRole('button', { name: /add/i }).click()
     })
 
     cy.findByTestId('managed-entities-table')
@@ -94,8 +96,10 @@ describe('institution managers', function () {
     })
 
     cy.findByTestId('add-members-form').within(() => {
-      cy.findByRole('textbox').type('someone.else@test.com')
-      cy.findByRole('button').click()
+      cy.findByLabelText(/Add more manager emails/i).type(
+        'someone.else@test.com'
+      )
+      cy.findByRole('button', { name: /add/i }).click()
     })
     cy.findByRole('alert').should('contain.text', 'Error: User already added')
   })

+ 2 - 2
services/web/test/frontend/features/group-management/components/managed-group-members.spec.tsx

@@ -131,7 +131,7 @@ describe('group members, with managed users', function () {
       },
     })
 
-    cy.findByTestId('add-members-input').type('someone.else@test.com')
+    cy.findByLabelText('Invite more members').type('someone.else@test.com')
     cy.findByRole('button', { name: /invite/i }).click()
 
     cy.findByTestId('managed-entities-table')
@@ -160,7 +160,7 @@ describe('group members, with managed users', function () {
       },
     })
 
-    cy.findByTestId('add-members-input').type('someone.else@test.com')
+    cy.findByLabelText('Invite more members').type('someone.else@test.com')
     cy.findByRole('button', { name: /invite/i }).click()
     cy.findByRole('alert').contains('Error: User already added')
   })

+ 8 - 4
services/web/test/frontend/features/group-management/components/publisher-managers.spec.tsx

@@ -68,8 +68,10 @@ describe('publisher managers', function () {
     })
 
     cy.findByTestId('add-members-form').within(() => {
-      cy.findByRole('textbox').type('someone.else@test.com')
-      cy.findByRole('button').click()
+      cy.findByLabelText(/Add more manager emails/i).type(
+        'someone.else@test.com'
+      )
+      cy.findByRole('button', { name: /add/i }).click()
     })
 
     cy.findByTestId('managed-entities-table')
@@ -94,8 +96,10 @@ describe('publisher managers', function () {
     })
 
     cy.findByTestId('add-members-form').within(() => {
-      cy.findByRole('textbox').type('someone.else@test.com')
-      cy.findByRole('button').click()
+      cy.findByLabelText(/Add more manager emails/i).type(
+        'someone.else@test.com'
+      )
+      cy.findByRole('button', { name: /add/i }).click()
     })
     cy.findByRole('alert').should('contain.text', 'Error: User already added')
   })

+ 1 - 1
services/web/test/frontend/features/history/components/change-list.spec.tsx

@@ -515,7 +515,7 @@ describe('change list', function () {
         cy.findByRole('heading', { name: /add label/i })
         cy.findByRole('button', { name: /cancel/i })
         cy.findByRole('button', { name: /add label/i }).should('be.disabled')
-        cy.findByPlaceholderText(/new label name/i).as('input')
+        cy.findByLabelText(/New label name/i).as('input')
         cy.get('@input').type(newLabel)
         cy.findByRole('button', { name: /add label/i }).should('be.enabled')
         cy.get('@input').type('{enter}')

+ 2 - 2
services/web/test/frontend/features/project-list/components/new-project-button.test.tsx

@@ -51,13 +51,13 @@ describe('<NewProjectButton />', function () {
     it('open new project modal when clicking at Blank project', function () {
       fireEvent.click(screen.getByRole('menuitem', { name: 'Blank project' }))
 
-      screen.getByPlaceholderText('Project Name')
+      screen.getByLabelText(/Project name/i)
     })
 
     it('open new project modal when clicking at Example project', function () {
       fireEvent.click(screen.getByRole('menuitem', { name: 'Example project' }))
 
-      screen.getByPlaceholderText('Project Name')
+      screen.getByLabelText(/Project name/i)
     })
 
     it('close the new project modal when clicking at the top right "x" button', function () {

+ 4 - 4
services/web/test/frontend/features/project-list/components/new-project-button/modal-content-new-project-form.test.tsx

@@ -34,7 +34,7 @@ describe('<ModalContentNewProjectForm />', function () {
 
     expect(createButton.getAttribute('disabled')).to.exist
 
-    fireEvent.change(screen.getByPlaceholderText('Project Name'), {
+    fireEvent.change(screen.getByLabelText('Project name'), {
       target: { value: 'Test Name' },
     })
 
@@ -61,7 +61,7 @@ describe('<ModalContentNewProjectForm />', function () {
 
     render(<ModalContentNewProjectForm onCancel={() => {}} />)
 
-    fireEvent.change(screen.getByPlaceholderText('Project Name'), {
+    fireEvent.change(screen.getByLabelText('Project name'), {
       target: { value: '/' },
     })
 
@@ -87,7 +87,7 @@ describe('<ModalContentNewProjectForm />', function () {
 
     render(<ModalContentNewProjectForm onCancel={() => {}} />)
 
-    fireEvent.change(screen.getByPlaceholderText('Project Name'), {
+    fireEvent.change(screen.getByLabelText('Project name'), {
       target: { value: '\\' },
     })
 
@@ -113,7 +113,7 @@ describe('<ModalContentNewProjectForm />', function () {
 
     render(<ModalContentNewProjectForm onCancel={() => {}} />)
 
-    fireEvent.change(screen.getByPlaceholderText('Project Name'), {
+    fireEvent.change(screen.getByLabelText('Project name'), {
       target: {
         value: `
       Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Arcu risus quis varius quam quisque id diam vel quam. Sit amet porttitor eget dolor morbi non arcu risus quis. In aliquam sem fringilla ut. Gravida cum sociis natoque penatibus. Semper risus in hendrerit gravida rutrum quisque non. Ut aliquam purus sit amet luctus venenatis. Neque ornare aenean euismod elementum nisi. Adipiscing bibendum est ultricies integer quis auctor elit. Nulla posuere sollicitudin aliquam ultrices sagittis. Nulla facilisi nullam vehicula ipsum a arcu cursus. Tristique senectus et netus et malesuada fames ac. Pulvinar pellentesque habitant morbi tristique senectus et netus et. Nisi scelerisque eu ultrices vitae auctor eu. Hendrerit gravida rutrum quisque non tellus orci. Volutpat blandit aliquam etiam erat velit scelerisque in dictum non. Donec enim diam vulputate ut pharetra sit amet aliquam id. Ullamcorper eget nulla facilisi etiam.

+ 4 - 2
services/web/test/frontend/features/project-list/components/project-list-root.test.tsx

@@ -894,7 +894,9 @@ describe('<ProjectListRoot />', function () {
             expect(confirmButton.disabled).to.be.true
 
             // no name
-            const input = screen.getByLabelText('New Name') as HTMLButtonElement
+            const input = screen.getByLabelText(
+              /New name/i
+            ) as HTMLButtonElement
             fireEvent.change(input, {
               target: { value: '' },
             })
@@ -928,7 +930,7 @@ describe('<ProjectListRoot />', function () {
             // a valid name
             const newProjectName = 'A new project name'
             const input = (await within(modal).findByLabelText(
-              'New Name'
+              /New name/i
             )) as HTMLButtonElement
             const oldName = input.value
             fireEvent.change(input, {

+ 1 - 1
services/web/test/frontend/features/project-list/components/sidebar/tags-list.test.tsx

@@ -175,7 +175,7 @@ describe('<TagsList />', function () {
 
     it('modal is open', async function () {
       const modal = screen.getAllByRole('dialog', { hidden: false })[0]
-      within(modal).getByRole('heading', { name: 'Edit Tag' })
+      within(modal).getByRole('heading', { name: 'Edit tag' })
     })
 
     it('click on cancel closes the modal', async function () {

+ 1 - 1
services/web/test/frontend/features/project-list/components/table/cells/action-buttons/copy-project-button.test.tsx

@@ -56,7 +56,7 @@ describe('<CopyProjectButton />', function () {
 
     fireEvent.click(btn)
     screen.getByText('Copy project')
-    screen.getByLabelText('New Name')
+    screen.getByLabelText(/New name/i)
     screen.getByDisplayValue(`${copyableProject.name} (Copy)`)
     const copyBtn = screen.getAllByRole<HTMLButtonElement>('button', {
       name: 'Copy',

+ 1 - 1
services/web/test/frontend/features/project-list/components/table/cells/action-buttons/rename-project-button.test.tsx

@@ -56,7 +56,7 @@ describe('<RenameProjectButton />', function () {
     }) as HTMLButtonElement
     expect(confirmBtn.disabled).to.be.true
     const nameInput = screen.getByDisplayValue(ownedProject.name)
-    fireEvent.change(nameInput, { target: { value: 'new name' } })
+    fireEvent.change(nameInput, { target: { value: /New name/i } })
     expect(confirmBtn.disabled).to.be.false
     fireEvent.click(confirmBtn)
     expect(confirmBtn.disabled).to.be.true

+ 2 - 2
services/web/test/frontend/features/project-list/components/table/project-tools/rename-project-modal.test.tsx

@@ -33,7 +33,7 @@ describe('<RenameProjectModal />', function () {
     )
     screen.getByText('Rename Project')
     const input = screen.getByRole('textbox', {
-      name: 'New Name',
+      name: /New name/i,
     }) as HTMLInputElement
     expect(input.value).to.equal(currentProjects[0].name)
 
@@ -80,7 +80,7 @@ describe('<RenameProjectModal />', function () {
       />
     )
     screen.getByText('Rename Project')
-    const input = screen.getByLabelText('New Name') as HTMLButtonElement
+    const input = screen.getByLabelText(/New name/i) as HTMLButtonElement
     expect(input.value).to.equal(currentProjects[0].name)
 
     fireEvent.change(input, {

+ 6 - 6
services/web/test/frontend/features/share-project-modal/components/share-project-modal.test.tsx

@@ -603,7 +603,7 @@ describe('<ShareProjectModal/>', function () {
       createContextProps({ publicAccessLevel: 'tokenBased' })
     )
 
-    const [inputElement] = await screen.findAllByLabelText('Add people')
+    const [inputElement] = await screen.findAllByLabelText('Add email address')
 
     // loading contacts
     await waitFor(() => {
@@ -775,7 +775,7 @@ describe('<ShareProjectModal/>', function () {
       expect(fetchMock.callHistory.called('express:/user/contacts')).to.be.true
     })
 
-    const [inputElement] = await screen.findAllByLabelText('Add people')
+    const [inputElement] = await screen.findAllByLabelText('Add email address')
 
     const submitButton: HTMLButtonElement = screen.getByRole('button', {
       name: 'Invite',
@@ -883,7 +883,7 @@ describe('<ShareProjectModal/>', function () {
       createContextProps()
     )
 
-    const [inputElement] = await screen.findAllByLabelText('Add people')
+    const [inputElement] = await screen.findAllByLabelText('Add email address')
 
     // Wait for contacts to load
     await waitFor(() => {
@@ -938,7 +938,7 @@ describe('<ShareProjectModal/>', function () {
       createContextProps()
     )
 
-    const [inputElement] = await screen.findAllByLabelText('Add people')
+    const [inputElement] = await screen.findAllByLabelText('Add email address')
 
     // Wait for contacts to load
     await waitFor(() => {
@@ -974,7 +974,7 @@ describe('<ShareProjectModal/>', function () {
       createContextProps()
     )
 
-    const [inputElement] = await screen.findAllByLabelText('Add people')
+    const [inputElement] = await screen.findAllByLabelText('Add email address')
 
     // Wait for contacts to load
     await waitFor(() => {
@@ -1009,7 +1009,7 @@ describe('<ShareProjectModal/>', function () {
       createContextProps()
     )
 
-    const [inputElement] = await screen.findAllByLabelText('Add people')
+    const [inputElement] = await screen.findAllByLabelText('Add email address')
 
     // Wait for contacts to load
     await waitFor(() => {