Showing posts with label SPFx. Show all posts
Showing posts with label SPFx. Show all posts

Wednesday, 16 April 2025

React Responsive Carousel auto play doesn't work in SPFx

Issue:

Recently, we were developing a carousal feature in SPFx webpart. We got react component code from UI Team which has used React Responsive Carousel with sample data, and it was working fine. We needed to make the carousal dynamic and fetch data from SharePoint list.

When we incorporated the changes, we faced a strange issue. When we provide the dynamic data, the carousal will load only last item and doesn’t rotate. But if we pass static data, it worked fine.

Analysis:

As the webpart working with static data, we were not sure it was issue from the component. It meant there was something missing from the implementation part. So we started looking in to online resources. One such link mentioned we needed to explicitly give the CSS path in the import as follow:

 import 'react-responsive-carousel/lib/styles/carousel.min.css'  

It didn’t change the output. So, we thought if dynamic loading might be causing the issue, then we can generate HTML first and directly pass it to the component. So, we changed our component code from below:

 public render(): React.ReactElement<IReactCarousalProps> {  
 const images = this.state.carousaldata;  
 return (  
 <div className='min-h-screen'>  
 <Carousel showArrows={true} showThumbs={true}>  
 {images.map((url, index) => {  
 <div key={index}>  
 <img src={url.name} />  
 </div>;  
 })}  
 </Carousel>  
 </div>  
 );  
 }  

And updated as below:

 public render(): React.ReactElement<IReactCarousalProps> {  
 var images = this.state.carousaldata;  
 var htmlBody = “”;  
 images.map((url, index) => {  
 htmlBody+=” <div key={index}><img src={url.name} /></div>”  
 })  
 return (  
 <div className='min-h-screen'>  
 <Carousel showArrows={true} showThumbs={true}>  
 {htmlBody}  
 </Carousel>  
 </div>  
 );  
 }  

But this completely hide the component and showcase nothing on the page (Though earlier code showcase at least last item😊). But this failure confirmed that fault is not within the implementation but is from component itself and after that we have gone through the repository issue and find out similar issue and found the resolution.

Resolution:

So, we found that it might be version issue. Many people have mentioned that downgrading the version to lower version (3.1.33) has resolved the issue for them.

But in one such comment we found that there is an alternative too. If the carousal is initialized with zero content, it will break. If it is initialized with proper data, it will work properly. So, the user has suggested that we need to check before binding component, it our data is not there, do not bind the component.

The sample code will look like:

 public render(): React.ReactElement<IReactCarousalProps> {  
 const images = this.state.carousaldata;  
 return (  
 <div className='min-h-screen'>  
 { images.length > 0 ? (  
 <Carousel showArrows={true} showThumbs={true}>  
 {images.map((url, index) => {  
 <div key={index}>  
 <img src={url.name} />  
 </div>;  
 })}  
 </Carousel>) : null}  
 </div>  
 );  
 }  

Though the user input was done on 2019, and the issue persist, I thought it would be helpful to someone with same issues.

References: 
  1. React Responsive Carousel Sample - https://github.com/leandrowd/react-responsive-carousel
  2. Issue resolution link - https://github.com/leandrowd/react-responsive-carousel/issues/321
  3. CSS Reference - https://stackoverflow.com/questions/66554854/react-responsive-carousel-is-not-displaying-properly

Friday, 6 December 2024

BaseDialog 'isBlocking' IDialogConfiguration override is not working.

Issue:

Recently, we were developing a SPFx webpart in which we needed to open a dialog. For that we decide to use Office Fabric UI Dialog control. For that we used this link as reference. The Microsoft blog explains the code well. We were able to open the dialog as expected. We needed one more functionality that if user clicks outside the dialog, it should not be closed.

