From 80e5b85977523f14c1bb14ea90cb0cc73f4aa59d Mon Sep 17 00:00:00 2001
From: Jay <jeremy.davis@sonarsource.com>
Date: Mon, 13 Sep 2021 15:55:09 +0200
Subject: SONAR-15366 Add Settings Search feature

---
 .../settings/components/SettingsSearchRenderer.tsx | 103 +++++++++++++++++++++
 1 file changed, 103 insertions(+)
 create mode 100644 server/sonar-web/src/main/js/apps/settings/components/SettingsSearchRenderer.tsx

(limited to 'server/sonar-web/src/main/js/apps/settings/components/SettingsSearchRenderer.tsx')

diff --git a/server/sonar-web/src/main/js/apps/settings/components/SettingsSearchRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/SettingsSearchRenderer.tsx
new file mode 100644
index 00000000000..c52febbcb5c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/settings/components/SettingsSearchRenderer.tsx
@@ -0,0 +1,103 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as classNames from 'classnames';
+import * as React from 'react';
+import { Link } from 'react-router';
+import { DropdownOverlay } from '../../../components/controls/Dropdown';
+import OutsideClickHandler from '../../../components/controls/OutsideClickHandler';
+import SearchBox from '../../../components/controls/SearchBox';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { scrollToElement } from '../../../helpers/scrolling';
+import { SettingCategoryDefinition } from '../../../types/settings';
+import { buildSettingLink, isRealSettingKey } from '../utils';
+
+export interface SettingsSearchRendererProps {
+  className?: string;
+  results?: SettingCategoryDefinition[];
+  searchQuery: string;
+  selectedResult?: string;
+  showResults: boolean;
+  onClickOutside: () => void;
+  onMouseOverResult: (key: string) => void;
+  onSearchInputChange: (query: string) => void;
+  onSearchInputFocus: () => void;
+  onSearchInputKeyDown: (event: React.KeyboardEvent) => void;
+}
+
+export default function SettingsSearchRenderer(props: SettingsSearchRendererProps) {
+  const { className, results, searchQuery, selectedResult, showResults } = props;
+
+  const scrollableNodeRef = React.useRef(null);
+  const selectedNodeRef = React.useRef<HTMLLIElement>(null);
+
+  React.useEffect(() => {
+    const parent = scrollableNodeRef.current;
+    const selectedNode = selectedNodeRef.current;
+    if (selectedNode && parent) {
+      scrollToElement(selectedNode, { topOffset: 30, bottomOffset: 30, parent });
+    }
+  });
+
+  return (
+    <OutsideClickHandler onClickOutside={props.onClickOutside}>
+      <div className={classNames('dropdown', className)}>
+        <SearchBox
+          onChange={props.onSearchInputChange}
+          onFocus={props.onSearchInputFocus}
+          onKeyDown={props.onSearchInputKeyDown}
+          placeholder={translate('settings.search.placeholder')}
+          value={searchQuery}
+        />
+        {showResults && (
+          <DropdownOverlay noPadding={true}>
+            <ul className="settings-search-results menu" ref={scrollableNodeRef}>
+              {results && results.length > 0 ? (
+                results.map(r => (
+                  <li
+                    key={r.key}
+                    className={classNames('spacer-bottom spacer-top', {
+                      active: selectedResult === r.key
+                    })}
+                    ref={selectedResult === r.key ? selectedNodeRef : undefined}>
+                    <Link
+                      onClick={props.onClickOutside}
+                      onMouseEnter={() => props.onMouseOverResult(r.key)}
+                      to={buildSettingLink(r)}>
+                      <div className="settings-search-result-title display-flex-space-between">
+                        <h3>{r.name || r.subCategory}</h3>
+                      </div>
+                      {isRealSettingKey(r.key) && (
+                        <div className="note spacer-top">
+                          {translateWithParameters('settings.key_x', r.key)}
+                        </div>
+                      )}
+                    </Link>
+                  </li>
+                ))
+              ) : (
+                <div className="big-padded">{translate('no_results')}</div>
+              )}
+            </ul>
+          </DropdownOverlay>
+        )}
+      </div>
+    </OutsideClickHandler>
+  );
+}
-- 
cgit v1.2.3

