commit ac3a9e1efa2105f04dc1ce06530cf313fe72196e parent e288e90522812552cf253b72357f962245da9157 Author: Irene Ni <ini@mozilla.com> Date: Fri, 7 Nov 2025 22:13:10 +0000 Bug 1996890 - Reset scroll to top when opening New Tab Manage topics and Wallpapers subpanels, and fix clipped content at smaller window height. r=home-newtab-reviewers,nbarrett,thecount Differential Revision: https://phabricator.services.mozilla.com/D271158 Diffstat:
10 files changed, 176 insertions(+), 78 deletions(-)
diff --git a/browser/extensions/newtab/content-src/components/CustomizeMenu/ContentSection/ContentSection.jsx b/browser/extensions/newtab/content-src/components/CustomizeMenu/ContentSection/ContentSection.jsx @@ -108,6 +108,7 @@ export class ContentSection extends React.PureComponent { setPref, mayHaveTopicSections, exitEventFired, + onSubpanelToggle, } = this.props; const { topSitesEnabled, @@ -128,6 +129,7 @@ export class ContentSection extends React.PureComponent { setPref={setPref} activeWallpaper={activeWallpaper} exitEventFired={exitEventFired} + onSubpanelToggle={onSubpanelToggle} /> </div> {/* If widgets section is visible, hide this divider */} @@ -330,6 +332,7 @@ export class ContentSection extends React.PureComponent { <SectionsMgmtPanel exitEventFired={exitEventFired} pocketEnabled={pocketEnabled} + onSubpanelToggle={onSubpanelToggle} /> )} </div> diff --git a/browser/extensions/newtab/content-src/components/CustomizeMenu/CustomizeMenu.jsx b/browser/extensions/newtab/content-src/components/CustomizeMenu/CustomizeMenu.jsx @@ -13,11 +13,17 @@ export class _CustomizeMenu extends React.PureComponent { super(props); this.onEntered = this.onEntered.bind(this); this.onExited = this.onExited.bind(this); + this.onSubpanelToggle = this.onSubpanelToggle.bind(this); this.state = { exitEventFired: false, + subpanelOpen: false, }; } + onSubpanelToggle(isOpen) { + this.setState({ subpanelOpen: isOpen }); + } + onEntered() { this.setState({ exitEventFired: false }); if (this.closeButton) { @@ -69,41 +75,46 @@ export class _CustomizeMenu extends React.PureComponent { onExited={this.onExited} appear={true} > - <div - className="customize-menu" - role="dialog" - data-l10n-id="newtab-settings-dialog-label" - > - <div className="close-button-wrapper"> - <moz-button - onClick={() => this.props.onClose()} - id="close-button" - type="icon ghost" - data-l10n-id="newtab-custom-close-menu-button" - iconsrc="chrome://global/skin/icons/close.svg" - ref={c => (this.closeButton = c)} - ></moz-button> + <div className="customize-menu-animate-wrapper"> + <div + className={`customize-menu ${ + this.state.subpanelOpen ? "subpanel-open" : "" + }`} + role="dialog" + data-l10n-id="newtab-settings-dialog-label" + > + <div className="close-button-wrapper"> + <moz-button + onClick={() => this.props.onClose()} + id="close-button" + type="icon ghost" + data-l10n-id="newtab-custom-close-menu-button" + iconsrc="chrome://global/skin/icons/close.svg" + ref={c => (this.closeButton = c)} + ></moz-button> + </div> + <ContentSection + openPreferences={this.props.openPreferences} + setPref={this.props.setPref} + enabledSections={this.props.enabledSections} + enabledWidgets={this.props.enabledWidgets} + wallpapersEnabled={this.props.wallpapersEnabled} + activeWallpaper={this.props.activeWallpaper} + pocketRegion={this.props.pocketRegion} + mayHaveTopicSections={this.props.mayHaveTopicSections} + mayHaveInferredPersonalization={ + this.props.mayHaveInferredPersonalization + } + mayHaveWeather={this.props.mayHaveWeather} + mayHaveTrendingSearch={this.props.mayHaveTrendingSearch} + mayHaveWidgets={this.props.mayHaveWidgets} + mayHaveTimerWidget={this.props.mayHaveTimerWidget} + mayHaveListsWidget={this.props.mayHaveListsWidget} + dispatch={this.props.dispatch} + exitEventFired={this.state.exitEventFired} + onSubpanelToggle={this.onSubpanelToggle} + /> </div> - <ContentSection - openPreferences={this.props.openPreferences} - setPref={this.props.setPref} - enabledSections={this.props.enabledSections} - enabledWidgets={this.props.enabledWidgets} - wallpapersEnabled={this.props.wallpapersEnabled} - activeWallpaper={this.props.activeWallpaper} - pocketRegion={this.props.pocketRegion} - mayHaveTopicSections={this.props.mayHaveTopicSections} - mayHaveInferredPersonalization={ - this.props.mayHaveInferredPersonalization - } - mayHaveWeather={this.props.mayHaveWeather} - mayHaveTrendingSearch={this.props.mayHaveTrendingSearch} - mayHaveWidgets={this.props.mayHaveWidgets} - mayHaveTimerWidget={this.props.mayHaveTimerWidget} - mayHaveListsWidget={this.props.mayHaveListsWidget} - dispatch={this.props.dispatch} - exitEventFired={this.state.exitEventFired} - /> </div> </CSSTransition> </span> diff --git a/browser/extensions/newtab/content-src/components/CustomizeMenu/SectionsMgmtPanel/SectionsMgmtPanel.jsx b/browser/extensions/newtab/content-src/components/CustomizeMenu/SectionsMgmtPanel/SectionsMgmtPanel.jsx @@ -8,7 +8,11 @@ import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; // eslint-disable-next-line no-shadow import { CSSTransition } from "react-transition-group"; -function SectionsMgmtPanel({ exitEventFired, pocketEnabled }) { +function SectionsMgmtPanel({ + exitEventFired, + pocketEnabled, + onSubpanelToggle, +}) { const [showPanel, setShowPanel] = useState(false); // State management with useState const { sectionPersonalization } = useSelector( state => state.DiscoveryStream @@ -174,6 +178,13 @@ function SectionsMgmtPanel({ exitEventFired, pocketEnabled }) { } }, [exitEventFired]); + // Notify parent menu when subpanel opens/closes + useEffect(() => { + if (onSubpanelToggle) { + onSubpanelToggle(showPanel); + } + }, [showPanel, onSubpanelToggle]); + const togglePanel = () => { setShowPanel(prevShowPanel => !prevShowPanel); diff --git a/browser/extensions/newtab/content-src/components/CustomizeMenu/_CustomizeMenu.scss b/browser/extensions/newtab/content-src/components/CustomizeMenu/_CustomizeMenu.scss @@ -93,7 +93,7 @@ } } -.customize-menu { +.customize-menu-animate-wrapper { color: var(--newtab-text-primary-color); background-color: var(--newtab-background-color-secondary); width: 432px; @@ -106,7 +106,6 @@ z-index: 1001; padding-block: 0 var(--space-large); padding-inline: var(--space-large); - overflow: auto; transform: translateX(435px); visibility: hidden; cursor: default; @@ -151,6 +150,15 @@ &.customize-animate-exit-active { box-shadow: $shadow-large; } +} + +.customize-menu { + overflow: auto; + height: 100%; + + &.subpanel-open { + overflow: clip; + } .close-button-wrapper { position: sticky; @@ -427,10 +435,7 @@ } .sections-mgmt-panel { - /* XXXdholbert The 32px subtraction here is to account for our 16px of - * margin on top and bottom. Ideally this should change to use - * 'height: stretch' when bug 1789477 lands. */ - height: calc(100% - var(--space-xxlarge)); + height: 100%; // Width of panel minus the margins inset-inline-start: var(--space-xlarge); position: absolute; @@ -441,6 +446,7 @@ margin-block: var(--space-large) 0; padding: 0; background-color: var(--newtab-background-color-secondary); + overflow-y: auto; &:dir(rtl) { transform: translateX(-100%); diff --git a/browser/extensions/newtab/content-src/components/WallpaperCategories/WallpaperCategories.jsx b/browser/extensions/newtab/content-src/components/WallpaperCategories/WallpaperCategories.jsx @@ -267,6 +267,11 @@ export class _WallpaperCategories extends React.PureComponent { this.handleUserEvent(at.WALLPAPER_CATEGORY_CLICK, event.target.id); + // Notify parent menu when subpanel opens + if (this.props.onSubpanelToggle) { + this.props.onSubpanelToggle(true); + } + let fluent_id; switch (event.target.id) { case "abstracts": @@ -363,6 +368,11 @@ export class _WallpaperCategories extends React.PureComponent { handleBack() { this.setState({ activeCategory: null }, () => { + // Notify parent menu when subpanel closes + if (this.props.onSubpanelToggle) { + this.props.onSubpanelToggle(false); + } + // Wait for the category grid to be back in the DOM requestAnimationFrame(() => { this.focusCategory(this.state.focusedCategoryIndex); diff --git a/browser/extensions/newtab/content-src/components/WallpaperCategories/_WallpaperCategories.scss b/browser/extensions/newtab/content-src/components/WallpaperCategories/_WallpaperCategories.scss @@ -208,17 +208,17 @@ gap: unset; grid-auto-rows: unset; grid-template-columns: unset; - - /* XXXdholbert The 32px subtraction here is to account for our 16px of - * margin on top and bottom. Ideally this should change to use - * 'height: stretch' when bug 1789477 lands. */ - height: calc(100% - var(--space-xlarge)); + height: 100%; + // Can get rid of padding and replace height with + // 'height: stretch' when bug 1789477 lands. + padding-block-end: var(--space-xxlarge); inset-inline-start: var(--space-xlarge); position: absolute; inset-block-start: 0; width: 380px; z-index: 2; transform: translateX(100%); + overflow-y: auto; &:dir(rtl) { transform: translateX(-100%); diff --git a/browser/extensions/newtab/css/activity-stream.css b/browser/extensions/newtab/css/activity-stream.css @@ -2064,7 +2064,7 @@ main section { max-width: 30ch; } -.customize-menu { +.customize-menu-animate-wrapper { color: var(--newtab-text-primary-color); background-color: var(--newtab-background-color-secondary); width: 432px; @@ -2076,35 +2076,42 @@ main section { z-index: 1001; padding-block: 0 var(--space-large); padding-inline: var(--space-large); - overflow: auto; transform: translateX(435px); visibility: hidden; cursor: default; } -.customize-menu.customize-animate-enter, .customize-menu.customize-animate-exit, .customize-menu.customize-animate-enter-done, .customize-menu.customize-animate-exit-done { +.customize-menu-animate-wrapper.customize-animate-enter, .customize-menu-animate-wrapper.customize-animate-exit, .customize-menu-animate-wrapper.customize-animate-enter-done, .customize-menu-animate-wrapper.customize-animate-exit-done { display: block; } @media (prefers-reduced-motion: no-preference) { - .customize-menu.customize-animate-enter, .customize-menu.customize-animate-exit, .customize-menu.customize-animate-enter-done, .customize-menu.customize-animate-exit-done { + .customize-menu-animate-wrapper.customize-animate-enter, .customize-menu-animate-wrapper.customize-animate-exit, .customize-menu-animate-wrapper.customize-animate-enter-done, .customize-menu-animate-wrapper.customize-animate-exit-done { transition: transform 250ms cubic-bezier(0.46, 0.03, 0.52, 0.96), visibility 250ms; } } @media (forced-colors: active) { - .customize-menu { + .customize-menu-animate-wrapper { border-inline-start: solid 1px; } } -.customize-menu:dir(rtl) { +.customize-menu-animate-wrapper:dir(rtl) { transform: translateX(-435px); } -.customize-menu.customize-animate-enter-done, .customize-menu.customize-animate-enter-active { +.customize-menu-animate-wrapper.customize-animate-enter-done, .customize-menu-animate-wrapper.customize-animate-enter-active { box-shadow: 0 2px 14px 0 rgba(0, 0, 0, 0.2); visibility: visible; transform: translateX(0); } -.customize-menu.customize-animate-exit-active { +.customize-menu-animate-wrapper.customize-animate-exit-active { box-shadow: 0 2px 14px 0 rgba(0, 0, 0, 0.2); } + +.customize-menu { + overflow: auto; + height: 100%; +} +.customize-menu.subpanel-open { + overflow: clip; +} .customize-menu .close-button-wrapper { position: sticky; inset-block-start: 0; @@ -2325,10 +2332,7 @@ main section { } .sections-mgmt-panel { - /* XXXdholbert The 32px subtraction here is to account for our 16px of - * margin on top and bottom. Ideally this should change to use - * 'height: stretch' when bug 1789477 lands. */ - height: calc(100% - var(--space-xxlarge)); + height: 100%; inset-inline-start: var(--space-xlarge); position: absolute; inset-block-start: 0; @@ -2338,6 +2342,7 @@ main section { margin-block: var(--space-large) 0; padding: 0; background-color: var(--newtab-background-color-secondary); + overflow-y: auto; } .sections-mgmt-panel:dir(rtl) { transform: translateX(-100%); @@ -2643,16 +2648,15 @@ main section { gap: unset; grid-auto-rows: unset; grid-template-columns: unset; - /* XXXdholbert The 32px subtraction here is to account for our 16px of - * margin on top and bottom. Ideally this should change to use - * 'height: stretch' when bug 1789477 lands. */ - height: calc(100% - var(--space-xlarge)); + height: 100%; + padding-block-end: var(--space-xxlarge); inset-inline-start: var(--space-xlarge); position: absolute; inset-block-start: 0; width: 380px; z-index: 2; transform: translateX(100%); + overflow-y: auto; } .wallpaper-list.category:dir(rtl) { transform: translateX(-100%); diff --git a/browser/extensions/newtab/data/content/activity-stream.bundle.js b/browser/extensions/newtab/data/content/activity-stream.bundle.js @@ -13947,7 +13947,8 @@ function SectionsMgmtPanel_extends() { return SectionsMgmtPanel_extends = Object function SectionsMgmtPanel({ exitEventFired, - pocketEnabled + pocketEnabled, + onSubpanelToggle }) { const [showPanel, setShowPanel] = (0,external_React_namespaceObject.useState)(false); // State management with useState const { @@ -14066,6 +14067,13 @@ function SectionsMgmtPanel({ setShowPanel(false); } }, [exitEventFired]); + + // Notify parent menu when subpanel opens/closes + (0,external_React_namespaceObject.useEffect)(() => { + if (onSubpanelToggle) { + onSubpanelToggle(showPanel); + } + }, [showPanel, onSubpanelToggle]); const togglePanel = () => { setShowPanel(prevShowPanel => !prevShowPanel); @@ -14385,6 +14393,11 @@ class _WallpaperCategories extends (external_React_default()).PureComponent { activeCategory: event.target.id }); this.handleUserEvent(actionTypes.WALLPAPER_CATEGORY_CLICK, event.target.id); + + // Notify parent menu when subpanel opens + if (this.props.onSubpanelToggle) { + this.props.onSubpanelToggle(true); + } let fluent_id; switch (event.target.id) { case "abstracts": @@ -14482,6 +14495,11 @@ class _WallpaperCategories extends (external_React_default()).PureComponent { this.setState({ activeCategory: null }, () => { + // Notify parent menu when subpanel closes + if (this.props.onSubpanelToggle) { + this.props.onSubpanelToggle(false); + } + // Wait for the category grid to be back in the DOM requestAnimationFrame(() => { this.focusCategory(this.state.focusedCategoryIndex); @@ -14862,7 +14880,8 @@ class ContentSection extends (external_React_default()).PureComponent { activeWallpaper, setPref, mayHaveTopicSections, - exitEventFired + exitEventFired, + onSubpanelToggle } = this.props; const { topSitesEnabled, @@ -14883,7 +14902,8 @@ class ContentSection extends (external_React_default()).PureComponent { }, /*#__PURE__*/external_React_default().createElement(WallpaperCategories, { setPref: setPref, activeWallpaper: activeWallpaper, - exitEventFired: exitEventFired + exitEventFired: exitEventFired, + onSubpanelToggle: onSubpanelToggle })), !mayHaveWidgets && /*#__PURE__*/external_React_default().createElement("span", { className: "divider", role: "separator" @@ -15041,7 +15061,8 @@ class ContentSection extends (external_React_default()).PureComponent { "data-l10n-id": "newtab-custom-stories-personalized-checkbox-label" })), mayHaveTopicSections && /*#__PURE__*/external_React_default().createElement(SectionsMgmtPanel, { exitEventFired: exitEventFired, - pocketEnabled: pocketEnabled + pocketEnabled: pocketEnabled, + onSubpanelToggle: onSubpanelToggle }))))))), /*#__PURE__*/external_React_default().createElement("span", { className: "divider", role: "separator" @@ -15068,10 +15089,17 @@ class _CustomizeMenu extends (external_React_default()).PureComponent { super(props); this.onEntered = this.onEntered.bind(this); this.onExited = this.onExited.bind(this); + this.onSubpanelToggle = this.onSubpanelToggle.bind(this); this.state = { - exitEventFired: false + exitEventFired: false, + subpanelOpen: false }; } + onSubpanelToggle(isOpen) { + this.setState({ + subpanelOpen: isOpen + }); + } onEntered() { this.setState({ exitEventFired: false @@ -15117,7 +15145,9 @@ class _CustomizeMenu extends (external_React_default()).PureComponent { onExited: this.onExited, appear: true }, /*#__PURE__*/external_React_default().createElement("div", { - className: "customize-menu", + className: "customize-menu-animate-wrapper" + }, /*#__PURE__*/external_React_default().createElement("div", { + className: `customize-menu ${this.state.subpanelOpen ? "subpanel-open" : ""}`, role: "dialog", "data-l10n-id": "newtab-settings-dialog-label" }, /*#__PURE__*/external_React_default().createElement("div", { @@ -15145,8 +15175,9 @@ class _CustomizeMenu extends (external_React_default()).PureComponent { mayHaveTimerWidget: this.props.mayHaveTimerWidget, mayHaveListsWidget: this.props.mayHaveListsWidget, dispatch: this.props.dispatch, - exitEventFired: this.state.exitEventFired - })))); + exitEventFired: this.state.exitEventFired, + onSubpanelToggle: this.onSubpanelToggle + }))))); } } const CustomizeMenu = (0,external_ReactRedux_namespaceObject.connect)(state => ({ diff --git a/browser/extensions/newtab/test/browser/browser_customize_menu_content.js b/browser/extensions/newtab/test/browser/browser_customize_menu_content.js @@ -57,7 +57,7 @@ test_newtab({ await ContentTaskUtils.waitForCondition( () => content.getComputedStyle( - content.document.querySelector(".customize-menu") + content.document.querySelector(".customize-menu-animate-wrapper") ).transform === defaultPos, "Customize Menu should be visible on screen" ); @@ -150,7 +150,7 @@ test_newtab({ await ContentTaskUtils.waitForCondition( () => content.getComputedStyle( - content.document.querySelector(".customize-menu") + content.document.querySelector(".customize-menu-animate-wrapper") ).transform === defaultPos, "Customize Menu should be visible on screen" ); @@ -201,7 +201,7 @@ test_newtab({ await ContentTaskUtils.waitForCondition( () => content.getComputedStyle( - content.document.querySelector(".customize-menu") + content.document.querySelector(".customize-menu-animate-wrapper") ).transform === defaultPos, "Customize Menu should be visible on screen" ); @@ -225,7 +225,7 @@ test_newtab({ await ContentTaskUtils.waitForCondition( () => content.getComputedStyle( - content.document.querySelector(".customize-menu") + content.document.querySelector(".customize-menu-animate-wrapper") ).transform !== defaultPos, "Customize Menu should not be visible anymore" ); @@ -249,7 +249,7 @@ test_newtab({ await ContentTaskUtils.waitForCondition( () => content.getComputedStyle( - content.document.querySelector(".customize-menu") + content.document.querySelector(".customize-menu-animate-wrapper") ).transform === defaultPos, "Customize Menu should be visible on screen now" ); @@ -259,7 +259,7 @@ test_newtab({ await ContentTaskUtils.waitForCondition( () => content.getComputedStyle( - content.document.querySelector(".customize-menu") + content.document.querySelector(".customize-menu-animate-wrapper") ).transform !== defaultPos, "Customize Menu should not be visible anymore" ); @@ -269,7 +269,7 @@ test_newtab({ await ContentTaskUtils.waitForCondition( () => content.getComputedStyle( - content.document.querySelector(".customize-menu") + content.document.querySelector(".customize-menu-animate-wrapper") ).transform === defaultPos, "Customize Menu should be visible on screen now" ); @@ -280,7 +280,7 @@ test_newtab({ await ContentTaskUtils.waitForCondition( () => content.getComputedStyle( - content.document.querySelector(".customize-menu") + content.document.querySelector(".customize-menu-animate-wrapper") ).transform !== defaultPos, "Customize Menu should not be visible anymore" ); diff --git a/browser/extensions/newtab/test/unit/content-src/components/CustomizeMenu/CustomizeMenu.test.jsx b/browser/extensions/newtab/test/unit/content-src/components/CustomizeMenu/CustomizeMenu.test.jsx @@ -137,4 +137,26 @@ describe("<CustomizeMenu>", () => { listsEnabled: true, }); }); + + it("adds subpanel-open class when onSubpanelToggle is called", () => { + wrapper = mount( + <WrapWithProvider> + <CustomizeMenu {...DEFAULT_PROPS} showing={true} /> + </WrapWithProvider> + ); + + const instance = wrapper.find("_CustomizeMenu").instance(); + + instance.onSubpanelToggle(true); + wrapper.update(); + + const menu = wrapper.find(".customize-menu").hostNodes(); + assert.isTrue(menu.hasClass("subpanel-open")); + + instance.onSubpanelToggle(false); + wrapper.update(); + + const menuAfter = wrapper.find(".customize-menu").hostNodes(); + assert.isFalse(menuAfter.hasClass("subpanel-open")); + }); });