Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #24041 - Move HW Models Table to pf-react #5755

Merged
merged 1 commit into from
Feb 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 2 additions & 28 deletions app/views/models/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,6 @@

<% title_actions new_link(_("Create Model")) %>

<table class="<%= table_css_classes 'table-fixed' %>">
<thead>
<tr>
ohadlevy marked this conversation as resolved.
Show resolved Hide resolved
<th class="col-md-4"><%= sort :name, :as => s_("Model|Name") %></th>
<th class="col-md-3"><%= sort :vendor_class, :as => _("Vendor Class") %></th>
<th class="col-md-3"><%= sort :hardware_model, :as => s_("Model|Hardware Model") %></th>
<th class="col-md-1"><%= _("Hosts") %></th>
<th><%= _('Actions') %></th>
</tr>
</thead>
<tbody>
<% @models.each do |model| %>
<tr>
<td class="ellipsis">
<%= link_to_if_authorized model.name,
hash_for_edit_model_path(:id => model).merge(:auth_object => model, :authorizer => authorizer) %></td>
<td class="ellipsis"><%= model.vendor_class %></td>
<td class="ellipsis"><%= model.hardware_model %></td>
<td class='ra'><%= link_to hosts_count[model], hosts_path(:search => "model = \"#{model}\"") %></td>
<td class="col-md-1">
<%= action_buttons(display_delete_if_authorized(hash_for_model_path(:id => model).
merge(:auth_object => model, :authorizer => authorizer),
:confirm => _("Delete %s?") % model.name)) %>
</td>
</tr>
<% end %>
</tbody>
</table>
<div id="models_table"></div>
<%= mount_react_component("ModelsTable", "#models_table") %>
<%= will_paginate_with_info @models %>
1 change: 0 additions & 1 deletion test/controllers/models_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

class ModelsControllerTest < ActionController::TestCase
basic_pagination_per_page_test
basic_pagination_rendered_test

def test_index
get :index, session: set_session_user
Expand Down
20 changes: 17 additions & 3 deletions test/integration/model_js_test.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
require 'integration_test_helper'

class ModelJSTest < IntegrationTestWithJavascript
test "index page" do
assert_index_page(models_path, "Hardware Models", "Create Model")
class ModelIntegrationTest < IntegrationTestWithJavascript
test "create new page" do
assert_new_button(models_path, "Create Model", new_model_path)
fill_in "model_name", :with => "IBM 123"
fill_in "model_hardware_model", :with => "IBMabcde"
fill_in "model_vendor_class", :with => "ABCDE"
fill_in "model_info", :with => "description text"
assert_submit_button(models_path)
assert page.has_link? "IBM 123"
end

test "edit page" do
visit models_path
click_link "KVM"
fill_in "model_name", :with => "RHEV 123"
assert_submit_button(models_path)
assert page.has_link? 'RHEV 123'
end
end
21 changes: 0 additions & 21 deletions test/integration/model_test.rb

This file was deleted.

8 changes: 8 additions & 0 deletions webpack/assets/javascripts/react_app/common/helpers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import debounce from 'lodash/debounce';
import { snakeCase, camelCase } from 'lodash';
import URI from 'urijs';
import { translate as __ } from './I18n';

/**
Expand Down Expand Up @@ -73,6 +74,13 @@ export const translateObject = obj =>
*/
export const translateArray = arr => arr.map(str => __(str));

