Improvement to OLButton loading labels (#28659)

* Create eslint rule for requiring loadingLabel prop when isLoading is specified on OLButton

* Add `loadingLabel` props for OLButton components with `isLoading`

* Clarify loading label and button loading state

GitOrigin-RevId: 89279d5b4c346f9c3b67a59d0db822a2ff04314a
This commit is contained in:
Rebeka Dekany
2025-09-25 11:45:47 +02:00
committed by Copybot
parent ab84e48545
commit aebff54a6b
28 changed files with 59 additions and 6 deletions

View File

@@ -531,6 +531,7 @@ module.exports = {
rules: {
'@overleaf/no-unnecessary-trans': 'error',
'@overleaf/should-unescape-trans': 'error',
'@overleaf/require-loading-label': 'error',
// https://astexplorer.net/
'no-restricted-syntax': [

View File

@@ -45,6 +45,7 @@
"accept_selected_changes": "",
"accept_terms_and_conditions": "",
"accepted_invite": "",
"accepting": "",
"access_all_premium_features": "",
"access_all_premium_features_including_more_collaborators_real_time_track_changes_and_a_longer_compile_time": "",
"access_denied": "",
@@ -268,6 +269,7 @@
"cite_faster": "",
"clear_cached_files": "",
"clear_search": "",
"clearing": "",
"click_here_to_view_sl_in_lng": "",
"click_to_unpause": "",
"clicking_delete_will_remove_sso_config_and_clear_saml_data": "",
@@ -359,6 +361,7 @@
"create_project_in_github": "",
"created": "",
"created_at": "",
"creating": "",
"cross_reference": "",
"current_file": "",
"current_password": "",
@@ -646,6 +649,7 @@
"generate_from_text_or_image": "",
"generate_tables_and_equations": "",
"generate_token": "",
"generating": "",
"generic_if_problem_continues_contact_us": "",
"generic_linked_file_compile_error": "",
"generic_something_went_wrong": "",
@@ -1393,9 +1397,11 @@
"recompile_from_scratch": "",
"recompile_pdf": "",
"reconfirm_secondary_email": "",
"reconfirming": "",
"reconnect": "",
"reconnecting": "",
"reconnecting_in_x_secs": "",
"recovering": "",
"recurly_email_update_needed": "",
"recurly_email_updated": "",
"redirect_to_editor": "",
@@ -1417,6 +1423,7 @@
"refresh_page_after_starting_free_trial": "",
"refreshing": "",
"regards": "",
"registering": "",
"reject_change": "",
"reject_selected_changes": "",
"relink_your_account": "",
@@ -1471,9 +1478,11 @@
"restore_file_version": "",
"restore_project_to_this_version": "",
"restore_this_version": "",
"restoring": "",
"resync_completed": "",
"resync_message": "",
"resync_project_history": "",
"resyncing": "",
"retry_test": "",
"reverse_x_sort_order": "",
"revert_pending_plan_change": "",
@@ -1576,6 +1585,7 @@
"send_first_message": "",
"send_message": "",
"send_request": "",
"sending": "",
"server_error": "",
"server_pro_license_entitlement_line_1": "",
"server_pro_license_entitlement_line_2": "",
@@ -1721,6 +1731,7 @@
"subject_area": "",
"subject_to_additional_vat": "",
"submit_title": "",
"submitting": "",
"subscribe": "",
"subscribe_to_find_the_symbols_you_need_faster": "",
"subscribe_to_plan": "",
@@ -2005,6 +2016,7 @@
"unlinking": "",
"unmerge_cells": "",
"unpause_subscription": "",
"unpausing": "",
"unpublish": "",
"unpublishing": "",
"unsubscribe": "",

View File

@@ -77,6 +77,7 @@ export function FileTreeModalCreateFileFooterContent({
form="create-file"
disabled={inFlight || !valid}
isLoading={inFlight}
loadingLabel={t('creating')}
>
{t('create')}
</OLButton>

View File

@@ -85,7 +85,12 @@ function FileTreeModalCreateFolder() {
<OLModalFooter>
{inFlight ? (
<OLButton variant="primary" disabled isLoading={inFlight} />
<OLButton
variant="primary"
disabled
isLoading={inFlight}
loadingLabel={t('creating')}
/>
) : (
<>
<OLButton variant="secondary" onClick={handleHide}>

View File

@@ -59,7 +59,12 @@ function FileTreeModalDelete() {
<OLModalFooter>
{inFlight ? (
<OLButton variant="danger" disabled isLoading />
<OLButton
variant="danger"
disabled
isLoading
loadingLabel={t('deleting')}
/>
) : (
<>
<OLButton variant="secondary" onClick={handleHide}>

View File

@@ -265,6 +265,7 @@ export function ManagersTable({
variant="primary"
onClick={addManagers}
isLoading={inviteUserInflightCount > 0}
loadingLabel={t('adding')}
>
{t('add')}
</OLButton>

View File

@@ -128,6 +128,7 @@ export default function RemoveManagedUserModal({
variant="danger"
disabled={isLoading || isSuccess || !shouldEnableRemoveUserButton}
isLoading={isLoading}
loadingLabel={t('removing')}
>
{t('remove_user')}
</OLButton>

View File

@@ -118,6 +118,7 @@ function AddLabelModal({ show, setShow, version }: AddLabelModalProps) {
variant="primary"
disabled={isLoading || !comment.length}
isLoading={isLoading}
loadingLabel={t('adding')}
>
{t('history_add_label')}
</OLButton>

View File

@@ -122,6 +122,7 @@ const ChangeTag = forwardRef<HTMLElement, TagProps>(
variant="danger"
disabled={isLoading}
isLoading={isLoading}
loadingLabel={t('deleting')}
onClick={localDeleteHandler}
>
{t('history_delete_label')}

View File

@@ -51,6 +51,7 @@ export const RestoreProjectModal = ({
onClick={onRestore}
disabled={isRestoring}
isLoading={isRestoring}
loadingLabel={t('restoring')}
>
{t('restore')}
</OLButton>

View File

@@ -20,6 +20,7 @@ export default function ToolbarRestoreFileButton({
size="sm"
className="history-react-toolbar-restore-file-button"
isLoading={isLoading}
loadingLabel={t('restoring')}
onClick={() => restoreDeletedFile(selection)}
>
{t('restore_file')}

View File

@@ -37,6 +37,7 @@ function ToolbarRestoreFileToVersionButton({
variant="secondary"
size="sm"
isLoading={isLoading}
loadingLabel={t('restoring')}
onClick={() => setShowConfirmModal(true)}
>
{t('restore_file_version')}

View File

@@ -36,6 +36,7 @@ function DetachCompileButton() {
})}
size="sm"
isLoading={compiling}
loadingLabel={t('compiling')}
>
{t('recompile')}
</OLButton>

View File

@@ -129,6 +129,7 @@ export default function CreateTagModal({
status === 'pending' || !tagName?.length || !!validationError
}
isLoading={isLoading}
loadingLabel={t('creating')}
>
{t('create')}
</OLButton>

View File

@@ -72,6 +72,7 @@ export default function DeleteTagModal({
variant="danger"
disabled={isLoading}
isLoading={isLoading}
loadingLabel={t('deleting')}
>
{t('delete')}
</OLButton>

View File

@@ -139,6 +139,7 @@ export function EditTagModal({ id, tag, onEdit, onClose }: EditTagModalProps) {
!!validationError
}
isLoading={isLoading}
loadingLabel={t('saving')}
>
{t('save')}
</OLButton>

View File

@@ -127,6 +127,7 @@ export function ManageTagModal({
className="me-auto"
disabled={isDeleteLoading || isUpdateLoading}
isLoading={isDeleteLoading}
loadingLabel={t('deleting')}
>
{t('delete_tag')}
</OLButton>
@@ -147,6 +148,7 @@ export function ManageTagModal({
(newTagName === tag?.name && selectedColor === getTagColor(tag))
)}
isLoading={isUpdateLoading}
loadingLabel={t('saving')}
>
{t('save_or_cancel-save')}
</OLButton>

View File

@@ -109,6 +109,7 @@ function ModalContentNewProjectForm({ onCancel, template = 'none' }: Props) {
onClick={createNewProject}
disabled={projectName === '' || isLoading || redirecting}
isLoading={isLoading}
loadingLabel={t('creating')}
>
{t('create')}
</OLButton>

View File

@@ -58,6 +58,7 @@ function ReconfirmAffiliation({
<OLButton
variant="secondary"
isLoading={isPending}
loadingLabel={t('reconfirming')}
disabled={isPending}
onClick={() => {
setIsPending(true)

View File

@@ -1,4 +1,5 @@
import OLButton, { OLButtonProps } from '@/shared/components/ol/ol-button'
import { useTranslation } from 'react-i18next'
function PrimaryButton({
children,
@@ -6,11 +7,13 @@ function PrimaryButton({
isLoading,
onClick,
}: OLButtonProps) {
const { t } = useTranslation()
return (
<OLButton
size="sm"
disabled={disabled && !isLoading}
isLoading={isLoading}
loadingLabel={t('processing')}
onClick={onClick}
variant="secondary"
>

View File

@@ -22,6 +22,7 @@ function AddNewEmailBtn({
variant="primary"
disabled={(disabled && !isLoading) || !isValidEmail(email)}
isLoading={isLoading}
loadingLabel={t('adding')}
{...props}
>
{t('add_new_email')}

View File

@@ -96,6 +96,7 @@ function ResendConfirmationCodeModal({
variant={triggerVariant}
disabled={groupLoading}
isLoading={isLoading}
loadingLabel={t('sending')}
onClick={handleResendConfirmationEmail}
className={triggerVariant === 'link' ? 'btn-inline-link' : undefined}
>

View File

@@ -31,6 +31,7 @@ function ReactivateSubscription() {
disabled={isLoading || isSuccess}
onClick={handleReactivate}
isLoading={isLoading}
loadingLabel={t('reactivating')}
>
{t('reactivate_subscription')}
</OLButton>

View File

@@ -93,6 +93,7 @@ export function ConfirmUnpauseSubscriptionModal() {
variant="primary"
disabled={inflight}
isLoading={inflight}
loadingLabel={t('unpausing')}
onClick={handleConfirmUnpause}
>
{t('unpause_subscription')}

View File

@@ -845,6 +845,7 @@
"generate_from_text_or_image": "From text or image",
"generate_tables_and_equations": "Generate tables and equations from text and images. Try it for free in the Overleaf toolbar!",
"generate_token": "Generate token",
"generating": "Generating",
"generic_if_problem_continues_contact_us": "If the problem continues please contact us",
"generic_linked_file_compile_error": "This projects output files are not available because it failed to compile. Please open the project to see the compilation error details.",
"generic_something_went_wrong": "Sorry, something went wrong",
@@ -1813,6 +1814,7 @@
"reconfirm_account": "Reconfirm account",
"reconfirm_explained": "We need to reconfirm your account. Please request a password reset link via the form below to reconfirm your account. If you have any problems reconfirming your account, please contact us at",
"reconfirm_secondary_email": "To enhance the security of your __appName__ account, please reconfirm your secondary email address __emailAddress__.",
"reconfirming": "Reconfirming",
"reconnect": "Try again",
"reconnecting": "Reconnecting",
"reconnecting_in_x_secs": "Reconnecting in __seconds__ secs",
@@ -1925,6 +1927,7 @@
"resync_completed": "Resync completed!",
"resync_message": "Resyncing project history can take several minutes depending on the size of the project.",
"resync_project_history": "Resync Project History",
"resyncing": "Resyncing",
"retry_test": "Retry test",
"return_to_login_page": "Return to Login page",
"reverse_x_sort_order": "Reverse __x__ sort order",
@@ -2507,7 +2510,6 @@
"uncategorized_projects": "Uncategorized Projects",
"unconfirmed": "Unconfirmed",
"undelete": "Undelete",
"undeleting": "Undeleting",
"understanding_labels": "Understanding labels",
"undo": "Undo",
"unfold_line": "Unfold line",
@@ -2541,6 +2543,7 @@
"unlinking": "Unlinking",
"unmerge_cells": "Unmerge cells",
"unpause_subscription": "Unpause subscription",
"unpausing": "Unpausing",
"unpublish": "Unpublish",
"unpublishing": "Unpublishing",
"unsubscribe": "Unsubscribe",

View File

@@ -78,7 +78,11 @@ function RegisterForm({
lg={4}
className="mt-3 mt-lg-0 d-flex align-items-center flex-column flex-lg-row"
>
<OLButton type="submit" isLoading={isLoading}>
<OLButton
type="submit"
isLoading={isLoading}
loadingLabel={t('registering')}
>
Register
</OLButton>
</OLCol>

View File

@@ -884,7 +884,7 @@ describe('<UserNotifications />', function () {
screen.getByRole('button', { name: 'Send confirmation code' })
)
await waitForElementToBeRemoved(() => screen.getByText(/loading/i))
await waitForElementToBeRemoved(() => screen.getByText(/sending/i))
screen.getByText(/Enter the 6-digit code sent to foo@overleaf.com/i)
expect(sendReconfirmationMock.callHistory.called()).to.be.true
fireEvent.click(

View File

@@ -220,7 +220,7 @@ describe('<EmailsSection />', function () {
await waitForElementToBeRemoved(() =>
screen.getByRole('button', {
name: 'Loading',
name: /adding/i,
})
)