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

Merge pull request #21238 from overleaf/rd-github-sync

Migrate the Github Sync modal to Bootstrap 5

GitOrigin-RevId: a6178fc2e9c7072d28d0a1b094b42dff12944781
Rebeka Dekany 1 год назад
Родитель
Сommit
850bbceab6

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

@@ -671,6 +671,7 @@
   "image_url": "",
   "image_width": "",
   "import_a_bibtex_file_from_your_provider_account": "",
+  "import_existing_projects_from_github": "",
   "import_from_github": "",
   "import_idp_metadata": "",
   "import_to_sharelatex": "",
@@ -1124,6 +1125,7 @@
   "publishing": "",
   "pull_github_changes_into_sharelatex": "",
   "push_sharelatex_changes_to_github": "",
+  "push_to_github_pull_to_overleaf": "",
   "quoted_text": "",
   "quoted_text_in": "",
   "raw_logs": "",
@@ -1201,6 +1203,7 @@
   "replace_from_url": "",
   "reply": "",
   "repository_name": "",
+  "repository_visibility": "",
   "republish": "",
   "resend": "",
   "resend_confirmation_code": "",
@@ -1778,7 +1781,9 @@
   "with_premium_subscription_you_also_get": "",
   "word_count": "",
   "work_offline": "",
+  "work_offline_pull_to_overleaf": "",
   "work_with_non_overleaf_users": "",
+  "work_with_other_github_users": "",
   "writefull": "",
   "writefull_learn_more": "",
   "writefull_loading_error_body": "",

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

@@ -24,6 +24,7 @@ import { UserProvider } from '@/shared/context/user-context'
 import OLButton from '@/features/ui/components/ol/ol-button'
 import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
 import OLNotification from '@/features/ui/components/ol/ol-notification'
+import { bsVersion } from '@/features/utils/bootstrap-5'
 
 const educationalPercentDiscount = 40
 const groupSizeForEducationalDiscount = 10
@@ -243,16 +244,22 @@ export function ChangeToGroupModal() {
                   <fieldset className="form-group">
                     <legend className="legend-as-label">{t('plan')}</legend>
                     {groupPlans.plans.map(option => (
-                      <OLFormCheckbox
+                      <div
+                        className={bsVersion({ bs3: 'radio' })}
                         key={option.code}
-                        type="radio"
-                        name="plan-code"
-                        value={option.code}
-                        id={`plan-option-${option.code}`}
-                        onChange={() => setGroupPlanToChangeToCode(option.code)}
-                        checked={option.code === groupPlanToChangeToCode}
-                        label={option.display}
-                      />
+                      >
+                        <OLFormCheckbox
+                          type="radio"
+                          name="plan-code"
+                          value={option.code}
+                          id={`plan-option-${option.code}`}
+                          onChange={() =>
+                            setGroupPlanToChangeToCode(option.code)
+                          }
+                          checked={option.code === groupPlanToChangeToCode}
+                          label={option.display}
+                        />
+                      </div>
                     ))}
                   </fieldset>
 

+ 92 - 7
services/web/frontend/js/features/ui/components/ol/ol-form-checkbox.tsx

@@ -1,11 +1,32 @@
-import { Form } from 'react-bootstrap-5'
-import { Checkbox as BS3Checkbox, Radio as BS3Radio } from 'react-bootstrap'
+import { Form, FormCheckProps } from 'react-bootstrap-5'
+import { Checkbox as BS3Checkbox } from 'react-bootstrap'
 import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
 import { getAriaAndDataProps } from '@/features/utils/bootstrap-5'
+import { MergeAndOverride } from '../../../../../../types/utils'
+import FormText from '../bootstrap-5/form/form-text'
 
-type OLFormCheckboxProps = React.ComponentProps<(typeof Form)['Check']> & {
-  inputRef?: React.MutableRefObject<HTMLInputElement | null>
-  bs3Props?: Record<string, unknown>
+type OLFormCheckboxProps = MergeAndOverride<
+  FormCheckProps,
+  {
+    inputRef?: React.MutableRefObject<HTMLInputElement | null>
+    bs3Props?: Record<PropertyKey, unknown>
+  } & (
+    | { description: string; id: string }
+    | { description?: undefined; id?: string }
+  )
+>
+
+type RadioButtonProps = {
+  checked?: boolean
+  className?: string
+  description?: string
+  disabled?: boolean
+  id: string
+  name?: string
+  onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
+  required?: boolean
+  label: React.ReactElement | string
+  value: string
 }
 
 function OLFormCheckbox(props: OLFormCheckboxProps) {
@@ -38,14 +59,78 @@ function OLFormCheckbox(props: OLFormCheckboxProps) {
     <BootstrapVersionSwitcher
       bs3={
         rest.type === 'radio' ? (
-          <BS3Radio {...bs3FormCheckboxProps} />
+          <BS3Radio {...(rest as RadioButtonProps)} />
         ) : (
           <BS3Checkbox {...bs3FormCheckboxProps} />
         )
       }
-      bs5={<Form.Check ref={inputRef} {...rest} />}
+      bs5={
+        rest.type === 'radio' ? (
+          <Form.Check
+            ref={inputRef}
+            aria-describedby={
+              rest.description ? `${rest.id}-description` : undefined
+            }
+            {...rest}
+            label={
+              <>
+                {rest.label}
+                {rest.description && (
+                  <FormText
+                    id={`${rest.id}-description`}
+                    className="form-check-label-description"
+                  >
+                    {rest.description}
+                  </FormText>
+                )}
+              </>
+            }
+          />
+        ) : (
+          <Form.Check ref={inputRef} {...rest} />
+        )
+      }
     />
   )
 }
 
+function BS3Radio(props: RadioButtonProps) {
+  const {
+    label,
+    checked,
+    className,
+    description,
+    disabled,
+    id,
+    name,
+    onChange,
+    required,
+    value,
+  } = props
+  return (
+    <div className={className}>
+      <input
+        checked={checked}
+        className="me-1"
+        disabled={disabled}
+        id={id}
+        name={name}
+        onChange={onChange}
+        type="radio"
+        required={required}
+        value={value}
+        aria-describedby={description ? `${id}-description` : undefined}
+      />
+      <label htmlFor={id} data-disabled={disabled ? 'true' : undefined}>
+        {label}
+      </label>{' '}
+      {description && (
+        <small id={`${id}-description`} aria-hidden="true">
+          {description}
+        </small>
+      )}
+    </div>
+  )
+}
+
 export default OLFormCheckbox

+ 1 - 1
services/web/frontend/js/shared/components/loading-spinner.tsx

@@ -46,7 +46,7 @@ function LoadingSpinner({
     <div className={classNames('loading', className, extraClasses)}>
       <OLSpinner size={size} />
       &nbsp;
-      {loadingText || t('loading')}…
+      {loadingText || `${t('loading')}…`}
     </div>
   )
 }

+ 3 - 0
services/web/frontend/stylesheets/app/editor.less

@@ -590,6 +590,9 @@ CodeMirror
     background-color: @gray-lightest;
   }
 }