/**
* Return the query in URL as Objects where keys are
* the parameters and the values are the parameters' values.
* @param {String} url - the URL
*/
export const getURIQuery = url => new URI(url).query(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll probably want to move all the URI helpers from PaginationHelper to common/helpers or another file in the future.


/**
* Transform object keys to snake case
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import { Spinner } from 'patternfly-react';
import PropTypes from 'prop-types';
import { Table } from '../common/table';
import { STATUS } from '../../constants';
import MessageBox from '../common/MessageBox';
import { translate as __ } from '../../common/I18n';
import createModelsTableSchema from './ModelsTableSchema';
import { getURIQuery } from '../../common/helpers';

class ModelsTable extends React.Component {
componentDidMount() {
this.props.getTableItems('models', getURIQuery(window.location.href));
boaz0 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we started to use more objects destruction in our components:
e.g: const { getTableItems } = this.props

if you want to get the query from the url, you could use the URI library which is already used in the project,
https://medialize.github.io/URI.js/docs.html#accessors-search

}

render() {
const {
getTableItems,
sortBy,
sortOrder,
error,
status,
results,
} = this.props;

const renderTable =
status === STATUS.ERROR ? (
<MessageBox
key="models-table-error"
icontype="error-circle-o"
msg={__(`Could not receive data: ${error && error.message}`)}
/>
) : (
<Table
key="models-table"
columns={createModelsTableSchema(getTableItems, sortBy, sortOrder)}
rows={results}
/>
);

if (results.length > 0) {
return renderTable;
}
return <Spinner size="lg" loading />;
}
}

ModelsTable.propTypes = {
ohadlevy marked this conversation as resolved.
Show resolved Hide resolved
results: PropTypes.array.isRequired,
getTableItems: PropTypes.func.isRequired,
status: PropTypes.oneOf(Object.keys(STATUS)),
sortBy: PropTypes.string,
sortOrder: PropTypes.string,
error: PropTypes.object,
};

ModelsTable.defaultProps = {
status: STATUS.PENDING,
sortBy: '',
sortOrder: '',
error: null,
};

export default ModelsTable;
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { shallow } from 'enzyme';
import ModelsTable from './ModelsTable';
import { Table } from '../common/table';
import MessageBox from '../common/MessageBox';

const data = {
pagination: {
vieType: 'table',
perPageOptions: [1, 2, 3],
itemCount: 5,
perPage: 1,
},
};

describe('ModelsTable', () => {
it('render table on sucess', () => {
const getModelItems = jest.fn().mockReturnValue([]);
const view = shallow(
<ModelsTable results={[{}]} getTableItems={getModelItems} data={data} />
);
expect(getModelItems.mock.calls).toHaveLength(1);
expect(view.find(Table)).toHaveLength(1);
});

it('render error message box on failure', () => {
const view = shallow(
<ModelsTable
getTableItems={jest.fn(() => [])}
results={[{}]}
error={Error('some error message')}
status="ERROR"
data={data}
/>
);
expect(view.find(MessageBox)).toHaveLength(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import createTableActionTypes from '../common/table/actionsHelpers/actionTypeCreator';

export const MODELS_TABLE_CONTROLLER = 'models';
export const MODELS_TABLE_ACTION_TYPES = createTableActionTypes(
MODELS_TABLE_CONTROLLER
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Immutable from 'seamless-immutable';
import { STATUS } from '../../constants';
import { MODELS_TABLE_ACTION_TYPES } from './ModelsTableConstants';

const initState = Immutable({
error: null,
sortBy: '',
sortOrder: '',
results: [],
status: STATUS.PENDING,
});
export default (state = initState, action) => {
const { REQUEST, FAILURE, SUCCESS } = MODELS_TABLE_ACTION_TYPES;
switch (action.type) {
case REQUEST:
return state.set('status', STATUS.PENDING);
case SUCCESS:
return Immutable.merge(state, {
ohadlevy marked this conversation as resolved.
Show resolved Hide resolved
error: null,
status: STATUS.RESOLVED,
results: action.payload.results,
sortBy: action.payload.sort.by,
sortOrder: action.payload.sort.order,
});
case FAILURE:
return Immutable.merge(state, {
error: action.payload.error,
status: STATUS.ERROR,
results: [],
});
default:
return state;
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { testReducerSnapshotWithFixtures } from '../../common/testHelpers';
import reducer from './ModelsTableReducer';
import { MODELS_TABLE_ACTION_TYPES } from './ModelsTableConstants';

const fixtures = {
'should return initial state': {},
'should handle MODELS_TABLE_REQUEST': {
action: {
type: MODELS_TABLE_ACTION_TYPES.REQUEST,
},
},
'should handle MODELS_TABLE_SUCCESS': {
action: {
type: MODELS_TABLE_ACTION_TYPES.SUCCESS,
payload: {
search: 'name=model',
results: [{ id: 23, name: 'model' }],
page: 1,
per_page: 5,
total: 20,
sort: { by: 'name', order: 'ASC' },
},
},
},
'should handle MODELS_TABLE_FAILURE': {
action: {
type: MODELS_TABLE_ACTION_TYPES.FAILURE,
payload: {
error: new Error('ooops!'),
},
},
},
};

describe('ModelsTable reducer', () =>
testReducerSnapshotWithFixtures(reducer, fixtures));
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
sortControllerFactory,
column,
sortableColumn,
headerFormatterWithProps,
cellFormatterWithProps,
nameCellFormatter,
hostsCountCellFormatter,
deleteActionCellFormatter,
cellFormatter,
} from '../common/table';

/**
* Generate a table schema to the Hardware Models page.
* @param {Function} apiCall a Redux async action that fetches and stores table data in Redux.
* See actions/common/getTableItemsAction.
* @param {String} by by which column the table is sorted.
* If none then set it to undefined/null.
* @param {String} order in what order to sort a column. If none then set it to undefined/null.
* Otherwise, 'ASC' for ascending and 'DESC' for descending
* @return {Array}
*/
const createModelsTableSchema = (apiCall, by, order) => {
boaz0 marked this conversation as resolved.
Show resolved Hide resolved
const sortController = sortControllerFactory(apiCall, by, order);
return [
sortableColumn('name', __('Name'), 4, sortController, [
nameCellFormatter('models'),
]),
sortableColumn('vendor_class', __('Vendor Class'), 3, sortController),
sortableColumn('hardware_model', __('Hardware Model'), 3, sortController),
column(
'hosts_count',
__('Hosts'),
[headerFormatterWithProps],
[hostsCountCellFormatter('model'), cellFormatterWithProps],
{ className: 'col-md-1' },
{ align: 'right' }
),
column(
'actions',
__('Actions'),
[headerFormatterWithProps],
[deleteActionCellFormatter('models'), cellFormatter]
),
];
};

export default createModelsTableSchema;
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ModelsTable reducer should handle MODELS_TABLE_FAILURE 1`] = `
Object {
"error": [Error: ooops!],
"results": Array [],
"sortBy": "",
"sortOrder": "",
"status": "ERROR",
}
`;

exports[`ModelsTable reducer should handle MODELS_TABLE_REQUEST 1`] = `
Object {
"error": null,
"results": Array [],
"sortBy": "",
"sortOrder": "",
"status": "PENDING",
}
`;

exports[`ModelsTable reducer should handle MODELS_TABLE_SUCCESS 1`] = `
Object {
"error": null,
"results": Array [
Object {
"id": 23,
"name": "model",
},
],
"sortBy": "name",
"sortOrder": "ASC",
"status": "RESOLVED",
}
`;

exports[`ModelsTable reducer should return initial state 1`] = `
Object {
"error": null,
"results": Array [],
"sortBy": "",
"sortOrder": "",
"status": "PENDING",
}
`;
Loading