Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scheduling Profiler: Redesign with DevTools styling #19707

Merged
merged 10 commits into from Sep 3, 2020

Conversation

taneliang
Copy link
Contributor

@taneliang taneliang commented Aug 27, 2020

Summary

Adopt DevTools styling in the scheduling profiler and show a persistent toolbar at the top of the page.

image

Resolves #19676.

cc @bvaughn, @kartikcho

High-Level Goals

Design: Match React DevTools's design, so that we carry the same brand and level of professionalism.

Code: Revamp App code structure and CSS to resemble the DevTools component -- this is intended to make it slightly easier to merge the scheduling profiler into DevTools when the time comes.

Future expansion: Create space on the canvas page to display the React version (with a view towards #19668) and other profile metadata e.g. profile name and browser type.

Misc features:

  • Display an import button even after a profile has been loaded.
  • Add an import error dialog.

Implementation Breakdown

This is a large PR, but a lot of the code is copied verbatim from react-devtools-shared.

  • Replaces the existing ImportPage, mainly with ImportButton
  • Fork these from react-devtools-shared unchanged: Button, ButtonIcon, Icon, ModalDialog, ReactLogo, TabBar, Tooltip, root.css, utils/storage.js
  • Fork these from react-devtools-shared with modifications:
    • Settings: I've modified this to remove its dependency on BridgeContext and StoreContext, both of which are not used in the scheduling profiler. I've left the list of CSS variables unchanged even though most of them aren't used as far as I can tell.
    • hooks.js: Removed unused hooks and add a useBrowserTheme hook for auto dark mode 馃帀
  • Inject a new DEVTOOLS_VERSION variable similar to DevTools', but computed from the scheduling profiler's package.json instead.
  • Overhaul App.js with the rough component hierarchy and same CSS styles as DevTools.js
  • Create a new SchedulingProfiler component following Profiler.js.

If we decide to accept this PR, we'll want to adapt #19690's help text to the new design and code structure.

Questions

  • Should we import code from react-devtools-shared instead of forking their implementations?
  • Are there any standard sources for the DevTools icons? I did a cursory search and found an icon in the Chromium source code, but I'm not sure where the others are from.
  • If we want to continue forking the implementations, should we delete unused code or leave them around? I'd imagine that leaving them around will ease the eventual merging as the diff will be minimized.

TODO

  • Resolve questions above
  • Place dark mode behind a new feature flag; the profiler canvas does not support dark mode. We'll leave comfortable sizing there; even though the canvas doesn't support comfortable sizing, I think people may appreciate having the rest of the UI text scale.

Test Plan

Before import (equivalent to the old ImportPage):

Before After
image image

After import:

Before After
image image

Because we're using DevTools code, there's built-in support for configurable (auto!) dark mode and display density. Unfortunately, this is not very useful now because the profiler canvas doesn't have support for CSS variables yet. I'll hide it behind a feature flag, but this is what it looks like now:

Settings page:

image

Comfortable dark:

image

Compact dark:

image

Compact dark:

image

Comfortable dark:

image

Hidden image when viewport is shorter than 600px:

image

New import failure dialog:

image

Known Issues

It's possible to hover over the canvas content even if the settings dialog is up. I can fix that in a separate PR to prevent this PR from bloating further.

image

@taneliang taneliang changed the title Scheduling Profiler: RFC: Redesign with DevTools styling Scheduling Profiler: Redesign with DevTools styling Aug 27, 2020
@codesandbox-ci
Copy link

codesandbox-ci bot commented Aug 27, 2020

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 40a5d22:

Sandbox Source
React Configuration

@sizebot
Copy link

sizebot commented Aug 27, 2020

No significant bundle size changes to report.

Size changes (stable)

Generated by 馃毇 dangerJS against 40a5d22

@sizebot
Copy link

sizebot commented Aug 27, 2020

No significant bundle size changes to report.

Size changes (experimental)

Generated by 馃毇 dangerJS against 40a5d22

@bvaughn
Copy link
Contributor

bvaughn commented Sep 1, 2020

This is in my queue to review. I was on PTO last week though and I have a lot of catch up to do. I'll try to get to it sometime in the next couple of days. 馃槃

There is a Yarn lockfile conflict that might be nice to fix when you get the chance though.

@taneliang
Copy link
Contributor Author

No problem, take your time! 馃槃

I've rebased on master and fixed the conflict, and made 2 tiny changes:

  • Bumped the Reach packages to their latest versions
  • Removed the version string from the toolbar as I doubt users will want/need to see it. The version number can still be found in the settings panel.
    image

@taneliang
Copy link
Contributor Author

Also hid the dark mode setting behind a new scheduling profiler feature flag enableDarkMode.

App no longer goes into dark mode even if the app was previously set to Auto or Dark

image

Theme setting is now also hidden

image

Dark mode still available when enableDarkMode = true

image

@taneliang
Copy link
Contributor Author

taneliang commented Sep 3, 2020

Oops, conflicts! I'll rebase this on master

Bug: suppose we have a file which causes the profiler to display an error when
imported. Importing the file once causes the error to be displayed correctly.
However, if the file is imported a second time, the error is not displayed
again.

This happens because we listen to the `input` element's `onChange` event.
Selecting the same file a second time does not cause the element's value to
change, so the `handleFiles` callback is not called.

This commit fixes this bug by emptying the `input` element's `value` in
`handleFiles`.

This bug is also present in the React DevTools Profiler.
@bvaughn
Copy link
Contributor

bvaughn commented Sep 3, 2020

  • Should we import code from react-devtools-shared instead of forking their implementations?

Importing seems nice!

  • Are there any standard sources for the DevTools icons? I did a cursory search and found an icon in the Chromium source code, but I'm not sure where the others are from.

Most of them come from Material UI: https://material-ui.com/components/material-icons/

  • If we want to continue forking the implementations, should we delete unused code or leave them around? I'd imagine that leaving them around will ease the eventual merging as the diff will be minimized.

No strong preference, but I also think using react-devtools-shared seems nice so maybe this question doesn't apply.

Copy link
Contributor

@bvaughn bvaughn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to sit down the review now since most of what I'm writing is "use the shared thing rather than fork" 馃榿 I'd be happy to make that change myself, or you could. Your call!

try {
return sessionStorage.setItem(key, value);
} catch (error) {}
}
Copy link
Contributor

