diff --git a/locales/en-US/app.ftl b/locales/en-US/app.ftl index 5437beb387..a0f681b894 100644 --- a/locales/en-US/app.ftl +++ b/locales/en-US/app.ftl @@ -413,6 +413,8 @@ MenuButtons--metaInfo--attempting-resymbolicate = Attempting to re-symbolicate p MenuButtons--metaInfo--currently-symbolicating = Currently symbolicating profile MenuButtons--metaInfo--cpu = CPU: MenuButtons--metaInfo--main-memory = Main memory: +MenuButtons--index--show-moreInfo-button = Show more +MenuButtons--index--hide-moreInfo-button = Show less # This string is used when we have the information about both physical and # logical CPU cores. diff --git a/res/css/photon/button.css b/res/css/photon/button.css index 21ae58569b..024e0819a2 100644 --- a/res/css/photon/button.css +++ b/res/css/photon/button.css @@ -28,11 +28,13 @@ opacity: 0.4; } -.photon-button:hover:enabled { +/* Note: we're using :not(:disabled) instead of :enabled so that this can apply + * to non-input elements too. */ +.photon-button:hover:not(:disabled) { background-color: var(--grey-90-a20); } -.photon-button:hover:active:enabled { +.photon-button:hover:active:not(:disabled) { background-color: var(--grey-90-a30); } @@ -50,11 +52,11 @@ color: #fff; } -.photon-button-primary:hover:enabled { +.photon-button-primary:hover:not(:disabled) { background-color: var(--blue-70); } -.photon-button-primary:hover:active:enabled { +.photon-button-primary:hover:active:not(:disabled) { background-color: var(--blue-80); } @@ -64,11 +66,11 @@ color: #fff; } -.photon-button-destructive:hover:enabled { +.photon-button-destructive:hover:not(:disabled) { background-color: var(--red-70); } -.photon-button-destructive:hover:active:enabled { +.photon-button-destructive:hover:active:not(:disabled) { background-color: var(--red-80); } @@ -78,11 +80,11 @@ background-color: transparent; } -.photon-button-ghost:hover:enabled { +.photon-button-ghost:hover:not(:disabled) { background-color: var(--grey-90-a10); } -.photon-button-ghost:hover:active:enabled { +.photon-button-ghost:hover:active:not(:disabled) { background-color: var(--grey-90-a20); } diff --git a/src/components/app/MenuButtons/MetaInfo.css b/src/components/app/MenuButtons/MetaInfo.css index a10ec27c05..1067be8932 100644 --- a/src/components/app/MenuButtons/MetaInfo.css +++ b/src/components/app/MenuButtons/MetaInfo.css @@ -27,7 +27,7 @@ .metaInfoLargeContent { overflow: scroll; - height: 100px; + max-height: 100px; overflow-wrap: anywhere; } @@ -67,3 +67,23 @@ font-size: 1.2em; font-weight: 700; } + +.moreInfoSummary { + display: flex; + align-items: center; + justify-content: center; + margin-top: 24px; + user-select: none; +} + +.moreInfoSummary::marker { + content: ''; +} + +.moreInfoPart { + margin-top: 15px; +} + +.moreInfoValue { + overflow-x: scroll; +} diff --git a/src/components/app/MenuButtons/MetaInfo.js b/src/components/app/MenuButtons/MetaInfo.js index 1736ebedb0..8cd6bf03f4 100644 --- a/src/components/app/MenuButtons/MetaInfo.js +++ b/src/components/app/MenuButtons/MetaInfo.js @@ -10,8 +10,10 @@ import { MetaOverheadStatistics } from './MetaOverheadStatistics'; import { getProfile, getSymbolicationStatus, + getProfileExtraInfo, } from 'firefox-profiler/selectors/profile'; import { resymbolicateProfile } from 'firefox-profiler/actions/receive-profile'; +import { formatFromMarkerSchema } from 'firefox-profiler/profile-logic/marker-schema'; import { formatBytes, @@ -25,14 +27,23 @@ import { import { assertExhaustiveCheck } from 'firefox-profiler/utils/flow'; import explicitConnect from 'firefox-profiler/utils/connect'; -import type { Profile, SymbolicationStatus } from 'firefox-profiler/types'; +import type { + Profile, + SymbolicationStatus, + ExtraProfileInfoSection, +} from 'firefox-profiler/types'; import type { ConnectedProps } from 'firefox-profiler/utils/connect'; import './MetaInfo.css'; +type State = {| + showsMoreInfo: boolean, +|}; + type StateProps = $ReadOnly<{| profile: Profile, symbolicationStatus: SymbolicationStatus, + profileExtraInfo: ExtraProfileInfoSection[], |}>; type DispatchProps = $ReadOnly<{| @@ -44,7 +55,9 @@ type Props = ConnectedProps<{||}, StateProps, DispatchProps>; /** * This component formats the profile's meta information into a dropdown panel. */ -class MetaInfoPanelImpl extends React.PureComponent { +class MetaInfoPanelImpl extends React.PureComponent { + state = { showsMoreInfo: false }; + /** * This method provides information about the symbolication status, and a button * to re-trigger symbolication. @@ -120,6 +133,55 @@ class MetaInfoPanelImpl extends React.PureComponent { } } + _handleMoreInfoButtonClick = (event: SyntheticMouseEvent<>) => { + event.preventDefault(); + this.setState((state) => ({ showsMoreInfo: !state.showsMoreInfo })); + }; + + _renderMoreInfoSection(section: ExtraProfileInfoSection) { + return ( +
+

