فهرست منبع

fix non-auto fixable biome errors

Futa Arai 3 ماه پیش
والد
کامیت
f453794495
19فایلهای تغییر یافته به همراه298 افزوده شده و 172 حذف شده
  1. 7 14
      apps/app/src/client/components/Admin/AdminHome/AdminHome.jsx
  2. 14 20
      apps/app/src/client/components/Admin/AuditLogManagement.tsx
  3. 6 6
      apps/app/src/client/components/Admin/ElasticsearchManagement/ElasticsearchManagement.tsx
  4. 5 5
      apps/app/src/client/components/Admin/ElasticsearchManagement/StatusTable.jsx
  5. 0 1
      apps/app/src/client/components/Admin/ExportArchiveData/ArchiveFilesTableMenu.tsx
  6. 4 5
      apps/app/src/client/components/Admin/ExportArchiveData/SelectCollectionsModal.tsx
  7. 3 3
      apps/app/src/client/components/Admin/G2GDataTransfer.tsx
  8. 204 97
      apps/app/src/client/components/Admin/G2GDataTransferExportForm.tsx
  9. 4 0
      apps/app/src/client/components/Admin/G2GDataTransferStatusIcon.tsx
  10. 6 0
      apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportCollectionConfigurationModal.jsx
  11. 4 4
      apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportCollectionItem.jsx
  12. 3 3
      apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportForm.jsx
  13. 1 0
      apps/app/src/client/components/Admin/ImportData/GrowiArchiveSection.jsx
  14. 2 0
      apps/app/src/client/components/Admin/LegacySlackIntegration/LegacySlackIntegration.jsx
  15. 19 5
      apps/app/src/client/components/Admin/LegacySlackIntegration/SlackConfiguration.jsx
  16. 2 1
      apps/app/src/client/components/Admin/MarkdownSetting/IndentForm.tsx
  17. 2 0
      apps/app/src/client/components/Admin/MarkdownSetting/LineBreakForm.jsx
  18. 7 5
      apps/app/src/client/components/Admin/MarkdownSetting/WhitelistInput.tsx
  19. 5 3
      apps/app/src/client/components/Admin/UserManagement.tsx

+ 7 - 14
apps/app/src/client/components/Admin/AdminHome/AdminHome.jsx

@@ -1,5 +1,5 @@
 import React, { useCallback, useEffect } from 'react';
-import { useTranslation } from 'next-i18next';
+import { Trans, useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import { CopyToClipboard } from 'react-copy-to-clipboard';
 import { Tooltip } from 'reactstrap';
@@ -98,12 +98,9 @@ const AdminHome = (props) => {
             {t('admin:admin_top.list_of_env_vars')}
           </h2>
           <p>{t('admin:admin_top.env_var_priority')}</p>
-          {/* eslint-disable-next-line react/no-danger */}
-          <p
-            dangerouslySetInnerHTML={{
-              __html: t('admin:admin_top.about_security'),
-            }}
-          />
+          <p>
+            <Trans i18nKey="admin:admin_top.about_security" t={t} />
+          </p>
           <EnvVarsTable envVars={adminHomeContainer.state.envVars} />
         </div>
       </div>
@@ -137,13 +134,9 @@ const AdminHome = (props) => {
             >
               {t('admin:admin_top:copy_prefilled_host_information:done')}
             </Tooltip>
-            {/* eslint-disable-next-line react/no-danger */}
-            <span
-              className="ms-2"
-              dangerouslySetInnerHTML={{
-                __html: t('admin:admin_top:submit_bug_report'),
-              }}
-            />
+            <span className="ms-2">
+              <Trans i18nKey="admin:admin_top:submit_bug_report" t={t} />
+            </span>
           </div>
         </div>
       </div>

+ 14 - 20
apps/app/src/client/components/Admin/AuditLogManagement.tsx

@@ -112,16 +112,18 @@ export const AuditLogManagement: FC = () => {
       actionMap.set(action, !actionMap.get(action));
       setActionMap(new Map(actionMap.entries()));
     },
-    [actionMap, setActionMap],
+    [actionMap],
   );
 
   const multipleActionCheckboxChangedHandler = useCallback(
     (actions: SupportedActionType[], isChecked) => {
       setActivePageNumber(1);
-      actions.forEach((action) => actionMap.set(action, isChecked));
+      actions.forEach((action) => {
+        actionMap.set(action, isChecked);
+      });
       setActionMap(new Map(actionMap.entries()));
     },
-    [actionMap, setActionMap],
+    [actionMap],
   );
 
   const setUsernamesHandler = useCallback((usernames: string[]) => {
@@ -143,14 +145,7 @@ export const AuditLogManagement: FC = () => {
         ),
       );
     }
-  }, [
-    setActivePageNumber,
-    setStartDate,
-    setEndDate,
-    setSelectedUsernames,
-    setActionMap,
-    auditLogAvailableActionsData,
-  ]);
+  }, [auditLogAvailableActionsData]);
 
   const reloadButtonPushedHandler = useCallback(() => {
     setActivePageNumber(1);
@@ -175,7 +170,7 @@ export const AuditLogManagement: FC = () => {
         setJumpPageNumber(activePageNumber);
       }
     },
-    [totalPagingPages, activePageNumber, setJumpPageNumber],
+    [totalPagingPages, activePageNumber],
   );
 
   const jumpPageInputKeyDownHandler = useCallback(
@@ -184,15 +179,15 @@ export const AuditLogManagement: FC = () => {
         setActivePageNumber(jumpPageNumber);
       }
     },
-    [setActivePageNumber, jumpPageNumber],
+    [jumpPageNumber],
   );
 
   const jumpPageButtonPushedHandler = useCallback(() => {
     setActivePageNumber(jumpPageNumber);
   }, [jumpPageNumber]);
 
-  // eslint-disable-next-line max-len
-  const activityCounter = `<b>${activityList.length === 0 ? 0 : offset + 1}</b> - <b>${PAGING_LIMIT * activePageNumber - (PAGING_LIMIT - activityList.length)}</b> of <b>${totalActivityNum}<b/>`;
+  const startIndex = activityList.length === 0 ? 0 : offset + 1;
+  const endIndex = activityList.length === 0 ? 0 : offset + activityList.length;
 
   if (!auditLogEnabled) {
     return <AuditLogDisableMode />;
@@ -275,11 +270,10 @@ export const AuditLogManagement: FC = () => {
             </div>
           </div>
 
-          <p
-            className="ms-2"
-            // eslint-disable-next-line react/no-danger
-            dangerouslySetInnerHTML={{ __html: activityCounter }}
-          />
+          <p className="ms-2">
+            <strong>{startIndex}</strong> - <strong>{endIndex}</strong> of{' '}
+            <strong>{totalActivityNum}</strong>
+          </p>
 
           {isLoading ? (
             <div className="text-muted text-center mb-5">

+ 6 - 6
apps/app/src/client/components/Admin/ElasticsearchManagement/ElasticsearchManagement.tsx

@@ -172,9 +172,9 @@ const ElasticsearchManagement = (): JSX.Element => {
 
       {/* Controls */}
       <div className="row">
-        <label className="col-md-3 col-form-label text-start text-md-end">
+        <div className="col-md-3 col-form-label text-start text-md-end">
           {t('full_text_search_management.reconnect')}
-        </label>
+        </div>
         <div className="col-md-6">
           <ReconnectControls
             isEnabled={isReconnectBtnEnabled}
@@ -187,9 +187,9 @@ const ElasticsearchManagement = (): JSX.Element => {
       <hr />
 
       <div className="row">
-        <label className="col-md-3 col-form-label text-start text-md-end">
+        <div className="col-md-3 col-form-label text-start text-md-end">
           {t('full_text_search_management.normalize')}
-        </label>
+        </div>
         <div className="col-md-6">
           <NormalizeIndicesControls
             isRebuildingProcessing={isRebuildingProcessing}
@@ -202,9 +202,9 @@ const ElasticsearchManagement = (): JSX.Element => {
       <hr />
 
       <div className="row">
-        <label className="col-md-3 col-form-label text-start text-md-end">
+        <div className="col-md-3 col-form-label text-start text-md-end">
           {t('full_text_search_management.rebuild')}
-        </label>
+        </div>
         <div className="col-md-6">
           <RebuildIndexControls
             isRebuildingProcessing={isRebuildingProcessing}

+ 5 - 5
apps/app/src/client/components/Admin/ElasticsearchManagement/StatusTable.jsx

@@ -85,17 +85,17 @@ class StatusTable extends React.PureComponent {
     return (
       <div className="card">
         <div className="card-header">
-          <a
-            role="button"
-            className="text-nowrap me-2"
+          <button
+            type="button"
+            className="text-nowrap me-2 btn btn-link p-0"
             data-bs-toggle="collapse"
-            href={`#${collapseId}`}
+            data-bs-target={`#${collapseId}`}
             aria-expanded="true"
             aria-controls={collapseId}
           >
             <span className="material-symbols-outlined">database</span>{' '}
             {indexName}
-          </a>
+          </button>
           <span className="ms-md-3">{aliasLabels}</span>
         </div>
         <div id={collapseId} className="collapse">

+ 0 - 1
apps/app/src/client/components/Admin/ExportArchiveData/ArchiveFilesTableMenu.tsx

@@ -41,7 +41,6 @@ const ArchiveFilesTableMenu = (
         <button
           type="button"
           className="dropdown-item"
-          role="button"
           onClick={() => props.onZipFileStatRemove(props.fileName)}
         >
           <span className="text-danger">

+ 4 - 5
apps/app/src/client/components/Admin/ExportArchiveData/SelectCollectionsModal.tsx

@@ -1,5 +1,5 @@
 import React, { type JSX, useCallback, useEffect, useState } from 'react';
-import { useTranslation } from 'next-i18next';
+import { Trans, useTranslation } from 'next-i18next';
 import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap';
 
 import { apiPost } from '~/client/util/apiv1-client';
@@ -121,13 +121,12 @@ const SelectCollectionsModal = (props: Props): JSX.Element => {
       return <></>;
     }
 
-    const html = t('admin:export_management.desc_password_seed');
-
     return (
       <div className="card">
         <div className="card-body">
-          {/* eslint-disable-next-line react/no-danger */}
-          <p className="card-text" dangerouslySetInnerHTML={{ __html: html }} />
+          <p className="card-text">
+            <Trans i18nKey="admin:export_management.desc_password_seed" t={t} />
+          </p>
         </div>
       </div>
     );

+ 3 - 3
apps/app/src/client/components/Admin/G2GDataTransfer.tsx

@@ -101,7 +101,7 @@ const G2GDataTransfer = (): JSX.Element => {
         toastError(t(key));
       });
     }
-  }, [socket, t, setTransferring, setG2GProgress]);
+  }, [socket, t]);
 
   const cleanUpWebsocketEventHandler = useCallback(() => {
     if (socket != null) {
@@ -135,7 +135,7 @@ const G2GDataTransfer = (): JSX.Element => {
         toastError(errs);
       }
     },
-    [setTransferring, startTransferKey, selectedCollections, optionsMap],
+    [startTransferKey, selectedCollections, optionsMap],
   );
 
   const documentationUrl = useGrowiDocumentationUrl();
@@ -332,7 +332,7 @@ const G2GDataTransfer = (): JSX.Element => {
         </p>
         <p
           className="mb-0"
-          // eslint-disable-next-line react/no-danger
+          // biome-ignore lint/security/noDangerouslySetInnerHtml: translation contains HTML link
           dangerouslySetInnerHTML={{
             __html: t('commons:g2g_data_transfer.transfer_to_growi_cloud', {
               documentationUrl,

+ 204 - 97
apps/app/src/client/components/Admin/G2GDataTransferExportForm.tsx

@@ -44,6 +44,164 @@ type Props = {
   updateOptionsMap: (newOptionsMap: any) => void;
 };
 
+type ImportItemsProps = {
+  collectionNames: string[];
+  selectedCollections: Set<string>;
+  optionsMap: Record<string, GrowiArchiveImportOption>;
+  onToggleCollection: (collectionName: string, isChecked: boolean) => void;
+  onOptionChange: (collectionName: string, data: any) => void;
+  onConfigButtonClicked: (collectionName: string) => void;
+};
+
+const ImportItems = ({
+  collectionNames,
+  selectedCollections,
+  optionsMap,
+  onToggleCollection,
+  onOptionChange,
+  onConfigButtonClicked,
+}: ImportItemsProps): JSX.Element => {
+  return (
+    <div className="row">
+      {collectionNames.map((collectionName) => {
+        const isConfigButtonAvailable = Object.keys(
+          IMPORT_OPTION_CLASS_MAPPING,
+        ).includes(collectionName);
+
+        if (optionsMap[collectionName] == null) {
+          return null;
+        }
+
+        return (
+          <div className="col-md-6 my-1" key={collectionName}>
+            <ImportCollectionItem
+              isImporting={false}
+              isImported={false}
+              insertedCount={0}
+              modifiedCount={0}
+              errorsCount={0}
+              collectionName={collectionName}
+              isSelected={selectedCollections.has(collectionName)}
+              option={optionsMap[collectionName]}
+              isConfigButtonAvailable={isConfigButtonAvailable}
+              // events
+              onChange={onToggleCollection}
+              onOptionChange={onOptionChange}
+              onConfigButtonClicked={onConfigButtonClicked}
+              // TODO: show progress
+              isHideProgress
+            />
+          </div>
+        );
+      })}
+    </div>
+  );
+};
+
+type WarnForGroupsProps = {
+  errors: Error[];
+};
+
+const WarnForGroups = ({ errors }: WarnForGroupsProps): JSX.Element => {
+  if (errors.length === 0) {
+    return <></>;
+  }
+
+  return (
+    <div className="alert alert-warning">
+      <ul>
+        {errors.map((error, index) => {
+          return <li key={`${error.message}-${index}`}>{error.message}</li>;
+        })}
+      </ul>
+    </div>
+  );
+};
+
+type GroupImportItemsProps = {
+  groupList: string[];
+  groupName: string;
+  errors: Error[];
+  allCollectionNames: string[];
+  selectedCollections: Set<string>;
+  optionsMap: Record<string, GrowiArchiveImportOption>;
+  onToggleCollection: (collectionName: string, isChecked: boolean) => void;
+  onOptionChange: (collectionName: string, data: any) => void;
+  onConfigButtonClicked: (collectionName: string) => void;
+};
+
+const GroupImportItems = ({
+  groupList,
+  groupName,
+  errors,
+  allCollectionNames,
+  selectedCollections,
+  optionsMap,
+  onToggleCollection,
+  onOptionChange,
+  onConfigButtonClicked,
+}: GroupImportItemsProps): JSX.Element => {
+  const collectionNames = groupList.filter((groupCollectionName) => {
+    return allCollectionNames.includes(groupCollectionName);
+  });
+
+  if (collectionNames.length === 0) {
+    return <></>;
+  }
+
+  return (
+    <div className="mt-4">
+      <legend>{groupName} Collections</legend>
+      <ImportItems
+        collectionNames={collectionNames}
+        selectedCollections={selectedCollections}
+        optionsMap={optionsMap}
+        onToggleCollection={onToggleCollection}
+        onOptionChange={onOptionChange}
+        onConfigButtonClicked={onConfigButtonClicked}
+      />
+      <WarnForGroups errors={errors} />
+    </div>
+  );
+};
+
+type OtherImportItemsProps = {
+  allCollectionNames: string[];
+  selectedCollections: Set<string>;
+  optionsMap: Record<string, GrowiArchiveImportOption>;
+  onToggleCollection: (collectionName: string, isChecked: boolean) => void;
+  onOptionChange: (collectionName: string, data: any) => void;
+  onConfigButtonClicked: (collectionName: string) => void;
+};
+
+const OtherImportItems = ({
+  allCollectionNames,
+  selectedCollections,
+  optionsMap,
+  onToggleCollection,
+  onOptionChange,
+  onConfigButtonClicked,
+}: OtherImportItemsProps): JSX.Element => {
+  const collectionNames = allCollectionNames.filter((collectionName) => {
+    return !ALL_GROUPED_COLLECTIONS.includes(collectionName);
+  });
+
+  // TODO: エラー対応
+  return (
+    <GroupImportItems
+      groupList={collectionNames}
+      groupName="Other"
+      errors={[]}
+      allCollectionNames={allCollectionNames}
+      selectedCollections={selectedCollections}
+      optionsMap={optionsMap}
+      onToggleCollection={onToggleCollection}
+      onOptionChange={onOptionChange}
+      onConfigButtonClicked={onConfigButtonClicked}
+    />
+  );
+};
+
 const G2GDataTransferExportForm = (props: Props): JSX.Element => {
   const { t } = useTranslation('admin');
 
@@ -83,8 +241,8 @@ const G2GDataTransferExportForm = (props: Props): JSX.Element => {
     [optionsMap, updateOptionsMap],
   );
 
-  const ImportItems = ({ collectionNames }): JSX.Element => {
-    const toggleCheckbox = (collectionName, bool) => {
+  const toggleCheckbox = useCallback(
+    (collectionName, bool) => {
       const collections = new Set(selectedCollections);
       if (bool) {
         collections.add(collectionName);
@@ -96,98 +254,14 @@ const G2GDataTransferExportForm = (props: Props): JSX.Element => {
 
       // TODO: validation
       // this.validate();
-    };
-
-    const openConfigurationModal = (collectionName) => {
-      setConfigurationModalOpen(true);
-      setCollectionNameForConfiguration(collectionName);
-    };
-
-    return (
-      <div className="row">
-        {collectionNames.map((collectionName) => {
-          const isConfigButtonAvailable = Object.keys(
-            IMPORT_OPTION_CLASS_MAPPING,
-          ).includes(collectionName);
-
-          if (optionsMap[collectionName] == null) {
-            return null;
-          }
-
-          return (
-            <div className="col-md-6 my-1" key={collectionName}>
-              <ImportCollectionItem
-                isImporting={false}
-                isImported={false}
-                insertedCount={0}
-                modifiedCount={0}
-                errorsCount={0}
-                collectionName={collectionName}
-                isSelected={selectedCollections.has(collectionName)}
-                option={optionsMap[collectionName]}
-                isConfigButtonAvailable={isConfigButtonAvailable}
-                // events
-                onChange={toggleCheckbox}
-                onOptionChange={updateOption}
-                onConfigButtonClicked={openConfigurationModal}
-                // TODO: show progress
-                isHideProgress
-              />
-            </div>
-          );
-        })}
-      </div>
-    );
-  };
-
-  const WarnForGroups = ({ errors }: { errors: Error[] }): JSX.Element => {
-    if (errors.length === 0) {
-      return <></>;
-    }
-
-    return (
-      <div className="alert alert-warning">
-        <ul>
-          {errors.map((error) => {
-            return <li>{error.message}</li>;
-          })}
-        </ul>
-      </div>
-    );
-  };
-
-  const GroupImportItems = ({ groupList, groupName, errors }): JSX.Element => {
-    const collectionNames = groupList.filter((groupCollectionName) => {
-      return allCollectionNames.includes(groupCollectionName);
-    });
-
-    if (collectionNames.length === 0) {
-      return <></>;
-    }
-
-    return (
-      <div className="mt-4">
-        <legend>{groupName} Collections</legend>
-        <ImportItems collectionNames={collectionNames} />
-        <WarnForGroups errors={errors} />
-      </div>
-    );
-  };
-
-  const OtherImportItems = (): JSX.Element => {
-    const collectionNames = allCollectionNames.filter((collectionName) => {
-      return !ALL_GROUPED_COLLECTIONS.includes(collectionName);
-    });
+    },
+    [selectedCollections, updateSelectedCollections],
+  );
 
-    // TODO: エラー対応
-    return (
-      <GroupImportItems
-        groupList={collectionNames}
-        groupName="Other"
-        errors={[]}
-      />
-    );
-  };
+  const openConfigurationModal = useCallback((collectionName) => {
+    setConfigurationModalOpen(true);
+    setCollectionNameForConfiguration(collectionName);
+  }, []);
 
   const configurationModal = useMemo(() => {
     if (collectionNameForConfiguration == null) {
@@ -229,7 +303,7 @@ const G2GDataTransferExportForm = (props: Props): JSX.Element => {
 
   useEffect(() => {
     setInitialOptionsMap();
-  }, []);
+  }, [setInitialOptionsMap]);
 
   return (
     <>
@@ -286,14 +360,47 @@ const G2GDataTransferExportForm = (props: Props): JSX.Element => {
       </div>
 
       {/* TODO: エラー追加 */}
-      <GroupImportItems groupList={GROUPS_PAGE} groupName="Page" errors={[]} />
-      <GroupImportItems groupList={GROUPS_USER} groupName="User" errors={[]} />
+      <GroupImportItems
+        groupList={GROUPS_PAGE}
+        groupName="Page"
+        errors={[]}
+        allCollectionNames={allCollectionNames}
+        selectedCollections={selectedCollections}
+        optionsMap={optionsMap}
+        onToggleCollection={toggleCheckbox}
+        onOptionChange={updateOption}
+        onConfigButtonClicked={openConfigurationModal}
+      />
+      <GroupImportItems
+        groupList={GROUPS_USER}
+        groupName="User"
+        errors={[]}
+        allCollectionNames={allCollectionNames}
+        selectedCollections={selectedCollections}
+        optionsMap={optionsMap}
+        onToggleCollection={toggleCheckbox}
+        onOptionChange={updateOption}
+        onConfigButtonClicked={openConfigurationModal}
+      />
       <GroupImportItems
         groupList={GROUPS_CONFIG}
         groupName="Config"
         errors={[]}
+        allCollectionNames={allCollectionNames}
+        selectedCollections={selectedCollections}
+        optionsMap={optionsMap}
+        onToggleCollection={toggleCheckbox}
+        onOptionChange={updateOption}
+        onConfigButtonClicked={openConfigurationModal}
+      />
+      <OtherImportItems
+        allCollectionNames={allCollectionNames}
+        selectedCollections={selectedCollections}
+        optionsMap={optionsMap}
+        onToggleCollection={toggleCheckbox}
+        onOptionChange={updateOption}
+        onConfigButtonClicked={openConfigurationModal}
       />
-      <OtherImportItems />
 
       {configurationModal}
     </>

+ 4 - 0
apps/app/src/client/components/Admin/G2GDataTransferStatusIcon.tsx

@@ -35,6 +35,7 @@ const G2GDataTransferStatusIcon = ({
     return (
       <span
         className={`material-symbols-outlined text-info ${className}`}
+        role="img"
         aria-label="completed"
         {...props}
       >
@@ -47,6 +48,7 @@ const G2GDataTransferStatusIcon = ({
     return (
       <span
         className={`material-symbols-outlined text-danger ${className}`}
+        role="img"
         aria-label="error"
         {...props}
       >
@@ -59,6 +61,7 @@ const G2GDataTransferStatusIcon = ({
     return (
       <span
         className={`material-symbols-outlined ${className}`}
+        role="img"
         aria-label="skipped"
         {...props}
       >
@@ -70,6 +73,7 @@ const G2GDataTransferStatusIcon = ({
   return (
     <span
       className={`material-symbols-outlined ${className}`}
+      role="img"
       aria-label="pending"
       {...props}
     >

+ 6 - 0
apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportCollectionConfigurationModal.jsx

@@ -74,6 +74,7 @@ class ImportCollectionConfigurationModal extends React.Component {
             {t(`${translationBase}.overwrite_author.label`)}
             <p
               className="form-text text-muted mt-0"
+              // biome-ignore lint/security/noDangerouslySetInnerHtml: translation contains HTML markup
               dangerouslySetInnerHTML={{
                 __html: t(`${translationBase}.overwrite_author.desc`),
               }}
@@ -98,6 +99,7 @@ class ImportCollectionConfigurationModal extends React.Component {
             })}
             <p
               className="form-text text-muted mt-0"
+              // biome-ignore lint/security/noDangerouslySetInnerHtml: translation contains HTML markup
               dangerouslySetInnerHTML={{
                 __html: t(`${translationBase}.set_public_to_page.desc`, {
                   from: t('Anyone with the link'),
@@ -124,6 +126,7 @@ class ImportCollectionConfigurationModal extends React.Component {
             })}
             <p
               className="form-text text-muted mt-0"
+              // biome-ignore lint/security/noDangerouslySetInnerHtml: translation contains HTML markup
               dangerouslySetInnerHTML={{
                 __html: t(`${translationBase}.set_public_to_page.desc`, {
                   from: t('Only me'),
@@ -150,6 +153,7 @@ class ImportCollectionConfigurationModal extends React.Component {
             })}
             <p
               className="form-text text-muted mt-0"
+              // biome-ignore lint/security/noDangerouslySetInnerHtml: translation contains HTML markup
               dangerouslySetInnerHTML={{
                 __html: t(`${translationBase}.set_public_to_page.desc`, {
                   from: t('Only inside the group'),
@@ -174,6 +178,7 @@ class ImportCollectionConfigurationModal extends React.Component {
             {t(`${translationBase}.initialize_meta_datas.label`)}
             <p
               className="form-text text-muted mt-0"
+              // biome-ignore lint/security/noDangerouslySetInnerHtml: translation contains HTML markup
               dangerouslySetInnerHTML={{
                 __html: t(`${translationBase}.initialize_meta_datas.desc`),
               }}
@@ -212,6 +217,7 @@ class ImportCollectionConfigurationModal extends React.Component {
             {t(`${translationBase}.overwrite_author.label`)}
             <p
               className="form-text text-muted mt-0"
+              // biome-ignore lint/security/noDangerouslySetInnerHtml: translation contains HTML markup
               dangerouslySetInnerHTML={{
                 __html: t(`${translationBase}.overwrite_author.desc`),
               }}

+ 4 - 4
apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportCollectionItem.jsx

@@ -221,15 +221,15 @@ export default class ImportCollectionItem extends React.Component {
         </span>
         ,&nbsp;
         {errorsCount > 0 ? (
-          <a
-            className="text-danger"
-            role="button"
+          <button
+            type="button"
+            className="btn btn-link text-danger p-0"
             onClick={this.errorLinkClickedHandler}
           >
             <u>
               <strong>{errorsCount}</strong> Failed
             </u>
-          </a>
+          </button>
         ) : (
           <span className="text-muted">
             <strong>0</strong> Failed

+ 3 - 3
apps/app/src/client/components/Admin/ImportData/GrowiArchive/ImportForm.jsx

@@ -97,6 +97,7 @@ class ImportForm extends React.Component {
     return Object.keys(this.state.collectionNameToFileNameMap);
   }
 
+  // biome-ignore lint/correctness/noNestedComponentDefinitions: lifecycle method on a class component
   UNSAFE_componentWillMount() {
     this.setupWebsocketEventHandler();
   }
@@ -366,9 +367,8 @@ class ImportForm extends React.Component {
     return (
       <div key={key} className="alert alert-warning">
         <ul>
-          {errors.map((error, index) => {
-            // eslint-disable-next-line react/no-array-index-key
-            return <li key={`${key}-${index}`}>{error}</li>;
+          {errors.map((error) => {
+            return <li key={`${key}-${String(error)}`}>{error}</li>;
           })}
         </ul>
       </div>

+ 1 - 0
apps/app/src/client/components/Admin/ImportData/GrowiArchiveSection.jsx

@@ -28,6 +28,7 @@ class GrowiArchiveSection extends React.Component {
       this.renderDefferentVersionAlert.bind(this);
   }
 
+  // biome-ignore lint/correctness/noNestedComponentDefinitions: lifecycle method on a class component
   async UNSAFE_componentWillMount() {
     // get uploaded file status
     const res = await apiv3Get('/import/status');

+ 2 - 0
apps/app/src/client/components/Admin/LegacySlackIntegration/LegacySlackIntegration.jsx

@@ -40,6 +40,7 @@ const LegacySlackIntegration = (props) => {
           <span className="material-symbols-outlined">remove</span>
           {/* eslint-disable-next-line react/no-danger */}
           <span
+            // biome-ignore lint/security/noDangerouslySetInnerHtml: translation contains HTML markup
             dangerouslySetInnerHTML={{
               __html: t('admin:slack_integration_legacy.alert_disabled'),
             }}
@@ -51,6 +52,7 @@ const LegacySlackIntegration = (props) => {
         <span className="material-symbols-outlined">info</span>
         {/* eslint-disable-next-line react/no-danger */}
         <span
+          // biome-ignore lint/security/noDangerouslySetInnerHtml: translation contains HTML markup
           dangerouslySetInnerHTML={{
             __html: t('admin:slack_integration_legacy.alert_deplicated'),
           }}

+ 19 - 5
apps/app/src/client/components/Admin/LegacySlackIntegration/SlackConfiguration.jsx

@@ -64,6 +64,7 @@ const SlackConfiguration = (props) => {
               </button>
               <div
                 className="dropdown-menu"
+                role="menu"
                 aria-labelledby="dropdownMenuButton"
               >
                 <button
@@ -100,11 +101,15 @@ const SlackConfiguration = (props) => {
             </h2>
 
             <div className="row mb-3">
-              <label className="form-label col-md-3 text-start text-md-end">
+              <label
+                className="form-label col-md-3 text-start text-md-end"
+                htmlFor="webhookUrl"
+              >
                 Webhook URL
               </label>
               <div className="col-md-6">
                 <input
+                  id="webhookUrl"
                   className="form-control"
                   type="text"
                   {...register('webhookUrl')}
@@ -154,6 +159,7 @@ const SlackConfiguration = (props) => {
               <br />
               {/* eslint-disable-next-line react/no-danger */}
               <span
+                // biome-ignore lint/security/noDangerouslySetInnerHtml: translation contains HTML markup
                 dangerouslySetInnerHTML={{
                   __html: t(
                     'notification_settings.slack_app_configuration_desc',
@@ -161,9 +167,11 @@ const SlackConfiguration = (props) => {
                 }}
               />
               <br />
-              <a
-                href="#slack-incoming-webhooks"
+              <button
+                type="button"
+                className="btn btn-link p-0"
                 data-bs-toggle="tab"
+                data-bs-target="#slack-incoming-webhooks"
                 onClick={() =>
                   adminSlackIntegrationLegacyContainer.switchSlackOption(
                     'Incoming Webhooks',
@@ -171,15 +179,19 @@ const SlackConfiguration = (props) => {
                 }
               >
                 {t('notification_settings.use_instead')}
-              </a>
+              </button>
             </div>
 
             <div className="row mb-5 mt-4">
-              <label className="form-label col-md-3 text-start text-md-end">
+              <label
+                className="form-label col-md-3 text-start text-md-end"
+                htmlFor="slackToken"
+              >
                 OAuth access token
               </label>
               <div className="col-md-6">
                 <input
+                  id="slackToken"
                   className="form-control"
                   type="text"
                   {...register('slackToken')}
@@ -214,6 +226,7 @@ const SlackConfiguration = (props) => {
             <ol>
               {/* eslint-disable-next-line react/no-danger */}
               <li
+                // biome-ignore lint/security/noDangerouslySetInnerHtml: translation contains HTML markup
                 dangerouslySetInnerHTML={{
                   __html: t('notification_settings.how_to.workspace_desc1'),
                 }}
@@ -227,6 +240,7 @@ const SlackConfiguration = (props) => {
             <ol>
               {/* eslint-disable-next-line react/no-danger */}
               <li
+                // biome-ignore lint/security/noDangerouslySetInnerHtml: translation contains HTML markup
                 dangerouslySetInnerHTML={{
                   __html: t('notification_settings.how_to.at_growi_desc'),
                 }}

+ 2 - 1
apps/app/src/client/components/Admin/MarkdownSetting/IndentForm.tsx

@@ -71,7 +71,7 @@ const IndentForm = (props: Props) => {
                       adminMarkDownContainer.setAdminPreferredIndentSize(num)
                     }
                   >
-                    <a role="menuitem">{num}</a>
+                    <span>{num}</span>
                   </DropdownItem>
                 );
               })}
@@ -116,6 +116,7 @@ const IndentForm = (props: Props) => {
         </div>
         <p
           className="form-text text-muted"
+          // biome-ignore lint/security/noDangerouslySetInnerHtml: translation contains HTML markup
           dangerouslySetInnerHTML={helpIndentInComment}
         />
       </div>

+ 2 - 0
apps/app/src/client/components/Admin/MarkdownSetting/LineBreakForm.jsx

@@ -67,6 +67,7 @@ class LineBreakForm extends React.Component {
         </div>
         <p
           className="form-text text-muted"
+          // biome-ignore lint/security/noDangerouslySetInnerHtml: translation contains HTML markup
           dangerouslySetInnerHTML={helpLineBreak}
         />
       </div>
@@ -108,6 +109,7 @@ class LineBreakForm extends React.Component {
         </div>
         <p
           className="form-text text-muted"
+          // biome-ignore lint/security/noDangerouslySetInnerHtml: translation contains HTML markup
           dangerouslySetInnerHTML={helpLineBreakInComment}
         />
       </div>

+ 7 - 5
apps/app/src/client/components/Admin/MarkdownSetting/WhitelistInput.tsx

@@ -40,7 +40,8 @@ export const WhitelistInput = (props: Props): JSX.Element => {
       <div className="mt-4">
         <div className="d-flex justify-content-between">
           {t('markdown_settings.xss_options.tag_names')}
-          <p
+          <button
+            type="button"
             id="btn-import-tags"
             className="btn btn-sm btn-primary"
             onClick={clickRecommendTagButtonHandler}
@@ -48,7 +49,7 @@ export const WhitelistInput = (props: Props): JSX.Element => {
             {t('markdown_settings.xss_options.import_recommended', {
               target: 'Tags',
             })}
-          </p>
+          </button>
         </div>
         <textarea
           className="form-control xss-list"
@@ -60,15 +61,16 @@ export const WhitelistInput = (props: Props): JSX.Element => {
       <div className="mt-4">
         <div className="d-flex justify-content-between">
           {t('markdown_settings.xss_options.tag_attributes')}
-          <p
-            id="btn-import-tags"
+          <button
+            type="button"
+            id="btn-import-attrs"
             className="btn btn-sm btn-primary"
             onClick={clickRecommendAttrButtonHandler}
           >
             {t('markdown_settings.xss_options.import_recommended', {
               target: 'Attrs',
             })}
-          </p>
+          </button>
         </div>
         <textarea
           className="form-control xss-list"

+ 5 - 3
apps/app/src/client/components/Admin/UserManagement.tsx

@@ -157,8 +157,10 @@ const UserManagement = (props: UserManagementProps) => {
                 onChange={changeSearchTextHandler}
               />
               {adminUsersContainer.state.searchText.length > 0 ? (
-                <span
-                  className="material-symbols-outlined me-1 search-clear"
+                <button
+                  type="button"
+                  className="btn btn-link p-0 material-symbols-outlined me-1 search-clear"
+                  aria-label={t('commons:Clear')}
                   onClick={async () => {
                     await adminUsersContainer.clearSearchText();
                     if (inputRef.current != null) {
@@ -167,7 +169,7 @@ const UserManagement = (props: UserManagementProps) => {
                   }}
                 >
                   cancel
-                </span>
+                </button>
               ) : (
                 ''
               )}