feat: Update People Picker functionality and remove validation logic; bump version to 1.0.4
This commit is contained in:
@@ -28,7 +28,7 @@ Es benötigt keine Microsoft-Graph-Berechtigungen und keine zusätzliche SharePo
|
|||||||
|
|
||||||
## Startperson
|
## Startperson
|
||||||
|
|
||||||
Im Bearbeitungsmodus der modernen Seite wird direkt oberhalb des Organigramms ein People Picker angezeigt. Dort kann die Startperson per Name oder E-Mail-Adresse gesucht werden. Das WebPart akzeptiert nur einen von SharePoint aufgelösten Benutzer und prüft bei der Auswahl zusätzlich, ob dessen Profil über den User Profile Service gelesen werden kann. Gespeichert werden der kanonische Login und der Accountname als Fallback.
|
Im Bearbeitungsmodus der modernen Seite wird direkt oberhalb des Organigramms ein People Picker angezeigt. Dort kann die Startperson per Name oder E-Mail-Adresse gesucht werden. Das WebPart akzeptiert nur einen von SharePoint eindeutig aufgelösten Benutzer. Gespeichert werden der kanonische Login und der Accountname als Fallback.
|
||||||
|
|
||||||
Wird die Auswahl entfernt, verwendet das WebPart wieder den angemeldeten Benutzer. Im Anzeigemodus ist der People Picker nicht sichtbar.
|
Wird die Auswahl entfernt, verwendet das WebPart wieder den angemeldeten Benutzer. Im Anzeigemodus ist der People Picker nicht sichtbar.
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"name": "organization-chart-webpart",
|
"name": "organization-chart-webpart",
|
||||||
"title": "organization-chart-webpart",
|
"title": "organization-chart-webpart",
|
||||||
"id": "b2f88ea8-f7da-4d0e-a945-6973bf1acd1e",
|
"id": "b2f88ea8-f7da-4d0e-a945-6973bf1acd1e",
|
||||||
"version": "1.0.3.0",
|
"version": "1.0.4.0",
|
||||||
"includeClientSideAssets": true,
|
"includeClientSideAssets": true,
|
||||||
"skipFeatureDeployment": true
|
"skipFeatureDeployment": true
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "organization-chart-webpart",
|
"name": "organization-chart-webpart",
|
||||||
"version": "1.0.3",
|
"version": "1.0.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"engines": { "node": ">=6.9.0 <9.0.0" },
|
"engines": { "node": ">=6.9.0 <9.0.0" },
|
||||||
|
|||||||
@@ -113,12 +113,6 @@ export class OrganizationChartDataService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public validatePerson(loginName: string, accountName: string, cacheMinutes: number): Promise<boolean> {
|
|
||||||
const candidates: string[] = uniqueAccounts([loginName, accountName], '', 2);
|
|
||||||
return this.getFirstAvailableProfile(candidates, cacheMinutes, 0)
|
|
||||||
.then((): boolean => true, (): boolean => false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getConfiguredProfile(
|
private getConfiguredProfile(
|
||||||
accountName: string,
|
accountName: string,
|
||||||
cacheMinutes: number,
|
cacheMinutes: number,
|
||||||
|
|||||||
@@ -142,10 +142,7 @@ export default class OrganizationChartWebPart extends BaseClientSideWebPart<IOrg
|
|||||||
selectedAccountName: normalizeAccount(this.properties.startUserAccountName),
|
selectedAccountName: normalizeAccount(this.properties.startUserAccountName),
|
||||||
selectedDisplayName: selectedDisplayName,
|
selectedDisplayName: selectedDisplayName,
|
||||||
selectedLoginName: normalizeAccount(this.properties.startUser),
|
selectedLoginName: normalizeAccount(this.properties.startUser),
|
||||||
suggestionsHeaderText: strings.PeoplePickerSuggestionsLabel,
|
suggestionsHeaderText: strings.PeoplePickerSuggestionsLabel
|
||||||
validationErrorText: strings.PeoplePickerValidationError,
|
|
||||||
validatePerson: (loginName: string, accountName: string): Promise<boolean> =>
|
|
||||||
this.service.validatePerson(loginName, accountName, normalizeCacheMinutes(this.properties.cacheMinutes))
|
|
||||||
});
|
});
|
||||||
ReactDom.render(picker, this.pickerHost);
|
ReactDom.render(picker, this.pickerHost);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
.pickerPanel { background: "[theme:neutralLighterAlt, default:#faf9f8]"; border: 1px solid "[theme:neutralLight, default:#edebe9]"; margin-bottom: 16px; padding: 12px; }
|
.pickerPanel { background: "[theme:neutralLighterAlt, default:#faf9f8]"; border: 1px solid "[theme:neutralLight, default:#edebe9]"; margin-bottom: 16px; padding: 12px; }
|
||||||
.pickerLabel { color: "[theme:bodyText, default:#323130]"; display: block; font-size: 14px; font-weight: 600; margin-bottom: 6px; }
|
.pickerLabel { color: "[theme:bodyText, default:#323130]"; display: block; font-size: 14px; font-weight: 600; margin-bottom: 6px; }
|
||||||
.pickerDescription { color: "[theme:neutralSecondary, default:#605e5c]"; font-size: 12px; line-height: 17px; margin-top: 6px; }
|
.pickerDescription { color: "[theme:neutralSecondary, default:#605e5c]"; font-size: 12px; line-height: 17px; margin-top: 6px; }
|
||||||
.pickerError { color: "[theme:errorText, default:#a80000]"; font-size: 12px; line-height: 17px; margin-top: 6px; }
|
|
||||||
.header { align-items: baseline; display: flex; justify-content: space-between; margin-bottom: 12px; }
|
.header { align-items: baseline; display: flex; justify-content: space-between; margin-bottom: 12px; }
|
||||||
.heading { font-size: 24px; font-weight: 300; line-height: 32px; margin: 0; }
|
.heading { font-size: 24px; font-weight: 300; line-height: 32px; margin: 0; }
|
||||||
.count { color: "[theme:neutralSecondary, default:#605e5c]"; font-size: 12px; }
|
.count { color: "[theme:neutralSecondary, default:#605e5c]"; font-size: 12px; }
|
||||||
|
|||||||
@@ -26,24 +26,16 @@ export interface IOrganizationChartPeoplePickerProps {
|
|||||||
selectedAccountName: string;
|
selectedAccountName: string;
|
||||||
selectedLoginName: string;
|
selectedLoginName: string;
|
||||||
suggestionsHeaderText: string;
|
suggestionsHeaderText: string;
|
||||||
validationErrorText: string;
|
|
||||||
validatePerson: (loginName: string, accountName: string) => Promise<boolean>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IOrganizationChartPeoplePickerState {
|
|
||||||
validationError: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OrganizationChartPeoplePicker
|
export class OrganizationChartPeoplePicker
|
||||||
extends React.Component<IOrganizationChartPeoplePickerProps, IOrganizationChartPeoplePickerState> {
|
extends React.Component<IOrganizationChartPeoplePickerProps, {}> {
|
||||||
|
|
||||||
public constructor(props: IOrganizationChartPeoplePickerProps) {
|
public constructor(props: IOrganizationChartPeoplePickerProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.resolveSuggestions = this.resolveSuggestions.bind(this);
|
this.resolveSuggestions = this.resolveSuggestions.bind(this);
|
||||||
this.onSelectionChanged = this.onSelectionChanged.bind(this);
|
this.onSelectionChanged = this.onSelectionChanged.bind(this);
|
||||||
this.onItemSelected = this.onItemSelected.bind(this);
|
|
||||||
this.getTextFromItem = this.getTextFromItem.bind(this);
|
this.getTextFromItem = this.getTextFromItem.bind(this);
|
||||||
this.state = { validationError: '' };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactElement<HTMLDivElement> {
|
public render(): React.ReactElement<HTMLDivElement> {
|
||||||
@@ -61,7 +53,6 @@ export class OrganizationChartPeoplePicker
|
|||||||
selectedItems={selectedItems}
|
selectedItems={selectedItems}
|
||||||
onResolveSuggestions={this.resolveSuggestions}
|
onResolveSuggestions={this.resolveSuggestions}
|
||||||
onChange={this.onSelectionChanged}
|
onChange={this.onSelectionChanged}
|
||||||
onItemSelected={this.onItemSelected}
|
|
||||||
getTextFromItem={this.getTextFromItem}
|
getTextFromItem={this.getTextFromItem}
|
||||||
onValidateInput={(): ValidationState => ValidationState.invalid}
|
onValidateInput={(): ValidationState => ValidationState.invalid}
|
||||||
itemLimit={1}
|
itemLimit={1}
|
||||||
@@ -76,8 +67,6 @@ export class OrganizationChartPeoplePicker
|
|||||||
}}
|
}}
|
||||||
removeButtonAriaLabel={this.props.removeButtonAriaLabel}
|
removeButtonAriaLabel={this.props.removeButtonAriaLabel}
|
||||||
/>
|
/>
|
||||||
{this.state.validationError ?
|
|
||||||
<div className={styles.pickerError} role='alert'>{this.state.validationError}</div> : null}
|
|
||||||
<div className={styles.pickerDescription}>{this.props.description}</div>
|
<div className={styles.pickerDescription}>{this.props.description}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -98,29 +87,12 @@ export class OrganizationChartPeoplePicker
|
|||||||
}
|
}
|
||||||
|
|
||||||
private onSelectionChanged(items?: IOrganizationChartPickerItem[]): void {
|
private onSelectionChanged(items?: IOrganizationChartPickerItem[]): void {
|
||||||
this.setState({ validationError: '' });
|
|
||||||
this.props.onChange(
|
this.props.onChange(
|
||||||
items && items.length ? items[0].loginName : '',
|
items && items.length ? items[0].loginName : '',
|
||||||
items && items.length ? items[0].accountName : ''
|
items && items.length ? items[0].accountName : ''
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onItemSelected(item?: IOrganizationChartPickerItem): Promise<IOrganizationChartPickerItem> {
|
|
||||||
if (!item) { return Promise.resolve(undefined as IOrganizationChartPickerItem); }
|
|
||||||
this.setState({ validationError: '' });
|
|
||||||
return this.props.validatePerson(item.loginName, item.accountName).then(
|
|
||||||
(valid: boolean): IOrganizationChartPickerItem => {
|
|
||||||
if (valid) { return item; }
|
|
||||||
this.setState({ validationError: this.props.validationErrorText });
|
|
||||||
return undefined as IOrganizationChartPickerItem;
|
|
||||||
},
|
|
||||||
(): IOrganizationChartPickerItem => {
|
|
||||||
this.setState({ validationError: this.props.validationErrorText });
|
|
||||||
return undefined as IOrganizationChartPickerItem;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getTextFromItem(item: IOrganizationChartPickerItem): string {
|
private getTextFromItem(item: IOrganizationChartPickerItem): string {
|
||||||
return item.primaryText || item.loginName;
|
return item.primaryText || item.loginName;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ define([], function() {
|
|||||||
PeoplePickerNoResultsText: 'Keine passenden Personen gefunden.',
|
PeoplePickerNoResultsText: 'Keine passenden Personen gefunden.',
|
||||||
PeoplePickerRemoveLabel: 'Startperson entfernen',
|
PeoplePickerRemoveLabel: 'Startperson entfernen',
|
||||||
PeoplePickerSuggestionsLabel: 'Vorgeschlagene Personen',
|
PeoplePickerSuggestionsLabel: 'Vorgeschlagene Personen',
|
||||||
PeoplePickerValidationError: 'Für diese Person ist kein lesbares Profil im SharePoint User Profile Service vorhanden.',
|
|
||||||
MaxDepthFieldLabel: 'Maximale Ebenen nach unten',
|
MaxDepthFieldLabel: 'Maximale Ebenen nach unten',
|
||||||
MaxPeopleFieldLabel: 'Maximale Personenzahl',
|
MaxPeopleFieldLabel: 'Maximale Personenzahl',
|
||||||
CacheMinutesFieldLabel: 'Profilcache (Minuten, 0 = aus)',
|
CacheMinutesFieldLabel: 'Profilcache (Minuten, 0 = aus)',
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ define([], function() {
|
|||||||
PeoplePickerNoResultsText: 'No matching people found.',
|
PeoplePickerNoResultsText: 'No matching people found.',
|
||||||
PeoplePickerRemoveLabel: 'Remove starting person',
|
PeoplePickerRemoveLabel: 'Remove starting person',
|
||||||
PeoplePickerSuggestionsLabel: 'Suggested people',
|
PeoplePickerSuggestionsLabel: 'Suggested people',
|
||||||
PeoplePickerValidationError: 'This person has no readable profile in the SharePoint User Profile Service.',
|
|
||||||
MaxDepthFieldLabel: 'Maximum levels below',
|
MaxDepthFieldLabel: 'Maximum levels below',
|
||||||
MaxPeopleFieldLabel: 'Maximum number of people',
|
MaxPeopleFieldLabel: 'Maximum number of people',
|
||||||
CacheMinutesFieldLabel: 'Profile cache (minutes, 0 = off)',
|
CacheMinutesFieldLabel: 'Profile cache (minutes, 0 = off)',
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ declare interface IOrganizationChartWebPartStrings {
|
|||||||
PeoplePickerNoResultsText: string;
|
PeoplePickerNoResultsText: string;
|
||||||
PeoplePickerRemoveLabel: string;
|
PeoplePickerRemoveLabel: string;
|
||||||
PeoplePickerSuggestionsLabel: string;
|
PeoplePickerSuggestionsLabel: string;
|
||||||
PeoplePickerValidationError: string;
|
|
||||||
MaxDepthFieldLabel: string;
|
MaxDepthFieldLabel: string;
|
||||||
MaxPeopleFieldLabel: string;
|
MaxPeopleFieldLabel: string;
|
||||||
CacheMinutesFieldLabel: string;
|
CacheMinutesFieldLabel: string;
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ assert(webPart.indexOf('OrganizationChartPeoplePicker') >= 0 &&
|
|||||||
'The verified starting-person picker must be rendered on the canvas instead of the property pane.');
|
'The verified starting-person picker must be rendered on the canvas instead of the property pane.');
|
||||||
assert(picker.indexOf('NormalPeoplePicker') >= 0 && picker.indexOf('ValidationState.invalid') >= 0,
|
assert(picker.indexOf('NormalPeoplePicker') >= 0 && picker.indexOf('ValidationState.invalid') >= 0,
|
||||||
'The on-canvas people picker must reject unverified free-text values.');
|
'The on-canvas people picker must reject unverified free-text values.');
|
||||||
assert(picker.indexOf('validatePerson') >= 0 && service.indexOf('public validatePerson') >= 0,
|
|
||||||
'Selected people must be validated against the User Profile Service.');
|
|
||||||
assert(webPart.indexOf("PropertyPaneSlider('maxDepth'") >= 0 && webPart.indexOf("PropertyPaneSlider('maxPeople'") >= 0,
|
assert(webPart.indexOf("PropertyPaneSlider('maxDepth'") >= 0 && webPart.indexOf("PropertyPaneSlider('maxPeople'") >= 0,
|
||||||
'Tree safety limits are missing.');
|
'Tree safety limits are missing.');
|
||||||
assert(component.indexOf("role='tree'") >= 0 && component.indexOf("role='treeitem'") >= 0,
|
assert(component.indexOf("role='tree'") >= 0 && component.indexOf("role='treeitem'") >= 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user