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

Editor: Make the AddTerm Component more reusable (revert the revert) #8644

Merged
merged 2 commits into from
Oct 12, 2016
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
99 changes: 67 additions & 32 deletions client/my-sites/term-tree-selector/add-term.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ReactDom from 'react-dom';
import classNames from 'classnames';
import { connect } from 'react-redux';
import { localize } from 'i18n-calypso';
import { get, find } from 'lodash';
import { get, find, noop } from 'lodash';

/**
* Internal dependencies
Expand All @@ -16,6 +16,7 @@ import TermTreeSelectorTerms from './terms';
import Button from 'components/button';
import Gridicon from 'components/gridicon';
import FormInputValidation from 'components/forms/form-input-validation';
import FormTextarea from 'components/forms/form-textarea';
import FormTextInput from 'components/forms/form-text-input';
import FormSectionHeading from 'components/forms/form-section-heading';
import FormCheckbox from 'components/forms/form-checkbox';
Expand All @@ -26,7 +27,7 @@ import viewport from 'lib/viewport';
import { getSelectedSiteId } from 'state/ui/selectors';
import { getPostTypeTaxonomy } from 'state/post-types/taxonomies/selectors';
import { getTerms } from 'state/terms/selectors';
import { addTermForPost } from 'state/posts/actions';
import { addTerm } from 'state/terms/actions';

class TermSelectorAddTerm extends Component {
static initialState = {
Expand All @@ -39,14 +40,21 @@ class TermSelectorAddTerm extends Component {

static propTypes = {
labels: PropTypes.object,
onSuccess: PropTypes.func,
postType: PropTypes.string,
postId: PropTypes.number,
showDescriptionInput: PropTypes.bool,
siteId: PropTypes.number,
terms: PropTypes.array,
taxonomy: PropTypes.string,
translate: PropTypes.func
};

static defaultProps = {
onSuccess: noop,
showDescriptionInput: false
};

constructor( props ) {
super( props );
this.state = this.constructor.initialState;
Expand Down Expand Up @@ -92,9 +100,15 @@ class TermSelectorAddTerm extends Component {

getFormValues() {
const name = ReactDom.findDOMNode( this.refs.termName ).value.trim();
const parent = this.state.selectedParent.length ? this.state.selectedParent[ 0 ] : 0;
const formValues = { name };
if ( this.props.isHierarchical ) {
formValues.parent = this.state.selectedParent.length ? this.state.selectedParent[ 0 ] : 0;
}
if ( this.props.showDescriptionInput ) {
formValues.description = ReactDom.findDOMNode( this.refs.termDescription ).value.trim();
}

return { name, parent };
return formValues;
}

isValid() {
Expand All @@ -109,7 +123,7 @@ class TermSelectorAddTerm extends Component {
const lowerCasedTermName = values.name.toLowerCase();
const matchingTerm = find( this.props.terms, ( term ) => {
return ( term.name.toLowerCase() === lowerCasedTermName ) &&
( term.parent === values.parent );
( ! this.props.isHierarchical || ( term.parent === values.parent ) );
} );

if ( matchingTerm ) {
Expand Down Expand Up @@ -145,12 +159,43 @@ class TermSelectorAddTerm extends Component {

const { postId, siteId, taxonomy } = this.props;

this.props.addTermForPost( siteId, taxonomy, term, postId );
this.props
.addTerm( siteId, taxonomy, term, postId )
.then( this.props.onSuccess );
this.closeDialog();
}

renderParentSelector() {
const { labels, siteId, taxonomy, translate } = this.props;
const { searchTerm, selectedParent } = this.state;
const query = {};
if ( searchTerm && searchTerm.length ) {
query.search = searchTerm;
}

return (
<FormFieldset>
<FormLegend>
{ labels.parent_item }
</FormLegend>
<FormLabel>
<FormCheckbox ref="topLevel" checked={ this.state.isTopLevel } onChange={ this.boundOnTopLevelChange } />
<span>{ translate( 'Top level', { context: 'Terms: New term being created is top level' } ) }</span>
</FormLabel>
<TermTreeSelectorTerms
siteId={ siteId }
taxonomy={ taxonomy }
onSearch={ this.boundOnSearch }
onChange={ this.boundOnParentChange }
query={ query }
selected={ selectedParent }
/>
</FormFieldset>
);
}

render() {
const { labels, siteId, taxonomy, translate, terms } = this.props;
const { isHierarchical, labels, translate, terms, showDescriptionInput } = this.props;
const buttons = [ {
action: 'cancel',
label: translate( 'Cancel' )
Expand All @@ -162,12 +207,6 @@ class TermSelectorAddTerm extends Component {
onClick: this.boundSaveTerm
} ];

const { searchTerm, selectedParent } = this.state;
const query = {};
if ( searchTerm && searchTerm.length ) {
query.search = searchTerm;
}

const isError = this.state.error && this.state.error.length;
const totalTerms = terms ? terms.length : 0;
const classes = classNames( 'term-tree-selector__add-term', { 'is-compact': totalTerms < 8 } );
Expand All @@ -193,23 +232,16 @@ class TermSelectorAddTerm extends Component {
onKeyUp={ this.boundValidateInput } />
{ isError && <FormInputValidation isError text={ this.state.error } /> }
</FormFieldset>
<FormFieldset>
<FormLegend>
{ labels.parent_item }
</FormLegend>
<FormLabel>
<FormCheckbox ref="topLevel" checked={ this.state.isTopLevel } onChange={ this.boundOnTopLevelChange } />
<span>{ translate( 'Top level', { context: 'Terms: New term being created is top level' } ) }</span>
</FormLabel>
<TermTreeSelectorTerms
siteId={ siteId }
taxonomy={ taxonomy }
onSearch={ this.boundOnSearch }
onChange={ this.boundOnParentChange }
query={ query }
selected={ selectedParent }
/>
</FormFieldset>
{ showDescriptionInput && <FormFieldset>
<FormLegend>
{ translate( 'Description', { context: 'Terms: Term description label' } ) }
</FormLegend>
<FormTextarea
ref="termDescription"
onKeyUp={ this.boundValidateInput } />
</FormFieldset>
}
{ isHierarchical && this.renderParentSelector() }
</Dialog>
</div>
);
Expand All @@ -220,13 +252,16 @@ export default connect(
( state, ownProps ) => {
const { taxonomy, postType } = ownProps;
const siteId = getSelectedSiteId( state );
const labels = get( getPostTypeTaxonomy( state, siteId, postType, taxonomy ), 'labels', {} );
const taxonomyDetails = getPostTypeTaxonomy( state, siteId, postType, taxonomy );
const labels = get( taxonomyDetails, 'labels', {} );
const isHierarchical = get( taxonomyDetails, 'hierarchical', false );
Copy link
Contributor

Choose a reason for hiding this comment

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

👍


return {
terms: getTerms( state, siteId, taxonomy ),
isHierarchical,
labels,
siteId
};
},
{ addTermForPost }
{ addTerm }
)( localize( TermSelectorAddTerm ) );
18 changes: 14 additions & 4 deletions client/post-editor/editor-term-selector/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { cloneDeep, findIndex, map, toArray } from 'lodash';
*/
import TermTreeSelector from 'my-sites/term-tree-selector';
import AddTerm from 'my-sites/term-tree-selector/add-term';
import { editPost } from 'state/posts/actions';
import { editPost, addTermForPost } from 'state/posts/actions';
import { getSelectedSiteId } from 'state/ui/selectors';
import { getEditorPostId } from 'state/ui/editor/selectors';
import { getEditedPostValue } from 'state/posts/selectors';
Expand All @@ -31,6 +31,11 @@ class EditorTermSelector extends Component {
this.boundOnTermsChange = this.onTermsChange.bind( this );
}

