]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6400 allow to show more items in a facet (#597)
authorStas Vilchik <stas.vilchik@sonarsource.com>
Wed, 8 Aug 2018 16:46:50 +0000 (18:46 +0200)
committerSonarTech <sonartech@sonarsource.com>
Tue, 21 Aug 2018 18:21:02 +0000 (20:21 +0200)
server/sonar-web/src/main/js/apps/coding-rules/components/LanguageFacet.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/TagFacet.tsx
server/sonar-web/src/main/js/components/facet/FacetItem.tsx
server/sonar-web/src/main/js/components/facet/ListStyleFacet.tsx
server/sonar-web/src/main/js/components/facet/ListStyleFacetFooter.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/__tests__/ListStyleFacet-test.tsx
server/sonar-web/src/main/js/components/facet/__tests__/ListStyleFacetFooter-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/FacetItem-test.tsx.snap
server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/ListStyleFacet-test.tsx.snap
server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/ListStyleFacetFooter-test.tsx.snap [new file with mode: 0644]
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 2157620ad2f6c05cac2a59f1cddd03d669be334d..fcb95135e1c77fec096677cf0fa430fab6cec04c 100644 (file)
@@ -76,6 +76,9 @@ class LanguageFacet extends React.PureComponent<Props> {
         getFacetItemText={this.getLanguageName}
         getSearchResultKey={(language: InstalledLanguage) => language.key}
         getSearchResultText={(language: InstalledLanguage) => language.name}
+        // TODO use defaults when rules search WS is updated
+        maxInitialItems={10}
+        maxItems={10}
         onChange={this.props.onChange}
         onSearch={this.handleSearch}
         onToggle={this.props.onToggle}
index be3049145fa895f3e63b3d880af9ad0876d6c63f..491a8e5e4dbff6cb2c17666f50ffa17023eb4750 100644 (file)
@@ -69,6 +69,9 @@ export default class TagFacet extends React.PureComponent<Props> {
         getFacetItemText={this.getTagName}
         getSearchResultKey={tag => tag}
         getSearchResultText={tag => tag}
+        // TODO use defaults when rules search WS is updated
+        maxInitialItems={10}
+        maxItems={10}
         onChange={this.props.onChange}
         onSearch={this.handleSearch}
         onToggle={this.props.onToggle}
index 0f86901fffba175d73237b841c03c4b609e1e191..70beec9c901261f6d52a069b637bf6ec7317703f 100644 (file)
@@ -55,7 +55,7 @@ export default class FacetItem extends React.PureComponent<Props> {
     });
 
     return this.props.disabled ? (
-      <span className={className} data-facet={this.props.value}>
+      <span className={className} data-facet={this.props.value} title={this.props.tooltip}>
         <span className="facet-name">{name}</span>
         {this.props.stat != null && <span className="facet-stat">{this.props.stat}</span>}
       </span>
index ae2222ea4cb79d3e72aa7fbd45f70032ab62824d..4b0bc9c93ac4ab075ecb8911507729d12a885cf4 100644 (file)
@@ -23,6 +23,7 @@ import FacetBox from './FacetBox';
 import FacetHeader from './FacetHeader';
 import FacetItem from './FacetItem';
 import FacetItemsList from './FacetItemsList';
+import ListStyleFacetFooter from './ListStyleFacetFooter';
 import MultipleSelectionHint from './MultipleSelectionHint';
 import { translate } from '../../helpers/l10n';
 import DeferredSpinner from '../common/DeferredSpinner';
@@ -38,6 +39,8 @@ export interface Props<S> {
   getSearchResultKey: (result: S) => string;
   getSearchResultText: (result: S) => string;
   loading?: boolean;
+  maxInitialItems?: number;
+  maxItems?: number;
   onChange: (changes: { [x: string]: string | string[] }) => void;
   onSearch: (query: string, page?: number) => Promise<{ results: S[]; paging: Paging }>;
   onToggle: (property: string) => void;
@@ -46,25 +49,32 @@ export interface Props<S> {
   renderFacetItem: (item: string) => React.ReactNode;
   renderSearchResult: (result: S, query: string) => React.ReactNode;
   searchPlaceholder: string;
-  values: string[];
   stats: { [x: string]: number } | undefined;
+  values: string[];
 }
 
 interface State<S> {
   autoFocus: boolean;
   query: string;
   searching: boolean;
-  searchResults?: S[];
   searchPaging?: Paging;
+  searchResults?: S[];
+  showFullList: boolean;
 }
 
 export default class ListStyleFacet<S> extends React.Component<Props<S>, State<S>> {
   mounted = false;
 
+  static defaultProps = {
+    maxInitialItems: 15,
+    maxItems: 100
+  };
+
   state: State<S> = {
     autoFocus: false,
     query: '',
-    searching: false
+    searching: false,
+    showFullList: false
   };
 
   componentDidMount() {
@@ -72,9 +82,18 @@ export default class ListStyleFacet<S> extends React.Component<Props<S>, State<S
   }
 
   componentDidUpdate(prevProps: Props<S>) {
-    // focus search field *only* if it was manually open
     if (!prevProps.open && this.props.open) {
+      // focus search field *only* if it was manually open
       this.setState({ autoFocus: true });
+    } else if (prevProps.open && !this.props.open) {
+      // reset state when closing the facet
+      this.setState({ query: '', searchResults: undefined, searching: false, showFullList: false });
+    } else if (
+      prevProps.stats !== this.props.stats &&
+      Object.keys(this.props.stats || {}).length < this.props.maxInitialItems!
+    ) {
+      // show limited list if `stats` changed and there are less than 15 items
+      this.setState({ showFullList: false });
     }
   }
 
@@ -139,11 +158,29 @@ export default class ListStyleFacet<S> extends React.Component<Props<S>, State<S
     }
   };
 
-  getStat(item: string) {
+  getStat(item: string, zeroIfAbsent = false) {
     const { stats } = this.props;
-    return stats ? stats[item] : undefined;
+    const defaultValue = zeroIfAbsent ? 0 : undefined;
+    return stats && stats[item] !== undefined ? stats && stats[item] : defaultValue;
   }
 
+  showFullList = () => {
+    this.setState({ showFullList: true });
+  };
+
+  hideFullList = () => {
+    this.setState({ showFullList: false });
+  };
+
+  getLastActiveIndex = (list: string[]) => {
+    for (let i = list.length - 1; i >= 0; i--) {
+      if (this.props.values.includes(list[i])) {
+        return i;
+      }
+    }
+    return 0;
+  };
+
   renderList() {
     const { stats } = this.props;
 
@@ -151,27 +188,51 @@ export default class ListStyleFacet<S> extends React.Component<Props<S>, State<S
       return null;
     }
 
-    const items = sortBy(
+    const sortedItems = sortBy(
       Object.keys(stats),
       key => -stats[key],
       key => this.props.getFacetItemText(key)
     );
 
+    // limit the number of items to this.props.maxInitialItems,
+    // but make sure all (in other words, the last) selected items are displayed
+    const lastSelectedIndex = this.getLastActiveIndex(sortedItems);
+    const countToDisplay = Math.max(this.props.maxInitialItems!, lastSelectedIndex + 1);
+    const limitedList = this.state.showFullList
+      ? sortedItems
+      : sortedItems.slice(0, countToDisplay);
+
+    const mightHaveMoreResults = sortedItems.length >= this.props.maxItems!;
+
     return (
-      <FacetItemsList>
-        {items.map(item => (
-          <FacetItem
-            active={this.props.values.includes(item)}
-            key={item}
-            loading={this.props.loading}
-            name={this.props.renderFacetItem(item)}
-            onClick={this.handleItemClick}
-            stat={formatFacetStat(this.getStat(item))}
-            tooltip={this.props.getFacetItemText(item)}
-            value={item}
-          />
-        ))}
-      </FacetItemsList>
+      <>
+        <FacetItemsList>
+          {limitedList.map(item => (
+            <FacetItem
+              active={this.props.values.includes(item)}
+              key={item}
+              loading={this.props.loading}
+              name={this.props.renderFacetItem(item)}
+              onClick={this.handleItemClick}
+              stat={formatFacetStat(this.getStat(item))}
+              tooltip={this.props.getFacetItemText(item)}
+              value={item}
+            />
+          ))}
+        </FacetItemsList>
+        <ListStyleFacetFooter
+          count={limitedList.length}
+          showLess={this.state.showFullList ? this.hideFullList : undefined}
+          showMore={this.showFullList}
+          total={sortedItems.length}
+        />
+        {mightHaveMoreResults &&
+          this.state.showFullList && (
+            <div className="alert alert-warning spacer-top">
+              {translate('facet_might_have_more_results')}
+            </div>
+          )}
+      </>
     );
   }
 
@@ -211,6 +272,7 @@ export default class ListStyleFacet<S> extends React.Component<Props<S>, State<S
           {searchResults.map(result => this.renderSearchResult(result))}
         </FacetItemsList>
         <ListFooter
+          className="spacer-bottom"
           count={searchResults.length}
           loadMore={this.searchMore}
           ready={!searching}
@@ -223,7 +285,11 @@ export default class ListStyleFacet<S> extends React.Component<Props<S>, State<S
   renderSearchResult(result: S) {
     const key = this.props.getSearchResultKey(result);
     const active = this.props.values.includes(key);
-    const stat = this.getStat(key);
+
+    // default to 0 if we're sure there are not more results
+    const isFacetExhaustive = Object.keys(this.props.stats || {}).length < this.props.maxItems!;
+    const stat = this.getStat(key, isFacetExhaustive);
+
     return (
       <FacetItem
         active={active}
@@ -232,7 +298,7 @@ export default class ListStyleFacet<S> extends React.Component<Props<S>, State<S
         loading={this.props.loading}
         name={this.props.renderSearchResult(result, this.state.query)}
         onClick={this.handleItemClick}
-        stat={stat && formatFacetStat(stat)}
+        stat={formatFacetStat(stat)}
         tooltip={this.props.getSearchResultText(result)}
         value={key}
       />
diff --git a/server/sonar-web/src/main/js/components/facet/ListStyleFacetFooter.tsx b/server/sonar-web/src/main/js/components/facet/ListStyleFacetFooter.tsx
new file mode 100644 (file)
index 0000000..b4fe7eb
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 React from 'react';
+import { translate, translateWithParameters } from '../../helpers/l10n';
+import { formatMeasure } from '../../helpers/measures';
+
+interface Props {
+  className?: string;
+  count: number;
+  showMore: () => void;
+  showLess: (() => void) | undefined;
+  total: number;
+}
+
+export default class ListStyleFacetFooter extends React.PureComponent<Props> {
+  handleShowMoreClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
+    event.preventDefault();
+    event.currentTarget.blur();
+    this.props.showMore();
+  };
+
+  handleShowLessClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
+    event.preventDefault();
+    event.currentTarget.blur();
+    if (this.props.showLess) {
+      this.props.showLess();
+    }
+  };
+
+  render() {
+    const { count, total } = this.props;
+    const hasMore = total > count;
+    const allShown = Boolean(total && total === count);
+
+    return (
+      <footer className="note spacer-top spacer-bottom text-center">
+        {translateWithParameters('x_show', formatMeasure(count, 'INT', null))}
+
+        {hasMore && (
+          <a className="spacer-left text-muted" href="#" onClick={this.handleShowMoreClick}>
+            {translate('show_more')}
+          </a>
+        )}
+
+        {this.props.showLess &&
+          allShown && (
+            <a className="spacer-left text-muted" href="#" onClick={this.handleShowLessClick}>
+              {translate('show_less')}
+            </a>
+          )}
+      </footer>
+    );
+  }
+}
index 7cb3f16a0a0769ed7db952ab8554dbcdf1411203..c9c4180d1eaa3d9b6dd7345cd7f572d63bdbeefd 100644 (file)
@@ -116,6 +116,50 @@ it('should search', async () => {
   expect(onSearch).lastCalledWith('blabla');
 });
 
+it('should limit the number of items', () => {
+  const wrapper = shallowRender({ maxInitialItems: 2, maxItems: 5 });
+  expect(wrapper.find('FacetItem').length).toBe(2);
+
+  wrapper.find('ListStyleFacetFooter').prop<Function>('showMore')();
+  wrapper.update();
+  expect(wrapper.find('FacetItem').length).toBe(3);
+
+  wrapper.find('ListStyleFacetFooter').prop<Function>('showLess')();
+  wrapper.update();
+  expect(wrapper.find('FacetItem').length).toBe(2);
+});
+
+it('should show warning that there might be more results', () => {
+  const wrapper = shallowRender({ maxInitialItems: 2, maxItems: 3 });
+  wrapper.find('ListStyleFacetFooter').prop<Function>('showMore')();
+  wrapper.update();
+  expect(wrapper.find('.alert-warning').exists()).toBe(true);
+});
+
+it('should reset state when closes', () => {
+  const wrapper = shallowRender();
+  wrapper.setState({
+    query: 'foobar',
+    searchResults: ['foo', 'bar'],
+    searching: true,
+    showFullList: true
+  });
+
+  wrapper.setProps({ open: false });
+  expect(wrapper.state('query')).toBe('');
+  expect(wrapper.state('searchResults')).toBe(undefined);
+  expect(wrapper.state('searching')).toBe(false);
+  expect(wrapper.state('showFullList')).toBe(false);
+});
+
+it('should collapse list when new stats have few results', () => {
+  const wrapper = shallowRender({ maxInitialItems: 2, maxItems: 3 });
+  wrapper.setState({ showFullList: true });
+
+  wrapper.setProps({ stats: { d: 1 } });
+  expect(wrapper.state('showFullList')).toBe(false);
+});
+
 function shallowRender(props: Partial<Props<string>> = {}) {
   return shallow(
     <ListStyleFacet
diff --git a/server/sonar-web/src/main/js/components/facet/__tests__/ListStyleFacetFooter-test.tsx b/server/sonar-web/src/main/js/components/facet/__tests__/ListStyleFacetFooter-test.tsx
new file mode 100644 (file)
index 0000000..5c9ec73
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 React from 'react';
+import { shallow } from 'enzyme';
+import ListStyleFacetFooter from '../ListStyleFacetFooter';
+import { click } from '../../../helpers/testUtils';
+
+it('should not render "show more"', () => {
+  expect(
+    shallow(<ListStyleFacetFooter count={3} showLess={undefined} showMore={jest.fn()} total={3} />)
+  ).toMatchSnapshot();
+});
+
+it('should show more', () => {
+  const showMore = jest.fn();
+  const wrapper = shallow(
+    <ListStyleFacetFooter count={3} showLess={undefined} showMore={showMore} total={15} />
+  );
+  expect(wrapper).toMatchSnapshot();
+  click(wrapper.find('a'));
+  expect(showMore).toBeCalled();
+});
+
+it('should show less', () => {
+  const showLess = jest.fn();
+  const wrapper = shallow(
+    <ListStyleFacetFooter count={15} showLess={showLess} showMore={jest.fn()} total={15} />
+  );
+  expect(wrapper).toMatchSnapshot();
+  click(wrapper.find('a'));
+  expect(showLess).toBeCalled();
+});
+
+it('should not render "show less"', () => {
+  const wrapper = shallow(
+    <ListStyleFacetFooter count={15} showLess={undefined} showMore={jest.fn()} total={15} />
+  );
+  expect(wrapper).toMatchSnapshot();
+});
index 725eb2a401fb91a1f5b13b985e2d8d894d5747bc..eaa0c2cf53cf0b5a1b2e81f50a64414fe6cf95d4 100644 (file)
@@ -36,6 +36,7 @@ exports[`should render disabled 1`] = `
 <span
   className="search-navigator-facet"
   data-facet="bar"
+  title="foo"
 >
   <span
     className="facet-name"
index 7b05d4a28f0ce131afebfff13ec2ab7729d29e2f..82fab9ce2f46741597f8a04b6522bf0b0bcae571 100644 (file)
@@ -25,44 +25,51 @@ exports[`should render 1`] = `
       placeholder="search for foo..."
       value=""
     />
-    <FacetItemsList>
-      <FacetItem
-        active={false}
-        disabled={false}
-        halfWidth={false}
-        key="a"
-        loading={false}
-        name="a"
-        onClick={[Function]}
-        stat="10"
-        tooltip="a"
-        value="a"
-      />
-      <FacetItem
-        active={false}
-        disabled={false}
-        halfWidth={false}
-        key="b"
-        loading={false}
-        name="b"
-        onClick={[Function]}
-        stat="8"
-        tooltip="b"
-        value="b"
-      />
-      <FacetItem
-        active={false}
-        disabled={false}
-        halfWidth={false}
-        key="c"
-        loading={false}
-        name="c"
-        onClick={[Function]}
-        stat="1"
-        tooltip="c"
-        value="c"
+    <React.Fragment>
+      <FacetItemsList>
+        <FacetItem
+          active={false}
+          disabled={false}
+          halfWidth={false}
+          key="a"
+          loading={false}
+          name="a"
+          onClick={[Function]}
+          stat="10"
+          tooltip="a"
+          value="a"
+        />
+        <FacetItem
+          active={false}
+          disabled={false}
+          halfWidth={false}
+          key="b"
+          loading={false}
+          name="b"
+          onClick={[Function]}
+          stat="8"
+          tooltip="b"
+          value="b"
+        />
+        <FacetItem
+          active={false}
+          disabled={false}
+          halfWidth={false}
+          key="c"
+          loading={false}
+          name="c"
+          onClick={[Function]}
+          stat="1"
+          tooltip="c"
+          value="c"
+        />
+      </FacetItemsList>
+      <ListStyleFacetFooter
+        count={3}
+        showMore={[Function]}
+        total={3}
       />
-    </FacetItemsList>
+    </React.Fragment>
     <MultipleSelectionHint
       options={3}
       values={0}
@@ -100,28 +107,31 @@ exports[`should search 1`] = `
       <FacetItemsList>
         <FacetItem
           active={false}
-          disabled={false}
+          disabled={true}
           halfWidth={false}
           key="d"
           loading={false}
           name="d"
           onClick={[Function]}
+          stat={0}
           tooltip="d"
           value="d"
         />
         <FacetItem
           active={false}
-          disabled={false}
+          disabled={true}
           halfWidth={false}
           key="e"
           loading={false}
           name="e"
           onClick={[Function]}
+          stat={0}
           tooltip="e"
           value="e"
         />
       </FacetItemsList>
       <ListFooter
+        className="spacer-bottom"
         count={2}
         loadMore={[Function]}
         ready={true}
@@ -165,39 +175,43 @@ exports[`should search 2`] = `
       <FacetItemsList>
         <FacetItem
           active={false}
-          disabled={false}
+          disabled={true}
           halfWidth={false}
           key="d"
           loading={false}
           name="d"
           onClick={[Function]}
+          stat={0}
           tooltip="d"
           value="d"
         />
         <FacetItem
           active={false}
-          disabled={false}
+          disabled={true}
           halfWidth={false}
           key="e"
           loading={false}
           name="e"
           onClick={[Function]}
+          stat={0}
           tooltip="e"
           value="e"
         />
         <FacetItem
           active={false}
-          disabled={false}
+          disabled={true}
           halfWidth={false}
           key="f"
           loading={false}
           name="f"
           onClick={[Function]}
+          stat={0}
           tooltip="f"
           value="f"
         />
       </FacetItemsList>
       <ListFooter
+        className="spacer-bottom"
         count={3}
         loadMore={[Function]}
         ready={true}
@@ -237,44 +251,51 @@ exports[`should search 3`] = `
       placeholder="search for foo..."
       value=""
     />
-    <FacetItemsList>
-      <FacetItem
-        active={false}
-        disabled={false}
-        halfWidth={false}
-        key="a"
-        loading={false}
-        name="a"
-        onClick={[Function]}
-        stat="10"
-        tooltip="a"
-        value="a"
-      />
-      <FacetItem
-        active={false}
-        disabled={false}
-        halfWidth={false}
-        key="b"
-        loading={false}
-        name="b"
-        onClick={[Function]}
-        stat="8"
-        tooltip="b"
-        value="b"
-      />
-      <FacetItem
-        active={false}
-        disabled={false}
-        halfWidth={false}
-        key="c"
-        loading={false}
-        name="c"
-        onClick={[Function]}
-        stat="1"
-        tooltip="c"
-        value="c"
+    <React.Fragment>
+      <FacetItemsList>
+        <FacetItem
+          active={false}
+          disabled={false}
+          halfWidth={false}
+          key="a"
+          loading={false}
+          name="a"
+          onClick={[Function]}
+          stat="10"
+          tooltip="a"
+          value="a"
+        />
+        <FacetItem
+          active={false}
+          disabled={false}
+          halfWidth={false}
+          key="b"
+          loading={false}
+          name="b"
+          onClick={[Function]}
+          stat="8"
+          tooltip="b"
+          value="b"
+        />
+        <FacetItem
+          active={false}
+          disabled={false}
+          halfWidth={false}
+          key="c"
+          loading={false}
+          name="c"
+          onClick={[Function]}
+          stat="1"
+          tooltip="c"
+          value="c"
+        />
+      </FacetItemsList>
+      <ListStyleFacetFooter
+        count={3}
+        showMore={[Function]}
+        total={3}
       />
-    </FacetItemsList>
+    </React.Fragment>
     <MultipleSelectionHint
       options={3}
       values={0}
diff --git a/server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/ListStyleFacetFooter-test.tsx.snap b/server/sonar-web/src/main/js/components/facet/__tests__/__snapshots__/ListStyleFacetFooter-test.tsx.snap
new file mode 100644 (file)
index 0000000..85a2744
--- /dev/null
@@ -0,0 +1,47 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should not render "show less" 1`] = `
+<footer
+  className="note spacer-top spacer-bottom text-center"
+>
+  x_show.15
+</footer>
+`;
+
+exports[`should not render "show more" 1`] = `
+<footer
+  className="note spacer-top spacer-bottom text-center"
+>
+  x_show.3
+</footer>
+`;
+
+exports[`should show less 1`] = `
+<footer
+  className="note spacer-top spacer-bottom text-center"
+>
+  x_show.15
+  <a
+    className="spacer-left text-muted"
+    href="#"
+    onClick={[Function]}
+  >
+    show_less
+  </a>
+</footer>
+`;
+
+exports[`should show more 1`] = `
+<footer
+  className="note spacer-top spacer-bottom text-center"
+>
+  x_show.3
+  <a
+    className="spacer-left text-muted"
+    href="#"
+    onClick={[Function]}
+  >
+    show_more
+  </a>
+</footer>
+`;
index cbe505478e91f71fea69fc8037ad8d6bfcc77139..56570e0cfd7f01da325668b9f88a8d6448e3fe51 100644 (file)
@@ -218,6 +218,7 @@ created_by=Created by
 default_error_message=The request cannot be processed. Try again later.
 default_severity=Default severity
 edit_permissions=Edit Permissions
+facet_might_have_more_results=There might be more results, try another set of filters to see them.
 false_positive=False positive
 go_back_to_homepage=Go back to the homepage
 last_analysis_before=Last analysis before
@@ -241,6 +242,7 @@ set_as_default=Set as Default
 short_number_suffix.g=G
 short_number_suffix.k=k
 short_number_suffix.m=M
+show_less=Show Less
 show_more=Show More
 show_all=Show All
 should_be_unique=Should be unique