@bvaughn bvaughn Sep 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should just use react-devtools-shared/src/storage.js.

/* Constant values shared between JS and CSS */
--interaction-commit-size: 10px;
--interaction-label-width: 200px;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not feel good to fork these styles. Too easy for them to get out of sync.

Also feels a little weird to import react-devtools-shared/src/devtools/views/root.css but that seems better as long as we eventually plan to merge this profiler into the main DevTools app.

@@ -19,6 +19,7 @@ import './index.css';

const container = document.createElement('div');
container.id = 'root';
container.style.height = '100%'; // Style specified here as CSS for #root will be stripped
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Could do

import styles from './index.css';

const container = document.createElement('div');
container.className = styles.Container;
container.id = 'root';

}, []);

return theme;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use react-devtools-shared/src/devtools/views/hooks.js for all of these but useBrowserTheme?

COMPACT_LINE_HEIGHT = 10;
}

export {COMFORTABLE_LINE_HEIGHT, COMPACT_LINE_HEIGHT};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just import these from react-devtools-shared/src/constants.js?

or e.g.

export {COMFORTABLE_LINE_HEIGHT, COMPACT_LINE_HEIGHT} from 'react-devtools-shared/src/constants.js';


try {
// $FlowFixMe
const rawStyleString = require('!!raw-loader!react-devtools-shared/src/devtools/views/root.css')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, we aren't even using our own forked root CSS here 馃槅

};

