Skip to content

Commit e37f4f8

Browse files
JakeStangermsftbot[bot]AJIXuMuKJake Stanger
authored
modern taxonomy picker: ability to disallow selecting children (pnp#1279)
* Add `.github/fabricbot.json` * feat(modern taxonomy picker): ability to disallow selecting children Adds new `allowSelectingChildren` prop, which is true by default. Setting to `false` means only the top-level terms appear in search results, and their tree nodes are not expandable. Respects `anchorTermId`. Co-authored-by: msftbot[bot] <48340428+msftbot[bot]@users.noreply.github.com> Co-authored-by: Alex Terentiev <[email protected]> Co-authored-by: Jake Stanger <[email protected]>
1 parent f5a16ba commit e37f4f8

File tree

5 files changed

+39
-6
lines changed

5 files changed

+39
-6
lines changed

docs/documentation/docs/controls/ModernTaxonomyPicker.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ The ModernTaxonomyPicker control can be configured with the following properties
177177
| isBlocking | boolean | no | Whether the panel uses a modal overlay or not. |
178178
| onRenderActionButton | function | no | Optional custom renderer for adding e.g. a button with additional actions to the terms in the tree view. |
179179
| isPathRendered | boolean | no | Whether the terms will be rendered with the term label or the full path up to the root. |
180+
| allowSelectingChildren | boolean | no | Whether child terms can be selected. Default value is true. |
180181

181182
## Standalone TaxonomyTree control
182183

src/controls/modernTaxonomyPicker/ModernTaxonomyPicker.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export interface IModernTaxonomyPickerProps {
6161
isLightDismiss?: boolean;
6262
isBlocking?: boolean;
6363
onRenderActionButton?: (termStoreInfo: ITermStoreInfo, termSetInfo: ITermSetInfo, termInfo?: ITermInfo) => JSX.Element;
64+
allowSelectingChildren?: boolean;
6465
}
6566

6667
export function ModernTaxonomyPicker(props: IModernTaxonomyPickerProps): JSX.Element {
@@ -160,15 +161,20 @@ export function ModernTaxonomyPicker(props: IModernTaxonomyPickerProps): JSX.Ele
160161
if (filter === '') {
161162
return [];
162163
}
163-
const filteredTerms = await taxonomyService.searchTerm(Guid.parse(props.termSetId), filter, currentLanguageTag, props.anchorTermId ? Guid.parse(props.anchorTermId) : Guid.empty);
164+
const filteredTerms = await taxonomyService.searchTerm(Guid.parse(props.termSetId), filter, currentLanguageTag, props.anchorTermId ? Guid.parse(props.anchorTermId) : Guid.empty, props.allowSelectingChildren);
165+
164166
const filteredTermsWithParentInformation = props.isPathRendered ? await addParentInformationToTerms(filteredTerms) : filteredTerms;
165167
const filteredTermsWithoutSelectedItems = filteredTermsWithParentInformation.filter((term) => {
166168
if (!selectedItems || selectedItems.length === 0) {
167169
return true;
168170
}
169171
return selectedItems.every((item) => item.id !== term.id);
170172
});
171-
const filteredTermsAndAvailable = filteredTermsWithoutSelectedItems.filter((term) => term.isAvailableForTagging.filter((t) => t.setId === props.termSetId)[0].isAvailable);
173+
174+
const filteredTermsAndAvailable = filteredTermsWithoutSelectedItems
175+
.filter((term) =>
176+
term.isAvailableForTagging
177+
.filter((t) => t.setId === props.termSetId)[0].isAvailable);
172178
return filteredTermsAndAvailable;
173179
}
174180

@@ -329,6 +335,7 @@ export function ModernTaxonomyPicker(props: IModernTaxonomyPickerProps): JSX.Ele
329335
themeVariant={props.themeVariant}
330336
termPickerProps={props.termPickerProps}
331337
onRenderActionButton={props.onRenderActionButton}
338+
allowSelectingChildren={props.allowSelectingChildren}
332339
/>
333340
</div>
334341
)