+.teaser-feature-icon {
+  vertical-align: sub;
+}
 
 .teaser-title,
 .dropbox-teaser-title {

+ 7 - 2
services/web/frontend/stylesheets/bootstrap-5/components/form.scss

@@ -99,9 +99,13 @@
   }
 }
 
-.form-text {
-  margin-top: var(--spacing-02);
+.form-check-label-description {
+  .form-text-inner {
+    margin-top: var(--spacing-01) !important;
+  }
+}
 
+.form-text {
   .form-control[disabled] ~ & {
     color: var(--content-disabled);
   }
@@ -110,6 +114,7 @@
     display: flex;
     gap: var(--spacing-02);
     line-height: var(--line-height-02);
+    margin-top: var(--spacing-04);
   }
 }
 

+ 4 - 0
services/web/frontend/stylesheets/bootstrap-5/pages/editor/ide.scss

@@ -282,6 +282,10 @@ $editor-toggler-bg-dark-color: color.adjust(
   inset: 0;
 }
 
+.teaser-feature-icon {
+  vertical-align: sub;
+}
+
 .teaser-title {
   margin-top: 0;
   text-align: center;

+ 5 - 0
services/web/locales/en.json

@@ -949,6 +949,7 @@
   "image_url": "Image URL",
   "image_width": "Image width",
   "import_a_bibtex_file_from_your_provider_account": "Import a BibTeX file from your __provider__ account",
+  "import_existing_projects_from_github": "Import existing projects from GitHub",
   "import_from_github": "Import from GitHub",
   "import_idp_metadata": "Import IdP metadata",
   "import_to_sharelatex": "Import to __appName__",
@@ -1617,6 +1618,7 @@
   "purchase_now": "Purchase Now",
   "purchase_now_lowercase": "Purchase now",
   "push_sharelatex_changes_to_github": "Push __appName__ changes to GitHub",
+  "push_to_github_pull_to_overleaf": "Push to GitHub, pull to __appName__",
   "quoted_text": "Quoted text",
   "quoted_text_in": "Quoted text in",
   "raw_logs": "Raw logs",
@@ -1715,6 +1717,7 @@
   "replace_from_url": "Replace from URL",
   "reply": "Reply",
   "repository_name": "Repository Name",
+  "repository_visibility": "Repository visibility",
   "republish": "Republish",
   "request_new_password_reset_email": "Request a new password reset email",
   "request_overleaf_common": "Request Overleaf Commons",
@@ -2430,8 +2433,10 @@
   "with_premium_subscription_you_also_get": "With an Overleaf Premium subscription you also get",
   "word_count": "Word Count",
   "work_offline": "Work offline",
+  "work_offline_pull_to_overleaf": "Work offline, then pull to __appName__",
   "work_or_university_sso": "Work/university single sign-on",
   "work_with_non_overleaf_users": "Work with non Overleaf users",
+  "work_with_other_github_users": "Work with other GitHub users",
   "would_you_like_to_see_a_university_subscription": "Would you like to see a university-wide __appName__ subscription at your university?",
   "write_and_collaborate_faster_with_features_like": "Write and collaborate faster with features like:",
   "writefull": "Writefull",