onAddTerm = ( term ) => {
const { postId, taxonomyName, siteId } = this.props;
this.props.addTermForPost( siteId, taxonomyName, term, postId );
}

onTermsChange( selectedTerm ) {
const { postTerms, taxonomyName, siteId, postId } = this.props;
const terms = cloneDeep( postTerms ) || {};
Expand Down Expand Up @@ -58,7 +63,7 @@ class EditorTermSelector extends Component {
}

render() {
const { postType, postId, siteId, taxonomyName, canEditTerms } = this.props;
const { postType, siteId, taxonomyName, canEditTerms } = this.props;

return (
<div>
Expand All @@ -70,7 +75,12 @@ class EditorTermSelector extends Component {
siteId={ siteId }
multiple={ true }
/>
{ canEditTerms && <AddTerm taxonomy={ taxonomyName } postType={ postType } postId={ postId } /> }
{ canEditTerms &&
<AddTerm
taxonomy={ taxonomyName }
postType={ postType }
onSuccess={ this.onAddTerm } />
}
</div>
);
}
Expand All @@ -89,5 +99,5 @@ export default connect(
postId
};
},
{ editPost }
{ editPost, addTermForPost }
)( EditorTermSelector );
3 changes: 1 addition & 2 deletions client/state/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
* Internal dependencies
*/
import noticesMiddleware from './notices/middleware';
import postsEditMiddleware from './posts/middleware';
import application from './application/reducer';
import comments from './comments/reducer';
import componentsUsageStats from './components-usage-stats/reducer';
Expand Down Expand Up @@ -94,7 +93,7 @@ export const reducer = combineReducers( {
wordads
} );

