]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-21384 Migrating group and actions modal for users page to new UI
authorRevanshu Paliwal <revanshu.paliwal@sonarsource.com>
Wed, 3 Jan 2024 15:57:13 +0000 (16:57 +0100)
committersonartech <sonartech@sonarsource.com>
Thu, 4 Jan 2024 20:02:48 +0000 (20:02 +0000)
server/sonar-web/src/main/js/apps/users/components/DeactivateForm.tsx
server/sonar-web/src/main/js/apps/users/components/GroupsForm.tsx
server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx

index 2e9a8daaaa20bb8ae56918f42ed070c084960e94..40c4f688b6aa21d25b2392dffa17b36fecdeca40 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import {
+  Checkbox,
+  DangerButtonPrimary,
+  FlagMessage,
+  LightPrimary,
+  Link,
+  Modal,
+} from 'design-system';
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
-import DocLink from '../../../components/common/DocLink';
-import Checkbox from '../../../components/controls/Checkbox';
-import Modal from '../../../components/controls/Modal';
-import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
-import { Alert } from '../../../components/ui/Alert';
+import { useDocUrl } from '../../../helpers/docs';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { useDeactivateUserMutation } from '../../../queries/users';
 import { RestUserDetailed } from '../../../types/users';
@@ -33,6 +37,8 @@ export interface Props {
   user: RestUserDetailed;
 }
 
+const DEACTIVATE_FORM_ID = 'deactivate-user-form';
+
 export default function DeactivateForm(props: Props) {
   const { user } = props;
   const [anonymize, setAnonymize] = React.useState(false);
@@ -50,50 +56,45 @@ export default function DeactivateForm(props: Props) {
   };
 
   const header = translate('users.deactivate_user');
+  const docUrl = useDocUrl('/instance-administration/authentication/overview/');
+
   return (
-    <Modal contentLabel={header} onRequestClose={props.onClose}>
-      <form autoComplete="off" id="deactivate-user-form" onSubmit={handleDeactivate}>
-        <header className="modal-head">
-          <h2>{header}</h2>
-        </header>
-        <div className="modal-body display-flex-column">
+    <Modal
+      headerTitle={header}
+      body={
+        <form autoComplete="off" id={DEACTIVATE_FORM_ID} onSubmit={handleDeactivate}>
           {translateWithParameters('users.deactivate_user.confirmation', user.name, user.login)}
           <Checkbox
             id="delete-user"
-            className="big-spacer-top"
+            className="sw-flex sw-items-center sw-mt-4"
             checked={anonymize}
             onCheck={(checked) => setAnonymize(checked)}
           >
-            <label className="little-spacer-left" htmlFor="delete-user">
-              {translate('users.delete_user')}
-            </label>
+            <LightPrimary className="sw-ml-3">{translate('users.delete_user')}</LightPrimary>
           </Checkbox>
           {anonymize && (
-            <Alert variant="warning" className="big-spacer-top">
-              <FormattedMessage
-                defaultMessage={translate('users.delete_user.help')}
-                id="delete-user-warning"
-                values={{
-                  link: (
-                    <DocLink to="/instance-administration/authentication/overview/">
-                      {translate('users.delete_user.help.link')}
-                    </DocLink>
-                  ),
-                }}
-              />
-            </Alert>
+            <FlagMessage variant="warning" className="sw-mt-2">
+              <span>
+                <FormattedMessage
+                  defaultMessage={translate('users.delete_user.help')}
+                  id="delete-user-warning"
+                  values={{
+                    link: <Link to={docUrl}>{translate('users.delete_user.help.link')}</Link>,
+                  }}
+                />
+              </span>
+            </FlagMessage>
           )}
-        </div>
-        <footer className="modal-foot">
-          {isLoading && <i className="spinner spacer-right" />}
-          <SubmitButton className="js-confirm button-red" disabled={isLoading}>
-            {translate('users.deactivate')}
-          </SubmitButton>
-          <ResetButtonLink className="js-modal-close" onClick={props.onClose}>
-            {translate('cancel')}
-          </ResetButtonLink>
-        </footer>
-      </form>
-    </Modal>
+        </form>
+      }
+      onClose={props.onClose}
+      loading={isLoading}
+      primaryButton={
+        <DangerButtonPrimary form={DEACTIVATE_FORM_ID} disabled={isLoading} type="submit">
+          {translate('users.deactivate')}
+        </DangerButtonPrimary>
+      }
+      secondaryButtonLabel={translate('cancel')}
+    />
   );
 }