src/controls/modernTaxonomyPicker/taxonomyPanelContents/TaxonomyPanelContents.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export interface ITaxonomyPanelContentsProps {
3939
themeVariant?: IReadonlyTheme;
4040
termPickerProps?: Optional<IModernTermPickerProps, 'onResolveSuggestions'>;
4141
onRenderActionButton?: (termStoreInfo: ITermStoreInfo, termSetInfo: ITermSetInfo, termInfo: ITermInfo, updateTaxonomyTreeViewCallback?: (newTermItems?: ITermInfo[], updatedTermItems?: ITermInfo[], deletedTermItems?: ITermInfo[]) => void) => JSX.Element;
42+
allowSelectingChildren?: boolean;
4243
}
4344

4445
export function TaxonomyPanelContents(props: ITaxonomyPanelContentsProps): React.ReactElement<ITaxonomyPanelContentsProps> {
@@ -114,6 +115,7 @@ export function TaxonomyPanelContents(props: ITaxonomyPanelContentsProps): React
114115
onRenderActionButton={props.onRenderActionButton}
115116
hideDeprecatedTerms={true}
116117
showIcons={false}
118+
allowSelectingChildren={props.allowSelectingChildren}
117119
/>
118120
</div>
119121
);

src/controls/modernTaxonomyPicker/taxonomyTree/TaxonomyTree.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export interface ITaxonomyTreeProps {
5858
selection?: Selection<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
5959
hideDeprecatedTerms?: boolean;
6060
showIcons?: boolean;
61+
allowSelectingChildren?: boolean;
6162
}
6263

6364
export function TaxonomyTree(props: ITaxonomyTreeProps): React.ReactElement<ITaxonomyTreeProps> {
@@ -228,7 +229,7 @@ export function TaxonomyTree(props: ITaxonomyTreeProps): React.ReactElement<ITax
228229
level: 1,
229230
isCollapsed: true,
230231
data: { skiptoken: '', term: term },
231-
hasMoreData: term.childrenCount > 0,
232+
hasMoreData: props.allowSelectingChildren !== false && term.childrenCount > 0,
232233
};
233234
if (g.hasMoreData) {
234235
g.children = [];

src/services/SPTaxonomyService.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,33 @@ export class SPTaxonomyService {
5858
}
5959
}
6060

61-
public async searchTerm(termSetId: Guid, label: string, languageTag: string, parentTermId?: Guid, stringMatchId: string = '0', pageSize: number = 50): Promise<ITermInfo[]> {
61+
public async searchTerm(termSetId: Guid, label: string, languageTag: string, parentTermId?: Guid, allowSelectingChildren = true, stringMatchId: string = '0', pageSize: number = 50): Promise<ITermInfo[]> {
6262
try {
63-
const searchTermUrl = sp.termStore.concat(`/searchTerm(label='${label}',setId='${termSetId}',languageTag='${languageTag}',stringMatchId='${stringMatchId}'${parentTermId && parentTermId !== Guid.empty ? `,parentTermId='${parentTermId}'` : ''})`).toUrl();
63+
let query = [
64+
`label='${label}'`,
65+
`setId='${termSetId}'`,
66+
`languageTag='${languageTag}'`,
67+
`stringMatchId='${stringMatchId}'`
68+
];
69+
70+
if(parentTermId !== Guid.empty) {
71+
query.push(`parentTermId='${parentTermId}'`);
72+
}
73+
74+
const searchTermUrl = sp.termStore.concat(`/searchTerm(${query.join(',')})`).toUrl();
6475
const searchTermQuery = SharePointQueryableCollection(searchTermUrl).top(pageSize);
65-
const filteredTerms = await searchTermQuery();
76+
let filteredTerms: ITermInfo[] = await searchTermQuery();
77+
78+
if(allowSelectingChildren === false) {
79+
const hasParentId = parentTermId !== Guid.empty;
80+
81+
const set = sp.termStore.sets.getById(termSetId.toString());
82+
const collection = hasParentId ? set.terms.getById(parentTermId.toString()).children : set.children;
83+
84+
const childrenIds = await collection.select("id").get().then(children => children.map(c => c.id));
85+
filteredTerms = filteredTerms.filter(term => childrenIds.includes(term.id));
86+
}
87+
6688
return filteredTerms;
6789
} catch (error) {
6890
return [];

0 commit comments

Comments
 (0)