const middleware = [ thunkMiddleware, noticesMiddleware, postsEditMiddleware ];
const middleware = [ thunkMiddleware, noticesMiddleware ];

if ( typeof window === 'object' ) {
// Browser-specific middlewares
Expand Down
34 changes: 28 additions & 6 deletions client/state/posts/actions.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
/**
* External dependencies
*/
import { isNumber, toArray } from 'lodash';

/**
* Internal dependencies
*/
import wpcom from 'lib/wp';
import { extendAction } from 'state/utils';
import { normalizePostForApi } from './utils';
import { addTerm } from 'state/terms/actions';
import { getEditedPost } from 'state/posts/selectors';
import {
POST_DELETE,
POST_DELETE_SUCCESS,
Expand Down Expand Up @@ -297,9 +301,7 @@ export function restorePost( siteId, postId ) {
}

/**
* Returns an action thunk which, when dispatched, triggers a network request
* to create a new term. All actions dispatched by the thunk will include meta
* to associate it with the specified post ID.
* Returns an action thunk which, when dispatched, adds a term to the current edited post
*
* @param {Number} siteId Site ID
* @param {String} taxonomy Taxonomy Slug
Expand All @@ -308,5 +310,25 @@ export function restorePost( siteId, postId ) {
* @return {Function} Action thunk
*/
export function addTermForPost( siteId, taxonomy, term, postId ) {
return extendAction( addTerm( siteId, taxonomy, term ), { postId } );
return ( dispatch, getState ) => {
const state = getState();
const post = getEditedPost( state, siteId, postId );

// if there is no post, no term, or term is temporary, bail
if ( ! post || ! term || ! isNumber( term.ID ) ) {
return;
}

const postTerms = post.terms || {};

// ensure we have an array since API returns an object
const taxonomyTerms = toArray( postTerms[ taxonomy ] );
taxonomyTerms.push( term );

dispatch( editPost( siteId, postId, {
terms: {
[ taxonomy ]: taxonomyTerms
}
} ) );
};
}
49 changes: 0 additions & 49 deletions client/state/posts/middleware.js

This file was deleted.

Loading