Skip to content

Commit

Permalink
Merge pull request #1058 from SherpasGroup/modern-taxonomy-picker-2
Browse files Browse the repository at this point in the history
Added feature to add action button to terms in ModernTaxonomyPicker
  • Loading branch information
AJIXuMuK committed Dec 21, 2021
2 parents 6010cad + e722a0b commit ee42842
Show file tree
Hide file tree
Showing 14 changed files with 946 additions and 445 deletions.
164 changes: 158 additions & 6 deletions docs/documentation/docs/controls/ModernTaxonomyPicker.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@ import { ModernTaxonomyPicker } from "@pnp/spfx-controls-react/lib/ModernTaxonom

```TypeScript
<ModernTaxonomyPicker allowMultipleSelections={true}
termSetId="f233d4b7-68fb-41ef-8b58-2af0bafc0d38"
panelTitle="Select Term"
label="Taxonomy Picker"
context={this.props.context}
onChange={this.onTaxPickerChange} />
termSetId="f233d4b7-68fb-41ef-8b58-2af0bafc0d38"
panelTitle="Select Term"
label="Taxonomy Picker"
context={this.props.context}
onChange={this.onTaxPickerChange}
/>
```

- With the `onChange` property you can capture the event of when the terms in the picker has changed:
Expand All @@ -50,6 +51,108 @@ private onTaxPickerChange(terms : ITermInfo[]) {
}
```

## Advanced example
Custom rendering of a More actions button that displays a context menu for each term in the term set and the term set itself and with different options for the terms and the term set. This could for example be used to add terms to an open term set. It also shows how to set the initialsValues property when just knowing the name and the id of the term.

```TypeScript
const termSetId = "36d21c3f-b83b-4acc-a223-4df6fa8e946d";
const [clickedActionTerm, setClickedActionTerm] = React.useState<ITermInfo>();

const addChildTerm = (parentTermId, updateTaxonomyTreeViewCallback): void => {
spPost(sp.termStore.sets.getById(termSetId).terms.getById(parentTermId).children, {
body: JSON.stringify({
"labels": [
{
"languageTag": "en-US",
"name": "Test",
"isDefault": true
}
]
}),
})
.then(addedTerm => {
return sp.termStore.sets.getById(termSetId).terms.getById(addedTerm.id).expand("parent")();
})
.then(term => {
updateTaxonomyTreeViewCallback([term], null, null);
});
}

...

<ModernTaxonomyPicker
allowMultipleSelections={true}
termSetId={termSetId}
panelTitle="Panel title"
label={"Field title"}
context={this.props.context}
required={false}
initialValues={[{labels: [{name: "Subprocess A1", isDefault: true, languageTag: "en-US"}], id: "29eced8f-cf08-454b-bd9e-6443bc0a0f5e"}]}
onChange={this.onTaxPickerChange}
disabled={false}
customPanelWidth={700}
isLightDismiss={false}
isBlocking={false}
onRenderActionButton={(
termStoreInfo: ITermStoreInfo,
termSetInfo: ITermSetInfo,
termInfo: ITermInfo
updateTaxonomyTreeViewCallback?: (newTermItems?: ITermInfo[], updatedTermItems?: ITermInfo[], deletedTermItems?: ITermInfo[]) => void
): JSX.Element => {
const menuIcon: IIconProps = { iconName: 'MoreVertical', "aria-label": "More actions", style: { fontSize: "medium" } };
if (termInfo) {
const menuProps: IContextualMenuProps = {
items: [
{
key: 'addTerm',
text: 'Add Term',
iconProps: { iconName: 'Tag' },
onClick: () => addChildTerm(termInfo.id, updateTaxonomyTreeViewCallback)
},
{
key: 'deleteTerm',
text: 'Delete term',
iconProps: { iconName: 'Untag' },
onClick: () => deleteTerm(termInfo.id, updateTaxonomyTreeViewCallback)
},
],
};

return (
<IconButton
menuProps={menuProps}
menuIconProps={menuIcon}
style={clickedActionTerm && clickedActionTerm.id === termInfo.id ? {opacity: 1} : null}
onMenuClick={(ev?: React.MouseEvent<HTMLElement, MouseEvent> | React.KeyboardEvent<HTMLElement>, button?: IButtonProps) => {
setClickedActionTerm(termInfo));
}}
onAfterMenuDismiss={() => setClickedActionTerm(null)}
/>
);
}
else {
const menuProps: IContextualMenuProps = {
items: [
{
key: 'addTerm',
text: 'Add term',
iconProps: { iconName: 'Tag' },
onClick: () => addTerm(termInfo.id, updateTaxonomyTreeViewCallback)
},
],
};
return (
<IconButton
menuProps={menuProps}
menuIconProps={menuIcon}
style={{opacity: 1}}
/>
);
}
}}
/>
```

## Implementation

The ModernTaxonomyPicker control can be configured with the following properties:
Expand All @@ -70,5 +173,54 @@ The ModernTaxonomyPicker control can be configured with the following properties
| customPanelWidth | number | no | Custom panel width in pixels. |
| termPickerProps | IModernTermPickerProps | no | Custom properties for the term picker (More info: [IBasePickerProps interface](https://developer.microsoft.com/en-us/fluentui#/controls/web/pickers#IBasePickerProps)). |
| 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)). |
| isLightDismiss | boolean | no | Whether the panel can be light dismissed. |
| 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. |

## Standalone TaxonomyTree control

You can also use the `TaxonomyTree` control separately to just render a stand-alone tree-view of a term set with action buttons.

- Use the `TaxonomyTree` control in your code as follows:
Initialize the taxonomy service and state, load basic info from term store and display the `TaxonomyTree` component.

```TypeScript
const taxonomyService = new SPTaxonomyService(props.context);
const [terms, setTerms] = React.useState<ITermInfo[]>([]);
const [currentTermStoreInfo, setCurrentTermStoreInfo] = React.useState<ITermStoreInfo>();
const [currentTermSetInfo, setCurrentTermSetInfo] = React.useState<ITermSetInfo>();
const [currentLanguageTag, setCurrentLanguageTag] = React.useState<string>("");

