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

Remove Font Awesome in BS5

GitOrigin-RevId: 3ee9b136ac9ecda57ba9985d1da7d7a7e6b275e6
This commit is contained in:
Tim Down
2025-07-17 09:03:55 +01:00
committed by Copybot
parent c368d44609
commit 8a75434552
55 changed files with 192 additions and 367 deletions
@@ -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
@@ -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')
@@ -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'
@@ -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'
@@ -272,6 +272,7 @@
"collaborate_online_and_offline": "",
"collaborator_chat": "",
"collabs_per_proj": "",
"collabs_per_proj_multiple": "",
"collabs_per_proj_single": "",
"collapse": "",
"column_width": "",
@@ -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)
@@ -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>
)
@@ -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>
)}
@@ -102,6 +102,7 @@ function setupSearch(formEl) {
// display initial results
handleChange()
updateClearBtnVisibility()
}
document.querySelectorAll('[data-ol-faq-search]').forEach(setupSearch)
@@ -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')
@@ -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)
@@ -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>
@@ -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;
@@ -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"
@@ -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"
@@ -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>
)
}
@@ -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>
)}
@@ -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({
@@ -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 && (
@@ -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>}
@@ -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
}
@@ -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}
@@ -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"
@@ -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')}
@@ -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}
@@ -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 && (
@@ -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,
@@ -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',
@@ -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
}
@@ -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')
@@ -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')
@@ -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 {
@@ -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>
@@ -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>
@@ -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
}
@@ -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>
@@ -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>
)
}
@@ -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
@@ -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
@@ -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
@@ -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 />
@@ -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,
}
@@ -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}
/>
)
@@ -46,3 +46,4 @@
@import 'upgrade-prompt';
@import 'integrations-panel';
@import 'group-members';
@import 'upgrade-benefits';
@@ -0,0 +1,3 @@
.upgrade-benefits li {
display: flex;
}
@@ -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
@@ -60,15 +60,6 @@
span {
text-decoration: underline;
}
.fa {
color: inherit;
text-decoration: none;
}
}
.fa {
color: var(--neutral-30);
}
}
@@ -199,9 +199,5 @@
.upgrade-benefits {
column-count: 2;
li {
display: flex;
}
}
}
@@ -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);
@@ -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
View File
@@ -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 Overleafs Professional features.",
"notification_personal_and_group_subscriptions": "Weve spotted that youve 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 Overleafs 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": "Youve joined <b>__projectName__</b>",
"notification_project_invite_message": "<b>__userName__</b> would like you to join <b>__projectName__</b>",
"november": "November",
@@ -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>
@@ -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'
)
})
})
@@ -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
})
})
@@ -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
})
})