index a9bb8e7dc133e39ab974ad917f98b6cfdc1c492e..86545b1bd40c89ba4c809cbea9f92965805d321b 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+
+import { LightPrimary, Modal, Note } from 'design-system';
 import { find, without } from 'lodash';
 import * as React from 'react';
 import { UserGroup, getUserGroups } from '../../../api/users';
-import Modal from '../../../components/controls/Modal';
 import SelectList, {
   SelectListFilter,
   SelectListSearchParams,
 } from '../../../components/controls/SelectList';
-import { ResetButtonLink } from '../../../components/controls/buttons';
 import { translate } from '../../../helpers/l10n';
 import { useAddUserToGroupMutation, useRemoveUserToGroupMutation } from '../../../queries/users';
 import { RestUserDetailed } from '../../../types/users';
@@ -88,14 +88,14 @@ export default function GroupsForm(props: Props) {
   const renderElement = (name: string): React.ReactNode => {
     const group = find(groups, { name });
     return (
-      <div className="select-list-list-item">
+      <div>
         {group === undefined ? (
           name
         ) : (
           <>
-            {group.name}
+            <LightPrimary>{group.name}</LightPrimary>
             <br />
-            <span className="note">{group.description}</span>
+            <Note>{group.description}</Note>
           </>
         )}
       </div>
@@ -105,30 +105,28 @@ export default function GroupsForm(props: Props) {
   const header = translate('users.update_groups');
 
   return (
-    <Modal contentLabel={header} onRequestClose={props.onClose}>
-      <div className="modal-head">
-        <h2>{header}</h2>
-      </div>
-
-      <div className="modal-body modal-container">
-        <SelectList
-          elements={groups.map((group) => group.name)}
-          elementsTotalCount={groupsTotalCount}
-          needToReload={
-            needToReload && lastSearchParams && lastSearchParams.filter !== SelectListFilter.All
-          }
-          onSearch={fetchUsers}
-          onSelect={handleSelect}
-          onUnselect={handleUnselect}
-          renderElement={renderElement}
-          selectedElements={selectedGroups}
-          withPaging
-        />
-      </div>
-
-      <footer className="modal-foot">
-        <ResetButtonLink onClick={props.onClose}>{translate('done')}</ResetButtonLink>
-      </footer>
-    </Modal>
+    <Modal
+      headerTitle={header}
+      body={
+        <div className="sw-pt-1">
+          <SelectList
+            elements={groups.map((group) => group.name)}
+            elementsTotalCount={groupsTotalCount}
+            needToReload={
+              needToReload && lastSearchParams && lastSearchParams.filter !== SelectListFilter.All
+            }
+            onSearch={fetchUsers}
+            onSelect={handleSelect}
+            onUnselect={handleUnselect}
+            renderElement={renderElement}
+            selectedElements={selectedGroups}
+            withPaging
+          />
+        </div>
+      }
+      onClose={props.onClose}
+      primaryButton={null}
+      secondaryButtonLabel={translate('done')}
+    />
   );
 }
index e2ba26a65f515cd6ed9da787b0c9688c78a4a747..563cd557aca771862d7fd5d0a2e5a2880e1fadff 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import { ButtonPrimary, FlagMessage, FormField, InputField, Modal } from 'design-system';
 import * as React from 'react';
 import { changePassword } from '../../../api/users';
 import { CurrentUserContext } from '../../../app/components/current-user/CurrentUserContext';
-import Modal from '../../../components/controls/Modal';
-import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
-import { Alert } from '../../../components/ui/Alert';
-import MandatoryFieldMarker from '../../../components/ui/MandatoryFieldMarker';
-import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation';
 import { addGlobalSuccessMessage } from '../../../helpers/globalMessages';
 import { translate } from '../../../helpers/l10n';
 import { ChangePasswordResults, RestUserDetailed, isLoggedIn } from '../../../types/users';
@@ -34,6 +30,8 @@ interface Props {
   user: RestUserDetailed;
 }
 
+const PASSWORD_FORM_ID = 'user-password-form';
+
 export default function PasswordForm(props: Props) {
   const { user } = props;
   const [confirmPassword, setConfirmPassword] = React.useState('');
@@ -76,75 +74,87 @@ export default function PasswordForm(props: Props) {
   const header = translate('my_profile.password.title');
 
   return (
-    <Modal contentLabel={header} onRequestClose={props.onClose} size="small">
-      <form autoComplete="off" id="user-password-form" onSubmit={handleChangePassword}>
-        <header className="modal-head">
-          <h2>{header}</h2>
-        </header>
-        <div className="modal-body">
-          {errorTranslationKey && <Alert variant="error">{translate(errorTranslationKey)}</Alert>}
-
-          <MandatoryFieldsExplanation className="modal-field" />
+    <Modal
+      headerTitle={header}
+      body={
+        <form
+          autoComplete="off"
+          id={PASSWORD_FORM_ID}
+          className="sw-mb-2"
+          onSubmit={handleChangePassword}
+        >
+          {errorTranslationKey && (
+            <FlagMessage variant="error" className="sw-mb-4">
+              {translate(errorTranslationKey)}
+            </FlagMessage>
+          )}
 
           {isCurrentUser && (
-            <div className="modal-field">
-              <label htmlFor="old-user-password">
-                {translate('my_profile.password.old')}
-                <MandatoryFieldMarker />
-              </label>
-              {/* keep this fake field to hack browser autofill */}
-              <input className="hidden" aria-hidden name="old-password-fake" type="password" />
-              <input
+            <FormField
+              htmlFor="old-user-password"
+              label={translate('my_profile.password.old')}
+              required
+            >
+              <InputField
+                autoFocus
                 id="old-user-password"
+                maxLength={255}
                 name="old-password"
                 onChange={(event) => setOldPassword(event.currentTarget.value)}
                 required
+                size="full"
                 type="password"
                 value={oldPassword}
               />
-            </div>
+              <input className="hidden" aria-hidden name="old-password-fake" type="password" />
+            </FormField>
           )}
-          <div className="modal-field">
-            <label htmlFor="user-password">
-              {translate('my_profile.password.new')}
-              <MandatoryFieldMarker />
-            </label>
-            {/* keep this fake field to hack browser autofill */}
-            <input className="hidden" aria-hidden name="password-fake" type="password" />
-            <input
+          <FormField htmlFor="user-password" label={translate('my_profile.password.new')} required>
+            <InputField
+              autoFocus
               id="user-password"
               name="password"
               onChange={(event) => setNewPassword(event.currentTarget.value)}
               required
               type="password"
               value={newPassword}
+              maxLength={255}
+              size="full"
             />
-          </div>
-          <div className="modal-field">
-            <label htmlFor="confirm-user-password">
-              {translate('my_profile.password.confirm')}
-              <MandatoryFieldMarker />
-            </label>
-            {/* keep this fake field to hack browser autofill */}
-            <input className="hidden" aria-hidden name="confirm-password-fake" type="password" />
-            <input
+            <input className="hidden" aria-hidden name="password-fake" type="password" />
+          </FormField>
+          <FormField
+            htmlFor="confirm-user-password"
+            label={translate('my_profile.password.confirm')}
+            required
+          >
+            <InputField
+              autoFocus
               id="confirm-user-password"
               name="confirm-password"
               onChange={(event) => setConfirmPassword(event.currentTarget.value)}
               required
               type="password"
               value={confirmPassword}
+              maxLength={255}
+              size="full"
             />
-          </div>
-        </div>
-        <footer className="modal-foot">
-          {submitting && <i className="spinner spacer-right" />}
-          <SubmitButton disabled={submitting || !newPassword || newPassword !== confirmPassword}>
-            {translate('change_verb')}
-          </SubmitButton>
-          <ResetButtonLink onClick={props.onClose}>{translate('cancel')}</ResetButtonLink>
-        </footer>
-      </form>
-    </Modal>
+            <input className="hidden" aria-hidden name="confirm-password-fake" type="password" />
+          </FormField>
+        </form>
+      }
+      onClose={props.onClose}
+      loading={submitting}
+      primaryButton={
+        <ButtonPrimary
+          form={PASSWORD_FORM_ID}
+          disabled={submitting || !newPassword || newPassword !== confirmPassword}
+          type="submit"
+        >
+          {translate('change_verb')}
+        </ButtonPrimary>
+      }
+      secondaryButtonLabel={translate('cancel')}
+    />
   );
 }