summaryrefslogtreecommitdiffstats
path: root/browser/components/newtab/content-src/components/CollapsibleSection
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/newtab/content-src/components/CollapsibleSection')
-rw-r--r--browser/components/newtab/content-src/components/CollapsibleSection/CollapsibleSection.jsx116
-rw-r--r--browser/components/newtab/content-src/components/CollapsibleSection/_CollapsibleSection.scss106
2 files changed, 222 insertions, 0 deletions
diff --git a/browser/components/newtab/content-src/components/CollapsibleSection/CollapsibleSection.jsx b/browser/components/newtab/content-src/components/CollapsibleSection/CollapsibleSection.jsx
new file mode 100644
index 0000000000..679e8e137f
--- /dev/null
+++ b/browser/components/newtab/content-src/components/CollapsibleSection/CollapsibleSection.jsx
@@ -0,0 +1,116 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import { ErrorBoundary } from "content-src/components/ErrorBoundary/ErrorBoundary";
+import { FluentOrText } from "content-src/components/FluentOrText/FluentOrText";
+import React from "react";
+import { connect } from "react-redux";
+
+/**
+ * A section that can collapse. As of bug 1710937, it can no longer collapse.
+ * See bug 1727365 for follow-up work to simplify this component.
+ */
+export class _CollapsibleSection extends React.PureComponent {
+ constructor(props) {
+ super(props);
+ this.onBodyMount = this.onBodyMount.bind(this);
+ this.onMenuButtonMouseEnter = this.onMenuButtonMouseEnter.bind(this);
+ this.onMenuButtonMouseLeave = this.onMenuButtonMouseLeave.bind(this);
+ this.onMenuUpdate = this.onMenuUpdate.bind(this);
+ this.state = {
+ menuButtonHover: false,
+ showContextMenu: false,
+ };
+ this.setContextMenuButtonRef = this.setContextMenuButtonRef.bind(this);
+ }
+
+ setContextMenuButtonRef(element) {
+ this.contextMenuButtonRef = element;
+ }
+
+ onBodyMount(node) {
+ this.sectionBody = node;
+ }
+
+ onMenuButtonMouseEnter() {
+ this.setState({ menuButtonHover: true });
+ }
+
+ onMenuButtonMouseLeave() {
+ this.setState({ menuButtonHover: false });
+ }
+
+ onMenuUpdate(showContextMenu) {
+ this.setState({ showContextMenu });
+ }
+
+ render() {
+ const { isAnimating, maxHeight, menuButtonHover, showContextMenu } =
+ this.state;
+ const { id, collapsed, learnMore, title, subTitle } = this.props;
+ const active = menuButtonHover || showContextMenu;
+ let bodyStyle;
+ if (isAnimating && !collapsed) {
+ bodyStyle = { maxHeight };
+ } else if (!isAnimating && collapsed) {
+ bodyStyle = { display: "none" };
+ }
+ let titleStyle;
+ if (this.props.hideTitle) {
+ titleStyle = { visibility: "hidden" };
+ }
+ const hasSubtitleClassName = subTitle ? `has-subtitle` : ``;
+ return (
+ <section
+ className={`collapsible-section ${this.props.className}${
+ active ? " active" : ""
+ }`}
+ // Note: data-section-id is used for web extension api tests in mozilla central
+ data-section-id={id}
+ >
+ <div className="section-top-bar">
+ <h3
+ className={`section-title-container ${hasSubtitleClassName}`}
+ style={titleStyle}
+ >
+ <span className="section-title">
+ <FluentOrText message={title} />
+ </span>
+ <span className="learn-more-link-wrapper">
+ {learnMore && (
+ <span className="learn-more-link">
+ <FluentOrText message={learnMore.link.message}>
+ <a href={learnMore.link.href} />
+ </FluentOrText>
+ </span>
+ )}
+ </span>
+ {subTitle && (
+ <span className="section-sub-title">
+ <FluentOrText message={subTitle} />
+ </span>
+ )}
+ </h3>
+ </div>
+ <ErrorBoundary className="section-body-fallback">
+ <div ref={this.onBodyMount} style={bodyStyle}>
+ {this.props.children}
+ </div>
+ </ErrorBoundary>
+ </section>
+ );
+ }
+}
+
+_CollapsibleSection.defaultProps = {
+ document: global.document || {
+ addEventListener: () => {},
+ removeEventListener: () => {},
+ visibilityState: "hidden",
+ },
+};
+
+export const CollapsibleSection = connect(state => ({
+ Prefs: state.Prefs,
+}))(_CollapsibleSection);
diff --git a/browser/components/newtab/content-src/components/CollapsibleSection/_CollapsibleSection.scss b/browser/components/newtab/content-src/components/CollapsibleSection/_CollapsibleSection.scss
new file mode 100644
index 0000000000..9811339b27
--- /dev/null
+++ b/browser/components/newtab/content-src/components/CollapsibleSection/_CollapsibleSection.scss
@@ -0,0 +1,106 @@
+.collapsible-section {
+ padding: $section-vertical-padding $section-horizontal-padding;
+
+ .section-title-container {
+ margin: 0;
+
+ &.has-subtitle {
+ display: flex;
+ flex-direction: column;
+
+ @media (min-width: $break-point-large) {
+ flex-direction: row;
+ align-items: baseline;
+ justify-content: space-between;
+ }
+ }
+ }
+
+ .section-title {
+ font-size: $section-title-font-size;
+ font-weight: 600;
+ color: var(--newtab-text-primary-color);
+
+ &.grey-title {
+ color: var(--newtab-text-primary-color);
+ display: inline-block;
+ fill: var(--newtab-text-primary-color);
+ vertical-align: middle;
+ }
+
+ .section-title-contents {
+ // Center "What's Pocket?" for "mobile" viewport
+ @media (max-width: $break-point-medium - 1) {
+ display: block;
+
+ .learn-more-link-wrapper {
+ display: block;
+ text-align: center;
+
+ .learn-more-link {
+ margin-inline-start: 0;
+ }
+ }
+ }
+
+ vertical-align: top;
+ }
+ }
+
+ .section-sub-title {
+ font-size: 14px;
+ line-height: 16px;
+ color: var(--newtab-text-secondary-color);
+ opacity: 0.3;
+ }
+
+ .section-top-bar {
+ min-height: 19px;
+ margin-bottom: 13px;
+ position: relative;
+ }
+
+ &.active {
+ background: var(--newtab-element-hover-color);
+ border-radius: 4px;
+ }
+
+ .learn-more-link {
+ font-size: 13px;
+ margin-inline-start: 12px;
+
+ a {
+ color: var(--newtab-primary-action-background);
+ }
+ }
+
+ .section-body-fallback {
+ height: $card-height;
+ }
+
+ .section-body {
+ // This is so the top sites favicon and card dropshadows don't get clipped during animation:
+ $horizontal-padding: 7px;
+
+ margin: 0 (-$horizontal-padding);
+ padding: 0 $horizontal-padding;
+
+ &.animating {
+ overflow: hidden;
+ pointer-events: none;
+ }
+ }
+
+ &[data-section-id='topsites'] {
+ .section-top-bar {
+ display: none;
+ }
+ }
+
+ // Hide first story card for the medium breakpoint to prevent orphaned third story
+ &[data-section-id='topstories'] .card-outer:first-child {
+ @media (min-width: $break-point-medium) and (max-width: $break-point-large - 1) {
+ display: none;
+ }
+ }
+}