Modify my SPFx web part to read css file instead of writing the css code inside the web part

Steel Contributor

I have built a web part which is similar to this web part from github @ https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-enhanced-list-formatting . The web part allow to embed custom css code inside modern pages. But is it possible to modify the web part, to referecne a css file instead of having to write the actual css code inside the web part? So instead of writing the css code as follow:-

 

css.png

to reference the css file as follow:-

 

listsref.png

Here is the EnhancedListFormattingWebPart.ts:-

import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
  IPropertyPaneConfiguration,
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';

import * as strings from 'EnhancedListFormattingWebPartStrings';
import EnhancedListFormatting from './components/EnhancedListFormatting';
import { IEnhancedListFormattingProps } from './components/IEnhancedListFormattingProps';

import { PropertyPaneWebPartInformation } from '@pnp/spfx-property-controls/lib/PropertyPaneWebPartInformation';
import { PropertyFieldMonacoEditor } from '../../controls/PropertyFieldMonacoEditor';


export interface IEnhancedListFormattingWebPartProps {
  css: string;
  acceptedDisclaimer?: boolean;
}

export default class EnhancedListFormattingWebPart extends BaseClientSideWebPart <IEnhancedListFormattingWebPartProps> {

  public render(): void {
    const element: React.ReactElement<IEnhancedListFormattingProps> = React.createElement(
      EnhancedListFormatting,
      {
        css: this.properties.css,
        acceptedDisclaimer: this.properties.acceptedDisclaimer,
        displayMode: this.displayMode,
        context: this.context,
        onAcceptDisclaimer: ()=>this._handleAcceptDisclaimer()
      }
    );

    ReactDom.render(element, this.domElement);
  }

  protected onDispose(): void {
    ReactDom.unmountComponentAtNode(this.domElement);
  }

  // protected get dataVersion(): Version {
  //   return Version.parse('1.0');
  // }

  protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
    return {
      pages: [
        {
          header: {
            description: strings.PropertyPaneDescription
          },
          groups: [
            {
              groupName: strings.BasicGroupName,
              groupFields: [
                PropertyFieldMonacoEditor('css', {
                  label: strings.CSSFieldLabel,
                  key: "cssText",
                  value: this.properties.css,
                  defaultValue: this.properties.css,
                  language: "css",
                  theme: "vs-light",
                  showLineNumbers: false,
                  onPropertyChange: (_propertyPath: string, _oldValue: string, value: string) => this._handleSave(value),
                }),
                PropertyPaneWebPartInformation({
                  description: strings.CSSDisclaimer,
                  key: 'cssDisclaimer'
                })
              ]
            }
          ]
        }
      ]
    };
  }

  private _handleAcceptDisclaimer = () => {
    this.properties.acceptedDisclaimer = true;
    this.render();
  }

  private _handleSave = (value: string) => {
    this.properties.css = value;
  }
}

here is the EnhancedListFormatting.tsx:-

import * as React from 'react';
import styles from './EnhancedListFormatting.module.scss';
import * as strings from 'EnhancedListFormattingWebPartStrings';
import { IEnhancedListFormattingProps } from './IEnhancedListFormattingProps';
import { MessageBarButton, MessageBar, MessageBarType } from 'office-ui-fabric-react';
import { DisplayMode } from '@microsoft/sp-core-library';

export default class EnhancedListFormatting extends React.Component<IEnhancedListFormattingProps, {}> {
  public render(): React.ReactElement<IEnhancedListFormattingProps> {
    const { css, displayMode, acceptedDisclaimer } = this.props;

     // If we accepted the disclaimer, let's work as expected

    // Determine if there is a CSS value
    const hasCSS: boolean = css !== undefined && css !== "";

    // Create a style element
    const styleElem: JSX.Element = <style type="text/css">{css}</style>;

    // If we're not in Edit mode, hide this web part
    if (displayMode !== DisplayMode.Edit) {
      return styleElem;
    }

    // if we didn't accept the disclaimer, show a disclaimer and nothing else
    if (acceptedDisclaimer !== true) {
      return (<MessageBar
        onDismiss={()=>this._onAcceptDisclaimer()}
        dismissButtonAriaLabel={strings.DismissDisclaimerAriaLabel}
        messageBarType={MessageBarType.warning}
        actions={
          <div>
            <MessageBarButton onClick={()=>this._onAcceptDisclaimer()}>{strings.AcceptDisclaimerButton}</MessageBarButton>
          </div>
        }
      >
        <div className={styles.disclaimerText} dangerouslySetInnerHTML={{__html: strings.DisclaimerText}}></div>
      </MessageBar>);
    }

    return (
      <div className={styles.enhancedListFormatting}>
        {styleElem}
        <MessageBar
          messageBarType={hasCSS ? MessageBarType.success : null}
          isMultiline={false}
          actions={
            <div>
              <MessageBarButton onClick={() => this._onConfigure()}>{hasCSS ? strings.PlaceholderButtonTitleHasStyles : strings.PlaceholderButtonTitleNoStyles}</MessageBarButton>
            </div>
          }
        >
          {hasCSS ? strings.PlaceholderDescriptionHasStyles : strings.PlaceholderDescriptionNoStyles}
        </MessageBar>
      </div>
    );
  }

  private _onAcceptDisclaimer() {
    this.props.onAcceptDisclaimer();
  }

  private _onConfigure() {
    this.props.context.propertyPane.open();
  }
}

any idea, how i can achieve this? I want to do so , as we are planing to create a lot of pages which have the custom css. And instead of having to modify all those pages in the future to change their css, we will only need to change the related css file, and the change will be applied automatically to all the pages at once.

Thanks

0 Replies