For that we found we can set “IsBlocking” flag to true. We applied the code as mentioned below:
 export default class ColorPickerDialog extends BaseDialog {  
  public message: string;  
  public colorCode: IColor;  
  public render(): void {  
   ReactDOM.render(<ColorPickerDialogContent  
   close={ this.close }  
   message={ this.message }  
   defaultColor={ this.colorCode }  
   submit={ this._submit }  
   />, this.domElement);  
  }  
  public getConfig(): IDialogConfiguration {  
   return { isBlocking: false };  
  }  
  protected onAfterClose(): void {  
   super.onAfterClose();  
   // Clean up the element for the next dialog  
   ReactDOM.unmountComponentAtNode(this.domElement);  
  }  
  private _submit = (color: IColor) => {  
   this.colorCode = color;  
   this.close();  
  }  
 }  
But still, that does not work.

Analysis:

The blog was written in 2017. So, we thought with the updates in SPFx version, there might be change in the code. We tried to find online if we can get any updated code for the same. But we are not able to find it anywhere. So, we decide to put a query in GitHub for solution. We found an old issue there which provide us the resolution.


Resolution:

The issue was reported in Dec 2017 and was given resolution in Jan 2018. The code documented in the was missing the actual implementations for the “IsBlocking” flag. It needs to be passed when you call the dialog component as below:

 const dialog: ColorPickerDialog = new ColorPickerDialog({isBlocking: true});  
So, the full code for the button click will look something like below:
 @override  
 public onExecute(event: IListViewCommandSetExecuteEventParameters): void {  
  switch (event.itemId) {  
   case 'COMMAND_1':  
    Dialog.alert(`${this.properties.sampleTextOne}`);  
    // actual code provided in the code  
    //const dialog: ColorPickerDialog = new ColorPickerDialog();  
    //code that needs to be updated.  
    const dialog: ColorPickerDialog = new ColorPickerDialog({isBlocking: true});  
    dialog.message = 'Pick a color:';  
    // Use 'FFFFFF' as the default color for first usage  
    let defaultColor : IColor = { hex: 'FFFFFF', str: '', r: null, g: null, b: null, h: null, s: null, v: null };  
    dialog.colorCode = this._colorCode|| defaultColor;  
    dialog.show().then(() => {  
     this._colorCode = dialog.colorCode;  
     Dialog.alert(`Picked color: ${dialog.colorCode.hex}`);  
    });  
    break;  
   default:  
    throw new Error('Unknown command');  
  }  
 }  

You can remove getConfig() method from ColorPickerDialog class as it is of no use.

It has been 6 years since the posting of the issue and still the blog is not updated. There isn’t any other resource that mentioned this. So, I am writing this again to help anyone who is stuck in the issue.


References:

Thursday, 7 November 2019

SPFx general code review points

Recently our client want to code review with Microsoft. We were not sure what can we implement in SPFx. Below were few suggestions after our code review:


1. Do not import * from name space

For this you need to make change in tsconfig.json file. Add compiler option - "allowSyntheticDefaultImports": true. Then you will able to make below change

For example, import * as React from 'react'; should be import React from 'react';

2. Delete unused reference, variables and commented code.

3. Use escape for string property values in tsx

you need to load below name space
import { escape } from '@microsoft/sp-lodash-subset';

Then for sting values use it with escape.
For example, any string property when you use in tsx, instead using -  this.props.scopeType
use it like -  escape(this.props.scopeType);

4. Remove for loops where possible.

For example in below scenario, you can push the whole array instead looping through each item.
for (var j = 0; j < projectTask.length; j++) {
            result.push(projectTask[j]);
          }
Can be done as
result.push(projectTask);

5. Use global varible for hardcoded values

For example, use variable to store site url and replace that with variable name
fetchUrl = "https:/test01.sharepoint.com/sites/ProjectCenter/_api/web/lists/GetByTitle('Projects')/items?$filter=" + filterQuery + "&$top=4999&$select=Title";

could be
var projectCenterURL = "https://test01.sharepoint.com/sites/ProjectCenter";

fetchUrl = projectCenterURL+"/_api/web/lists/GetByTitle('Projects')/items?$filter=" + filterQuery + "&$top=4999&$select=Title";

6. use console log for values instead alert
private _onItemInvoked(item: any): void {
    alert(`Item invoked: ${item.ID}`);
  }

7. Move common methods to common file to improve code re-usability.

8. Use switch case instead of multiple if else statement

9. Enable public CDN and load CSS/ Js files from there.

