Просмотр исходного кода

Merge pull request #24030 from overleaf/td-bs5-remove-font-awesome

Remove Font Awesome in BS5

GitOrigin-RevId: 3ee9b136ac9ecda57ba9985d1da7d7a7e6b275e6
Tim Down 1 год назад
Родитель
Сommit
9c16a85295
55 измененных файлов с 192 добавлено и 367 удалено
  1. 2 2
      services/web/app/views/_mixins/faq_search-marketing.pug
  2. 2 1
      services/web/app/views/layout/navbar-marketing-bootstrap-5.pug
  3. 1 1
      services/web/app/views/layout/navbar-marketing.pug
  4. 1 1
      services/web/app/views/layout/navbar-website-redesign.pug
  5. 1 0
      services/web/frontend/extracted-translations.json
  6. 3 5
      services/web/frontend/js/features/contact-form/search.js
  7. 1 1
      services/web/frontend/js/features/editor-left-menu/components/left-menu-button.tsx
  8. 1 1
      services/web/frontend/js/features/editor-navigation-toolbar/components/layout-dropdown-button.tsx
  9. 1 0
      services/web/frontend/js/features/faq-search/index.js
  10. 3 3
      services/web/frontend/js/features/form-helpers/hydrate-form.js
  11. 2 2
      services/web/frontend/js/features/form-helpers/input-validator.js
  12. 1 1
      services/web/frontend/js/features/group-management/components/group-members.tsx
  13. 1 1
      services/web/frontend/js/features/group-management/components/managers-table.tsx
  14. 2 19
      services/web/frontend/js/features/history/components/change-list/owner-paywall-prompt.tsx
  15. 3 1
      services/web/frontend/js/features/history/components/change-list/toggle-switch.tsx
  16. 11 1
      services/web/frontend/js/features/ide-react/components/resize/horizontal-toggler.tsx
  17. 2 10
      services/web/frontend/js/features/pdf-preview/components/pdf-log-entry-raw-content.tsx
  18. 1 1
      services/web/frontend/js/features/pdf-preview/components/pdf-zoom-dropdown.tsx
  19. 1 1
      services/web/frontend/js/features/project-list/components/color-picker/color-picker.tsx
  20. 1 1
      services/web/frontend/js/features/project-list/components/notifications/accessibility-survey-banner.tsx
  21. 7 15
      services/web/frontend/js/features/settings/components/linking/status.tsx
  22. 1 1
      services/web/frontend/js/features/share-project-modal/components/add-collaborators.tsx
  23. 1 1
      services/web/frontend/js/features/source-editor/components/editor-switch.tsx
  24. 2 3
      services/web/frontend/js/features/source-editor/components/paste-html/pasted-content-menu.tsx
  25. 4 1
      services/web/frontend/js/features/source-editor/components/table-generator/toolbar/column-width-modal/modal.tsx
  26. 2 2
      services/web/frontend/js/features/source-editor/components/toolbar/section-heading-dropdown.tsx
  27. 3 0
      services/web/frontend/js/features/source-editor/extensions/visual/pasted-content.tsx
  28. 3 0
      services/web/frontend/js/features/source-editor/extensions/visual/visual-theme.ts
  29. 14 4
      services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/editable-graphics.ts
  30. 37 9
      services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/preamble.ts
  31. 2 6
      services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/table-rendering-error.ts
  32. 7 5
      services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/individual-plans-table.tsx
  33. 3 3
      services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/change-to-group-modal.tsx
  34. 3 3
      services/web/frontend/js/features/token-access/components/token-access-root.tsx
  35. 4 3
      services/web/frontend/js/features/utils/material-icon.ts
  36. 4 4
      services/web/frontend/js/features/word-count-modal/components/word-counts.tsx
  37. 1 2
      services/web/frontend/js/shared/components/close.tsx
  38. 0 10
      services/web/frontend/js/shared/components/icon-checked.jsx
  39. 0 44
      services/web/frontend/js/shared/components/icon.tsx
  40. 0 23
      services/web/frontend/js/shared/components/processing.jsx
  41. 4 3
      services/web/frontend/js/shared/components/upgrade-benefits.tsx
  42. 0 50
      services/web/frontend/stories/icon.stories.jsx
  43. 2 2
      services/web/frontend/stories/ui/badge.stories.tsx
  44. 1 0
      services/web/frontend/stylesheets/bootstrap-5/components/all.scss
  45. 3 0
      services/web/frontend/stylesheets/bootstrap-5/components/upgrade-benefits.scss
  46. 0 1
      services/web/frontend/stylesheets/bootstrap-5/main-style.scss
  47. 0 9
      services/web/frontend/stylesheets/bootstrap-5/modals/contact-us-modal.scss
  48. 0 4
      services/web/frontend/stylesheets/bootstrap-5/modules/symbol-palette.scss
  49. 9 26
      services/web/frontend/stylesheets/bootstrap-5/pages/editor/ide.scss
  50. 4 2
      services/web/frontend/stylesheets/core/utilities.less
  51. 1 1
      services/web/locales/en.json
  52. 1 1
      services/web/modules/user-activate/frontend/js/components/register-form.jsx
  53. 28 9
      services/web/test/frontend/features/source-editor/components/codemirror-editor-figure-modal.spec.tsx
  54. 0 51
      services/web/test/frontend/shared/components/icon.test.jsx
  55. 0 16
      services/web/test/frontend/shared/components/processing.test.jsx

+ 2 - 2
services/web/app/views/_mixins/faq_search-marketing.pug

@@ -21,7 +21,7 @@ mixin faq_search-marketing(headerText, headerClass)
 							data-ol-clear-search
 							hidden
 						)
