Merge branch 'master' into react-accordion
This commit is contained in:
commit
317d03c8e1
|
@ -58,7 +58,6 @@ Version|Date|Comments
|
|||
1.5|September 1, 2020|Adds ability to click on expanded section headers to collapse accordions
|
||||
1.6|September 2, 2020|Added Web Part Title, and ability to expand multiple sections
|
||||
|
||||
|
||||
## Disclaimer
|
||||
|
||||
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "react-accordion",
|
||||
"version": "0.0.1",
|
||||
"version": "1.5.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
@ -32,13 +32,14 @@ This web part shows how to use the Microsoft Graph APIs (beta) for Taxonomy to g
|
|||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
react-graph-cascading-managed-metadata| Anoop Tatti ([@anooptells](https://twitter.com/anooptells))
|
||||
react-graph-cascading-managed-metadata| Anoop Tatti ([anoopt](https://github.com/anoopt), [@anooptells](https://twitter.com/anooptells))
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0.0|Aug 24, 2020|Initial release
|
||||
1.0.1|Sep 03, 2020|Error handling and logging improvements
|
||||
|
||||
## Disclaimer
|
||||
|
||||
|
|
|
@ -3215,9 +3215,9 @@
|
|||
}
|
||||
},
|
||||
"@pnp/spfx-controls-react": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@pnp/spfx-controls-react/-/spfx-controls-react-1.19.0.tgz",
|
||||
"integrity": "sha512-W3PS6I8NsdbOZjE9I9djloYutQW17QHd4nT7jFwPyJFoxnt1MDfWyN6nrPhaeGnnPde3t3TlUbWP4HKLXChFiw==",
|
||||
"version": "1.20.0-beta.52e4e9f",
|
||||
"resolved": "https://registry.npmjs.org/@pnp/spfx-controls-react/-/spfx-controls-react-1.20.0-beta.52e4e9f.tgz",
|
||||
"integrity": "sha512-2lh6EI+0M6H/cgQyDq8av2/LvG/0hCgZG0/XfjWUIFGHcdqpbmcifJbVUeEX46nVt5gHDVeI3vcpqhbaAA5GrA==",
|
||||
"requires": {
|
||||
"@pnp/common": "1.0.1",
|
||||
"@pnp/logging": "1.0.1",
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
"@microsoft/sp-office-ui-fabric-core": "1.11.0",
|
||||
"@microsoft/sp-property-pane": "1.11.0",
|
||||
"@microsoft/sp-webpart-base": "1.11.0",
|
||||
"@pnp/spfx-controls-react": "1.19.0",
|
||||
"@pnp/spfx-controls-react": "^1.20.0-beta.52e4e9f",
|
||||
"office-ui-fabric-react": "6.214.0",
|
||||
"react": "16.8.5",
|
||||
"react-dom": "16.8.5"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Version, DisplayMode } from '@microsoft/sp-core-library';
|
||||
import { Version, DisplayMode, Guid } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
BaseClientSideWebPart,
|
||||
IPropertyPaneConfiguration,
|
||||
|
@ -22,8 +22,7 @@ export default class CascadingManagedMetadataWebPart extends BaseClientSideWebPa
|
|||
public async render(): Promise<void> {
|
||||
await MSGraph.Init(this.context);
|
||||
let renderElement = null;
|
||||
//TODO: Use function to check if GUID?
|
||||
if (this.properties.termSetId && this.properties.termSetId.length == 36) {
|
||||
if (this.properties.termSetId) {
|
||||
renderElement = React.createElement(
|
||||
CascadingManagedMetadata,
|
||||
{
|
||||
|
@ -69,6 +68,19 @@ export default class CascadingManagedMetadataWebPart extends BaseClientSideWebPa
|
|||
return Version.parse('1.0');
|
||||
}
|
||||
|
||||
private validateTermSetId(value: string): string {
|
||||
if (value === null ||
|
||||
value.trim().length === 0) {
|
||||
return 'Provide a term set Id.';
|
||||
}
|
||||
|
||||
if (!Guid.isValid(value.trim())) {
|
||||
return 'Term set Id must be a valid GUID.';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
return {
|
||||
pages: [
|
||||
|
@ -81,7 +93,8 @@ export default class CascadingManagedMetadataWebPart extends BaseClientSideWebPa
|
|||
groupName: strings.BasicGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneTextField('termSetId', {
|
||||
label: strings.TermSetIdFieldLabel
|
||||
label: strings.TermSetIdFieldLabel,
|
||||
onGetErrorMessage: this.validateTermSetId.bind(this)
|
||||
})
|
||||
]
|
||||
}
|
||||
|
|
|
@ -16,31 +16,74 @@ const CascadingManagedMetadata: React.SFC<ICascadingManagedMetadataProps> = (pro
|
|||
const [citiesList, setCitiesList] = React.useState<IDropdownOption[]>([]);
|
||||
const [selectedCityCoordinates, setSelectedCityCoordinates] = React.useState<string>(null);
|
||||
const [selectedCity, setSelectedCity] = React.useState<string>(null);
|
||||
const [showMap, setShowMap] = React.useState<boolean>(false);
|
||||
const [coordinates, setCoordinates] = React.useState<ICoordinates>({ latitude: null, longitude: null });
|
||||
const [messageBarStatus, setMessageBarStatus] = React.useState({
|
||||
type: MessageBarType.info,
|
||||
message: <span></span>,
|
||||
show: false
|
||||
});
|
||||
|
||||
const LOG_SOURCE: string = "Cascading MMD -";
|
||||
|
||||
React.useEffect(() => {
|
||||
_getCountries().then(countries => {
|
||||
const options: IDropdownOption[] = countries.value.map(c => ({ key: c.id, text: c.labels[0].name }));
|
||||
setCountriesList(options);
|
||||
if (countries) {
|
||||
const options: IDropdownOption[] = countries.value.map(c => ({ key: c.id, text: c.labels[0].name }));
|
||||
setCountriesList(options);
|
||||
} else {
|
||||
setCountriesList([]);
|
||||
clearData();
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
}, [props.termSetId]); //* Run this also when the property termSetId changes
|
||||
|
||||
//* Get the country terms i.e. level 1 children using Graph
|
||||
const _getCountries = async (): Promise<ITerms> => {
|
||||
let countries: ITerms = await MSGraph.Get(`/termStore/sets/${props.termSetId}/children`, "beta");
|
||||
return (countries);
|
||||
try {
|
||||
let countries: ITerms = await MSGraph.Get(`/termStore/sets/${props.termSetId}/children`, "beta");
|
||||
setMessageBarStatus(state => ({ ...state, show: false }));
|
||||
console.debug("%s Retrieved countries. %o", LOG_SOURCE, countries);
|
||||
return countries;
|
||||
}
|
||||
catch (error) {
|
||||
console.error("%s Error retrieving countries. Details - %o", LOG_SOURCE, error);
|
||||
setMessageBarStatus({
|
||||
type: MessageBarType.error,
|
||||
message: <span>Error in retrieving countries. Please contact admin.</span>,
|
||||
show: true
|
||||
});
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
//* Get the city terms under a country i.e. level 2 children using Graph
|
||||
const _onCountryChange = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
|
||||
setCoordinates({ latitude: null, longitude: null });
|
||||
setSelectedCityCoordinates(null);
|
||||
setSelectedCity(null);
|
||||
clearData();
|
||||
let countryTermId: string = item.key.toString();
|
||||
|
||||
MMDService.GetTermsAsDropdownOptions(`/termStore/sets/${props.termSetId}/terms/${countryTermId}/children`, countryTermId, true).then(options => {
|
||||
setCitiesList(options);
|
||||
});
|
||||
MMDService.GetTermsAsDropdownOptions(`/termStore/sets/${props.termSetId}/terms/${countryTermId}/children`, countryTermId, true)
|
||||
.then(options => {
|
||||
setCitiesList(options);
|
||||
//setShowMap(false);
|
||||
console.debug("%s Retrieved cities. %o", LOG_SOURCE, options);
|
||||
setMessageBarStatus({
|
||||
type: MessageBarType.warning,
|
||||
message: options.length > 0 ?
|
||||
<span>To see the map, please select a city. </span> :
|
||||
<span> No city terms in the selected country.</span>,
|
||||
show: true
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("%s Error retrieving cities. Details - %o", LOG_SOURCE, error);
|
||||
setMessageBarStatus({
|
||||
type: MessageBarType.error,
|
||||
message: <span>Error in retrieving cities. Please contact admin.</span>,
|
||||
show: true
|
||||
});
|
||||
setCitiesList([]);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
@ -56,41 +99,65 @@ const CascadingManagedMetadata: React.SFC<ICascadingManagedMetadataProps> = (pro
|
|||
longitude: isNaN(Number(long)) ? null : Number(long)
|
||||
};
|
||||
setCoordinates(coordinates);
|
||||
|
||||
console.debug("%s Retrieved coordinates. %o", LOG_SOURCE, coordinates);
|
||||
|
||||
if (coordinates.latitude && coordinates.longitude) {
|
||||
setShowMap(true);
|
||||
setMessageBarStatus(state => ({ ...state, show: false }));
|
||||
} else {
|
||||
setShowMap(false);
|
||||
setMessageBarStatus({
|
||||
type: MessageBarType.error,
|
||||
message: <span>To see the map, please check if the coordinates have been configured correctly.</span>,
|
||||
show: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//* Clear the data related to cities and maps
|
||||
const clearData = () => {
|
||||
setCoordinates({ latitude: null, longitude: null });
|
||||
setSelectedCityCoordinates(null);
|
||||
setSelectedCity(null);
|
||||
setCitiesList([]);
|
||||
setShowMap(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Dropdown
|
||||
label="Country"
|
||||
placeHolder="Select a country"
|
||||
options={countriesList}
|
||||
onChange={_onCountryChange} />
|
||||
defaultSelectedKey=""
|
||||
onChange={_onCountryChange}
|
||||
disabled={!(countriesList.length > 0)} />
|
||||
|
||||
<Dropdown
|
||||
label="City"
|
||||
selectedKey={selectedCityCoordinates}
|
||||
placeHolder="Select a city"
|
||||
options={citiesList}
|
||||
onChange={_onCityChange} />
|
||||
onChange={_onCityChange}
|
||||
disabled={!(citiesList.length > 0)} />
|
||||
|
||||
{
|
||||
coordinates.latitude && coordinates.longitude ?
|
||||
(
|
||||
<React.Fragment>
|
||||
<Map
|
||||
titleText={`Map of our office in ${selectedCity}`}
|
||||
coordinates={coordinates}
|
||||
zoom={15}
|
||||
enableSearch={false} />
|
||||
</React.Fragment>
|
||||
) :
|
||||
(
|
||||
<div style={{ marginTop: "15px" }}>
|
||||
<MessageBar messageBarType={MessageBarType.warning}>
|
||||
{selectedCity ? "To see the map, please check if the coordinates have been configured correctly."
|
||||
: "To see the map, please select a city."}
|
||||
</MessageBar>
|
||||
</div>
|
||||
)
|
||||
showMap &&
|
||||
<Map
|
||||
titleText={`Map of our office in ${selectedCity}`}
|
||||
coordinates={coordinates}
|
||||
zoom={15}
|
||||
enableSearch={false} />
|
||||
}
|
||||
|
||||
{
|
||||
messageBarStatus.show &&
|
||||
<div style={{ marginTop: "15px" }}>
|
||||
<MessageBar messageBarType={messageBarStatus.type}>
|
||||
{messageBarStatus.message}
|
||||
</MessageBar>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -5,6 +5,7 @@ import { IDropdownOption } from "office-ui-fabric-react/lib/Dropdown";
|
|||
export class MMDService {
|
||||
|
||||
private static _sessionStorageKey: string = "CMMD_Options";
|
||||
private static _logSource: string = "Cascading MMD Service -";
|
||||
|
||||
public static async GetTermsAsDropdownOptions(apiUrl: string, parent: string, tryFromCache: boolean): Promise<IDropdownOption[]> {
|
||||
|
||||
|
@ -24,27 +25,34 @@ export class MMDService {
|
|||
}
|
||||
|
||||
private static async _getTermsAsDropdownOptionsUsingGraph(apiUrl: string, parent: string): Promise<IDropdownOption[]> {
|
||||
let terms: ITerms = await MSGraph.Get(apiUrl, "beta");
|
||||
if (terms.value) {
|
||||
//* Set key as description of the term
|
||||
//* Description will be of the format latitude;longitude
|
||||
//* This will be used to render maps
|
||||
const options: IDropdownOption[] = terms.value.map(t => ({
|
||||
key: t.descriptions[0] ? t.descriptions[0].description : t.id,
|
||||
text: t.labels[0].name }));
|
||||
let optionsToStoreInCache: IOption[] = options.map(o => ({
|
||||
key: o.key.toString(),
|
||||
text: o.text,
|
||||
parent
|
||||
}));
|
||||
let optionsFromCache: IOption[] = this._fetchFromSessionStorge();
|
||||
optionsToStoreInCache = [...optionsFromCache, ...optionsToStoreInCache];
|
||||
window.sessionStorage.setItem(this._sessionStorageKey, JSON.stringify(optionsToStoreInCache));
|
||||
console.log("Options added in cache");
|
||||
return options;
|
||||
} else {
|
||||
try {
|
||||
let terms: ITerms = await MSGraph.Get(apiUrl, "beta");
|
||||
if (terms.value) {
|
||||
//* Set key as description of the term
|
||||
//* Description will be of the format latitude;longitude
|
||||
//* This will be used to render maps
|
||||
const options: IDropdownOption[] = terms.value.map(t => ({
|
||||
key: t.descriptions[0] ? t.descriptions[0].description : t.id,
|
||||
text: t.labels[0].name
|
||||
}));
|
||||
let optionsToStoreInCache: IOption[] = options.map(o => ({
|
||||
key: o.key.toString(),
|
||||
text: o.text,
|
||||
parent
|
||||
}));
|
||||
let optionsFromCache: IOption[] = this._fetchFromSessionStorge();
|
||||
optionsToStoreInCache = [...optionsFromCache, ...optionsToStoreInCache];
|
||||
window.sessionStorage.setItem(this._sessionStorageKey, JSON.stringify(optionsToStoreInCache));
|
||||
console.debug("%s Data added in cache.", this._logSource);
|
||||
return options;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("%s Error getting data from Graph. Details - %o", this._logSource, error);
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static _fetchFromSessionStorge(): IOption[] {
|
||||
|
@ -53,11 +61,11 @@ export class MMDService {
|
|||
if (stringResult) {
|
||||
try {
|
||||
result = JSON.parse(stringResult);
|
||||
if(result.length) {
|
||||
console.log("Fetched options from cache");
|
||||
if (result.length) {
|
||||
console.debug("%s Fetched data from cache", this._logSource);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
console.error("%s Error getting data from cache. Details - %o", this._logSource, error);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
Loading…
Reference in New Issue