Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(modern taxonomy picker): ability to disallow selecting children #1279

Merged
merged 5 commits into from
Sep 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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