-						button.sr-only(
+						button.visually-hidden(
 							type='button'
 							hidden
 							data-ol-clear-search
@@ -31,7 +31,7 @@ mixin faq_search-marketing(headerText, headerClass)
 				.row(role='region' aria-label='search results')
 					.col-md-12
 						div(data-ol-search-results-wrapper)
-							span.sr-only(aria-live='polite' data-ol-search-sr-help-message)
+							span.visually-hidden(aria-live='polite' data-ol-search-sr-help-message)
 							div(data-ol-search-results)
 						.row-spaced-small.search-result.card.card-thin(
 							hidden

+ 2 - 1
services/web/app/views/layout/navbar-marketing-bootstrap-5.pug

@@ -1,4 +1,5 @@
 include ../_mixins/navbar
+include ../_mixins/material_symbol
 
 nav.navbar.navbar-default.navbar-main.navbar-expand-lg(
 	class={
@@ -44,7 +45,7 @@ nav.navbar.navbar-default.navbar-main.navbar-expand-lg(
 				aria-expanded='false'
 				aria-label='Toggle ' + translate('navigation')
 			)
-				i.fa.fa-bars(aria-hidden='true')
+				+material-symbol('menu')
 
 			#navbar-main-collapse.navbar-collapse.collapse
 				ul.nav.navbar-nav.navbar-right.ms-auto(role='menubar')

+ 1 - 1
services/web/app/views/layout/navbar-marketing.pug

@@ -16,7 +16,7 @@ nav.navbar.navbar-default.navbar-main(
 			- var enableUpgradeButton = projectDashboardReact && usersBestSubscription && (usersBestSubscription.type === 'free' || usersBestSubscription.type === 'standalone-ai-add-on')
 			if enableUpgradeButton
 				//- prettier-ignore
-				a.btn.btn-primary.pull-right.me-2.visible-xs(
+				a.btn.btn-primary.float-end.me-2.visible-xs(
 					href='/user/subscription/plans'
 					event-tracking='upgrade-button-click'
 					event-tracking-mb='true'

+ 1 - 1
services/web/app/views/layout/navbar-website-redesign.pug

@@ -11,7 +11,7 @@ nav.navbar.navbar-default.navbar-main.website-redesign-navbar
 					i.fa.fa-bars(aria-hidden='true')
 			- var enableUpgradeButton = projectDashboardReact && usersBestSubscription && (usersBestSubscription.type === 'free' || usersBestSubscription.type === 'standalone-ai-add-on')
 			if enableUpgradeButton
-				a.btn.btn-primary.pull-right.me-2.visible-xs(
+				a.btn.btn-primary.float-end.me-2.visible-xs(
 					href='/user/subscription/plans'
 					event-tracking='upgrade-button-click'
 					event-tracking-mb='true'

+ 1 - 0
services/web/frontend/extracted-translations.json

@@ -272,6 +272,7 @@
   "collaborate_online_and_offline": "",
   "collaborator_chat": "",
   "collabs_per_proj": "",
+  "collabs_per_proj_multiple": "",
   "collabs_per_proj_single": "",
   "collapse": "",
   "column_width": "",

+ 3 - 5
services/web/frontend/js/features/contact-form/search.js

@@ -1,6 +1,7 @@
 import _ from 'lodash'
 import { formatWikiHit, searchWiki } from '../algolia-search/search-wiki'
 import { sendMB } from '../../infrastructure/event-tracking'
+import { materialIcon } from '@/features/utils/material-icon'
 
 export function setupSearch(formEl) {
   const inputEl = formEl.querySelector('[name="subject"]')
@@ -45,11 +46,8 @@ export function setupSearch(formEl) {
         contentEl.innerHTML = pageName
         linkEl.append(contentEl)
 
-        const iconEl = document.createElement('i')
-        iconEl.className = 'material-symbols dropdown-item-trailing-icon'
-        iconEl.textContent = 'open_in_new'
-        iconEl.setAttribute('aria-hidden', 'true')
-        iconEl.translate = false
+        const iconEl = materialIcon('open_in_new')
+        iconEl.classList.add('dropdown-item-trailing-icon')
         linkEl.append(iconEl)
 
         resultsEl.append(liEl)

+ 1 - 1
services/web/frontend/js/features/editor-left-menu/components/left-menu-button.tsx

@@ -43,7 +43,7 @@ export default function LeftMenuButton({
         <LeftMenuButtonIcon svgIcon={svgIcon} icon={icon} />
         <span translate={translate}>{children}</span>
         {disabledAccesibilityText ? (
-          <span className="sr-only">{disabledAccesibilityText}</span>
+          <span className="visually-hidden">{disabledAccesibilityText}</span>
         ) : null}
       </div>
     )

+ 1 - 1
services/web/frontend/js/features/editor-navigation-toolbar/components/layout-dropdown-button.tsx

@@ -172,7 +172,7 @@ export const LayoutDropdownButtonUi = ({
   return (
     <>
       {processing && (
-        <div aria-live="assertive" className="sr-only">
+        <div aria-live="assertive" className="visually-hidden">
           {t('layout_processing')}
         </div>
       )}

+ 1 - 0
services/web/frontend/js/features/faq-search/index.js

@@ -102,6 +102,7 @@ function setupSearch(formEl) {
 
   // display initial results
   handleChange()
+  updateClearBtnVisibility()
 }
 
 document.querySelectorAll('[data-ol-faq-search]').forEach(setupSearch)

+ 3 - 3
services/web/frontend/js/features/form-helpers/hydrate-form.js

@@ -4,7 +4,7 @@ import { canSkipCaptcha, validateCaptchaV2 } from './captcha'
 import inputValidator from './input-validator'
 import { disableElement, enableElement } from '../utils/disableElement'
 import { isBootstrap5 } from '@/features/utils/bootstrap-5'
-import createIcon from '@/features/form-helpers/create-icon'
+import { materialIcon } from '@/features/utils/material-icon'
 
 // Form helper(s) to handle:
 // - Attaching to the relevant form elements
@@ -165,7 +165,7 @@ function createNotificationFromMessageBS5(message) {
   if (materialIcon) {
     const iconEl = document.createElement('div')
     iconEl.className = 'notification-icon'
-    const iconSpan = createIcon(materialIcon)
+    const iconSpan = materialIcon(materialIcon)
     iconEl.append(iconSpan)
     messageEl.append(iconEl)
   }
@@ -313,7 +313,7 @@ function showMessagesNewStyle(formEl, messageBag) {
       }
 
       // create the left icon
-      const icon = createIcon(
+      const icon = materialIcon(
         message.type === 'error' ? 'error' : 'check_circle'
       )
       const messageIcon = document.createElement('div')

+ 2 - 2
services/web/frontend/js/features/form-helpers/input-validator.js

@@ -1,5 +1,5 @@
 import { isBootstrap5 } from '@/features/utils/bootstrap-5'
-import createIcon from '@/features/form-helpers/create-icon'
+import { materialIcon } from '@/features/utils/material-icon'
 
 export default function inputValidator(inputEl) {
   const messageEl = document.createElement('div')
@@ -15,7 +15,7 @@ export default function inputValidator(inputEl) {
 
   // In Bootstrap 5, add an icon
   if (isBootstrap5()) {
-    const iconEl = createIcon('error')
+    const iconEl = materialIcon('error')
     messageInnerEl.append(iconEl)
   }
   messageInnerEl.append(messageTextNode)

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

@@ -113,7 +113,7 @@ export default function GroupMembers() {
               className="page-header mb-4"
               data-testid="page-header-members-details"
             >
-              <div className="pull-right">
+              <div className="float-end">
                 {selectedUsers.length === 0 && groupSizeDetails()}
                 {removeMemberLoading ? (
                   <OLButton variant="danger" disabled>

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

@@ -153,7 +153,7 @@ export function ManagersTable({
               className="page-header mb-4"
               data-testid="page-header-members-details"
             >
-              <div className="pull-right">
+              <div className="float-end">
                 {removeMemberInflightCount > 0 ? (
                   <OLButton variant="danger" disabled>
                     {t('removing')}&hellip;

+ 2 - 19
services/web/frontend/js/features/history/components/change-list/owner-paywall-prompt.tsx

@@ -1,16 +1,8 @@
 import { useTranslation } from 'react-i18next'
-import Icon from '../../../../shared/components/icon'
 import { useCallback, useEffect, useState } from 'react'
 import * as eventTracking from '../../../../infrastructure/event-tracking'
 import StartFreeTrialButton from '../../../../shared/components/start-free-trial-button'
-
-function FeatureItem({ text }: { text: string }) {
-  return (
-    <li>
-      <Icon type="check" /> {text}
-    </li>
-  )
-}
+import UpgradeBenefits from '@/shared/components/upgrade-benefits'
 
 export function OwnerPaywallPrompt() {
   const { t } = useTranslation()
@@ -34,16 +26,7 @@ export function OwnerPaywallPrompt() {
           {t('upgrade_to_get_feature', { feature: 'full project history' })}
         </strong>
       </p>
-      <ul className="history-feature-list">
-        <FeatureItem text={t('unlimited_projects')} />
-        <FeatureItem
-          text={t('collabs_per_proj', { collabcount: 'Multiple' })}
-        />
-        <FeatureItem text={t('full_doc_history')} />
-        <FeatureItem text={t('sync_to_dropbox')} />
-        <FeatureItem text={t('sync_to_github')} />
-        <FeatureItem text={t('compile_larger_projects')} />
-      </ul>
+      <UpgradeBenefits className="history-feature-list" />
       <p>
         <StartFreeTrialButton
           source="history"

+ 3 - 1
services/web/frontend/js/features/history/components/change-list/toggle-switch.tsx

@@ -61,7 +61,9 @@ function ToggleSwitch({ labelsOnly, setLabelsOnly }: ToggleSwitchProps) {
 
   return (
     <fieldset className="toggle-switch">
-      <legend className="sr-only">{t('history_view_a11y_description')}</legend>
+      <legend className="visually-hidden">
+        {t('history_view_a11y_description')}
+      </legend>
       <input
         type="radio"
         name="labels-only-toggle-switch"

+ 11 - 1
services/web/frontend/js/features/ide-react/components/resize/horizontal-toggler.tsx

@@ -1,5 +1,6 @@
 import classNames from 'classnames'
 import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
+import MaterialIcon from '@/shared/components/material-icon'
 
 type HorizontalTogglerType = 'west' | 'east'
 
@@ -42,7 +43,16 @@ export function HorizontalToggler({
         aria-label={description}
         title=""
         onClick={() => setIsOpen(!isOpen)}
-      />
+      >
+        <MaterialIcon
+          type={
+            (togglerType === 'west' && isOpen) ||
+            (togglerType === 'east' && !isOpen)
+              ? 'chevron_left'
+              : 'chevron_right'
+          }
+        />
+      </button>
     </OLTooltip>
   )
 }

+ 2 - 10
services/web/frontend/js/features/pdf-preview/components/pdf-log-entry-raw-content.tsx

@@ -3,7 +3,6 @@ import { useResizeObserver } from '../../../shared/hooks/use-resize-observer'
 import { useTranslation } from 'react-i18next'
 import classNames from 'classnames'
 import OLButton from '@/features/ui/components/ol/ol-button'
-import Icon from '../../../shared/components/icon'
 
 export default function PdfLogEntryRawContent({
   rawContent,
@@ -53,17 +52,10 @@ export default function PdfLogEntryRawContent({
           <OLButton
             variant="secondary"
             size="sm"
+            leadingIcon={expanded ? 'expand_less' : 'expand_more'}
             onClick={() => setExpanded(value => !value)}
           >
-            {expanded ? (
-              <>
-                <Icon type="angle-up" /> {t('collapse')}
-              </>
-            ) : (
-              <>
-                <Icon type="angle-down" /> {t('expand')}
-              </>
-            )}
+            {expanded ? t('collapse') : t('expand')}
           </OLButton>
         </div>
       )}

+ 1 - 1
services/web/frontend/js/features/pdf-preview/components/pdf-zoom-dropdown.tsx

@@ -172,7 +172,7 @@ function PdfZoomDropdown({
 
 function Shortcut({ keys }: { keys: string[] }) {
   return (
-    <span className="pull-right">
+    <span className="float-end">
       {keys.map((key, idx) => (
         <span
           className={classNames({

+ 1 - 1
services/web/frontend/js/features/project-list/components/color-picker/color-picker.tsx

@@ -44,7 +44,7 @@ function ColorPickerItem({ color, name }: ColorPickerItemProps) {
       tabIndex={0}
       onKeyDown={handleKeyDown}
     >
-      <span id={name} className="sr-only">
+      <span id={name} className="visually-hidden">
         {t('select_color', { name })}
       </span>
       {!pickingCustomColor && color === selectedColor && (

+ 1 - 1
services/web/frontend/js/features/project-list/components/notifications/accessibility-survey-banner.tsx

@@ -27,7 +27,7 @@ function AccessibilitySurveyBanner() {
 
   return (
     <Notification
-      className="sr-only"
+      className="visually-hidden"
       type="info"
       onDismiss={handleClose}
       content={<p>{t('help_improve_screen_reader_fill_out_this_survey')}</p>}

+ 7 - 15
services/web/frontend/js/features/settings/components/linking/status.tsx

@@ -1,5 +1,6 @@
 import { ReactNode } from 'react'
-import Icon from '../../../../shared/components/icon'
+import MaterialIcon from '@/shared/components/material-icon'
+import OLSpinner from '@/features/ui/components/ol/ol-spinner'
 
 type Status = 'pending' | 'success' | 'error'
 
@@ -28,29 +29,20 @@ function StatusIcon({ status }: StatusIconProps) {
   switch (status) {
     case 'success':
       return (
-        <Icon
-          type="check-circle"
-          fw
+        <MaterialIcon
+          type="check_circle"
           className="settings-widget-status-icon status-success"
         />
       )
     case 'error':
       return (
-        <Icon
-          type="times-circle"
-          fw
+        <MaterialIcon
+          type="cancel"
           className="settings-widget-status-icon status-error"
         />
       )
     case 'pending':
-      return (
-        <Icon
-          type="circle"
-          fw
-          className="settings-widget-status-icon status-pending"
-          spin
-        />
-      )
+      return <OLSpinner size="sm" />
     default:
       return null
   }

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

@@ -216,7 +216,7 @@ export default function AddCollaborators({ readOnly }: { readOnly?: boolean }) {
       </OLFormGroup>
 
       <OLFormGroup>
-        <div className="pull-right add-collaborator-controls">
+        <div className="float-end add-collaborator-controls">
           <Select
             dataTestId="add-collaborator-select"
             items={privilegeOptions}

+ 1 - 1
services/web/frontend/js/features/source-editor/components/editor-switch.tsx

@@ -39,7 +39,7 @@ function EditorSwitch() {
       aria-label={t('toolbar_code_visual_editor_switch')}
     >
       <fieldset className="toggle-switch">
-        <legend className="sr-only">Editor mode.</legend>
+        <legend className="visually-hidden">Editor mode.</legend>
 
         <input
           type="radio"

+ 2 - 3
services/web/frontend/js/features/source-editor/components/paste-html/pasted-content-menu.tsx

@@ -7,7 +7,6 @@ import {
   useRef,
   useState,
 } from 'react'
-import Icon from '../../../../shared/components/icon'
 import { useTranslation } from 'react-i18next'
 import { EditorView } from '@codemirror/view'
 import { PastedContent } from '../../extensions/visual/pasted-content'
@@ -135,7 +134,7 @@ export const PastedContentMenu: FC<{
                 }}
               >
                 <span style={{ visibility: formatted ? 'visible' : 'hidden' }}>
-                  <Icon type="check" fw />
+                  <MaterialIcon type="check" />
                 </span>
                 <span className="ol-cm-pasted-content-menu-item-label">
                   {t('paste_with_formatting')}
@@ -155,7 +154,7 @@ export const PastedContentMenu: FC<{
                 }}
               >
                 <span style={{ visibility: formatted ? 'hidden' : 'visible' }}>
-                  <Icon type="check" fw />
+                  <MaterialIcon type="check" />
                 </span>
                 <span className="ol-cm-pasted-content-menu-item-label">
                   {t('paste_without_formatting')}

+ 4 - 1
services/web/frontend/js/features/source-editor/components/table-generator/toolbar/column-width-modal/modal.tsx

@@ -163,7 +163,10 @@ const ColumnWidthModalBody = () => {
                 <Select
                   label={
                     <>
-                      &nbsp;<span className="sr-only">{t('length_unit')}</span>
+                      &nbsp;
+                      <span className="visually-hidden">
+                        {t('length_unit')}
+                      </span>
                     </>
                   }
                   items={UNITS}

+ 2 - 2
services/web/frontend/js/features/source-editor/components/toolbar/section-heading-dropdown.tsx

@@ -13,7 +13,7 @@ import OLPopover from '@/features/ui/components/ol/ol-popover'
 import useEventListener from '../../../../shared/hooks/use-event-listener'
 import useDropdown from '../../../../shared/hooks/use-dropdown'
 import { emitToolbarEvent } from '../../extensions/toolbar/utils/analytics'
-import Icon from '../../../../shared/components/icon'
+import MaterialIcon from '@/shared/components/material-icon'
 import { useTranslation } from 'react-i18next'
 
 const levels = new Map([
@@ -66,7 +66,7 @@ export const SectionHeadingDropdown = () => {
         onClick={() => setOverflowOpen(!overflowOpen)}
       >
         <span>{currentLabel}</span>
-        <Icon type="caret-down" fw />
+        <MaterialIcon type="expand_more" />
       </button>
 
       {overflowOpen && (

+ 3 - 0
services/web/frontend/js/features/source-editor/extensions/visual/pasted-content.tsx

@@ -100,6 +100,9 @@ const pastedContentTheme = EditorView.baseTheme({
     '&:hover': {
       backgroundColor: 'rgba(125, 125, 125, 0.2)',
     },
+    '& .material-symbols': {
+      verticalAlign: 'middle',
+    },
   },
   '.ol-cm-pasted-content-menu-item-label': {
     flex: 1,

+ 3 - 0
services/web/frontend/js/features/source-editor/extensions/visual/visual-theme.ts

@@ -379,6 +379,9 @@ const mainVisualTheme = EditorView.theme({
     display: 'flex',
     justifyContent: 'space-between',
     alignItems: 'center',
+    '& .material-symbols': {
+      verticalAlign: 'middle',
+    },
   },
   '.ol-cm-preamble-expand-icon': {
     width: '32px',

+ 14 - 4
services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/editable-graphics.ts

@@ -2,6 +2,7 @@ import { EditorView } from '@codemirror/view'
 import { GraphicsWidget } from './graphics'
 import { editFigureDataEffect } from '../../figure-modal'
 import { emitToolbarEvent } from '../../toolbar/utils/analytics'
+import { materialIcon } from '@/features/utils/material-icon'
 
 export class EditableGraphicsWidget extends GraphicsWidget {
   setEditDispatcher(button: HTMLButtonElement, view: EditorView) {
@@ -49,10 +50,19 @@ export class EditableGraphicsWidget extends GraphicsWidget {
     const button = document.createElement('button')
     button.setAttribute('aria-label', view.state.phrase('edit_figure'))
     this.setEditDispatcher(button, view)
-    button.classList.add('btn', 'btn-secondary', 'ol-cm-graphics-edit-button')
-    const buttonLabel = document.createElement('span')
-    buttonLabel.classList.add('fa', 'fa-pencil')
-    button.append(buttonLabel)
+    button.classList.add(
+      'btn',
+      'btn-secondary',
+      'ol-cm-graphics-edit-button',
+      'icon-button',
+      'd-inline-grid'
+    )
+
+    const buttonContent = button.appendChild(document.createElement('span'))
+    buttonContent.className = 'button-content'
+
+    buttonContent.appendChild(materialIcon('edit'))
+
     return button
   }
 

+ 37 - 9
services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/preamble.ts

@@ -1,6 +1,34 @@
 import { EditorSelection, StateEffect } from '@codemirror/state'
 import { EditorView, WidgetType } from '@codemirror/view'
 import { SyntaxNode } from '@lezer/common'
+import { materialIcon } from '@/features/utils/material-icon'
+
+function createIcon({
+  type,
+  accessibilityLabel,
+  className,
+}: {
+  type: string
+  accessibilityLabel: string
+  className?: string
+}) {
+  const docFragment = document.createDocumentFragment()
+
+  const buttonIcon = materialIcon(type)
+  if (className) {
+    buttonIcon.classList.add(className)
+  }
+  docFragment.append(buttonIcon)
+
+  if (accessibilityLabel) {
+    const accessibilityLabelEl = document.createElement('span')
+    accessibilityLabelEl.className = 'visually-hidden'
+    accessibilityLabelEl.textContent = accessibilityLabel
+    docFragment.append(accessibilityLabelEl)
+  }
+
+  return docFragment
+}
 
 export type Preamble = {
   from: number
@@ -29,20 +57,20 @@ export class PreambleWidget extends WidgetType {
     const element = document.createElement('div')
     wrapper.appendChild(element)
     element.classList.add('ol-cm-preamble-widget')
-    const expandIcon = document.createElement('i')
-    expandIcon.classList.add(
-      'ol-cm-preamble-expand-icon',
-      'fa',
-      'fa-chevron-down'
-    )
+    const expandIcon = createIcon({
+      type: 'expand_more',
+      accessibilityLabel: view.state.phrase('expand'),
+      className: 'ol-cm-preamble-expand-icon',
+    })
     const helpText = document.createElement('div')
     const helpLink = document.createElement('a')
     helpLink.href =
       '/learn/latex/Learn_LaTeX_in_30_minutes#The_preamble_of_a_document'
     helpLink.target = '_blank'
-    const icon = document.createElement('i')
-    icon.classList.add('fa', 'fa-question-circle')
-    icon.title = view.state.phrase('learn_more')
+    const icon = createIcon({
+      type: 'help',
+      accessibilityLabel: view.state.phrase('learn_more'),
+    })
     helpLink.appendChild(icon)
     const textNode = document.createElement('span')
     textNode.classList.add('ol-cm-preamble-text')

+ 2 - 6
services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/table-rendering-error.ts

@@ -1,5 +1,6 @@
 import { EditorView, WidgetType } from '@codemirror/view'
 import { SyntaxNode } from '@lezer/common'
+import { materialIcon } from '@/features/utils/material-icon'
 
 export class TableRenderingErrorWidget extends WidgetType {
   private hasTableNode: boolean
@@ -14,12 +15,7 @@ export class TableRenderingErrorWidget extends WidgetType {
     warning.role = 'alert'
     const icon = document.createElement('div')
     icon.classList.add('notification-icon')
-    const iconType = document.createElement('span')
-    iconType.classList.add('material-symbols')
-    iconType.setAttribute('aria-hidden', 'true')
-    iconType.setAttribute('translate', 'no')
-    iconType.textContent = 'info'
-    icon.appendChild(iconType)
+    icon.appendChild(materialIcon('info'))
     warning.appendChild(icon)
     const messageWrapper = document.createElement('div')
     messageWrapper.classList.add('notification-content-and-cta')

+ 7 - 5
services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/individual-plans-table.tsx

@@ -1,7 +1,7 @@
 import { useMemo } from 'react'
 import { useTranslation } from 'react-i18next'
 import { Plan } from '../../../../../../../../../types/subscription/plan'
-import Icon from '../../../../../../../shared/components/icon'
+import MaterialIcon from '@/shared/components/material-icon'
 import { useSubscriptionDashboardContext } from '../../../../../context/subscription-dashboard-context'
 import OLButton from '@/features/ui/components/ol/ol-button'
 
@@ -46,16 +46,18 @@ function ChangePlanButton({ plan }: { plan: Plan }) {
     return <KeepCurrentPlanButton />
   } else if (isCurrentPlanForUser && !personalSubscription.pendingPlan) {
     return (
-      <b>
-        <Icon type="check" /> {t('your_plan')}
+      <b className="d-inline-flex align-items-center">
+        <MaterialIcon type="check" />
+        &nbsp;{t('your_plan')}
       </b>
     )
   } else if (
     personalSubscription?.pendingPlan?.planCode?.split('_')[0] === plan.planCode
   ) {
     return (
-      <b>
-        <Icon type="check" /> {t('your_new_plan')}
+      <b className="d-inline-flex align-items-center">
+        <MaterialIcon type="check" />
+        &nbsp;{t('your_new_plan')}
       </b>
     )
   } else {

+ 3 - 3
services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/change-to-group-modal.tsx

@@ -73,7 +73,7 @@ function GroupPrice({
       <span aria-hidden>
         {totalPrice} <span className="small">/ {t('year')}</span>
       </span>
-      <span className="sr-only">
+      <span className="visually-hidden">
         {queryingGroupPlanToChangeToPrice
           ? t('loading_prices')
           : t('x_price_per_year', {
@@ -87,7 +87,7 @@ function GroupPrice({
             price: perUserPrice,
           })}
         </span>
-        <span className="sr-only">
+        <span className="visually-hidden">
           {queryingGroupPlanToChangeToPrice
             ? t('loading_prices')
             : t('x_price_per_user', {
@@ -216,7 +216,7 @@ export function ChangeToGroupModal() {
                   <li>{t('track_changes')}</li>
                   <li>
                     <span aria-hidden>+ {t('more').toLowerCase()}</span>
-                    <span className="sr-only">{t('plus_more')}</span>
+                    <span className="visually-hidden">{t('plus_more')}</span>
                   </li>
                 </ul>
               </div>

+ 3 - 3
services/web/frontend/js/features/token-access/components/token-access-root.tsx

@@ -11,8 +11,8 @@ import {
   RequireAcceptData,
   RequireAcceptScreen,
 } from '@/features/token-access/components/require-accept-screen'
-import Icon from '@/shared/components/icon'
 import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
+import MaterialIcon from '@/shared/components/material-icon'
 
 type Mode = 'access-attempt' | 'v1Import' | 'requireAccept'
 
@@ -111,14 +111,14 @@ function TokenAccessRoot() {
   }
 
   return (
-    <div className="full-size">
+    <div>
       <div>
         <a
           href="/project"
           // TODO: class name
           style={{ fontSize: '2rem', marginLeft: '1rem', color: '#ddd' }}
         >
-          <Icon type="arrow-left" />
+          <MaterialIcon type="arrow_left_alt" style={{ fontSize: 'inherit' }} />
         </a>
       </div>
 

+ 4 - 3
services/web/frontend/js/features/form-helpers/create-icon.js → services/web/frontend/js/features/utils/material-icon.ts

@@ -1,8 +1,9 @@
-export default function createIcon(type) {
+export function materialIcon(type: string) {
   const icon = document.createElement('span')
   icon.className = 'material-symbols'
-  icon.setAttribute('aria-hidden', 'true')
-  icon.setAttribute('translate', 'no')
   icon.textContent = type
+  icon.ariaHidden = 'true'
+  icon.translate = false
+
   return icon
 }

+ 4 - 4
services/web/frontend/js/features/word-count-modal/components/word-counts.tsx

@@ -26,27 +26,27 @@ export const WordCounts: FC<{
 
       <Row>
         <Col xs={4}>
-          <div className="pull-right">{t('total_words')}:</div>
+          <div className="float-end">{t('total_words')}:</div>
         </Col>
         <Col xs={6}>{data.textWords}</Col>
       </Row>
       <Row>
         <Col xs={4}>
-          <div className="pull-right">{t('headers')}:</div>
+          <div className="float-end">{t('headers')}:</div>
         </Col>
         <Col xs={6}>{data.headers}</Col>
       </Row>
 
       <Row>
         <Col xs={4}>
-          <div className="pull-right">{t('math_inline')}:</div>
+          <div className="float-end">{t('math_inline')}:</div>
         </Col>
         <Col xs={6}>{data.mathInline}</Col>
       </Row>
 
       <Row>
         <Col xs={4}>
-          <div className="pull-right">{t('math_display')}:</div>
+          <div className="float-end">{t('math_display')}:</div>
         </Col>
         <Col xs={6}>{data.mathDisplay}</Col>
       </Row>

+ 1 - 2
services/web/frontend/js/shared/components/close.tsx

@@ -12,7 +12,7 @@ function Close({ onDismiss, variant = 'light' }: CloseProps) {
   return (
     <button
       type="button"
-      className={`close pull-right ${variant}`}
+      className={`close float-end ${variant}`}
       onClick={onDismiss}
     >
       <MaterialIcon
@@ -20,7 +20,6 @@ function Close({ onDismiss, variant = 'light' }: CloseProps) {
         className="align-text-bottom"
         accessibilityLabel={t('close')}
       />
-      <span className="sr-only">{t('close')}</span>
     </button>
   )
 }

+ 0 - 10
services/web/frontend/js/shared/components/icon-checked.jsx

@@ -1,10 +0,0 @@
-import { useTranslation } from 'react-i18next'
-
-import Icon from './icon'
-
-function IconChecked() {
-  const { t } = useTranslation()
-  return <Icon type="check" fw accessibilityLabel={t('selected')} />
-}
-
-export default IconChecked

+ 0 - 44
services/web/frontend/js/shared/components/icon.tsx

@@ -1,44 +0,0 @@
-import classNames from 'classnames'
-
-type IconOwnProps = {
-  type: string
-  spin?: boolean
-  fw?: boolean
-  modifier?: string
-  accessibilityLabel?: string
-}
-
-export type IconProps = IconOwnProps &
-  Omit<React.ComponentProps<'i'>, keyof IconOwnProps>
-
-function Icon({
-  type,
-  spin,
-  fw,
-  modifier,
-  className = '',
-  accessibilityLabel,
-  ...rest
-}: IconProps) {
-  const iconClassName = classNames(
-    'fa',
-    `fa-${type}`,
-    {
-      'fa-spin': spin,
-      'fa-fw': fw,
-      [`fa-${modifier}`]: modifier,
-    },
-    className
-  )
-
-  return (
-    <>
-      <i className={iconClassName} aria-hidden="true" {...rest} />
-      {accessibilityLabel && (
-        <span className="visually-hidden">{accessibilityLabel}</span>
-      )}
-    </>
-  )
-}
-
-export default Icon

+ 0 - 23
services/web/frontend/js/shared/components/processing.jsx

@@ -1,23 +0,0 @@
-import PropTypes from 'prop-types'
-import { useTranslation } from 'react-i18next'
-import Icon from './icon'
-
-function Processing({ isProcessing }) {
-  const { t } = useTranslation()
-  if (isProcessing) {
-    return (
-      <div aria-live="polite">
-        {t('processing')}…&nbsp;
-        <Icon type="refresh" fw spin />
-      </div>
-    )
-  } else {
-    return <></>
-  }
-}
-
-Processing.propTypes = {
-  isProcessing: PropTypes.bool.isRequired,
-}
-
-export default Processing

+ 4 - 3
services/web/frontend/js/shared/components/upgrade-benefits.jsx → services/web/frontend/js/shared/components/upgrade-benefits.tsx

@@ -1,16 +1,17 @@
 import MaterialIcon from '@/shared/components/material-icon'
 import { useTranslation } from 'react-i18next'
 import { memo } from 'react'
+import classNames from 'classnames'
 
 function Check() {
   return <MaterialIcon type="check" />
 }
 
-function UpgradeBenefits() {
+function UpgradeBenefits({ className }: { className?: string }) {
   const { t } = useTranslation()
 
   return (
-    <ul className="list-unstyled upgrade-benefits">
+    <ul className={classNames('list-unstyled upgrade-benefits', className)}>
       <li>
         <Check />
         &nbsp;
@@ -19,7 +20,7 @@ function UpgradeBenefits() {
       <li>
         <Check />
         &nbsp;
-        {t('collabs_per_proj', { collabcount: 'Multiple' })}
+        {t('collabs_per_proj_multiple')}
       </li>
       <li>
         <Check />

+ 0 - 50
services/web/frontend/stories/icon.stories.jsx

@@ -1,50 +0,0 @@
-import Icon from '../js/shared/components/icon'
-
-export const Type = args => {
-  return (
-    <>
-      <Icon {...args} />
-      <div>
-        <a
-          href="https://fontawesome.com/v4.7.0/icons/"
-          target="_blank"
-          rel="noopener noreferrer"
-        >
-          Font Awesome icons
-        </a>
-      </div>
-    </>
-  )
-}
-Type.args = {
-  type: 'tasks',
-}
-
-export const Spinner = args => {
-  return <Icon {...args} />
-}
-Spinner.args = {
-  type: 'spinner',
-  spin: true,
-}
-
-export const FixedWidth = args => {
-  return <Icon {...args} />
-}
-FixedWidth.args = {
-  type: 'tasks',
-  fw: true,
-}
-
-export const AccessibilityLabel = args => {
-  return <Icon {...args} />
-}
-AccessibilityLabel.args = {
-  type: 'check',
-  accessibilityLabel: 'Check',
-}
-
-export default {
-  title: 'Shared / Components / Icon',
-  component: Icon,
-}

+ 2 - 2
services/web/frontend/stories/ui/badge.stories.tsx

@@ -1,5 +1,5 @@
 import Badge from '@/features/ui/components/bootstrap-5/badge'
-import Icon from '@/shared/components/icon'
+import MaterialIcon from '@/shared/components/material-icon'
 import type { Meta, StoryObj } from '@storybook/react'
 import classnames from 'classnames'
 
@@ -49,7 +49,7 @@ export const BadgePrepend: Story = {
     return (
       <Badge
         className={classnames({ 'text-dark': args.bg === 'light' })}
-        prepend={<Icon type="star" fw />}
+        prepend={<MaterialIcon type="star" />}
         {...args}
       />
     )

+ 1 - 0
services/web/frontend/stylesheets/bootstrap-5/components/all.scss

@@ -46,3 +46,4 @@
 @import 'upgrade-prompt';
 @import 'integrations-panel';
 @import 'group-members';
+@import 'upgrade-benefits';

+ 3 - 0
services/web/frontend/stylesheets/bootstrap-5/components/upgrade-benefits.scss

@@ -0,0 +1,3 @@
+.upgrade-benefits li {
+  display: flex;
+}

+ 0 - 1
services/web/frontend/stylesheets/bootstrap-5/main-style.scss

@@ -7,7 +7,6 @@
 @import '../../fonts/noto-serif/noto-serif.css';
 @import '../../fonts/open-dyslexic-mono/open-dyslexic-mono.css';
 @import '../../fonts/material-symbols/material-symbols.css';
-@import '../../fonts/font-awesome/font-awesome.css';
 
 // Vendor CSS
 // TODO Bootstrap 5: Check whether this works with Bootstrap 5, and whether we can replace it

+ 0 - 9
services/web/frontend/stylesheets/bootstrap-5/modals/contact-us-modal.scss

@@ -60,15 +60,6 @@
     span {
       text-decoration: underline;
     }
-
-    .fa {
-      color: inherit;
-      text-decoration: none;
-    }
-  }
-
-  .fa {
-    color: var(--neutral-30);
   }
 }
 

+ 0 - 4
services/web/frontend/stylesheets/bootstrap-5/modules/symbol-palette.scss

@@ -199,9 +199,5 @@
 
   .upgrade-benefits {
     column-count: 2;
-
-    li {
-      display: flex;
-    }
   }
 }

+ 9 - 26
services/web/frontend/stylesheets/bootstrap-5/pages/editor/ide.scss

@@ -248,6 +248,15 @@ $editor-toggler-bg-dark-color: color.adjust(
   top: 50%;
   background-color: var(--editor-toggler-bg-color);
 
+  .material-symbols {
+    font-size: var(--font-size-02);
+    -moz-osx-font-smoothing: grayscale;
+    font-weight: bold;
+    color: var(--white);
+    user-select: none;
+    pointer-events: none;
+  }
+
   &:hover,
   &:focus {
     outline: none;
@@ -262,37 +271,11 @@ $editor-toggler-bg-dark-color: color.adjust(
     inset: 0 -3px;
   }
 
-  &::after {
-    font-family: FontAwesome; /* stylelint-disable-line font-family-no-missing-generic-family-keyword */
-    -moz-osx-font-smoothing: grayscale;
-    font-size: 65%;
-    font-weight: bold;
-    color: var(--white);
-    user-select: none;
-    pointer-events: none;
-  }
-
   &:hover {
     background-color: var(--bg-accent-01);
   }
 }
 
-.custom-toggler-east::after {
-  content: '\f105';
-}
-
-.custom-toggler-west::after {
-  content: '\f104';
-}
-
-.custom-toggler-closed.custom-toggler-east::after {
-  content: '\f104';
-}
-
-.custom-toggler-closed.custom-toggler-west::after {
-  content: '\f105';
-}
-
 .vertical-resize-handle {
   height: 6px;
   background-color: var(--editor-resizer-bg-color);

+ 4 - 2
services/web/frontend/stylesheets/core/utilities.less

@@ -11,10 +11,12 @@
 .center-block {
   .center-block();
 }
-.pull-right {
+.pull-right,
+.float-end {
   float: right !important;
 }
-.pull-left {
+.pull-left,
+.float-start {
   float: left !important;
 }
 

+ 1 - 1
services/web/locales/en.json

@@ -355,6 +355,7 @@
   "collaborator_chat": "Collaborator chat",
   "collabratec_account_not_registered": "IEEE Collabratec™ account not registered. Please connect to Overleaf from IEEE Collabratec™ or log in with a different account.",
   "collabs_per_proj": "__collabcount__ collaborators per project",
+  "collabs_per_proj_multiple": "Multiple collaborators per project",
   "collabs_per_proj_single": "__collabcount__ collaborator per project",
   "collapse": "Collapse",
   "column_width": "Column width",
@@ -1475,7 +1476,6 @@
   "notification_features_upgraded_by_affiliation": "Good news! Your affiliated organization __institutionName__ has an Overleaf subscription, and you now have access to all of Overleaf’s Professional features.",
   "notification_personal_and_group_subscriptions": "We’ve spotted that you’ve got <0>more than one active __appName__ subscription</0>. To avoid paying more than you need to, <1>review your subscriptions</1>.",
   "notification_personal_subscription_not_required_due_to_affiliation": " Good news! Your affiliated organization __institutionName__ has an Overleaf subscription, and you now have access to Overleaf’s Professional features through your affiliation. You can cancel your individual subscription without losing access to any features.",
-  "notification_project_invite": "<b>__userName__</b> would like you to join <b>__projectName__</b> <a class=\"btn btn-sm btn-info pull-right\" href=\"/project/__projectId__/invite/token/__token__\">Join Project</a>",
   "notification_project_invite_accepted_message": "You’ve joined <b>__projectName__</b>",
   "notification_project_invite_message": "<b>__userName__</b> would like you to join <b>__projectName__</b>",
   "november": "November",

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

@@ -63,7 +63,7 @@ function RegisterForm({
             aria-label="emails to register"
             aria-describedby="input-details"
           />
-          <p id="input-details" className="sr-only">
+          <p id="input-details" className="visually-hidden">
             Enter the emails you would like to register and separate them using
             commas
           </p>

+ 28 - 9
services/web/test/frontend/features/source-editor/components/codemirror-editor-figure-modal.spec.tsx

@@ -126,9 +126,11 @@ describe('<FigureModal />', function () {
         matchUrl(`/project/test-project/upload?folder_id=${rootFolderId}`)
       )
 
+      // Note that we have to include the 'edit' text from the edit button's
+      // icon, which is literal text in the document
       cy.get('.cm-content').should(
         'have.text',
-        '\\begin{figure}    \\centering    \\caption{Enter Caption}    🏷fig:enter-label\\end{figure}'
+        '\\begin{figure}    \\centeringedit    \\caption{Enter Caption}    🏷fig:enter-label\\end{figure}'
       )
     })
 
@@ -172,9 +174,12 @@ describe('<FigureModal />', function () {
         cy.findByText('frog.jpg').click()
       })
       cy.findByRole('button', { name: 'Insert figure' }).click()
+
+      // Note that we have to include the 'edit' text from the edit button's
+      // icon, which is literal text in the document
       cy.get('.cm-content').should(
         'have.text',
-        '\\begin{figure}    \\centering    \\caption{Enter Caption}    🏷fig:enter-label\\end{figure}'
+        '\\begin{figure}    \\centeringedit    \\caption{Enter Caption}    🏷fig:enter-label\\end{figure}'
       )
     })
   })
@@ -258,9 +263,11 @@ describe('<FigureModal />', function () {
         },
       })
 
+      // Note that we have to include the 'edit' text from the edit button's
+      // icon, which is literal text in the document
       cy.get('.cm-content').should(
         'have.text',
-        '\\begin{figure}    \\centering    \\caption{Enter Caption}    🏷fig:enter-label\\end{figure}'
+        '\\begin{figure}    \\centeringedit    \\caption{Enter Caption}    🏷fig:enter-label\\end{figure}'
       )
     })
 
@@ -285,9 +292,11 @@ describe('<FigureModal />', function () {
         },
       })
 
+      // Note that we have to include the 'edit' text from the edit button's
+      // icon, which is literal text in the document
       cy.get('.cm-content').should(
         'have.text',
-        '\\begin{figure}    \\centering    \\caption{Enter Caption}    🏷fig:enter-label\\end{figure}'
+        '\\begin{figure}    \\centeringedit    \\caption{Enter Caption}    🏷fig:enter-label\\end{figure}'
       )
     })
   })
@@ -429,9 +438,11 @@ describe('<FigureModal />', function () {
         },
       })
 
+      // Note that we have to include the 'edit' text from the edit button's
+      // icon, which is literal text in the document
       cy.get('.cm-content').should(
         'have.text',
-        '\\begin{figure}    \\centering    \\caption{Enter Caption}    🏷fig:enter-label\\end{figure}'
+        '\\begin{figure}    \\centeringedit    \\caption{Enter Caption}    🏷fig:enter-label\\end{figure}'
       )
     })
 
@@ -455,9 +466,12 @@ describe('<FigureModal />', function () {
 
       // If caption is selected then typing will replace the whole caption
       cy.focused().type('My caption')
+
+      // Note that we have to include the 'edit' text from the edit button's
+      // icon, which is literal text in the document
       cy.get('.cm-content').should(
         'have.text',
-        '\\begin{figure}    \\centering    \\caption{My caption}    🏷fig:enter-label\\end{figure}'
+        '\\begin{figure}    \\centeringedit    \\caption{My caption}    🏷fig:enter-label\\end{figure}'
       )
     })
 
@@ -482,9 +496,12 @@ describe('<FigureModal />', function () {
 
       // If label is selected then typing will replace the whole label
       cy.focused().type('fig:my-label')
+
+      // Note that we have to include the 'edit' text from the edit button's
+      // icon, which is literal text in the document
       cy.get('.cm-content').should(
         'have.text',
-        '\\begin{figure}    \\centering    \\label{fig:my-label}\\end{figure}'
+        '\\begin{figure}    \\centeringedit    \\label{fig:my-label}\\end{figure}'
       )
     })
 
@@ -504,16 +521,18 @@ describe('<FigureModal />', function () {
         },
       })
 
+      // Note that we have to include the 'edit' text from the edit button's
+      // icon, which is literal text in the document
       cy.get('.cm-content').should(
         'have.text',
-        '\\begin{figure}    \\centering\\end{figure}'
+        '\\begin{figure}    \\centeringedit\\end{figure}'
       )
 
       cy.focused().type('Some more text')
 
       cy.get('.cm-content').should(
         'have.text',
-        '\\begin{figure}    \\centering\\end{figure}Some more text'
+        '\\begin{figure}    \\centeringedit\\end{figure}Some more text'
       )
     })
   })

+ 0 - 51
services/web/test/frontend/shared/components/icon.test.jsx

@@ -1,51 +0,0 @@
-import { expect } from 'chai'
-import { screen, render } from '@testing-library/react'
-
-import Icon from '../../../../frontend/js/shared/components/icon'
-
-describe('<Icon />', function () {
-  it('renders basic fa classes', function () {
-    const { container } = render(<Icon type="angle-down" />)
-    const element = container.querySelector('i.fa.fa-angle-down')
-    expect(element).to.exist
-  })
-
-  it('renders with aria-hidden', function () {
-    const { container } = render(<Icon type="angle-down" />)
-    const element = container.querySelector('i[aria-hidden="true"]')
-    expect(element).to.exist
-  })
-
-  it('renders accessible label', function () {
-    render(<Icon type="angle-down" accessibilityLabel="Accessible Foo" />)
-    screen.getByText('Accessible Foo')
-  })
-
-  it('renders with spin', function () {
-    const { container } = render(<Icon type="angle-down" spin />)
-    const element = container.querySelector('i.fa.fa-angle-down.fa-spin')
-    expect(element).to.exist
-  })
-
-  it('renders with fw', function () {
-    const { container } = render(<Icon type="angle-down" fw />)
-    const element = container.querySelector('i.fa.fa-angle-down.fa-fw')
-    expect(element).to.exist
-  })
-
-  it('renders with modifier', function () {
-    const { container } = render(<Icon type="angle-down" modifier="2x" />)
-    const element = container.querySelector('i.fa.fa-angle-down.fa-2x')
-    expect(element).to.exist
-  })
-
-  it('renders with custom clases', function () {
-    const { container } = render(
-      <Icon type="angle-down" className="custom-icon-class" />
-    )
-    const element = container.querySelector(
-      'i.fa.fa-angle-down.custom-icon-class'
-    )
-    expect(element).to.exist
-  })
-})

+ 0 - 16
services/web/test/frontend/shared/components/processing.test.jsx

@@ -1,16 +0,0 @@
-import { expect } from 'chai'
-import { render } from '@testing-library/react'
-import Processing from '../../../../frontend/js/shared/components/processing'
-
-describe('<Processing />', function () {
-  it('renders processing UI when isProcessing is true', function () {
-    const { container } = render(<Processing isProcessing />)
-    const element = container.querySelector('i.fa.fa-refresh')
-    expect(element).to.exist
-  })
-  it('does not render processing UI when isProcessing is false', function () {
-    const { container } = render(<Processing isProcessing={false} />)
-    const element = container.querySelector('i.fa.fa-refresh')
-    expect(element).to.not.exist
-  })
-})