Skip to content

Commit 95654ed

Browse files
committed
#237 - Added the ability to invoke the term actions after rendering
1 parent 8107ea8 commit 95654ed

File tree

7 files changed

+170
-79
lines changed

7 files changed

+170
-79
lines changed

docs/documentation/docs/controls/TaxonomyPicker.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,10 @@ Interface `ITermAction`
146146
| id | string | yes | Unique id of the term action |
147147
| title | string | yes | Action title |
148148
| iconName | string | no | Name of the icon to be used to display action |
149+
| 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. |
150+
| invokeActionOnRender | boolean | no | Specifies if you want to invoke the action on render |
149151
| applyToTerm | (currentTerm: ITerm) => Promise\<boolean\> \| boolean | yes | Method checks if the current term is supported |
150152
| actionCallback | (spTermService: SPTermStorePickerService, currentTerm: ITerm) => Promise\<UpdateAction\> | yes | Method to be executed when action is fired |
151-
| initialize | (spTermService: SPTermStorePickerService) => Promise\<void\> | no | Initializes the term action with the taxonomy service |
152153

153154

154155
Interface `UpdateAction`

src/controls/taxonomyPicker/TaxonomyPicker.module.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
}
44

55
.listItem {
6-
height: 36px;
6+
min-height: 36px;
77
line-height: 36px;
88
cursor: pointer;
99

src/controls/taxonomyPicker/termActions/ButtonTermAction.tsx

Lines changed: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,18 @@ import { CommandBarButton } from 'office-ui-fabric-react/lib/Button';
33
import { ITermAction, TermActionsDisplayStyle, IConcreteTermActionProps } from './ITermsActions';
44

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

9-
return (
10-
<div style={{ display: 'flex', alignItems: 'stretch', height: '32px' }}>
11-
{
12-
termActions &&
13-
termActions.map(termAction => {
14-
const { name, text, iconName, btnTitle } = this._prepareCommandBarButton(termAction);
15-
return (
16-
<div>
17-
<CommandBarButton split={true}
18-
onClick={() => { this._onActionExecute(termAction); }}
19-
iconProps={{
20-
iconName: iconName || null,
21-
style: { display: iconName ? null : "none"}
22-
}}
23-
text={text}
24-
title={btnTitle}
25-
name={name}
26-
key={term.Id}
27-
style={this._getTermActionActionButtonStyle()}
28-
/>
29-
</div>
30-
);
31-
})
32-
}
33-
</div>
34-
);
7+
/**
8+
* componentWillMount lifecycle hook
9+
*/
10+
public componentWillMount(): void {
11+
this.checkForImmediateInvocations();
3512
}
3613

37-
38-
private _prepareCommandBarButton = (termAction: ITermAction): { name: string, text: string, iconName: string, btnTitle: string } => {
14+
/**
15+
* Prepares the command bar button
16+
*/
17+
private prepareCommandBarButton = (termAction: ITermAction): { name: string, text: string, iconName: string, btnTitle: string } => {
3918
let name: string = "";
4019
let text: string = "";
4120
let iconName: string = "";
@@ -54,7 +33,10 @@ export default class ButtonTermAction extends React.Component<IConcreteTermActio
5433
return { name, text, iconName, btnTitle };
5534
}
5635

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

67-
private _onActionExecute = async (termAction: ITermAction) => {
49+
/**
50+
* Check if there are action to immediatly invoke
51+
*/
52+
private checkForImmediateInvocations() {
53+
const { termActions } = this.props;
54+
for (const action of termActions) {
55+
if (action.invokeActionOnRender) {
56+
this.onActionExecute(action);
57+
}
58+
}
59+
}
60+
61+
/**
62+
* On action execution
63+
*/
64+
private onActionExecute = async (termAction: ITermAction) => {
6865
const updateAction = await termAction.actionCallback(this.props.spTermService, this.props.term);
6966
this.props.termActionCallback(updateAction);
7067
}
68+
69+
/**
70+
* Default React render method
71+
*/
72+
public render(): React.ReactElement<IConcreteTermActionProps> {
73+
const { term, termActions } = this.props;
74+
75+
return (
76+
<div style={{ display: 'flex', alignItems: 'stretch', height: '32px' }}>
77+
{
78+
termActions &&
79+
termActions.map(termAction => {
80+
const { name, text, iconName, btnTitle } = this.prepareCommandBarButton(termAction);
81+
return (
82+
termAction.hidden ? (
83+
null
84+
) : (
85+
<div>
86+
<CommandBarButton split={true}
87+
onClick={() => { this.onActionExecute(termAction); }}
88+
iconProps={{
89+
iconName: iconName || null,
90+
style: { display: iconName ? null : "none"}
91+
}}
92+
text={text}
93+
title={btnTitle}
94+
name={name}
95+
key={term.Id}
96+
style={this.getTermActionActionButtonStyle()} />
97+
</div>
98+
)
99+
);
100+
})
101+
}
102+
</div>
103+
);
104+
}
71105
}

src/controls/taxonomyPicker/termActions/DropdownTermAction.tsx

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,44 +5,41 @@ import { ITermAction, TermActionsDisplayStyle, IConcreteTermActionProps } from '
55
import { IContextualMenuItem, IContextualMenuProps } from 'office-ui-fabric-react/lib/ContextualMenu';
66

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

11-
const termActionButtonStyle = this._getTermActionActionButtonStyle();
12-
const contextualMenuProps = this._prepareContextualMenuProps(term, termActions);
13-
14-
return (
15-
<div style={{ display: 'flex', alignItems: 'stretch', height: '32px' }}>
16-
<DefaultButton style={termActionButtonStyle} menuProps={contextualMenuProps} />
17-
</div>
18-
);
9+
/**
10+
* componentWillMount lifecycle hook
11+
*/
12+
public componentWillMount(): void {
13+
this.checkForImmediateInvocations();
1914
}
2015

2116
/**
2217
* Prepates contextual menu items for dropdown.
2318
*/
24-
private _prepareContextualMenuProps = (term: ITerm, termActions: ITermAction[]): IContextualMenuProps => {
25-
const items: IContextualMenuItem[] = [];
19+
private prepareContextualMenuProps = (term: ITerm, termActions: ITermAction[]): IContextualMenuProps => {
20+
let items: IContextualMenuItem[] = [];
2621
const displayStyle = this.props.displayStyle;
2722
let useTargetWidth = true;
2823

29-
termActions.forEach(termAction => {
30-
let termActionMenuItem: IContextualMenuItem = {
31-
key: term.Id.toString(),
32-
onClick: () => { this._onActionExecute(termAction); }
33-
};
24+
for (const termAction of termActions) {
25+
if (!termAction.hidden) {
26+
let termActionMenuItem: IContextualMenuItem = {
27+
key: term.Id.toString(),
28+
onClick: () => { this.onActionExecute(termAction); }
29+
};
3430

35-
if (displayStyle && (displayStyle === TermActionsDisplayStyle.text || displayStyle === TermActionsDisplayStyle.textAndIcon)) {
36-
termActionMenuItem.text = termAction.title;
37-
termActionMenuItem.name = termAction.title;
38-
useTargetWidth = false;
39-
}
40-
if (displayStyle && (displayStyle === TermActionsDisplayStyle.icon || displayStyle === TermActionsDisplayStyle.textAndIcon)) {
41-
termActionMenuItem.iconProps = { iconName: termAction.iconName };
42-
}
31+
if (displayStyle && (displayStyle === TermActionsDisplayStyle.text || displayStyle === TermActionsDisplayStyle.textAndIcon)) {
32+
termActionMenuItem.text = termAction.title;
33+
termActionMenuItem.name = termAction.title;
34+
useTargetWidth = false;
35+
}
36+
if (displayStyle && (displayStyle === TermActionsDisplayStyle.icon || displayStyle === TermActionsDisplayStyle.textAndIcon)) {
37+
termActionMenuItem.iconProps = { iconName: termAction.iconName };
38+
}
4339

44-
items.push(termActionMenuItem);
45-
});
40+
items.push(termActionMenuItem);
41+
}
42+
}
4643

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

65+
/**
66+
* Check if there are action to immediatly invoke
67+
*/
68+
private checkForImmediateInvocations() {
69+
const { termActions } = this.props;
70+
for (const action of termActions) {
71+
if (action.invokeActionOnRender) {
72+
this.onActionExecute(action);
73+
}
74+
}
75+
}
76+
6877
/**
6978
* Handler to execute selected action.
7079
*/
71-
private _onActionExecute = async (termAction: ITermAction) => {
80+
private onActionExecute = async (termAction: ITermAction) => {
7281
const updateAction = await termAction.actionCallback(this.props.spTermService, this.props.term);
7382
this.props.termActionCallback(updateAction);
7483
}
84+
85+
/**
86+
* Default React render method
87+
*/
88+
public render(): React.ReactElement<IConcreteTermActionProps> {
89+
const { term, termActions } = this.props;
90+
91+
const termActionButtonStyle = this.getTermActionActionButtonStyle();
92+
const contextualMenuProps = this.prepareContextualMenuProps(term, termActions);
93+
94+
return (
95+
<div style={{ display: 'flex', alignItems: 'stretch', height: '32px' }}>
96+
<DefaultButton style={termActionButtonStyle} menuProps={contextualMenuProps} />
97+
</div>
98+
);
99+
}
75100
}

src/controls/taxonomyPicker/termActions/ITermsActions.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ export interface ITermActions {
8989
* Interface represents the possible action that could be execute on term level.
9090
*/
9191
export interface ITermAction {
92+
/**
93+
* Action ID
94+
*/
9295
id: string;
9396
/**
9497
* Action title
@@ -98,6 +101,14 @@ export interface ITermAction {
98101
* Icon class name to be displayed for the action.
99102
*/
100103
iconName?: string;
104+
/**
105+
* Specify if the action is hidden. This could be used for instance when you want to invoke the action right after rendering.
106+
*/
107+
hidden?: boolean;
108+
/**
109+
* Specifies if you want to invoke the action on render
110+
*/
111+
invokeActionOnRender?: boolean;
101112

102113
/**
103114
* Method checks if the current term is supported.

src/controls/taxonomyPicker/termActions/TermActionsControl.tsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,35 @@ export default class TermActionsControl extends React.Component<ITermActionsCont
88
constructor(props: ITermActionsControlProps) {
99
super(props);
1010

11-
const { term, termActions } = this.props;
11+
const { termActions } = this.props;
1212

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

1816
this.state = {
19-
availableActions,
17+
availableActions: [],
2018
displayMode,
2119
displayStyle
2220
};
2321
}
2422

23+
/**
24+
* componentWillMount lifecycle hook
25+
*/
26+
public componentWillMount(): void {
27+
const { term, termActions } = this.props;
28+
29+
// Prepare list of the available actions
30+
const availableActions: ITermAction[] = termActions.actions.filter(async (termAction) => { return await termAction.applyToTerm(term); });
31+
32+
this.setState({
33+
availableActions
34+
});
35+
}
36+
37+
/**
38+
* Default React render method
39+
*/
2540
public render(): React.ReactElement<ITermActionsControlProps> {
2641
const { term } = this.props;
2742
const { displayStyle, displayMode, availableActions } = this.state;
@@ -41,6 +56,4 @@ export default class TermActionsControl extends React.Component<ITermActionsCont
4156
</div>
4257
);
4358
}
44-
45-
4659
}

src/webparts/controlsTest/components/ControlsTest.tsx

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ export default class ControlsTest extends React.Component<IControlsTestProps, IC
434434
<div className="ms-font-m">Services tester:
435435
<TaxonomyPicker
436436
allowMultipleSelections={true}
437-
termsetNameOrID="505d4df0-4045-41aa-b4f3-23458eafb413" // id to termset that has a custom sort
437+
termsetNameOrID="61837936-29c5-46de-982c-d1adb6664b32" // id to termset that has a custom sort
438438
panelTitle="Select Sorted Term"
439439
label="Service Picker with custom actions"
440440
context={this.props.context}
@@ -445,16 +445,23 @@ export default class ControlsTest extends React.Component<IControlsTestProps, IC
445445
title: "Get term labels",
446446
iconName: "LocaleLanguage",
447447
id: "test",
448+
invokeActionOnRender: true,
449+
hidden: true,
448450
actionCallback: async (taxService: SPTermStorePickerService, term: ITerm) => {
449-
const labels = await taxService.getTermLabels(term.Id);
450-
if (labels) {
451-
let termLabel: string = labels.join(" ; ");
452-
const updateAction = {
453-
updateActionType: UpdateType.updateTermLabel,
454-
value: `${termLabel} (updated)`
455-
};
456-
return updateAction;
457-
}
451+
// const labels = await taxService.getTermLabels(term.Id);
452+
// if (labels) {
453+
// let termLabel: string = labels.join(" ; ");
454+
// const updateAction = {
455+
// updateActionType: UpdateType.updateTermLabel,
456+
// value: `${termLabel} (updated)`
457+
// };
458+
// return updateAction;
459+
// }
460+
const updateAction = {
461+
updateActionType: UpdateType.updateTermLabel,
462+
value: `${term.Name} (updated)`
463+
};
464+
return updateAction;
458465
},
459466
applyToTerm: () => (true)
460467
},

0 commit comments

Comments
 (0)