Skip to content
Permalink
Browse files

#237 - Added the ability to invoke the term actions after rendering

  • Loading branch information...
estruyf committed Feb 13, 2019
1 parent 8107ea8 commit 95654ed9a742b71b7cebb5bec335822588f4d0fc
@@ -146,9 +146,10 @@ Interface `ITermAction`
| id | string | yes | Unique id of the term action |
| title | string | yes | Action title |
| iconName | string | no | Name of the icon to be used to display action |
| hidden | boolean | no | Specify if the action is hidden. This could be used for instance when you want to invoke the action right after rendering. |
| invokeActionOnRender | boolean | no | Specifies if you want to invoke the action on render |
| applyToTerm | (currentTerm: ITerm) => Promise\<boolean\> \| boolean | yes | Method checks if the current term is supported |
| actionCallback | (spTermService: SPTermStorePickerService, currentTerm: ITerm) => Promise\<UpdateAction\> | yes | Method to be executed when action is fired |
| initialize | (spTermService: SPTermStorePickerService) => Promise\<void\> | no | Initializes the term action with the taxonomy service |


Interface `UpdateAction`
@@ -3,7 +3,7 @@
}

.listItem {
height: 36px;
min-height: 36px;
line-height: 36px;
cursor: pointer;

@@ -3,39 +3,18 @@ import { CommandBarButton } from 'office-ui-fabric-react/lib/Button';
import { ITermAction, TermActionsDisplayStyle, IConcreteTermActionProps } from './ITermsActions';

export default class ButtonTermAction extends React.Component<IConcreteTermActionProps> {
public render(): React.ReactElement<IConcreteTermActionProps> {
const { term, termActions } = this.props;

return (
<div style={{ display: 'flex', alignItems: 'stretch', height: '32px' }}>
{
termActions &&
termActions.map(termAction => {
const { name, text, iconName, btnTitle } = this._prepareCommandBarButton(termAction);
return (
<div>
<CommandBarButton split={true}
onClick={() => { this._onActionExecute(termAction); }}
iconProps={{
iconName: iconName || null,
style: { display: iconName ? null : "none"}
}}
text={text}
title={btnTitle}
name={name}
key={term.Id}
style={this._getTermActionActionButtonStyle()}
/>
</div>
);
})
}
</div>
);
/**
* componentWillMount lifecycle hook
*/
public componentWillMount(): void {
this.checkForImmediateInvocations();
}


private _prepareCommandBarButton = (termAction: ITermAction): { name: string, text: string, iconName: string, btnTitle: string } => {
/**
* Prepares the command bar button
*/
private prepareCommandBarButton = (termAction: ITermAction): { name: string, text: string, iconName: string, btnTitle: string } => {
let name: string = "";
let text: string = "";
let iconName: string = "";
@@ -54,7 +33,10 @@ export default class ButtonTermAction extends React.Component<IConcreteTermActio
return { name, text, iconName, btnTitle };
}

private _getTermActionActionButtonStyle = (): React.CSSProperties => {
/**
* Gets the action button styling
*/
private getTermActionActionButtonStyle = (): React.CSSProperties => {
let result: React.CSSProperties = {
backgroundColor: "transparent",
width: this.props.displayStyle === TermActionsDisplayStyle.icon ? "32px" : null,
@@ -64,8 +46,60 @@ export default class ButtonTermAction extends React.Component<IConcreteTermActio
return result;
}

private _onActionExecute = async (termAction: ITermAction) => {
/**
* Check if there are action to immediatly invoke
*/
private checkForImmediateInvocations() {
const { termActions } = this.props;
for (const action of termActions) {
if (action.invokeActionOnRender) {
this.onActionExecute(action);
}
}
}

/**
* On action execution
*/
private onActionExecute = async (termAction: ITermAction) => {
const updateAction = await termAction.actionCallback(this.props.spTermService, this.props.term);
this.props.termActionCallback(updateAction);
}

/**
* Default React render method
*/
public render(): React.ReactElement<IConcreteTermActionProps> {
const { term, termActions } = this.props;

return (
<div style={{ display: 'flex', alignItems: 'stretch', height: '32px' }}>
{
termActions &&
termActions.map(termAction => {
const { name, text, iconName, btnTitle } = this.prepareCommandBarButton(termAction);
return (
termAction.hidden ? (
null
) : (
<div>
<CommandBarButton split={true}
onClick={() => { this.onActionExecute(termAction); }}
iconProps={{
iconName: iconName || null,
style: { display: iconName ? null : "none"}
}}
text={text}
title={btnTitle}
name={name}
key={term.Id}
style={this.getTermActionActionButtonStyle()} />
</div>
)
);
})
}
</div>
);
}
}
@@ -5,44 +5,41 @@ import { ITermAction, TermActionsDisplayStyle, IConcreteTermActionProps } from '
import { IContextualMenuItem, IContextualMenuProps } from 'office-ui-fabric-react/lib/ContextualMenu';

export class DropdownTermAction extends React.Component<IConcreteTermActionProps> {
public render(): React.ReactElement<IConcreteTermActionProps> {
const { term, termActions } = this.props;

const termActionButtonStyle = this._getTermActionActionButtonStyle();
const contextualMenuProps = this._prepareContextualMenuProps(term, termActions);

return (
<div style={{ display: 'flex', alignItems: 'stretch', height: '32px' }}>
<DefaultButton style={termActionButtonStyle} menuProps={contextualMenuProps} />
</div>
);
/**
* componentWillMount lifecycle hook
*/
public componentWillMount(): void {
this.checkForImmediateInvocations();
}

/**
* Prepates contextual menu items for dropdown.
*/
private _prepareContextualMenuProps = (term: ITerm, termActions: ITermAction[]): IContextualMenuProps => {
const items: IContextualMenuItem[] = [];
private prepareContextualMenuProps = (term: ITerm, termActions: ITermAction[]): IContextualMenuProps => {
let items: IContextualMenuItem[] = [];
const displayStyle = this.props.displayStyle;
let useTargetWidth = true;

termActions.forEach(termAction => {
let termActionMenuItem: IContextualMenuItem = {
key: term.Id.toString(),
onClick: () => { this._onActionExecute(termAction); }
};
for (const termAction of termActions) {
if (!termAction.hidden) {
let termActionMenuItem: IContextualMenuItem = {
key: term.Id.toString(),
onClick: () => { this.onActionExecute(termAction); }
};

if (displayStyle && (displayStyle === TermActionsDisplayStyle.text || displayStyle === TermActionsDisplayStyle.textAndIcon)) {
termActionMenuItem.text = termAction.title;
termActionMenuItem.name = termAction.title;
useTargetWidth = false;
}
if (displayStyle && (displayStyle === TermActionsDisplayStyle.icon || displayStyle === TermActionsDisplayStyle.textAndIcon)) {
termActionMenuItem.iconProps = { iconName: termAction.iconName };
}
if (displayStyle && (displayStyle === TermActionsDisplayStyle.text || displayStyle === TermActionsDisplayStyle.textAndIcon)) {
termActionMenuItem.text = termAction.title;
termActionMenuItem.name = termAction.title;
useTargetWidth = false;
}
if (displayStyle && (displayStyle === TermActionsDisplayStyle.icon || displayStyle === TermActionsDisplayStyle.textAndIcon)) {
termActionMenuItem.iconProps = { iconName: termAction.iconName };
}

items.push(termActionMenuItem);
});
items.push(termActionMenuItem);
}
}

const contextualMenuProps: IContextualMenuProps = {
items,
@@ -54,7 +51,7 @@ export class DropdownTermAction extends React.Component<IConcreteTermActionProps
/**
* Prepare term action button style.
*/
private _getTermActionActionButtonStyle = (): React.CSSProperties => {
private getTermActionActionButtonStyle = (): React.CSSProperties => {
let result: React.CSSProperties = {
backgroundColor: "transparent",
width: "14px",
@@ -65,11 +62,39 @@ export class DropdownTermAction extends React.Component<IConcreteTermActionProps
return result;
}

/**
* Check if there are action to immediatly invoke
*/
private checkForImmediateInvocations() {
const { termActions } = this.props;
for (const action of termActions) {
if (action.invokeActionOnRender) {
this.onActionExecute(action);
}
}
}

/**
* Handler to execute selected action.
*/
private _onActionExecute = async (termAction: ITermAction) => {
private onActionExecute = async (termAction: ITermAction) => {
const updateAction = await termAction.actionCallback(this.props.spTermService, this.props.term);
this.props.termActionCallback(updateAction);
}

/**
* Default React render method
*/
public render(): React.ReactElement<IConcreteTermActionProps> {
const { term, termActions } = this.props;

const termActionButtonStyle = this.getTermActionActionButtonStyle();
const contextualMenuProps = this.prepareContextualMenuProps(term, termActions);

return (
<div style={{ display: 'flex', alignItems: 'stretch', height: '32px' }}>
<DefaultButton style={termActionButtonStyle} menuProps={contextualMenuProps} />
</div>
);
}
}
@@ -89,6 +89,9 @@ export interface ITermActions {
* Interface represents the possible action that could be execute on term level.
*/
export interface ITermAction {
/**
* Action ID
*/
id: string;
/**
* Action title
@@ -98,6 +101,14 @@ export interface ITermAction {
* Icon class name to be displayed for the action.
*/
iconName?: string;
/**
* Specify if the action is hidden. This could be used for instance when you want to invoke the action right after rendering.
*/
hidden?: boolean;
/**
* Specifies if you want to invoke the action on render
*/
invokeActionOnRender?: boolean;

/**
* Method checks if the current term is supported.
@@ -8,20 +8,35 @@ export default class TermActionsControl extends React.Component<ITermActionsCont
constructor(props: ITermActionsControlProps) {
super(props);

const { term, termActions } = this.props;
const { termActions } = this.props;

const displayMode = termActions.termActionsDisplayMode ? termActions.termActionsDisplayMode : TermActionsDisplayMode.buttons;
const displayStyle = termActions.termActionsDisplayStyle ? termActions.termActionsDisplayStyle : TermActionsDisplayStyle.text;
// Prepate list of the available actions
const availableActions: ITermAction[] = termActions.actions.filter(termAction => { return termAction.applyToTerm(term); });

this.state = {
availableActions,
availableActions: [],
displayMode,
displayStyle
};
}

/**
* componentWillMount lifecycle hook
*/
public componentWillMount(): void {
const { term, termActions } = this.props;

// Prepare list of the available actions
const availableActions: ITermAction[] = termActions.actions.filter(async (termAction) => { return await termAction.applyToTerm(term); });

this.setState({
availableActions
});
}

/**
* Default React render method
*/
public render(): React.ReactElement<ITermActionsControlProps> {
const { term } = this.props;
const { displayStyle, displayMode, availableActions } = this.state;
@@ -41,6 +56,4 @@ export default class TermActionsControl extends React.Component<ITermActionsCont
</div>
);
}


}
@@ -434,7 +434,7 @@ export default class ControlsTest extends React.Component<IControlsTestProps, IC
<div className="ms-font-m">Services tester:
<TaxonomyPicker
allowMultipleSelections={true}
termsetNameOrID="505d4df0-4045-41aa-b4f3-23458eafb413" // id to termset that has a custom sort
termsetNameOrID="61837936-29c5-46de-982c-d1adb6664b32" // id to termset that has a custom sort
panelTitle="Select Sorted Term"
label="Service Picker with custom actions"
context={this.props.context}
@@ -445,16 +445,23 @@ export default class ControlsTest extends React.Component<IControlsTestProps, IC
title: "Get term labels",
iconName: "LocaleLanguage",
id: "test",
invokeActionOnRender: true,
hidden: true,
actionCallback: async (taxService: SPTermStorePickerService, term: ITerm) => {
const labels = await taxService.getTermLabels(term.Id);
if (labels) {
let termLabel: string = labels.join(" ; ");
const updateAction = {
updateActionType: UpdateType.updateTermLabel,
value: `${termLabel} (updated)`
};
return updateAction;
}
// const labels = await taxService.getTermLabels(term.Id);
// if (labels) {
// let termLabel: string = labels.join(" ; ");
// const updateAction = {
// updateActionType: UpdateType.updateTermLabel,
// value: `${termLabel} (updated)`
// };
// return updateAction;
// }
const updateAction = {
updateActionType: UpdateType.updateTermLabel,
value: `${term.Name} (updated)`
};
return updateAction;
},
applyToTerm: () => (true)
},

0 comments on commit 95654ed

Please sign in to comment.
You can’t perform that action at this time.