{section.label}

+
+ {section.entries.map(({ label, format, value }) => { + return ( +
+ {label} +
+ {formatFromMarkerSchema('moreInfo', format, value)} +
+
+ ); + })} +
+
+ ); + } + + _renderMoreInfo() { + return ( +
+ + + {this.state.showsMoreInfo ? 'Show Less' : 'Show More'} + + +
+ {this.props.profileExtraInfo.map(this._renderMoreInfoSection)} +
+
+ ); + } + render() { const { meta, profilerOverhead } = this.props.profile; const { configuration } = meta; @@ -403,6 +465,9 @@ class MetaInfoPanelImpl extends React.PureComponent { {profilerOverhead ? ( ) : null} + {this.props.profileExtraInfo.length !== 0 + ? this._renderMoreInfo() + : null} ); } @@ -444,6 +509,7 @@ export const MetaInfoPanel = explicitConnect<{||}, StateProps, DispatchProps>({ mapStateToProps: (state) => ({ profile: getProfile(state), symbolicationStatus: getSymbolicationStatus(state), + profileExtraInfo: getProfileExtraInfo(state), }), mapDispatchToProps: { resymbolicateProfile, diff --git a/src/selectors/profile.js b/src/selectors/profile.js index 3f756e549b..5b819ca7ab 100644 --- a/src/selectors/profile.js +++ b/src/selectors/profile.js @@ -74,6 +74,7 @@ import type { MarkerSchemaByName, SampleUnits, IndexIntoSamplesTable, + ExtraProfileInfoSection, } from 'firefox-profiler/types'; export const getProfileView: Selector = (state) => @@ -924,3 +925,6 @@ export const getProfileUsesMultipleStackTypes: Selector = (state) => { } return profile.meta.usesOnlyOneStackType !== true; }; + +export const getProfileExtraInfo: Selector = + createSelector(getProfile, (profile) => profile.meta.extra || []); diff --git a/src/test/components/MenuButtons.test.js b/src/test/components/MenuButtons.test.js index b202340db8..1d157b5eb5 100644 --- a/src/test/components/MenuButtons.test.js +++ b/src/test/components/MenuButtons.test.js @@ -517,6 +517,57 @@ describe('app/MenuButtons', function () { expect(getMetaInfoPanel()).toMatchSnapshot(); }); + it('with no extra info there is no more info button', async () => { + const { profile } = getProfileFromTextSamples('A'); + const { displayMetaInfoPanel } = await setupForMetaInfoPanel(profile); + await displayMetaInfoPanel(); + + expect(screen.queryByText('Show more')).not.toBeInTheDocument(); + }); + + it('with more extra info, opens more info section if clicked', async () => { + const { profile } = getProfileFromTextSamples('A'); + profile.meta.extra = [ + { + label: 'CPU', + entries: [ + { + label: 'CPU 1', + format: 'string', + value: 'Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz', + }, + ], + }, + { + label: 'Memory', + entries: [], + }, + { + label: 'Hard Drives', + entries: [ + { + label: 'SSD', + format: 'string', + value: 'Samsung SSD 850 EVO 500GB', + }, + { + label: 'HDD', + format: 'string', + value: 'Seagate ST1000LM035-1RK172', + }, + ], + }, + ]; + + const { displayMetaInfoPanel } = await setupForMetaInfoPanel(profile); + await displayMetaInfoPanel(); + + const summary = screen.getByText('Show more'); + fireFullClick(summary); + const moreInfoPart = document.querySelector('.moreInfoPart'); + expect(moreInfoPart).toMatchSnapshot(); + }); + describe('deleting a profile', () => { const FAKE_HASH = 'FAKE_HASH'; const FAKE_PROFILE_DATA = { diff --git a/src/test/components/__snapshots__/MenuButtons.test.js.snap b/src/test/components/__snapshots__/MenuButtons.test.js.snap index 783ac42320..e701b493b3 100644 --- a/src/test/components/__snapshots__/MenuButtons.test.js.snap +++ b/src/test/components/__snapshots__/MenuButtons.test.js.snap @@ -1132,6 +1132,87 @@ exports[`app/MenuButtons matches the snapshot with device inform `; +exports[`app/MenuButtons with more extra info, opens more info section if clicked 1`] = ` +
+
+

+ CPU +

+
+
+ + CPU 1 + +
+ Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz +
+
+
+
+
+

+ Memory +

+
+
+
+

+ Hard Drives +

+
+
+ + SSD + +
+ Samsung SSD 850 EVO 500GB +
+
+
+ + HDD + +
+ Seagate ST1000LM035-1RK172 +
+
+
+
+
+`; + exports[`app/MenuButtons with no statistics object should not make the app crash 1`] = `
, +|}; + /** * Meta information associated for the entire profile. */ @@ -872,6 +883,9 @@ export type ProfileMeta = {| doesNotUseFrameImplementation?: boolean, // Hide the "Look up the function name on Searchfox" menu entry? sourceCodeIsNotOnSearchfox?: boolean, + // Extra information about the profile, not shown in the "Profile Info" panel, + // but in the more info panel + extra?: ExtraProfileInfoSection[], |}; /**