Skip to content

Commit 3aec762

Browse files
authored
Merge pull request #1922 from NishkalankBezawada/feature/FixIssue-1911
Fixing Issue 1911
2 parents fe8f98f + 38e212d commit 3aec762

File tree

4 files changed

+166
-35
lines changed

4 files changed

+166
-35
lines changed

.nvmrc

14 Bytes
Binary file not shown.

docs/documentation/docs/controls/ModernTaxonomyPicker.md

Lines changed: 134 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,114 @@ Custom rendering of a More actions button that displays a context menu for each
5858
const termSetId = "36d21c3f-b83b-4acc-a223-4df6fa8e946d";
5959
const [clickedActionTerm, setClickedActionTerm] = React.useState<ITermInfo>();
6060

61-
const addChildTerm = (parentTermId, updateTaxonomyTreeViewCallback): void => {
61+
private addTermSwitch = async (index: number, term: ITermInfo, updateTaxonomyTreeViewCallback: any, isParent: boolean) => {
62+
if (isParent) {
63+
this.addTerm(index, term, updateTaxonomyTreeViewCallback);
64+
} else {
65+
this.addChildTerm(index, term, updateTaxonomyTreeViewCallback);
66+
}
67+
}
68+
69+
//Below methods are using graphClient to create terms
70+
private addTerm = async (index: number, term: ITermInfo, updateTaxonomyTreeViewCallback: any) => {
71+
try {
72+
if (termSetId) {
73+
this.props.context?.msGraphClientFactory?.getClient("3").then((client) => {
74+
client
75+
.api(`/sites/${this.props.context?.pageContext.site.id}/termStore/sets/${termSetId}/terms/${term.id}`)
76+
.post({
77+
labels: [
78+
{
79+
languageTag: "en-US",
80+
name: "TestTerm",
81+
isDefault: true,
82+
},
83+
],
84+
})
85+
.then((response) => {
86+
const { "@odata.context": _, ...filteredResponse } = response;
87+
filteredResponse.isAvailableForTagging = [
88+
{
89+
"setId": termSetId,
90+
"isAvailable": true
91+
}
92+
];
93+
// Pass the filtered response to updateTaxonomyTreeViewCallback
94+
updateTaxonomyTreeViewCallback([filteredResponse] as ITermInfo[], [term] as ITermInfo[], null, null);
95+
})
96+
.catch((error) => {
97+
console.error("Error adding child term", error);
98+
});
99+
});
100+
} else {
101+
console.error("termSetId is undefined");
102+
}
103+
} catch (error) {
104+
console.error("Error adding child term", error);
105+
}
106+
}
107+
108+
private addChildTerm = async (index: number, term: ITermInfo, updateTaxonomyTreeViewCallback: any) => {
109+
try {
110+
if (termSetId) {
111+
this.props.context?.msGraphClientFactory?.getClient("3").then((client) => {
112+
client
113+
.api(`/sites/${this.props.context?.pageContext.site.id}/termStore/sets/${termSetId}/terms/${term.id}/children`)
114+
.post({
115+
labels: [
116+
{
117+
languageTag: "en-US",
118+
name: "TestChild",
119+
isDefault: true,
120+
},
121+
],
122+
})
123+
.then((response) => {
124+
// Exclude @odata.context from the response
125+
const { "@odata.context": _, ...filteredResponse } = response;
126+
filteredResponse.isAvailableForTagging = [
127+
{
128+
"setId": termSetId,
129+
"isAvailable": true
130+
}
131+
];
132+
// Pass the filtered response to updateTaxonomyTreeViewCallback
133+
updateTaxonomyTreeViewCallback([filteredResponse] as ITermInfo[], [term] as ITermInfo[], null, null);
134+
})
135+
.catch((error) => {
136+
console.error("Error adding child term", error);
137+
});
138+
});
139+
} else {
140+
console.error("termSetId is undefined");
141+
}
142+
} catch (error) {
143+
console.error("Error adding child term", error);
144+
}
145+
}
146+
147+
148+
private deleteTerm = (_: number, term: ITermInfo,updateTaxonomyTreeViewCallback: any) => {
149+
if (termSetId) {
150+
this.props.context?.msGraphClientFactory?.getClient("3").then((client) => {
151+
client
152+
.api(`/sites/${this.props.context?.pageContext.site.id}/termStore/sets/${termSetId}/terms/${term.id}`)
153+
.delete()
154+
.then(() => {
155+
console.log("Term deleted");
156+
updateTaxonomyTreeViewCallback(null,null,[term] as ITermInfo[]);
157+
})
158+
.catch((error) => {
159+
console.error("Error deleting term", error);
160+
});
161+
});
162+
} else {
163+
console.error("termSetId is undefined");
164+
}
165+
}
166+
167+
//Another example in how to create term
168+
const addChildTerm = (parentTermId, updateTaxonomyTreeViewCallback): void => {
62169
spPost(sp.termStore.sets.getById(termSetId).terms.getById(parentTermId).children, {
63170
body: JSON.stringify({
64171
"labels": [
@@ -76,58 +183,53 @@ const addChildTerm = (parentTermId, updateTaxonomyTreeViewCallback): void => {
76183
.then(term => {
77184
updateTaxonomyTreeViewCallback([term], null, null);
78185
});
79-
}
80-
81-
...
186+
}
82187

188+
//Usage
83189
<ModernTaxonomyPicker
84-
allowMultipleSelections={true}
85-
termSetId={termSetId}
86-
panelTitle="Panel title"
87-
label={"Field title"}
88-
context={this.props.context}
89-
required={false}
90-
initialValues={[{labels: [{name: "Subprocess A1", isDefault: true, languageTag: "en-US"}], id: "29eced8f-cf08-454b-bd9e-6443bc0a0f5e"}]}
91-
onChange={this.onTaxPickerChange}
92-
disabled={false}
93-
customPanelWidth={700}
94-
isLightDismiss={false}
95-
isBlocking={false}
96-
onRenderActionButton={(
97-
termStoreInfo: ITermStoreInfo,
98-
termSetInfo: ITermSetInfo,
99-
termInfo: ITermInfo
100-
updateTaxonomyTreeViewCallback?: (newTermItems?: ITermInfo[], updatedTermItems?: ITermInfo[], deletedTermItems?: ITermInfo[]) => void
190+
allowMultipleSelections={true}
191+
termSetId={'8ed8c9ea-7052-4c1d-a4d7-b9c10bffea6f'}
192+
panelTitle='Select Term'
193+
label={'Modern Taxonomy Picker'}
194+
context={this.props.context}
195+
required={false}
196+
disabled={false}
197+
onRenderActionButton={(
198+
termStoreInfo: any,
199+
termSetInfo: any,
200+
termInfo: ITermInfo,
201+
updateTaxonomyTreeViewCallback?: (newTermItems?: ITermInfo[], parentTerm?:ITermInfo[], updatedTermItems?: ITermInfo[], deletedTermItems?: ITermInfo[]) => void
101202
): JSX.Element => {
102-
const menuIcon: IIconProps = { iconName: 'MoreVertical', "aria-label": "More actions", style: { fontSize: "medium" } };
203+
const menuIcon: IIconProps = { iconName: 'MoreVertical', 'aria-label': 'Add or delete terms', style: { fontSize: 'medium' } };
103204
if (termInfo) {
104205
const menuProps: IContextualMenuProps = {
105206
items: [
106207
{
107208
key: 'addTerm',
108209
text: 'Add Term',
109210
iconProps: { iconName: 'Tag' },
110-
onClick: () => addChildTerm(termInfo.id, updateTaxonomyTreeViewCallback)
211+
onClick: () => { (async () => await this.addTermSwitch(0, termInfo, updateTaxonomyTreeViewCallback, false))(); }
111212
},
112213
{
113214
key: 'deleteTerm',
114215
text: 'Delete term',
115216
iconProps: { iconName: 'Untag' },
116-
onClick: () => deleteTerm(termInfo.id, updateTaxonomyTreeViewCallback)
217+
onClick: () => this.deleteTerm(0,termInfo, updateTaxonomyTreeViewCallback)
117218
},
118219
],
119220
};
120-
121221
return (
122-
<IconButton
222+
<>
223+
<IconButton
123224
menuProps={menuProps}
124225
menuIconProps={menuIcon}
125-
style={clickedActionTerm && clickedActionTerm.id === termInfo.id ? {opacity: 1} : null}
126-
onMenuClick={(ev?: React.MouseEvent<HTMLElement, MouseEvent> | React.KeyboardEvent<HTMLElement>, button?: IButtonProps) => {
127-
setClickedActionTerm(termInfo));
226+
style={{opacity: 1}}
227+
onMenuClick={(_: React.MouseEvent<HTMLElement, MouseEvent> | React.KeyboardEvent<HTMLElement>, __?: IButtonProps) => {
228+
console.log('IconButton clicked');
229+
this.setClickedActionTerm(termInfo);
128230
}}
129-
onAfterMenuDismiss={() => setClickedActionTerm(null)}
130231
/>
232+
</>
131233
);
132234
}
133235
else {
@@ -137,7 +239,7 @@ const addChildTerm = (parentTermId, updateTaxonomyTreeViewCallback): void => {
137239
key: 'addTerm',
138240
text: 'Add term',
139241
iconProps: { iconName: 'Tag' },
140-
onClick: () => addTerm(termInfo.id, updateTaxonomyTreeViewCallback)
242+
onClick: () => { (async () => await this.addTermSwitch(0, termInfo, updateTaxonomyTreeViewCallback, true))(); }
141243
},
142244
],
143245
};
@@ -175,7 +277,7 @@ The ModernTaxonomyPicker control can be configured with the following properties
175277
| themeVariant | IReadonlyTheme | no | The current loaded SharePoint theme/section background (More info: [Supporting section backgrounds](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/guidance/supporting-section-backgrounds)). |
176278
| isLightDismiss | boolean | no | Whether the panel can be light dismissed. |
177279
| isBlocking | boolean | no | Whether the panel uses a modal overlay or not. |
178-
| onRenderActionButton | function | no | Optional custom renderer for adding e.g. a button with additional actions to the terms in the tree view. |
280+
| onRenderActionButton | function | no | Optional custom renderer for adding e.g. a button with additional actions to the terms in the tree view. See advanced example section |
179281
| isPathRendered | boolean | no | Whether the terms will be rendered with the term label or the full path up to the root. |
180282
| allowSelectingChildren | boolean | no | Whether child terms can be selected. Default value is true. |
181283

gulpfile.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ const fs = require('fs');
66
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
77

88
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
9+
build.addSuppression(`Warning - [sass] The local CSS class 'ms-motion-slideLeftIn' is not camelCase and will not be type-safe.`);
10+
build.addSuppression(`Warning - [sass] The local CSS class 'ms-motion-fadeIn' is not camelCase and will not be type-safe.`);
11+
build.addSuppression(`Warning - [sass] The local CSS class 'ms-motion-slideRightIn' is not camelCase and will not be type-safe.`);
12+
build.addSuppression(`Warning - [sass] The local CSS class 'ms-motion-fadeOut' is not camelCase and will not be type-safe.`);
13+
build.addSuppression(`Warning - [sass] The local CSS class 'ms-motion-scaleDownIn' is not camelCase and will not be type-safe.`);
14+
build.addSuppression(`Warning - [sass] The local CSS class 'ms-motion-scaleDownOut' is not camelCase and will not be type-safe.`);
15+
build.addSuppression(`Warning - [sass] The local CSS class 'ms-motion-slideLeftOut' is not camelCase and will not be type-safe.`);
16+
build.addSuppression(`Warning - [sass] The local CSS class 'ms-motion-slideRightOut' is not camelCase and will not be type-safe.`);
17+
build.addSuppression(`Warning - [sass] The local CSS class 'ms-motion-slideUpOut' is not camelCase and will not be type-safe.`);
18+
build.addSuppression(`Warning - [sass] The local CSS class 'ms-motion-slideDownOut' is not camelCase and will not be type-safe.`);
19+
build.addSuppression(`Warning - [sass] The local CSS class 'ms-motion-slideUpIn' is not camelCase and will not be type-safe.`);
20+
build.addSuppression(`Warning - [sass] The local CSS class 'ms-motion-slideDownIn' is not camelCase and will not be type-safe.`);
921

1022
// Update the version number in the version.ts file
1123
gulp.task('versionUpdater', (done) => {

src/controls/modernTaxonomyPicker/taxonomyTree/TaxonomyTree.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export interface ITaxonomyTreeProps {
6565
termInfo: ITermInfo,
6666
updateTaxonomyTreeViewCallback?: (
6767
newTermItems?: ITermInfo[],
68+
parentTerm?: ITermInfo[], //only for adding new terms
6869
updatedTermItems?: ITermInfo[],
6970
deletedTermItems?: ITermInfo[]
7071
) => void
@@ -84,7 +85,7 @@ export function TaxonomyTree(
8485
const [groups, setGroups] = React.useState<IGroup[]>([]);
8586

8687
const updateTaxonomyTreeViewWithNewTermItems = (
87-
newTermItems: ITermInfo[]
88+
newTermItems: ITermInfo[], parentTerm?: ITermInfo[]
8889
): void => {
8990
for (const term of newTermItems) {
9091
const findGroupContainingTerm = (currentGroup: IGroup): IGroup => {
@@ -102,6 +103,21 @@ export function TaxonomyTree(
102103
return null;
103104
};
104105

106+
const findParentTermLevel = (groups: IGroup[], parentTermId: string): number | null => {
107+
for (const group of groups) {
108+
if (group.key === parentTermId) {
109+
return group.level;
110+
}
111+
if (group.children && group.children.length > 0) {
112+
const level = findParentTermLevel(group.children, parentTermId);
113+
if (level !== null) {
114+
return level;
115+
}
116+
}
117+
}
118+
return null;
119+
};
120+
const parentTermLevel = findParentTermLevel([groups[0]], parentTerm[0].id );
105121
const groupToAddTermTo = findGroupContainingTerm(groups[0]);
106122
let termNames = term.labels.filter(
107123
(termLabel) =>
@@ -121,7 +137,7 @@ export function TaxonomyTree(
121137
key: term.id,
122138
startIndex: -1,
123139
count: 50,
124-
level: groupToAddTermTo.level + 1,
140+
level: parentTermLevel + 1,
125141
isCollapsed: true,
126142
data: { skiptoken: "", term: term },
127143
hasMoreData: term.childrenCount > 0,
@@ -216,11 +232,12 @@ export function TaxonomyTree(
216232

217233
const updateTaxonomyTreeView = (
218234
newTermItems?: ITermInfo[],
235+
parentTerm?:ITermInfo[],
219236
updatedTermItems?: ITermInfo[],
220237
deletedTermItems?: ITermInfo[]
221238
): void => {
222239
if (newTermItems) {
223-
updateTaxonomyTreeViewWithNewTermItems(newTermItems);
240+
updateTaxonomyTreeViewWithNewTermItems(newTermItems,parentTerm);
224241
}
225242

226243
if (updatedTermItems) {

0 commit comments

Comments
 (0)