React.useEffect(() => {
sp.setup(props.context);
taxonomyService.getTermStoreInfo()
.then((termStoreInfo) => {
setCurrentTermStoreInfo(termStoreInfo);
setCurrentLanguageTag(props.context.pageContext.cultureInfo.currentUICultureName !== '' ?
props.context.pageContext.cultureInfo.currentUICultureName :
currentTermStoreInfo.defaultLanguageTag);
});
taxonomyService.getTermSetInfo(Guid.parse(props.termSetId))
.then((termSetInfo) => {
setCurrentTermSetInfo(termSetInfo);
});
}, []);

return (
{currentTermStoreInfo && currentTermSetInfo && currentLanguageTag && (
<TaxonomyTree
languageTag={currentLanguageTag}
onLoadMoreData={taxonomyService.getTerms}
pageSize={50}
setTerms={setTerms}
termSetInfo={currentTermSetInfo}
termStoreInfo={currentTermStoreInfo}
terms={terms}
onRenderActionButton={onRenderActionButton}
hideDeprecatedTerms={false}
showIcons={true}
/>
)}
```
![](https://telemetry.sharepointpnp.com/sp-dev-fx-controls-react/wiki/controls/TaxonomyPicker)
![](https://telemetry.sharepointpnp.com/sp-dev-fx-controls-react/wiki/controls/ModernTaxonomyPicker)
13 changes: 8 additions & 5 deletions src/controls/modernTaxonomyPicker/ModernTaxonomyPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export interface IModernTaxonomyPickerProps {
panelTitle: string;
label: string;
context: BaseComponentContext;
initialValues?: ITermInfo[];
initialValues?: Optional<ITermInfo, "childrenCount" | "createdDateTime" | "lastModifiedDateTime" | "descriptions" | "customSortOrder" | "properties" | "localProperties" | "isDeprecated" | "isAvailableForTagging" | "topicRequested">[];
disabled?: boolean;
required?: boolean;
onChange?: (newValue?: ITermInfo[]) => void;
Expand All @@ -60,10 +60,13 @@ export interface IModernTaxonomyPickerProps {
customPanelWidth?: number;
themeVariant?: IReadonlyTheme;
termPickerProps?: Optional<IModernTermPickerProps, 'onResolveSuggestions'>;
isLightDismiss?: boolean;
isBlocking?: boolean;
onRenderActionButton?: (termStoreInfo: ITermStoreInfo, termSetInfo: ITermSetInfo, termInfo?: ITermInfo) => JSX.Element;
}

export function ModernTaxonomyPicker(props: IModernTaxonomyPickerProps) {
const [taxonomyService] = React.useState(() => new SPTaxonomyService(props.context));
const taxonomyService = new SPTaxonomyService(props.context);
const [panelIsOpen, setPanelIsOpen] = React.useState(false);
const [selectedOptions, setSelectedOptions] = React.useState<ITermInfo[]>([]);
const [selectedPanelOptions, setSelectedPanelOptions] = React.useState<ITermInfo[]>([]);
Expand Down Expand Up @@ -235,7 +238,8 @@ export function ModernTaxonomyPicker(props: IModernTaxonomyPickerProps) {
hasCloseButton={true}
closeButtonAriaLabel={strings.ModernTaxonomyPickerPanelCloseButtonText}
onDismiss={onClosePanel}
isLightDismiss={true}
isLightDismiss={props.isLightDismiss}
isBlocking={props.isBlocking}
type={props.customPanelWidth ? PanelType.custom : PanelType.medium}
customWidth={props.customPanelWidth ? `${props.customPanelWidth}px` : undefined}
headerText={props.panelTitle}
Expand All @@ -261,8 +265,6 @@ export function ModernTaxonomyPicker(props: IModernTaxonomyPickerProps) {
anchorTermInfo={currentAnchorTermInfo}
termSetInfo={currentTermSetInfo}
termStoreInfo={currentTermStoreInfo}
context={props.context}
termSetId={Guid.parse(props.termSetId)}
pageSize={50}
selectedPanelOptions={selectedPanelOptions}
setSelectedPanelOptions={setSelectedPanelOptions}
Expand All @@ -273,6 +275,7 @@ export function ModernTaxonomyPicker(props: IModernTaxonomyPickerProps) {
languageTag={currentLanguageTag}
themeVariant={props.themeVariant}
termPickerProps={props.termPickerProps}
onRenderActionButton={props.onRenderActionButton}
/>
</div>
)
Expand Down
6 changes: 5 additions & 1 deletion src/controls/modernTaxonomyPicker/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
export * from './ModernTaxonomyPicker';
export * from './termItem/TermItem';
export * from './modernTermPicker/index';
export * from './taxonomyPanelContents/index';
export * from './taxonomyTree/index';
export * from './termItem/index';
export * from '../../services/SPTaxonomyService';
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export interface ITermItemStyles {
close: IStyle;
}

export interface ITermItemSuggestionProps extends React.AllHTMLAttributes<HTMLElement> {
export interface ITermItemSuggestionElementProps extends React.AllHTMLAttributes<HTMLElement> {
/** Additional CSS class(es) to apply to the TermItemSuggestion div element */
className?: string;

Expand All @@ -49,8 +49,8 @@ export interface ITermItemSuggestionProps extends React.AllHTMLAttributes<HTMLEl
theme?: ITheme;
}

export type ITermItemSuggestionStyleProps = Required<Pick<ITermItemSuggestionProps, 'theme'>> &
Pick<ITermItemSuggestionProps, 'className'> & {};
export type ITermItemSuggestionStyleProps = Required<Pick<ITermItemSuggestionElementProps, 'theme'>> &
Pick<ITermItemSuggestionElementProps, 'className'> & {};

export interface ITermItemSuggestionStyles {
/** Refers to the text element of the TermItemSuggestion */
Expand Down
2 changes: 2 additions & 0 deletions src/controls/modernTaxonomyPicker/modernTermPicker/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './ModernTermPicker';
export * from './ModernTermPicker.types';
Original file line number Diff line number Diff line change
@@ -1,36 +1,6 @@
@import '~office-ui-fabric-react/dist/sass/References.scss';

.taxonomyPanelContents {
.choiceOption {
color: "[theme: bodyText, default: #323130]";
display: inline-block;
padding-inline-start: 26px;
}

.disabledChoiceOption {
color: "[theme: disabledBodyText, default: #323130]";
display: inline-block;
padding-inline-start: 26px;
}

.selectedChoiceOption {
font-weight: bold;
}

.checkbox {
color: "[theme: bodyText, default: #323130]";
margin-inline-start: 4px;
}

.disabledCheckbox {
color: "[theme: disabledBodyText, default: #323130]";
margin-inline-start: 4px;
}

.selectedCheckbox {
font-weight: bold;
}

.taxonomyTreeSelector {
border-bottom-color: blue;
border-bottom-style: solid;
Expand All @@ -42,17 +12,4 @@
font-size: 18px;
font-weight: 100;
}

.spinnerContainer {
height: 48px;
line-height: 48px;
display: flex;
justify-content: center;
align-items: center;
}

.loadMoreContainer {
height: 48px;
line-height: 48px;
}
}
Loading

0 comments on commit ee42842

Please sign in to comment.