Преглед изворни кода

filter plugins and layout mode

Peter Holmberg пре 7 година
родитељ
комит
0b7576a1f9

+ 24 - 0
public/app/core/components/LayoutSelector/LayoutSelector.tsx

@@ -0,0 +1,24 @@
+import React from 'react';
+
+export default function LayoutSelector({ mode, onLayoutModeChanged }) {
+  return (
+    <div className="layout-selector">
+      <button
+        onClick={() => {
+          onLayoutModeChanged('list');
+        }}
+        className={mode === 'list' ? 'active' : ''}
+      >
+        <i className="fa fa-list" />
+      </button>
+      <button
+        onClick={() => {
+          onLayoutModeChanged('grid');
+        }}
+        className={mode === 'grid' ? 'active' : ''}
+      >
+        <i className="fa fa-th" />
+      </button>
+    </div>
+  );
+}

+ 59 - 21
public/app/features/plugins/PluginActionBar.tsx

@@ -1,24 +1,62 @@
-import React from 'react';
+import React, { PureComponent } from 'react';
+import { connect } from 'react-redux';
+import LayoutSelector from '../../core/components/LayoutSelector/LayoutSelector';
+import { setLayoutMode, setPluginsSearchQuery } from './state/actions';
+import { getPluginsSearchQuery, getLayoutMode } from './state/selectors';
 