const mediaQueryList = window.matchMedia('(prefers-color-scheme: dark)');
mediaQueryList.addEventListener('change', handlePreferredColorSchemeChange);
Copy link
Contributor

@bvaughn bvaughn Sep 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think聽this picks up on dark mode if it's already set (only if the user changes their system theme after the profiler has been opened). At least that seems to be the behavior on OSX (the "change" event won't get fired).

We could also check window.matchMedia(DARK_MODE_QUERY).matches in the effect and update, but then we might have to render twice (once for the default "light" and then again for "dark").

I think this might be a nice opportunity to use the useMutableSource hook! 馃槃 It will handle all of this for us. Here's how you'd use it:

import {
  unstable_createMutableSource as createMutableSource,
  unstable_useMutableSource as useMutableSource,
} from 'react';

export type BrowserTheme = 'dark' | 'light';

const DARK_MODE_QUERY = '(prefers-color-scheme: dark)';

const getSnapshot = window =>
  window.matchMedia(DARK_MODE_QUERY).matches ? 'dark' : 'light';

const darkModeMutableSource = createMutableSource(
  window,
  () => window.matchMedia(DARK_MODE_QUERY).matches,
);

const subscribe = (window, callback) => {
  const mediaQueryList = window.matchMedia(DARK_MODE_QUERY);
  mediaQueryList.addEventListener('change', callback);
  return () => {
    mediaQueryList.removeEventListener('change', callback);
  };
};

export function useBrowserTheme(): BrowserTheme {
  return useMutableSource(darkModeMutableSource, getSnapshot, subscribe);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, cool! I didn't know this existed 馃槅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not released yet in stable, but DevTools uses prerelease APIs 馃槃

https://github.com/reactjs/rfcs/blob/master/text/0147-use-mutable-source.md

@@ -0,0 +1,133 @@
/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use react-devtools-shared/src/devtools/views/TabBar.js and react-devtools-shared/src/devtools/views/TabBar.css rather than fork.

@bvaughn
Copy link
Contributor

bvaughn commented Sep 3, 2020

Eh, screw it 馃槃 I'll hop in and help since it's after hours for you. Give me a few minutes and I'll push a commit that implements my own nits.

import ImportPage from './ImportPage';
import CanvasPage from './CanvasPage';
import {ModalDialogContextController} from './ModalDialog';
import {SettingsContextController} from './Settings/SettingsContext';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced the settings dialog is worth the hassle of the forked contexts and files. For now, I'm going to just rip it out :D

@taneliang
Copy link
Contributor Author

Give me a few minutes and I'll push a commit that implements my own nits.

Thanks! I gotta get back to my art history class readings 馃槅 Let me know if there's anything I can do! I'll be happy to follow up on anything tomorrow afternoon.

Copy link
Contributor

@bvaughn bvaughn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hope it's okay that I DRYed it up a bit.

We could still do more (with the context package and useContextMenu hook) but that can come later.

@taneliang
Copy link
Contributor Author

taneliang commented Sep 3, 2020

Yeah! This is much better.

I just pushed a commit to call updateThemeVariables when enableDarkMode is false, to fix this:

image

@@ -53,6 +52,8 @@ export function useBrowserTheme(): void {
default:
throw Error(`Unsupported theme value "${theme}"`);
}
} else {
updateThemeVariables('light', documentElements);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taneliang This should be theme not 'light' right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah this was outside of the feature flag conditional. My apologies.

Copy link
Contributor Author

@taneliang taneliang Sep 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I think we want 'light' to prevent the app from going into dark mode?

Edit: yep! Sorry, ignore this comment, your second comment didn't load until I commented

@bvaughn bvaughn merged commit 38a512a into facebook:master Sep 3, 2020
@taneliang taneliang deleted the devtools-styles branch September 4, 2020 14:58
koto pushed a commit to koto/react that referenced this pull request Jun 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bug: Broken Scheduling Profiler landing styles
4 participants