-
Notifications
You must be signed in to change notification settings - Fork 19
Conversation
Any reason why this shouldn't be part of patternfly react? I think the pattern is generic enough? |
/cc @serenamarie125 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like a great start :), thank you.
I've added a few inline comments from a very quick code scanning.
a few general comments
- I think we should use patternfly-react as much as we can, this includes components that already available, and move the view/react code layers to that repository.
- I would suggest you look into how v2v plugin and foreman currently handle redux, I think it would make a lot of sense to be aligned from the beginning vs create technical debt later on.
on a side note, i think it makes sense to send pr's to patternfly-react, as you would get a lot of value out of the review comments while learning react.
.eslintrc
Outdated
@@ -12,6 +12,9 @@ | |||
"ignoreUrls": true, | |||
"ignoreComments": false | |||
}], | |||
"react/require-default-props": [0], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
while i dont have an opinion, maybe linting rule changes should happen in separate pr's so their impact could be discussed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/tagging/components/tag.jsx
Outdated
@@ -0,0 +1,29 @@ | |||
import * as React from 'react'; | |||
import PropTypes from 'prop-types'; | |||
import style from './style'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: extra whitespace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
deleted
src/tagging/components/tag.jsx
Outdated
import PropTypes from 'prop-types'; | ||
import style from './style'; | ||
|
||
class Tag extends React.Component { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imho you dont need a class here, you can simply use a const function instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
src/tagging/components/tag.jsx
Outdated
<span className="label label-info"> | ||
{this.props.tagCategory}: {this.props.tagValue} | ||
<a href="#" onClick={this.handleClick}> | ||
<span className="pficon pficon-close" aria-hidden="true" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use the icon component from patternfly-react
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will use patternfly/patternfly-react#223 as soon as it's merged
src/tagging/components/tag.jsx
Outdated
{this.props.tagCategory}: {this.props.tagValue} | ||
<a href="#" onClick={this.handleClick}> | ||
<span className="pficon pficon-close" aria-hidden="true" /> | ||
<span className="sr-only">Remove</span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all strings should be available as props, so later on you can i18n them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, strings are available as props with default values.
const tagCategories = Object.keys(props.tags) || []; | ||
return ( | ||
<div className="col-lg-6"> | ||
<div className="row"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use col and row components from patternfly-react
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
import TagSelector from './tagSelector'; | ||
import ValueSelector from './valueSelector'; | ||
|
||
const TagModifier = (props) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please extact the strings, so you can do
const TagModifier = ({ tags, selectedTagCategory, onTagCategroyChange... })
and then just use them without the props.<>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
src/tagging/reducers/index.js
Outdated
import { combineReducers } from 'redux'; | ||
import { modifySetTags, toggle } from './reducers'; | ||
|
||
const TAGS = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these should move into fixtures files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be part of this component, It was there because of testing purposes. I moved it to stories and demo. Now I pass it as a default state, when creating this component
src/tagging/reducers/reducers.js
Outdated
@@ -0,0 +1,24 @@ | |||
// state = state.setTags | |||
export const modifySetTags = (state = [], action) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imho you should use immutable library to avoid changing the existing state.
@ohadlevy I think this could be part of patternfly react in the future, but not yet. First I would like to test it in ManageIQ and see if there are some changes to be made. After I test it, I would move it to patternfly, preferably to monorepo(when it's done). Because I think this component have very specific usecase and it is not necessary for everybody who uses patternfly to load it. |
I have probably should have marked it as WIP, because there is still some work to be done. @miq-bot add_label WIP |
src/tagging/actions/actions.js
Outdated
@@ -0,0 +1,5 @@ | |||
// action creators | |||
export const TOGGLE_TAG_CATEGORY_CHANGE = 'TOGGLE_TAG_CATEGORY_CHANGE'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add prefix for these actions so we don't clash with other action types.
export const TOGGLE_TAG_CATEGORY_CHANGE = 'UI-COMPONENTS_TOGGLE_TAG_CATEGORY_CHANGE';
export const TOGGLE_TAG_VALUE_CHANGE = 'UI-COMPONENTS_TOGGLE_TAG_VALUE_CHANGE';
export const DELETE_SET_TAG = 'UI-COMPONENTS_DELETE_SET_TAG';
export const ADD_SET_TAG = 'UI-COMPONENTS_ADD_SET_TAG';
b66b4e9
to
546fe8e
Compare
@ohadlevy @PanSpagetka I think someone already implemented something in the managieq-ui-service repo in conjunction with UX from a design perspective. @AllenBW I think there was a tag editor modal submitted - I couldn't find a PR to reference. Might you have it handy? |
@ohadlevy re: your PF React suggestion, I agree that this would be a great contribution! |
@serenamarie125 yeah!! that was a while back... ManageIQ/manageiq-ui-service#402 |
Actually, here's the initial piece of it ... although it looks like it never went through a ux review |
@serenamarie125 Thanks for showing me this, I didn't know that we have official design. But I still think my PR is needed. Even though my knowledge of angular is very poor, I don't think, that somebody can take that Tag editor from ServiceUI and use it in ManageIQ-ui-classic(or anywhere else) easily. It would require some amount of work. The point of my PR is make reusable component, composed of several simpler components creating some logical structure, using preferable technologies(React, Redux). So you can use this component, if I oversimplify it, with one line of code. Because logic, data and design are separated, you can change design of the component without affecting it's functionality easily. Or you can simply add another component into it. For example View of Affected Entities, without need for modifying existing code, only adding new. As I said earlier, I would like to(and will) make PR into Patternfly React, when I have working example in ManageIQ. If you have some suggestions about design, please let me know. |
@serenamarie125 correct, that MIQ implementation is in line with the design we had. Thanks! |
@PanSpagetka Could we get closer to the design shown here https://manageiq.github.io/manageiq-design/UX/common/Tags/design? Is it possible to have multiple values assigned to the same category? In the design, we are showing a design where we would show that in the "chip". |
@serenamarie125 Yes it is. And I will make it look more like in the design. @terezanovotna offered me help, so when I am done with coding I will redesigned it according to the https://manageiq.github.io/manageiq-design/UX/common/Tags/design Multiple values are not possible now, because there weren't possible in ManageIQ, but I can rewrite it so they will be possible. |
f056f02
to
c168b26
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It needs some refactoring changes so the code is much redable and is less prone to bugs. Not reviewed stories and tests, that will be step 2.
demo/demo-app.jsx
Outdated
|
||
export default function renderApp() { | ||
ReactDOM.render(<cmpA.HelloCmp />, document.getElementById('demo-app')); | ||
const store = createStore(taggingApp); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use module.hot reload so we can change store on the fly:
if (module.hot) {
const store = createStore(taggingApp);
module.hot.accept('../src/tagging/reducers', () => {
const nextRootReducer = taggingApp;
store.replaceReducer(nextRootReducer);
});
}
store.dispatch(loadState(defaultState));
ReactDOM.render(
<TaggingWithButtonsConnected store={store} />,
document.getElementById('demo-app'),
);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolved
package.json
Outdated
@@ -91,12 +95,13 @@ | |||
"dependencies": { | |||
"lodash": "^4.17.4", | |||
"patternfly-react": "^1.8.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update PF react to newer version "patternfly-react": "^1.16.4",
scripts/webpack.config.js
Outdated
@@ -31,7 +31,6 @@ module.exports = (env) => { | |||
enforce: 'pre', | |||
test: /\.(js|jsx)$/, | |||
exclude: /node_modules/, | |||
loader: 'eslint-loader', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please turn the eslint rule back? You will have few errors, but these are OK and easy to fix.
src/tagging/components/tag.jsx
Outdated
const Tag = ({ | ||
onTagDeleteClick, tagCategory, tagValue, | ||
}) => ( | ||
<li key={`${tagValue.id}`}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove ${..}
and use just <li key={tagValue.id}>
src/tagging/components/tag.jsx
Outdated
<Label | ||
key={tagValue.id} | ||
bsStyle='primary' | ||
onRemoveClick={() => { onTagDeleteClick(tagCategory, tagValue); }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to wrap function in body
onRemoveClick={() => onTagDeleteClick(tagCategory, tagValue)}
} | ||
|
||
onTagValueChange = (selectedTagValue) => { | ||
if (this.props.multiValue) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not create same object twice
const action = { tagCategory: this.props.selectedTagCategory, tagValue: selectedTagValue };
} | ||
} | ||
|
||
onTagDeleteClick = (tagCategory, tagValue) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to wrap it up with {}
onTagDeleteClick = (tagCategory, tagValue) => this.props.onTagDeleteClick({ tagCategory, tagValue });
selectedTagValue={this.props.selectedTagValue} | ||
/> | ||
<Row className={'pull-right'}> | ||
<ButtonGroup> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would put ButtonGroup in seperate component so we have easier way of combining these two components together.
|
||
|
||
class ValueSelector extends React.Component { | ||
handleChange = (selectedOption) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactor this component to be simpler
handleChange = (selectedOption) => {
this.props.onTagValueChange({ description: selectedOption.label, id: selectedOption.value });
};
value = this.props.selectedOption.id;
label = this.props.selectedOption.description;
tagValues = this.props.tagValues.map(tag => ({ value: tag.id, label: tag.description }));
render() {
return (
<Select
name="form-field-name"
value={this.value}
label={this.label}
onChange={this.handleChange}
options={this.tagValues}
clearable={false}
/>
);
}
src/tagging/reducers/reducers.js
Outdated
export const modifyassignedTags = (state = [], action) => { | ||
switch (action.type) { | ||
case actionsConstants.DELETE_ASSIGNED_TAG: | ||
return [...state.filter(tag => (tag.tagCategory.id !== action.tag.tagCategory.id)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please refactor this to use functions and make it simpler.
99600ae
to
d299ee1
Compare
@remove_label wip |
@miq_bot remove_label wip |
d299ee1
to
a3cbbf9
Compare
@serenamarie125 Thank you for review.
I have addressed the visual feedback, changed spacing, colors, alignments and updated screenshots/gif. EDIT: |
ca989b8
to
f798b0c
Compare
Checked commits PanSpagetka/react-ui-components@fc69c86~...f798b0c with ruby 2.3.3, rubocop 0.52.1, haml-lint 0.20.0, and yamllint 1.10.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@PanSpagetka thanks so much for making all the styling changes. This looks really nice, great job!
f798b0c
to
250d1b2
Compare
This pull request is not mergeable. Please rebase and repush. |
250d1b2
to
68c1c06
Compare
How Does it look
Interaction
How does it work
Tags are made up of two parts, a category and value. Some categories allow for more than one value to be associated. Categories which can have assigned only one value are marked with
*
character.Tags can be applied to a single item or multiple items at one time depending on the use case.
By selecting Category and Value from select box, you can assign tags to items. You can also remove assigned values by clicking on cross in Tag View.
How to use it
taggingApp
or Add it to your store.store.dispatch({ initialState: defaultState, type: "UI-COMPONENTS_TAGGING_LOAD_STATE" });
TaggingConnected
orTaggingWithButtonsConnected
, those are the components which work with Redux and contains logic(they are working).initialState must be structured like this:
*
in UI.description
and Intid
affectedItems
TaggingConnected