Skip to content

Commit

Permalink
modern taxonomy picker: ability to disallow selecting children (pnp#1279
Browse files Browse the repository at this point in the history
)

* 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 <aleksei.terentiev@gmail.com>
Co-authored-by: Jake Stanger <jake.stanger@core.co.uk>
  • Loading branch information
4 people committed Sep 7, 2022
1 parent f5a16ba commit e37f4f8
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 6 deletions.
1 change: 1 addition & 0 deletions docs/documentation/docs/controls/ModernTaxonomyPicker.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ The ModernTaxonomyPicker control can be configured with the following properties
| isBlocking | boolean | no | Whether the panel uses a modal overlay or not. |
| onRenderActionButton | function | no | Optional custom renderer for adding e.g. a button with additional actions to the terms in the tree view. |
| isPathRendered | boolean | no | Whether the terms will be rendered with the term label or the full path up to the root. |
| allowSelectingChildren | boolean | no | Whether child terms can be selected. Default value is true. |

## Standalone TaxonomyTree control

Expand Down
11 changes: 9 additions & 2 deletions src/controls/modernTaxonomyPicker/ModernTaxonomyPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export interface IModernTaxonomyPickerProps {
isLightDismiss?: boolean;
isBlocking?: boolean;
onRenderActionButton?: (termStoreInfo: ITermStoreInfo, termSetInfo: ITermSetInfo, termInfo?: ITermInfo) => JSX.Element;
allowSelectingChildren?: boolean;
}

export function ModernTaxonomyPicker(props: IModernTaxonomyPickerProps): JSX.Element {
Expand Down Expand Up @@ -160,15 +161,20 @@ export function ModernTaxonomyPicker(props: IModernTaxonomyPickerProps): JSX.Ele
if (filter === '') {
return [];
}
const filteredTerms = await taxonomyService.searchTerm(Guid.parse(props.termSetId), filter, currentLanguageTag, props.anchorTermId ? Guid.parse(props.anchorTermId) : Guid.empty);
const filteredTerms = await taxonomyService.searchTerm(Guid.parse(props.termSetId), filter, currentLanguageTag, props.anchorTermId ? Guid.parse(props.anchorTermId) : Guid.empty, props.allowSelectingChildren);

const filteredTermsWithParentInformation = props.isPathRendered ? await addParentInformationToTerms(filteredTerms) : filteredTerms;
const filteredTermsWithoutSelectedItems = filteredTermsWithParentInformation.filter((term) => {
if (!selectedItems || selectedItems.length === 0) {
return true;
}
return selectedItems.every((item) => item.id !== term.id);
});
const filteredTermsAndAvailable = filteredTermsWithoutSelectedItems.filter((term) => term.isAvailableForTagging.filter((t) => t.setId === props.termSetId)[0].isAvailable);

const filteredTermsAndAvailable = filteredTermsWithoutSelectedItems
.filter((term) =>
term.isAvailableForTagging
.filter((t) => t.setId === props.termSetId)[0].isAvailable);
return filteredTermsAndAvailable;
}

Expand Down Expand Up @@ -329,6 +335,7 @@ export function ModernTaxonomyPicker(props: IModernTaxonomyPickerProps): JSX.Ele
themeVariant={props.themeVariant}
termPickerProps={props.termPickerProps}
onRenderActionButton={props.onRenderActionButton}
allowSelectingChildren={props.allowSelectingChildren}
/>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export interface ITaxonomyPanelContentsProps {
themeVariant?: IReadonlyTheme;
termPickerProps?: Optional<IModernTermPickerProps, 'onResolveSuggestions'>;
onRenderActionButton?: (termStoreInfo: ITermStoreInfo, termSetInfo: ITermSetInfo, termInfo: ITermInfo, updateTaxonomyTreeViewCallback?: (newTermItems?: ITermInfo[], updatedTermItems?: ITermInfo[], deletedTermItems?: ITermInfo[]) => void) => JSX.Element;
allowSelectingChildren?: boolean;
}

export function TaxonomyPanelContents(props: ITaxonomyPanelContentsProps): React.ReactElement<ITaxonomyPanelContentsProps> {
Expand Down Expand Up @@ -114,6 +115,7 @@ export function TaxonomyPanelContents(props: ITaxonomyPanelContentsProps): React
onRenderActionButton={props.onRenderActionButton}
hideDeprecatedTerms={true}
showIcons={false}
allowSelectingChildren={props.allowSelectingChildren}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export interface ITaxonomyTreeProps {
selection?: Selection<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
hideDeprecatedTerms?: boolean;
showIcons?: boolean;
allowSelectingChildren?: boolean;
}

export function TaxonomyTree(props: ITaxonomyTreeProps): React.ReactElement<ITaxonomyTreeProps> {
Expand Down Expand Up @@ -228,7 +229,7 @@ export function TaxonomyTree(props: ITaxonomyTreeProps): React.ReactElement<ITax
level: 1,
isCollapsed: true,
data: { skiptoken: '', term: term },
hasMoreData: term.childrenCount > 0,
hasMoreData: props.allowSelectingChildren !== false && term.childrenCount > 0,
};
if (g.hasMoreData) {
g.children = [];
Expand Down
28 changes: 25 additions & 3 deletions src/services/SPTaxonomyService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,33 @@ export class SPTaxonomyService {
}
}

public async searchTerm(termSetId: Guid, label: string, languageTag: string, parentTermId?: Guid, stringMatchId: string = '0', pageSize: number = 50): Promise<ITermInfo[]> {
public async searchTerm(termSetId: Guid, label: string, languageTag: string, parentTermId?: Guid, allowSelectingChildren = true, stringMatchId: string = '0', pageSize: number = 50): Promise<ITermInfo[]> {
try {
const searchTermUrl = sp.termStore.concat(`/searchTerm(label='${label}',setId='${termSetId}',languageTag='${languageTag}',stringMatchId='${stringMatchId}'${parentTermId && parentTermId !== Guid.empty ? `,parentTermId='${parentTermId}'` : ''})`).toUrl();
let query = [
`label='${label}'`,
`setId='${termSetId}'`,
`languageTag='${languageTag}'`,
`stringMatchId='${stringMatchId}'`
];

if(parentTermId !== Guid.empty) {
query.push(`parentTermId='${parentTermId}'`);
}

const searchTermUrl = sp.termStore.concat(`/searchTerm(${query.join(',')})`).toUrl();
const searchTermQuery = SharePointQueryableCollection(searchTermUrl).top(pageSize);
const filteredTerms = await searchTermQuery();
let filteredTerms: ITermInfo[] = await searchTermQuery();

if(allowSelectingChildren === false) {
const hasParentId = parentTermId !== Guid.empty;

const set = sp.termStore.sets.getById(termSetId.toString());
const collection = hasParentId ? set.terms.getById(parentTermId.toString()).children : set.children;

const childrenIds = await collection.select("id").get().then(children => children.map(c => c.id));
filteredTerms = filteredTerms.filter(term => childrenIds.includes(term.id));
}

return filteredTerms;
} catch (error) {
return [];
Expand Down

0 comments on commit e37f4f8

Please sign in to comment.