pnp/sp-dev-fx-controls-react

repository·master·Indexed 19 days ago

https://github.com/pnp/sp-dev-fx-controls-react

A collection of reusable React controls and components specifically built for developers creating SharePoint Framework (SPFx) solutions. The library includes UI controls for the main rendering area of web parts, such as the ListToolbar and Accessible Accordion, and supports SharePoint and Microsoft Teams theming.

Tokens
144.8K
Snippets
342
Records
547
Agent score
65%

What's inside @pnp/spfx-controls-react

  1. Access SPFx context in Adaptive Card templates via @context

    master

    When you pass the SPFx context to the AdaptiveCardHost via the context property, the control automatically injects a @context field into your data object. This allows you to use Adaptive Cards Templating syntax to access SharePoint and Microsoft Teams information directly within your card template.

    Available fields under @context include:

    • theme: The current theme applied to the card.
    • aadInfo: Azure AD information.
    • cultureInfo: Culture information.
    • userInfo: User information.
    • spListInfo: Current List information.
    • spListItemInfo: Current List item information.
    • spSiteInfo: Current Site information.
    • spWebInfo: Current Web information.
  2. Use EnhancedThemeProvider to support Teams and SharePoint themes

    master

    The EnhancedThemeProvider acts as a wrapper for React and non-React controls to ensure consistent styling across different hosting environments. It extends the standard Fluent UI ThemeProvider by adding logic to handle specific hosting scenarios:

    • In SharePoint: It uses the theme passed via the theme property or defaults to the current site's theme.
    • In Microsoft Teams: It ignores the theme property and instead uses the context property to detect the active Teams theme (Default, Dark, or High Contrast). It also includes a handler to update the theme in real-time when a user changes it in Teams, without requiring a reload of the Tab or Personal App.

    Note on High Contrast in Teams: The High Contrast theme is manually emulated and only fully supports specific controls: ChoiceGroup, Checkbox, ComboBox, DatePicker, SpinButton, TextField, Toggle, PrimaryButton, DefaultButton, CompoundButton, and IconButton. Other Fluent controls may experience rendering issues.

    <EnhancedThemeProvider context={this.props.context}>
      {/* controls to apply the theme to */}
    </EnhancedThemeProvider>
  3. Handle ETag updates with DynamicForm and ListItemAttachments

    master

    When using ListItemAttachments alongside a DynamicForm (or any component using ETags for optimistic concurrency control), you must use the onAttachmentChange callback.

    This callback provides updated item data, including a new ETag. You should pass this data to the form's updateETag method to prevent 412 Precondition Failed errors when saving the form after attachments have been modified.

    import * as React from 'react';
    import { DynamicForm } from '@pnp/spfx-controls-react/lib/DynamicForm';
    import { ListItemAttachments } from '@pnp/spfx-controls-react/lib/ListItemAttachments';
    
    export class MyFormComponent extends React.Component<any, any> {
      private dynamicFormRef = React.createRef<DynamicForm>();
    
      /**
       * Callback invoked when attachments are added or removed
       * Updates the ETag in DynamicForm to prevent 412 conflicts
       */
      private onAttachmentChange = (itemData: any): void => {
        if (this.dynamicFormRef.current) {
          this.dynamicFormRef.current.updateETag(itemData);
        }
      }
    
      public render(): React.ReactElement {
        return (
          <div>
            <ListItemAttachments
              listId={listId}
              itemId={itemId}
              context={this.props.context}
              onAttachmentChange={this.onAttachmentChange}
            />
            
            <DynamicForm
              ref={this.dynamicFormRef}
              context={this.props.context}
              listId={listId}
              listItemId={itemId}
              respectETag={true}
            />
          </div>
        );
      }
    }
  4. Use the Carousel control with triggerPageEvent for dynamic content

    master

    If your content is loaded dynamically (e.g., from an API), use the triggerPageEvent prop instead of providing a full array to element.

    When triggerPageEvent is used:

    1. You must provide a single JSX.Element to the element prop (the current slide).
    2. You must provide canMoveNext and canMovePrev booleans to indicate if the buttons should be enabled.
    3. The element elements are distinguished based on their key property.
    4. After triggerPageEvent is executed, the carousel enters a processing mode and displays the loadingComponent (or a default Spinner).
    <Carousel
      buttonsLocation={CarouselButtonsLocation.bottom}
      buttonsDisplay={CarouselButtonsDisplay.buttonsOnly}
    
      contentContainerStyles={styles.carouselContent}
      containerButtonsStyles={styles.carouselButtonsContainer}
    
      canMoveNext={this.state.canMoveNext}
      canMovePrev={this.state.canMovePrev}
      triggerPageEvent={this.triggerNextElement}
      element={this.state.currentCarouselElement}
    />
  5. Configure grouping in ListView

    master

    You can group items in the ListView using the groupByFields property. This property accepts an array of IGrouping objects. The order of the objects in the array determines the hierarchy of the grouping (the first object is the primary group).

    Each IGrouping object requires a name (the field name) and an order (GroupOrder.ascending or GroupOrder.descending).

    const groupByFields: IGrouping[] = [
      { 
        name: "Extension", 
        order: GroupOrder.ascending 
      }, 
      { 
        name: "Author", 
        order: GroupOrder.descending
      }
    ];
  6. Load chart data asynchronously with datapromise

    master

    Instead of providing a static data object, you can use the datapromise property to load data asynchronously. This is useful when fetching data from an API or service.

    When using datapromise, you can also provide:

    • loadingtemplate: A function that returns a React element to display while the promise is pending (e.g., a Spinner).
    • rejectedtemplate: A function that receives an error: string and returns a React element to display if the promise fails.
    // 1. Define the async data loader
    private _loadAsyncData(): Promise<Chart.ChartData> {
      return new Promise<Chart.ChartData>((resolve, reject) => {
        // Your data fetching logic here...
        const data: Chart.ChartData = {
          labels: ['Jan', 'Feb'],
          datasets: [{ label: 'Dataset 1', data: [10, 20] }]
        };
        resolve(data);
      });
    }
    
    // 2. Use it in the component
    <ChartControl
      type='bar'
      datapromise={this._loadAsyncData()}
      loadingtemplate={() => <div>Please wait...</div>}
      rejectedtemplate={(error: string) => <div>Error: {error}</div>}
    />
  7. Configure Bar Chart data structures

    master

    The data property in a dataset can be provided in two formats:

    1. Simple Array: An array of numbers (number[]) where each index corresponds to a label on the X axis.
    2. Time Scales: An array of objects containing t (a Date object or a moment timestamp) and y (the value). To render the X axis as a time series, you must configure the xAxes scale type to 'time' in the options object and define displayFormats for the desired time units (e.g., 'day', 'month', 'year').
    // Time Scale Data Example
    data: [
      { "t": new Date('December 1 2018'), "y": 46 },
      { "t": new Date('December 3 2018'), "y": 9 }
    ]
    
    // Time Scale Options Example
    options={{
      scales: {
        xAxes: [{
          type: 'time',
          time: {
            displayFormats: {
              'day': 'MMM DD YYYY',
              'month': 'MMM YYYY'
            }
          }
        }]
      }
    }}
  8. Configure SecurityTrimmedControl permission levels

    master

    The level property determines the scope of the permission check. Depending on the level selected, different properties become required:

    • currentWeb: Checks permissions on the current web. Requires context and permissions.
    • currentList: Checks permissions in the current loaded list. Requires context and permissions.
    • remoteWeb: Checks permissions on a specified site URL. Requires context, permissions, and remoteSiteUrl.
    • remoteListOrLib: Checks permissions on a specific list/library URL within a site. Requires context, permissions, remoteSiteUrl, and relativeLibOrListUrl.
    • remoteListItem: Checks permissions on a specific item. Requires context, permissions, remoteSiteUrl, relativeLibOrListUrl, and itemId.
    • remoteFolder: Checks permissions on a specific folder. Requires context, permissions, remoteSiteUrl, relativeLibOrListUrl, and folderPath.
  9. ListToolbar features and behavior

    master

    The ListToolbar is designed for responsive and accessible command bars with the following behaviors:

    • Alignment: Supports left-aligned items (via items) and right-aligned items (via farItems).
    • Grouping: Items with the same group name are automatically separated by dividers if showGroupDividers is enabled.
    • Overflow: When space is limited, left-aligned items automatically collapse into an overflow menu (represented by "...").
    • Responsiveness: On small screens, labels for far items are automatically hidden to save space.
    • Theming: Supports SharePoint and Teams themes. When provided with the SPFx context, it can automatically detect and adapt to Teams dark or high-contrast modes.
  10. Configure PeoplePicker search behavior

    master

    If your organization's user/group configuration makes standard search difficult, set useSubstrateSearch={true}. This uses the Microsoft 365 Substrate to perform a wider search via centralized stored data.

    To allow searching for and selecting external users (email addresses that have not been validated), set allowUnvalidated={true}. This includes results with EntityData.PrincipalType set to UNVALIDATED_EMAIL_ADDRESS.