From 5984a4a40bed0530c33f731cd99382e786e86dfb Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Wed, 26 Apr 2017 09:36:46 -0400 Subject: [PATCH] React search box tool bar (#10821) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add a react tool bar search box Create basic react toolbar elements and update uiframework docs * Package.json: put back ngreact (edits got overwritten) * Add jest tests * Combine basic tests into one, eliminate helper functions * Address code review comments - move tool_bar_footer into it’s own file. - some stylistic html changes - comments * Remove toolbar_with_search_only It isn’t being used in kibana currently, so we probably don’t need to support it in our ui_Framework, and the need for the custom “className="kuiToolBar--searchOnly”” indicates we should resdesign it if we do need it some day. * Fix issue with default to named conversion merge * rename commonHtmlProps = requiredProps --- package.json | 1 + src/core_plugins/kibana/public/kibana.js | 1 + .../visualize/listing/visualize_listing.html | 17 ++---- .../visualize/listing/visualize_listing.js | 8 ++- src/ui/public/react_components.js | 11 ++++ ui_framework/components/index.js | 5 ++ .../__snapshots__/tool_bar.test.js.snap | 11 ++++ .../tool_bar_footer.test.js.snap | 11 ++++ .../tool_bar_search_box.test.js.snap | 44 ++++++++++++++ ui_framework/components/tool_bar/index.js | 3 + ui_framework/components/tool_bar/tool_bar.js | 11 ++++ .../components/tool_bar/tool_bar.test.js | 13 ++++ .../components/tool_bar/tool_bar_footer.js | 11 ++++ .../tool_bar/tool_bar_footer.test.js | 12 ++++ .../tool_bar/tool_bar_search_box.js | 27 +++++++++ .../tool_bar/tool_bar_search_box.test.js | 33 +++++++++++ .../doc_site/src/views/tool_bar/tool_bar.html | 48 --------------- .../doc_site/src/views/tool_bar/tool_bar.js | 59 +++++++++++++++++++ .../src/views/tool_bar/tool_bar_example.js | 41 +++++++------ .../src/views/tool_bar/tool_bar_footer.html | 22 ------- .../src/views/tool_bar/tool_bar_footer.js | 35 +++++++++++ .../views/tool_bar/tool_bar_search_only.html | 12 ---- ui_framework/test/required_props.js | 7 +++ 23 files changed, 325 insertions(+), 118 deletions(-) create mode 100644 src/ui/public/react_components.js create mode 100644 ui_framework/components/tool_bar/__snapshots__/tool_bar.test.js.snap create mode 100644 ui_framework/components/tool_bar/__snapshots__/tool_bar_footer.test.js.snap create mode 100644 ui_framework/components/tool_bar/__snapshots__/tool_bar_search_box.test.js.snap create mode 100644 ui_framework/components/tool_bar/index.js create mode 100644 ui_framework/components/tool_bar/tool_bar.js create mode 100644 ui_framework/components/tool_bar/tool_bar.test.js create mode 100644 ui_framework/components/tool_bar/tool_bar_footer.js create mode 100644 ui_framework/components/tool_bar/tool_bar_footer.test.js create mode 100644 ui_framework/components/tool_bar/tool_bar_search_box.js create mode 100644 ui_framework/components/tool_bar/tool_bar_search_box.test.js delete mode 100644 ui_framework/doc_site/src/views/tool_bar/tool_bar.html create mode 100644 ui_framework/doc_site/src/views/tool_bar/tool_bar.js delete mode 100644 ui_framework/doc_site/src/views/tool_bar/tool_bar_footer.html create mode 100644 ui_framework/doc_site/src/views/tool_bar/tool_bar_footer.js delete mode 100644 ui_framework/doc_site/src/views/tool_bar/tool_bar_search_only.html create mode 100644 ui_framework/test/required_props.js diff --git a/package.json b/package.json index 8cd5867e807ed..2bd5321e137d2 100644 --- a/package.json +++ b/package.json @@ -154,6 +154,7 @@ "mkdirp": "0.5.1", "moment": "2.13.0", "moment-timezone": "0.5.4", + "ngreact": "0.3.0", "no-ui-slider": "1.2.0", "node-fetch": "1.3.2", "node-uuid": "1.4.7", diff --git a/src/core_plugins/kibana/public/kibana.js b/src/core_plugins/kibana/public/kibana.js index a95c455643a54..4ffdc3b770b27 100644 --- a/src/core_plugins/kibana/public/kibana.js +++ b/src/core_plugins/kibana/public/kibana.js @@ -18,6 +18,7 @@ import 'ui/vislib'; import 'ui/agg_response'; import 'ui/agg_types'; import 'ui/timepicker'; +import 'ui/react_components'; import { Notifier } from 'ui/notify/notifier'; import 'leaflet'; diff --git a/src/core_plugins/kibana/public/visualize/listing/visualize_listing.html b/src/core_plugins/kibana/public/visualize/listing/visualize_listing.html index 0e31a44ab64d8..cdbcebe534831 100644 --- a/src/core_plugins/kibana/public/visualize/listing/visualize_listing.html +++ b/src/core_plugins/kibana/public/visualize/listing/visualize_listing.html @@ -17,18 +17,11 @@
-
-
- - -
-
+
diff --git a/src/core_plugins/kibana/public/visualize/listing/visualize_listing.js b/src/core_plugins/kibana/public/visualize/listing/visualize_listing.js index bc86a2dea0da6..d0485b4a68641 100644 --- a/src/core_plugins/kibana/public/visualize/listing/visualize_listing.js +++ b/src/core_plugins/kibana/public/visualize/listing/visualize_listing.js @@ -4,7 +4,7 @@ import 'ui/pager'; import { SortableProperties } from 'ui_framework/services'; -export function VisualizeListingController($injector, $scope) { +export function VisualizeListingController($injector) { const $filter = $injector.get('$filter'); const confirmModal = $injector.get('confirmModal'); const Notifier = $injector.get('Notifier'); @@ -76,10 +76,12 @@ export function VisualizeListingController($injector, $scope) { this.pager = pagerFactory.create(this.items.length, 20, 1); - $scope.$watch(() => this.filter, () => { + this.onFilter = (newFilter) => { + this.filter = newFilter; deselectAll(); fetchItems(); - }); + }; + fetchItems(); this.toggleAll = function toggleAll() { if (this.areAllItemsChecked()) { diff --git a/src/ui/public/react_components.js b/src/ui/public/react_components.js new file mode 100644 index 0000000000000..86a33e102d651 --- /dev/null +++ b/src/ui/public/react_components.js @@ -0,0 +1,11 @@ +import 'ngreact'; + +import { + KuiToolBarSearchBox, +} from 'ui_framework/components'; + +import { uiModules } from 'ui/modules'; +const app = uiModules.get('app/kibana', ['react']); +app.directive('toolBarSearchBox', function (reactDirective) { + return reactDirective(KuiToolBarSearchBox); +}); diff --git a/ui_framework/components/index.js b/ui_framework/components/index.js index 105bb302e746e..1e5c618f9f616 100644 --- a/ui_framework/components/index.js +++ b/ui_framework/components/index.js @@ -5,3 +5,8 @@ export { KuiLinkButton, KuiSubmitButton, } from './button'; +export { + KuiToolBarSearchBox, + KuiToolBar, + KuiToolBarFooter, +} from './tool_bar'; diff --git a/ui_framework/components/tool_bar/__snapshots__/tool_bar.test.js.snap b/ui_framework/components/tool_bar/__snapshots__/tool_bar.test.js.snap new file mode 100644 index 0000000000000..517a7b8eb1825 --- /dev/null +++ b/ui_framework/components/tool_bar/__snapshots__/tool_bar.test.js.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders KuiToolBar 1`] = ` +
+ children +
+`; diff --git a/ui_framework/components/tool_bar/__snapshots__/tool_bar_footer.test.js.snap b/ui_framework/components/tool_bar/__snapshots__/tool_bar_footer.test.js.snap new file mode 100644 index 0000000000000..8136fe26ac25a --- /dev/null +++ b/ui_framework/components/tool_bar/__snapshots__/tool_bar_footer.test.js.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders KuiToolBarFooter 1`] = ` +
+ children +
+`; diff --git a/ui_framework/components/tool_bar/__snapshots__/tool_bar_search_box.test.js.snap b/ui_framework/components/tool_bar/__snapshots__/tool_bar_search_box.test.js.snap new file mode 100644 index 0000000000000..e9f2e43e592c2 --- /dev/null +++ b/ui_framework/components/tool_bar/__snapshots__/tool_bar_search_box.test.js.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`filter initializes search box value 1`] = ` +
+
+ +
+`; + +exports[`renders KuiToolBarSearchBox 1`] = ` +
+
+ +
+`; diff --git a/ui_framework/components/tool_bar/index.js b/ui_framework/components/tool_bar/index.js new file mode 100644 index 0000000000000..000d7f30b825e --- /dev/null +++ b/ui_framework/components/tool_bar/index.js @@ -0,0 +1,3 @@ +export { KuiToolBarSearchBox } from './tool_bar_search_box'; +export { KuiToolBar } from './tool_bar'; +export { KuiToolBarFooter } from './tool_bar_footer'; diff --git a/ui_framework/components/tool_bar/tool_bar.js b/ui_framework/components/tool_bar/tool_bar.js new file mode 100644 index 0000000000000..54f6b1c815446 --- /dev/null +++ b/ui_framework/components/tool_bar/tool_bar.js @@ -0,0 +1,11 @@ +import React from 'react'; +import classNames from 'classnames'; + +export const KuiToolBar = ({ children, className, ...rest }) => { + const classes = classNames('kuiToolBar', className); + return
{children}
; +}; +KuiToolBar.propTypes = { + children: React.PropTypes.node, + className: React.PropTypes.string, +}; diff --git a/ui_framework/components/tool_bar/tool_bar.test.js b/ui_framework/components/tool_bar/tool_bar.test.js new file mode 100644 index 0000000000000..5fc1eb6d38227 --- /dev/null +++ b/ui_framework/components/tool_bar/tool_bar.test.js @@ -0,0 +1,13 @@ +import React from 'react'; +import { render } from 'enzyme'; +import { requiredProps } from '../../test/required_props'; + +import { + KuiToolBar, +} from './tool_bar'; + +test('renders KuiToolBar', () => { + const component = children; + expect(render(component)).toMatchSnapshot(); +}); + diff --git a/ui_framework/components/tool_bar/tool_bar_footer.js b/ui_framework/components/tool_bar/tool_bar_footer.js new file mode 100644 index 0000000000000..8bdeacad190b6 --- /dev/null +++ b/ui_framework/components/tool_bar/tool_bar_footer.js @@ -0,0 +1,11 @@ +import React from 'react'; +import classNames from 'classnames'; + +export const KuiToolBarFooter = ({ children, className, ...rest }) => { + const classes = classNames('kuiToolBarFooter', className); + return
{children}
; +}; +KuiToolBarFooter.propTypes = { + children: React.PropTypes.node, + className: React.PropTypes.string, +}; diff --git a/ui_framework/components/tool_bar/tool_bar_footer.test.js b/ui_framework/components/tool_bar/tool_bar_footer.test.js new file mode 100644 index 0000000000000..c48ee7fcdcac0 --- /dev/null +++ b/ui_framework/components/tool_bar/tool_bar_footer.test.js @@ -0,0 +1,12 @@ +import React from 'react'; +import { render } from 'enzyme'; +import { requiredProps } from '../../test/required_props'; + +import { + KuiToolBarFooter, +} from './tool_bar_footer'; + +test('renders KuiToolBarFooter', () => { + const component = children; + expect(render(component)).toMatchSnapshot(); +}); diff --git a/ui_framework/components/tool_bar/tool_bar_search_box.js b/ui_framework/components/tool_bar/tool_bar_search_box.js new file mode 100644 index 0000000000000..b7fd3e584eee7 --- /dev/null +++ b/ui_framework/components/tool_bar/tool_bar_search_box.js @@ -0,0 +1,27 @@ +import React from 'react'; +import classNames from 'classnames'; + +export function KuiToolBarSearchBox({ filter, onFilter, className, ...rest }) { + function onChange(event) { + onFilter(event.target.value); + } + const classes = classNames('kuiToolBarSearch', className); + return
+
+
+ +
+
; +} + +KuiToolBarSearchBox.propTypes = { + filter: React.PropTypes.string, + onFilter: React.PropTypes.func.isRequired +}; diff --git a/ui_framework/components/tool_bar/tool_bar_search_box.test.js b/ui_framework/components/tool_bar/tool_bar_search_box.test.js new file mode 100644 index 0000000000000..2f366f2f621ea --- /dev/null +++ b/ui_framework/components/tool_bar/tool_bar_search_box.test.js @@ -0,0 +1,33 @@ +import React from 'react'; +import { render, mount } from 'enzyme'; +import sinon from 'sinon'; +import { requiredProps } from '../../test/required_props'; + +import { + KuiToolBarSearchBox, +} from './tool_bar_search_box'; + +const onFilter = sinon.spy(); + +test('renders KuiToolBarSearchBox', () => { + const component = ; + expect(render(component)).toMatchSnapshot(); +}); + +describe('onFilter', () => { + test('is called on change event, with the value entered', () => { + const searchBox = mount(); + onFilter.reset(); + const event = { target: { value: 'a' } }; + searchBox.find('input').simulate('change', event); + sinon.assert.calledWith(onFilter, 'a'); + }); +}); + +describe('filter', () => { + test('initializes search box value', () => { + const component = ; + expect(render(component)).toMatchSnapshot(); + }); +}); + diff --git a/ui_framework/doc_site/src/views/tool_bar/tool_bar.html b/ui_framework/doc_site/src/views/tool_bar/tool_bar.html deleted file mode 100644 index 7cd8c1b751871..0000000000000 --- a/ui_framework/doc_site/src/views/tool_bar/tool_bar.html +++ /dev/null @@ -1,48 +0,0 @@ -
-
-
- - -
- - -
- -
- - - - - -
- -
- -
- 1 – 20 of 33 -
- -
- - -
-
-
diff --git a/ui_framework/doc_site/src/views/tool_bar/tool_bar.js b/ui_framework/doc_site/src/views/tool_bar/tool_bar.js new file mode 100644 index 0000000000000..9b0c2f71d865b --- /dev/null +++ b/ui_framework/doc_site/src/views/tool_bar/tool_bar.js @@ -0,0 +1,59 @@ +import React from 'react'; + +import { + KuiToolBar, + KuiToolBarSearchBox, + KuiButton, + KuiButtonIcon, + KuiButtonGroup, +} from '../../../../components'; + +export const ToolBar = () => ( + + {}} /> + +
+ +
+ +
+ } + > + Create + + + } + > + Delete + +
+ +
+ +
+ 1 – 20 of 33 +
+ + + } + > + + } + > + + +
+
+ ); diff --git a/ui_framework/doc_site/src/views/tool_bar/tool_bar_example.js b/ui_framework/doc_site/src/views/tool_bar/tool_bar_example.js index ca7ee7d0b2d47..b1f6f5c3c1dab 100644 --- a/ui_framework/doc_site/src/views/tool_bar/tool_bar_example.js +++ b/ui_framework/doc_site/src/views/tool_bar/tool_bar_example.js @@ -1,4 +1,5 @@ import React from 'react'; +import { renderToHtml } from '../../services'; import { GuideDemo, @@ -8,15 +9,22 @@ import { GuideText, } from '../../components'; -const toolBarHtml = require('./tool_bar.html'); -const toolBarSearchOnlyHtml = require('./tool_bar_search_only.html'); -const toolBarFooterHtml = require('./tool_bar_footer.html'); +import { ToolBar } from './tool_bar'; +const toolBarSource = require('!!raw!./tool_bar'); +const toolBarHtml = renderToHtml(ToolBar); + +import { ToolBarFooter } from './tool_bar_footer'; +const toolBarFooterSource = require('!!raw!./tool_bar_footer'); +const toolBarFooterHtml = renderToHtml(ToolBarFooter); export default props => ( ( list, table, or menu. - - - - - + + + ( controls or a subset of the primary controls. - + + + ); diff --git a/ui_framework/doc_site/src/views/tool_bar/tool_bar_footer.html b/ui_framework/doc_site/src/views/tool_bar/tool_bar_footer.html deleted file mode 100644 index 21e437f7e16c9..0000000000000 --- a/ui_framework/doc_site/src/views/tool_bar/tool_bar_footer.html +++ /dev/null @@ -1,22 +0,0 @@ -
-
-
- 5 Items selected -
-
- -
-
- 1 – 20 of 33 -
- -
- - -
-
-
diff --git a/ui_framework/doc_site/src/views/tool_bar/tool_bar_footer.js b/ui_framework/doc_site/src/views/tool_bar/tool_bar_footer.js new file mode 100644 index 0000000000000..b5c8f931ea600 --- /dev/null +++ b/ui_framework/doc_site/src/views/tool_bar/tool_bar_footer.js @@ -0,0 +1,35 @@ +import React from 'react'; + +import { + KuiToolBarFooter, + KuiButtonIcon, + KuiButton, + KuiButtonGroup, +} from '../../../../components'; + +export const ToolBarFooter = () => ( + +
+
+ 5 Items selected +
+
+ +
+
+ 1 – 20 of 33 +
+ + + } + > + } + > + +
+
+); diff --git a/ui_framework/doc_site/src/views/tool_bar/tool_bar_search_only.html b/ui_framework/doc_site/src/views/tool_bar/tool_bar_search_only.html deleted file mode 100644 index ce8a04e9d635d..0000000000000 --- a/ui_framework/doc_site/src/views/tool_bar/tool_bar_search_only.html +++ /dev/null @@ -1,12 +0,0 @@ -
-
-
- - -
-
-
diff --git a/ui_framework/test/required_props.js b/ui_framework/test/required_props.js new file mode 100644 index 0000000000000..1d991b3d81e0d --- /dev/null +++ b/ui_framework/test/required_props.js @@ -0,0 +1,7 @@ +// We expect all React components to be able to support these props, +// which will be rendered as HTML attributes. +export const requiredProps = { + 'aria-label': 'aria-label', + 'className': 'testClass1 testClass2', + 'data-test-subj': 'test subject string' +};