-export default function({ searchQuery, onQueryChange }) {
-  return (
-    <div className="page-action-bar">
-      <div className="gf-form gf-form--grow">
-        <label className="gf-form--has-input-icon">
-          <input
-            type="text"
-            className="gf-form-input width-20"
-            value={searchQuery}
-            onChange={onQueryChange}
-            placeholder="Filter by name or type"
-          />
-          <i className="gf-form-input-icon fa fa-search" />
-        </label>
+export interface Props {
+  searchQuery: string;
+  layoutMode: string;
+  setLayoutMode: typeof setLayoutMode;
+  setPluginsSearchQuery: typeof setPluginsSearchQuery;
+}
+
+export class PluginActionBar extends PureComponent<Props> {
+  onSearchQueryChange = event => {
+    this.props.setPluginsSearchQuery(event.target.value);
+  };
+
+  render() {
+    const { searchQuery, layoutMode, setLayoutMode } = this.props;
+
+    return (
+      <div className="page-action-bar">
+        <div className="gf-form gf-form--grow">
+          <label className="gf-form--has-input-icon">
+            <input
+              type="text"
+              className="gf-form-input width-20"
+              value={searchQuery}
+              onChange={this.onSearchQueryChange}
+              placeholder="Filter by name or type"
+            />
+            <i className="gf-form-input-icon fa fa-search" />
+          </label>
+          <LayoutSelector mode={layoutMode} onLayoutModeChanged={mode => setLayoutMode(mode)} />
+        </div>
+        <div className="page-action-bar__spacer" />
+        <a
+          className="btn btn-success"
+          href="https://grafana.com/plugins?utm_source=grafana_plugin_list"
+          target="_blank"
+        >
+          Find more plugins on Grafana.com
+        </a>
       </div>
-      <div className="page-action-bar__spacer" />
-      <a className="btn btn-success" href="https://grafana.com/plugins?utm_source=grafana_plugin_list" target="_blank">
-        Find more plugins on Grafana.com
-      </a>
-    </div>
-  );
+    );
+  }
 }
+
+function mapStateToProps(state) {
+  return {
+    searchQuery: getPluginsSearchQuery(state.plugins),
+    layoutMode: getLayoutMode(state.plugins),
+  };
+}
+
+const mapDispatchToProps = {
+  setPluginsSearchQuery,
+  setLayoutMode,
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(PluginActionBar);

+ 5 - 3
public/app/features/plugins/PluginListPage.tsx

@@ -7,11 +7,12 @@ import PluginList from './PluginList';
 import { NavModel, Plugin } from '../../types';
 import { loadPlugins } from './state/actions';
 import { getNavModel } from '../../core/selectors/navModel';
-import { getPlugins } from './state/selectors';
+import { getLayoutMode, getPlugins } from './state/selectors';
 
 interface Props {
   navModel: NavModel;
   plugins: Plugin[];
+  layoutMode: string;
   loadPlugins: typeof loadPlugins;
 }
 
@@ -25,14 +26,14 @@ export class PluginListPage extends PureComponent<Props> {
   }
 
   render() {
-    const { navModel, plugins } = this.props;
+    const { navModel, plugins, layoutMode } = this.props;
 
     return (
       <div>
         <PageHeader model={navModel} />
         <div className="page-container page-body">
           <PluginActionBar searchQuery="" onQueryChange={() => {}} />
-          {plugins && <PluginList plugins={plugins} layout="grid" />}
+          {plugins && <PluginList plugins={plugins} layout={layoutMode} />}
         </div>
       </div>
     );
@@ -43,6 +44,7 @@ function mapStateToProps(state) {
   return {
     navModel: getNavModel(state.navIndex, 'plugins'),
     plugins: getPlugins(state.plugins),
+    layoutMode: getLayoutMode(state.plugins),
   };
 }
 

+ 24 - 2
public/app/features/plugins/state/actions.ts

@@ -4,6 +4,8 @@ import { getBackendSrv } from '../../../core/services/backend_srv';
 
 export enum ActionTypes {
   LoadPlugins = 'LOAD_PLUGINS',
+  SetPluginsSearchQuery = 'SET_PLUGIN_SEARCH_QUERY',
+  SetLayoutMode = 'SET_LAYOUT_MODE',
 }
 
 export interface LoadPluginsAction {
@@ -11,12 +13,32 @@ export interface LoadPluginsAction {
   payload: Plugin[];
 }
 
-export const pluginsLoaded = (plugins: Plugin[]): LoadPluginsAction => ({
+export interface SetPluginsSearchQueryAction {
+  type: ActionTypes.SetPluginsSearchQuery;
+  payload: string;
+}
+
+export interface SetLayoutModeAction {
+  type: ActionTypes.SetLayoutMode;
+  payload: string;
+}
+
+export const setLayoutMode = (mode: string): SetLayoutModeAction => ({
+  type: ActionTypes.SetLayoutMode,
+  payload: mode,
+});
+
+export const setPluginsSearchQuery = (query: string): SetPluginsSearchQueryAction => ({
+  type: ActionTypes.SetPluginsSearchQuery,
+  payload: query,
+});
+
+const pluginsLoaded = (plugins: Plugin[]): LoadPluginsAction => ({
   type: ActionTypes.LoadPlugins,
   payload: plugins,
 });
 
-export type Action = LoadPluginsAction;
+export type Action = LoadPluginsAction | SetPluginsSearchQueryAction | SetLayoutModeAction;
 
 type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action>;
 

+ 7 - 1
public/app/features/plugins/state/reducers.ts

@@ -1,12 +1,18 @@
 import { Action, ActionTypes } from './actions';
 import { Plugin, PluginsState } from 'app/types';
 
-export const initialState: PluginsState = { plugins: [] as Plugin[] };
+export const initialState: PluginsState = { plugins: [] as Plugin[], searchQuery: '', layoutMode: 'grid' };
 
 export const pluginsReducer = (state = initialState, action: Action): PluginsState => {
   switch (action.type) {
     case ActionTypes.LoadPlugins:
       return { ...state, plugins: action.payload };
+
+    case ActionTypes.SetPluginsSearchQuery:
+      return { ...state, searchQuery: action.payload };
+
+    case ActionTypes.SetLayoutMode:
+      return { ...state, layoutMode: action.payload };
   }
   return state;
 };

+ 10 - 1
public/app/features/plugins/state/selectors.ts

@@ -1 +1,10 @@
-export const getPlugins = state => state.plugins;
+export const getPlugins = state => {
+  const regex = new RegExp(state.searchQuery, 'i');
+
+  return state.plugins.filter(item => {
+    return regex.test(item.name);
+  });
+};
+
+export const getPluginsSearchQuery = state => state.searchQuery;
+export const getLayoutMode = state => state.layoutMode;

+ 2 - 0
public/app/types/plugins.ts

@@ -46,4 +46,6 @@ export interface Plugin {
 
 export interface PluginsState {
   plugins: Plugin[];
+  searchQuery: string;
+  layoutMode: string;
 }