DEV Community

Cover image for Virtual PCF Controls are GA... but theming is not!
Riccardo Gregori
Riccardo Gregori

Posted on

Virtual PCF Controls are GA... but theming is not!

The Virtual PCF Controls, now generally available (GA), are among the most eagerly awaited features for Power Platform developers, and for good reason.

As someone who values consistent user interfaces, one of the primary challenges I often encounter when implementing Web Resources and/or PCF controls is related to styling. It is no secret that the theme used by Model-Driven Apps differs from the one employed in Canvas Apps, and both, in turn, differ from the default Fluent UI theme.

Differences between standard theme and Canvas/Model Driven One

In professional Power Apps development, this "inconsistency" often leaves clients with a subjective impression of poor quality. To mitigate this perception, developers frequently resort to manually adjusting CSS to create as much uniformity as possible across controls. However, this manual tweaking can be both time-consuming for developers who lack extensive frontend expertise and prone to requiring rework whenever Microsoft updates the design language of its applications.

One of the most exciting promises of Virtual PCFs directly addresses this pain point:

Wrapping Fluent UI v9 controls as a component is the easiest way to utilize modern theming because the modern theme is automatically applied to these controls.

Even if the Virtual PCFs are now GA, the page containing that statement has still the (preview) tag in it... 🤔

Does it truly deliver on this promise? Unfortunately, ❌ the answer is no ❌.

Testing this is straightforward. Simply create a Virtual PCF after ensuring you have the latest version of the PAC CLI installed. In my case, I attempted to create a custom DateTime Picker that allows specifying seconds. It is a very simple control:

/* eslint-disable @typescript-eslint/no-explicit-any */
import * as React from 'react';
import { Input, makeStyles, tokens } from '@fluentui/react-components';

export interface IDateTimePickerControlProps {
  theme?: any;
  value?: Date;
  isEnabled?: boolean;
  onChange?: (value?: Date) => void;
}

const useStyles = makeStyles({
  container: {
    display: 'grid',
    width: '100%',
    gridTemplateColumns: '1fr 1fr',
    '@media (max-width: 200px)': {
      gridTemplateColumns: '1fr',
    },
    overflow: 'none'
  }
});

const DateTimePickerControl: React.FC<IDateTimePickerControlProps> = ({ theme, value, isEnabled, onChange }) => {
  const [dateValue, setDateValue] = React.useState(value?.toISOString().slice(0, 10) || '');
  const [timeValue, setTimeValue] = React.useState(value?.toTimeString().slice(0, 8) || '');
  const styles = useStyles();

  const onDateChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
    const date = new Date(event.target.value);
    const time = value || new Date();
    date.setHours(time.getHours(), time.getMinutes(), time.getSeconds());
    onChange?.(date);
    setDateValue(date.toISOString().slice(0, 10));
  };

  const onTimeChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
    const date = value || new Date();
    const [hours, minutes, seconds] = event.target.value.split(':').map(Number);
    date.setHours(hours, minutes, seconds);
    onChange?.(date);
    setTimeValue(date.toTimeString().slice(0, 8));
  };

  return (
    <div className={styles.container} style={{ gap: tokens.spacingHorizontalS, rowGap: tokens.spacingVerticalS, columnGap: tokens.spacingHorizontalS }}>
      <Input
        type="date"
        value={dateValue}
        onChange={onDateChange}
        disabled={!isEnabled}
      />
      <Input
        type="time"
        value={timeValue}
        onChange={onTimeChange}
        disabled={!isEnabled}
        step={1}
      />
    </div>
  );
};

export default DateTimePickerControl;
Enter fullscreen mode Exit fullscreen mode

I've then put that control in a Form and...

Control placed in the form

The field Start date is the one that uses my own control, while End date uses the standard one. As you can see even if I've not put any custom style in my control, the default style does not match the one shipped in the form 😕.

😠 Conclusions

Let me say, I've always been quite disappointed by this lack of consistency, since the beginning. One would expect that a huge company would have started with a framework and stay stick to it... but no, unfortunately we're still swimming in a sea of stratified libraries and versions.

For how long should we continue tweaking manually the UI to make it look professional?

FAQ

Why haven't you used the standard DatePicker control shipped by Fluent UI 9?

Simply because it's not part of the standard set of controls, but it's shipped via "@fluentui/react-datepicker-compat", that is not part of the default Virtual PCF framework.

Have you tryed the approach suggested by Diana's post, by wrapping the control in a FluentProvider?

Even if in the official documentation is stated that wrapping is only required when you want to apply a theme that is different from the one of the app, I tried and... Yes, the resulting effect is exactly the same 😤.

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (2)

Collapse
 
brian_kraemer_ec616d158cf profile image
Brian Kraemer

I've been running into the same issue. Any updates on your end on fixes or work arounds besides custom CSS or a custom theme?

Collapse
 
_neronotte profile image
Riccardo Gregori

Hi,
a quick tweak is to just add appearance=’filled-darker’ and it should blend well with other forms controls. Hope MS will set it as default...

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay