Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/components/shared/chart/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,18 @@ export class ChartCanvas<Item> extends React.Component<
}
}

override componentDidMount() {
window.addEventListener('profiler-theme-change', this._onThemeChange);
}

override componentWillUnmount() {
window.removeEventListener('profiler-theme-change', this._onThemeChange);
}

_onThemeChange = () => {
this._scheduleDraw();
};

override componentDidUpdate(prevProps: Props<Item>, prevState: State<Item>) {
if (prevProps !== this.props) {
if (
Expand Down
12 changes: 12 additions & 0 deletions src/components/shared/thread/ActivityGraphCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,20 @@ export class ActivityGraphCanvas extends React.PureComponent<CanvasProps> {

override componentDidMount() {
this._renderCanvas();
window.addEventListener('profiler-theme-change', this._onThemeChange);
}

override componentWillUnmount() {
window.removeEventListener('profiler-theme-change', this._onThemeChange);
}

_onThemeChange = () => {
// Invalidate the cached category draw styles,
// so they are recreated with the new theme colors.
this._categoryDrawStyles = null;
this._renderCanvas();
};

override componentDidUpdate() {
this._renderCanvas();
}
Expand Down
9 changes: 9 additions & 0 deletions src/components/shared/thread/SampleGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,17 @@ class ThreadSampleGraphCanvas extends React.PureComponent<CanvasProps> {

override componentDidMount() {
this._renderCanvas();
window.addEventListener('profiler-theme-change', this._onThemeChange);
}

override componentWillUnmount() {
window.removeEventListener('profiler-theme-change', this._onThemeChange);
}

_onThemeChange = () => {
this._renderCanvas();
};

override componentDidUpdate() {
this._renderCanvas();
}
Expand Down
9 changes: 9 additions & 0 deletions src/components/timeline/Markers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,17 @@ class TimelineMarkersCanvas extends React.PureComponent<CanvasProps> {

override componentDidMount() {
this._scheduleDraw();
window.addEventListener('profiler-theme-change', this._onThemeChange);
}

override componentWillUnmount() {
window.removeEventListener('profiler-theme-change', this._onThemeChange);
}

_onThemeChange = () => {
this._scheduleDraw();
};

override componentDidUpdate() {
this._scheduleDraw();
}
Expand Down
14 changes: 14 additions & 0 deletions src/test/components/FlameGraph.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,20 @@ describe('FlameGraph', function () {
expect(drawCalls).toMatchSnapshot();
});

it('redraws when the system theme changes', () => {
setupFlameGraph();
// Flush the initial draw calls.
flushDrawLog();

// Simulate a theme change.
window.dispatchEvent(new CustomEvent('profiler-theme-change'));

// drawCanvasAfterRaf={false} means the redraw is synchronous, so new draw
// calls should be available immediately without flushing rAF.
const drawCalls = flushDrawLog();
expect(drawCalls.length).toBeGreaterThan(0);
});

it('ignores invertCallstack and always displays non-inverted', () => {
const { getState, dispatch } = setupFlameGraph();
expect(getInvertCallstack(getState())).toBe(false);
Expand Down
15 changes: 15 additions & 0 deletions src/test/components/SampleGraph.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,21 @@ describe('SampleGraph', function () {
};
}

it('redraws when the system theme changes', () => {
const { getContextDrawCalls } = setup();

// Flush the initial draw calls.
getContextDrawCalls();

// Simulate a theme change.
window.dispatchEvent(new CustomEvent('profiler-theme-change'));

const drawCalls = getContextDrawCalls();
expect(drawCalls.some(([operation]) => operation === 'fillRect')).toBe(
true
);
});

it('matches the component snapshot', () => {
const { sampleGraphCanvas } = setup();
expect(sampleGraphCanvas).toMatchSnapshot();
Expand Down
16 changes: 16 additions & 0 deletions src/test/components/ThreadActivityGraph.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,22 @@ describe('ThreadActivityGraph', function () {
);
});

it('redraws when the system theme changes', () => {
const { getContextDrawCalls } = setup();

// Flush out any existing draw calls.
getContextDrawCalls();
expect(getContextDrawCalls().length).toEqual(0);

// Simulate a theme change.
window.dispatchEvent(new CustomEvent('profiler-theme-change'));

const drawCalls = getContextDrawCalls();
expect(drawCalls.some(([operation]) => operation === 'beginPath')).toBe(
true
);
});

it('matches the 2d canvas draw snapshot with CPU values', () => {
const profile = getSamplesProfile();
profile.meta.interval = 1;
Expand Down
19 changes: 19 additions & 0 deletions src/test/components/TimelineMarkers.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,25 @@ describe('TimelineMarkers', function () {
beforeEach(addRootOverlayElement);
afterEach(removeRootOverlayElement);

it('redraws when the system theme changes', () => {
const { flushRafCalls } = setupWithMarkers(
{ rangeStart: 0, rangeEnd: 10 },
[['DOMEvent', 0, 10]]
);

// Flush the initial draw calls.
flushRafCalls();
flushDrawLog();

// Simulate a theme change.
window.dispatchEvent(new CustomEvent('profiler-theme-change'));

// _scheduleDraw() uses RAF, so flush it before checking.
flushRafCalls();
const drawCalls = flushDrawLog();
expect(drawCalls.length).toBeGreaterThan(0);
});

it('renders correctly overview markers', () => {
window.devicePixelRatio = 1;

Expand Down
44 changes: 44 additions & 0 deletions src/test/unit/dark-mode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,50 @@ describe('isDarkMode', function () {
});
});

describe('profiler-theme-change event', function () {
it('is dispatched when the theme changes', function () {
resetForTest();
// Initialize in light mode.
isDarkMode();

const listener = jest.fn();
window.addEventListener('profiler-theme-change', listener);

// Switch to dark via a storage event.
jest.spyOn(Storage.prototype, 'getItem').mockImplementation(() => 'dark');
window.dispatchEvent(new StorageEvent('storage', { key: 'theme' }));

expect(listener).toHaveBeenCalledTimes(1);
window.removeEventListener('profiler-theme-change', listener);
});

it('is not dispatched during initialization', function () {
resetForTest();

const listener = jest.fn();
window.addEventListener('profiler-theme-change', listener);

isDarkMode(); // triggers setup

expect(listener).not.toHaveBeenCalled();
window.removeEventListener('profiler-theme-change', listener);
});

it('is not dispatched when the theme stays the same', function () {
resetForTest();
isDarkMode(); // initialize as light

const listener = jest.fn();
window.addEventListener('profiler-theme-change', listener);

// Storage event fires but the resolved theme is still light.
window.dispatchEvent(new StorageEvent('storage', { key: 'theme' }));

expect(listener).not.toHaveBeenCalled();
window.removeEventListener('profiler-theme-change', listener);
});
});

describe('initTheme', function () {
it('sets the document element class', function () {
resetForTest();
Expand Down
5 changes: 5 additions & 0 deletions src/utils/dark-mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,18 @@ function _applyTheme(): void {
shouldBeDark = getSystemTheme() === 'dark';
}

const changed = _isDarkModeSetup && _isDarkMode !== shouldBeDark;
_isDarkMode = shouldBeDark;

if (shouldBeDark) {
document.documentElement.classList.add('dark-mode');
} else {
document.documentElement.classList.remove('dark-mode');
}

if (changed) {
window.dispatchEvent(new CustomEvent('profiler-theme-change'));
}
}

export function setThemePreference(pref: ThemePreference): void {
Expand Down