10. You can make a single fetch api call in common and reuse it instead of writing code again:

fetch(URL, {
        method: 'GET',
        mode: 'cors',
        credentials: "include",     
        headers: new Headers({
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'Access-Control-Allow-Origin': '*',
            'Cache-Control': 'no-cache',
            'pragma': 'no-cache',
        }),
    }).then(async(response) => await response.json())
    .then(async(data)=> {
       return data;
    });

Friday, 1 November 2019

Access _spPageContextInfo details in SharePoint Framework

If you are into SharePoint development, then you should be familiar with your best friend _spPageContextInfo which gives you more valuable context-based information. In classic SharePoint Pages, you can access it directly like below:
  1. //This is how you access page context  
  2. _spPageContextInfo.  
  3.   
  4. // retrieve site url  
  5. _spPageContextInfo.webAbsoluteUrl  
You can also access the same now from SharePoint Framework too.
  1. // This is how you can access the page context info  
  2. this.context.pageContext.legacyPageContext;  
  3.   
  4. // Retreive current user display name  
  5. this.context.pageContext.legacyPageContext[‘userDisplayName’]);  
  6.   
  7. // Even site classification  
  8. this.context.pageContext.legacyPageContext[‘siteClassification’]);  
Below is the complete list of object’s properties which sample values,
  1. CorrelationId : "b215479e-f056-5000-11a1-ec000000000010"  
  2. DesignPackageId : "00000000-0000-0000-0000-000000000000"  
  3. PreviewFeaturesEnabled: true  
  4. PublishingFeatureOn : false  
  5. ReycleBinItemCount : -1  
  6. aadInstanceUrl : "https://login.windows.net"  
  7. aadTenantId : "96d8e75d--14a031e2320f"  
  8. aadUserId : "e7d71f70-13e9-0000-0000-86d1269cd536"  
  9. alertsEnabled : true  
  10. allowSilverlightPrompt : "True"  
  11. blockDownloadsExperienceEnabled : false  
  12. canUserCreateMicrosoftForm : true  
  13. canUserCreateVisioDrawing : true  
  14. cdnPrefix : "static.sharepointonline.com/bld"  
  15. crossDomainPhotosEnabled : true  
  16. currentCultureLCID : 1033  
  17. currentCultureName : "en-US"  
  18. currentLanguage : 1033  
  19. currentUICultureName : "en-US"  
  20. departmentId : null  
  21. disableAppViews : false  
  22. disableFlows : false  
  23. env : "prod"  
  24. farmLabel : "US_9_Content"  
  25. fid : 16190  
  26. formDigestTimeoutSeconds : 1800  
  27. groupColor : "#188387"  
  28. groupHasHomepage : true  
  29. groupHasQuickLaunchConversationsLink : false  
  30. groupId : "c771d35a-fee4-4447-9c0a-6c0b199d00fc"  
  31. groupType : "Private"  
  32. guestsEnabled : false  
  33. hasManageWebPermissions : true  
  34. hasPendingWebTemplateExtension : false  
  35. hideSyncButtonOnODB : false  
  36. hubSiteId : null  
  37. idleSessionSignOutEnabled : false  
  38. isAnonymousGuestUser : false  
  39. isAppWeb : false  
  40. isEmailAuthenticationGuestUser : false  
  41. isExternalGuestUser : false  
  42. isHubSite : false  
  43. isMultiGeoTenant : false  
  44. isNoScriptEnabled : true  
  45. isSPO : true  
  46. isSiteAdmin : true  
  47. isTenantDevSite : false  
  48. isWebWelcomePage : false  
  49. layoutsUrl: "_layouts/15"  
  50. listBaseTemplate : 100  
  51. listId : "{7069a902-4347-000-8a03-00fceecfdc70}"  
  52. listPermsMask : {High: 2147483647, Low: 4294705151}  
  53. listTitle : "Property Locations"  
  54. listUrl : "/sites/DemoModernTeamSite/Lists/Property Locations"  
  55. maximumFileSize : 15360  
  56. msGraphEndpointUrl : "graph.microsoft.com"  
  57. navigationInfo : {quickLaunch: Array(14), topNav: Array(2)}  
  58. nid : 107  
  59. openInClient : false  
  60. pageItemId : -1  
  61. pageListId : "{7069a902-0000-4000-8a03-21fceecfdc70}"  
  62. pagePermsMask : null  
  63. pagePersonalizationScope : 1  
  64. preferUserTimeZone : false  
  65. readOnlyState : null  
  66. serverRedirectedUrl : null  
  67. serverRequestPath : "/sites/DemoModernTeamSite/Lists/Property Locations/AllItems.aspx"  
  68. serverTime : "2018-02-04T04:58:48.0656761Z"  
  69. showNGSCDialogForSyncOnODB : false  
  70. howNGSCDialogForSyncOnTS : true  
  71. siteAbsoluteUrl : "https://constoso.sharepoint.com/sites/DemoModernTeamSite"  
  72. siteClassification : ""  
  73. siteClientTag : "0$$16.0.7324.1203"  
  74. siteColor : "#188387"  
  75. siteId : "{1c2e6438-80d2-40cc-9bc5-5aa657c00000}"  
  76. sitePagesEnabled : true  
  77. siteServerRelativeUrl : "/sites/DemoModernTeamSite"  
  78. siteSubscriptionId : "0f6d0000-f22c-47d7-a17f-cd78e6b74a54"  
  79. supportPercentStorePath : true  
  80. supportPoundStorePath : true  
  81. systemUserKey : "i:0h.f|membership|10030000000003d@live.com"  
  82. tenantAppVersion : "3704586950"  
  83. themeCacheToken : "/sites/DemoModernTeamSite:/sites/DemoModernTeamSite/_catalogs/theme/Themed/6FE0689A:9:16.0.7324.1203"  
  84. themedCssFolderUrl : "/sites/DemoModernTeamSite/_catalogs/theme/Themed/6FE0689A"  
  85. themedImageFileNames : {spcommon.png: "spcommon-B35BB0A9.themedpng?ctag=9",  
  86. ellipsis.11x11x32.png: "ellipsis.11x11x32-2F01F47D.themedpng?ctag=9",  
  87. O365BrandSuite.95x30x32.png: "O365BrandSuite.95x30x32-C212E2FD.themedpng?ctag=9",  
  88. socialcommon.png: "socialcommon-6F3394A9.themedpng?ctag=9",  
  89. spnav.png: "spnav-230C537D.themedpng?ctag=9", …}  
  90. userDisplayName : "Rajesh Sitaraman"  
  91. userEmail : "rajesh.sitaraman@contoso.com"  
  92. userFirstDayOfWeek : null  
  93. userId : 3  
  94. userLoginName : "rajesh.sitaraman@contoso.com"  
  95. userTime24 : false  
  96. userTimeZoneData : null  
  97. viewId : "{7edde8c2-5458-42b4-0000-883094b3837e}"  
  98. viewOnlyExperienceEnabled : false  
  99. webAbsoluteUrl : "https://contoso.sharepoint.com/sites/DemoModernTeamSite"  
  100. webDescription : "Demo Modern Team Site"  
  101. webFirstDayOfWeek : 0  
  102. webId : "{8985f800-0000-42f2-899a-abb9f24251e3}"  
  103. webLanguage : 1033  
  104. webLogoUrl : "/sites/DemoModernTeamSite/_api/GroupService/GetGroupImage?  
  105. id='8c1914df-0000-4088-a812-96183d42ae27'&hash=636533140690897999"  
  106. webPermMasks : {High: 2147483647, Low: 4294705151}  
  107. webServerRelativeUrl : "/sites/DemoModernTeamSite"  
  108. webTemplate : "64"  
  109. webTemplateConfiguration : "GROUP#0"  
  110. webTime24 : false  
  111. webTimeZoneData : {Description: "(UTC-08:00) Pacific Time (US and Canada)",  
  112. Bias: 480, Id: 13, DaylightBias: -60, DaylightDate: {…}, …}  
  113. webTitle : "Demo Modern TeamSite"  
  114. webUIVersion : 15  
Reference - https://rjesh.com/spPageContextInfo-spfx/