Bug 1890136 - Add weather widget component r=home-newtab-reviewers,omc-reviewers,aminomancer,thecount

This commit adds the component - including markup, logic and styles for the weather widget on the New Tab page

Differential Revision: https://phabricator.services.mozilla.com/D208885
This commit is contained in:
Maxx Crawford 2024-05-09 22:11:57 +00:00
parent 58428bed84
commit 1a188ffcb2
16 changed files with 2079 additions and 6 deletions

View file

@ -124,6 +124,9 @@
.icon.icon-info {
background-image: url("chrome://global/skin/icons/info.svg");
}
.icon.icon-info-critical {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-info-critical-16.svg");
}
.icon.icon-help {
background-image: url("chrome://global/skin/icons/help.svg");
}
@ -204,6 +207,9 @@
.icon.icon-webextension {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-webextension-16.svg");
}
.icon.icon-weather {
background-image: url("chrome://browser/skin/weather/sunny.svg");
}
.icon.icon-highlights {
background-image: url("chrome://global/skin/icons/highlights.svg");
}

View file

@ -12,6 +12,7 @@ import { CustomizeMenu } from "content-src/components/CustomizeMenu/CustomizeMen
import React from "react";
import { Search } from "content-src/components/Search/Search";
import { Sections } from "content-src/components/Sections/Sections";
import { Weather } from "content-src/components/Weather/Weather";
const VISIBLE = "visible";
const VISIBILITY_CHANGE_EVENT = "visibilitychange";
@ -278,6 +279,15 @@ export class BaseContent extends React.PureComponent {
`--newtab-wallpaper-dark`,
`url(${darkWallpaper?.wallpaperUrl || ""})`
);
// Add helper class to body if user has a wallpaper selected
if (lightWallpaper) {
global.document?.body.classList.add("hasWallpaperLight");
}
if (darkWallpaper) {
global.document?.body.classList.add("hasWallpaperDark");
}
}
}
@ -290,6 +300,7 @@ export class BaseContent extends React.PureComponent {
const activeWallpaper =
prefs[`newtabWallpapers.wallpaper-${this.state.colorMode}`];
const wallpapersEnabled = prefs["newtabWallpapers.enabled"];
const weatherEnabled = prefs.showWeather;
const { pocketConfig } = prefs;
@ -396,6 +407,13 @@ export class BaseContent extends React.PureComponent {
<ConfirmDialog />
{wallpapersEnabled && this.renderWallpaperAttribution()}
</main>
<aside>
{weatherEnabled && (
<ErrorBoundary>
<Weather />
</ErrorBoundary>
)}
</aside>
</div>
</div>
);

View file

@ -0,0 +1,213 @@
/* 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 { connect } from "react-redux";
import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
import React from "react";
export class _Weather extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
contextMenuKeyboard: false,
showContextMenu: false,
url: "https://example.com",
};
this.onClick = this.onClick.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
this.onUpdate = this.onUpdate.bind(this);
}
openContextMenu(isKeyBoard) {
if (this.props.onUpdate) {
this.props.onUpdate(true);
}
this.setState({
showContextMenu: true,
contextMenuKeyboard: isKeyBoard,
});
}
onClick(event) {
event.preventDefault();
this.openContextMenu(false, event);
}
onKeyDown(event) {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
this.openContextMenu(true, event);
}
}
onUpdate(showContextMenu) {
if (this.props.onUpdate) {
this.props.onUpdate(showContextMenu);
}
this.setState({ showContextMenu });
}
render() {
// Check if weather should be rendered
const isWeatherEnabled = this.props.Prefs.values["system.showWeather"];
if (!isWeatherEnabled) {
return false;
}
const { showContextMenu } = this.state;
const WEATHER_SUGGESTION = this.props.Weather.suggestions?.[0];
const {
className,
index,
dispatch,
eventSource,
shouldSendImpressionStats,
} = this.props;
const { props } = this;
const isContextMenuOpen = this.state.activeCard === index;
const outerClassName = [
"weather",
className,
isContextMenuOpen && "active",
props.placeholder && "placeholder",
]
.filter(v => v)
.join(" ");
const showDetailedView =
this.props.Prefs.values["weather.display"] === "detailed";
// Note: The temperature units/display options will become secondary menu items
const WEATHER_SOURCE_CONTEXT_MENU_OPTIONS = [
...(this.props.Prefs.values["weather.locationSearchEnabled"]
? ["ChangeWeatherLocation"]
: []),
...(this.props.Prefs.values["weather.temperatureUnits"] === "f"
? ["ChangeTempUnitCelsius"]
: ["ChangeTempUnitFahrenheit"]),
...(this.props.Prefs.values["weather.display"] === "simple"
? ["ChangeWeatherDisplayDetailed"]
: ["ChangeWeatherDisplaySimple"]),
"HideWeather",
"OpenLearnMoreURL",
];
// Only return the widget if we have data. Otherwise, show error state
if (WEATHER_SUGGESTION) {
return (
<div className={outerClassName}>
<div className="weatherCard">
<a
data-l10n-id="newtab-weather-see-forecast"
data-l10n-args='{"provider": "AccuWeather"}'
href={WEATHER_SUGGESTION.forecast.url}
className="weatherInfoLink"
>
<div className="weatherIconCol">
<span
className={`weatherIcon iconId${WEATHER_SUGGESTION.current_conditions.icon_id}`}
/>
</div>
<div className="weatherText">
<div className="weatherForecastRow">
<span className="weatherTemperature">
{
WEATHER_SUGGESTION.current_conditions.temperature[
this.props.Prefs.values["weather.temperatureUnits"]
]
}
&deg;{this.props.Prefs.values["weather.temperatureUnits"]}
</span>
</div>
<div className="weatherCityRow">
<span className="weatherCity">
{WEATHER_SUGGESTION.city_name}
</span>
</div>
{showDetailedView ? (
<div className="weatherDetailedSummaryRow">
<div className="weatherHighLowTemps">
{/* Low Forecasted Temperature */}
<span>
{
WEATHER_SUGGESTION.forecast.high[
this.props.Prefs.values["weather.temperatureUnits"]
]
}
&deg;
{this.props.Prefs.values["weather.temperatureUnits"]}
</span>
{/* Spacer / Bullet */}
<span>&bull;</span>
{/* Low Forecasted Temperature */}
<span>
{
WEATHER_SUGGESTION.forecast.low[
this.props.Prefs.values["weather.temperatureUnits"]
]
}
&deg;
{this.props.Prefs.values["weather.temperatureUnits"]}
</span>
</div>
<span className="weatherTextSummary">
{WEATHER_SUGGESTION.current_conditions.summary}
</span>
</div>
) : null}
</div>
</a>
<div className="weatherButtonContextMenuWrapper">
<button
aria-haspopup="true"
onKeyDown={this.onKeyDown}
onClick={this.onClick}
data-l10n-id="newtab-menu-section-tooltip"
className="weatherButtonContextMenu"
>
{showContextMenu ? (
<LinkMenu
dispatch={dispatch}
index={index}
source={eventSource}
onUpdate={this.onUpdate}
options={WEATHER_SOURCE_CONTEXT_MENU_OPTIONS}
site={{
url: "https://support.mozilla.org/kb/customize-items-on-firefox-new-tab-page",
}}
link="https://support.mozilla.org/kb/customize-items-on-firefox-new-tab-page"
shouldSendImpressionStats={shouldSendImpressionStats}
/>
) : null}
</button>
</div>
</div>
<span
data-l10n-id="newtab-weather-sponsored"
data-l10n-args='{"provider": "AccuWeather"}'
className="weatherSponsorText"
></span>
</div>
);
}
return (
<div className={outerClassName}>
<div className="weatherNotAvailable">
<span className="icon icon-small-spacer icon-info-critical" />{" "}
<span data-l10n-id="newtab-weather-error-not-available"></span>
</div>
</div>
);
}
}
export const Weather = connect(state => ({
Weather: state.Weather,
Prefs: state.Prefs,
}))(_Weather);

View file

@ -0,0 +1,393 @@
// Custom font sizing for weather widget
:root {
--newtab-weather-content-font-size: 11px;
--newtab-weather-sponsor-font-size: 8px;
}
.weather {
font-size: var(--font-size-root);
position: absolute;
left: var(--space-xlarge);
top: var(--space-xlarge);
z-index: 1;
}
// Unavailable / Error State
.weatherNotAvailable {
font-size: var(--newtab-weather-content-font-size);
color: var(--text-color-error);
display: flex;
align-items: center;
.icon {
fill: var(--icon-color-critical);
-moz-context-properties: fill;
}
}
.weatherCard {
margin-block-end: var(--space-xsmall);
display: flex;
flex-wrap: nowrap;
align-items: stretch;
border-radius: var(--border-radius-medium);
overflow: hidden;
&:hover, &:focus-within {
~ .weatherSponsorText {
visibility: visible;
}
}
&:focus-within {
overflow: visible;
}
&:hover {
box-shadow: var(--box-shadow-10);
background: var(--background-color-box);
}
a {
color: var(--text-color);
}
}
.weatherSponsorText {
visibility: hidden;
font-size: var(--newtab-weather-sponsor-font-size);
color: var(--text-color-deemphasized);
}
.weatherInfoLink, .weatherButtonContextMenuWrapper {
appearance: none;
background-color: var(--background-color-ghost);
border: 0;
padding: var(--space-small);
cursor: pointer;
&:hover {
// TODO: Add Wallpaper Background Color Fix
background-color: var(--button-background-color-ghost-hover);
&::after {
background-color: transparent
}
&:active {
// TODO: Add Wallpaper Background Color Fix
background-color: var(--button-background-color-ghost-active);
}
}
&:focus-visible {
outline: var(--focus-outline);
}
// Contrast fix for users who have wallpapers set
.hasWallpaperDark & {
@media (prefers-color-scheme: dark) {
// TODO: Replace with token
background-color: rgba(35, 34, 43, 70%);
&:hover {
background-color: var(--newtab-button-static-hover-background);
}
&:hover:active {
background-color: var(--newtab-button-static-active-background);
}
}
@media (prefers-contrast) and (prefers-color-scheme: dark) {
background-color: var(--background-color-box);
}
}
.hasWallpaperLight & {
@media (prefers-color-scheme: light) {
// TODO: Replace with token
background-color: rgba(255, 255, 255, 70%);
&:hover {
background-color: var(--newtab-button-static-hover-background);
}
&:hover:active {
background-color: var(--newtab-button-static-active-background);
}
}
@media (prefers-contrast) and (prefers-color-scheme: light) {
background-color: var(--background-color-box);
}
}
}
.weatherInfoLink {
display: flex;
gap: var(--space-medium);
padding: var(--space-small) var(--space-medium);
border-radius: var(--border-radius-medium) 0 0 var(--border-radius-medium);
text-decoration: none;
color: var(--text-color);;
min-width: 130px;
max-width: 190px;
text-overflow: ellipsis;
@media(min-width: $break-point-medium) {
min-width: unset;
}
&:hover ~.weatherButtonContextMenuWrapper {
&::after {
background-color: transparent
}
}
&:focus-visible {
border-radius: var(--border-radius-medium);
~ .weatherButtonContextMenuWrapper {
&::after {
background-color: transparent
}
}
}
}
.weatherButtonContextMenuWrapper {
position: relative;
cursor: pointer;
border-radius: 0 var(--border-radius-medium) var(--border-radius-medium) 0;
display: flex;
align-items: stretch;
width: 50px;
padding: 0;
&::after {
content: '';
left: 0;
top: 10px;
height: calc(100% - 20px);
width: 1px;
background-color: var(--newtab-button-static-background);
display: block;
position: absolute;
z-index: 0;
}
@media (prefers-color-scheme: dark) {
&::after {
background-color: var(--color-gray-70);
}
}
&:hover {
&::after {
background-color: transparent
}
}
&:focus-visible {
border-radius: var(--border-radius-medium);
&::after {
background-color: transparent
}
}
}
.weatherButtonContextMenu {
background-image: url('chrome://global/skin/icons/more.svg');
background-repeat: no-repeat;
background-size: var(--size-item-small) auto;
background-position: center;
background-color: transparent;
cursor: pointer;
fill: var(--icon-color);
-moz-context-properties: fill;
width: 100%;
height: 100%;
border: 0;
appearance: none;
min-width: var(--size-item-large);
}
.weatherText {
height: min-content;
}
.weatherCityRow, .weatherForecastRow, .weatherDetailedSummaryRow {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--space-small);
}
.weatherForecastRow {
text-transform: uppercase;
font-weight: var(--font-weight-bold);
}
.weatherCityRow {
color: var(--text-color-deemphasized);
}
.weatherCity {
text-overflow: ellipsis;
font-size: var(--font-size-small);
}
// Add additional margin if detailed summary is in view
.weatherCityRow + .weatherDetailedSummaryRow {
margin-block-start: var(--space-xsmall);
}
.weatherDetailedSummaryRow {
font-size: var(--newtab-weather-content-font-size);
gap: var(--space-large);
}
.weatherHighLowTemps {
display: flex;
gap: var(--space-xxsmall);
text-transform: uppercase;
word-spacing: var(--space-xxsmall);
}
.weatherTextSummary {
text-align: center;
max-width: 90px;
}
.weatherTemperature {
font-size: var(--font-size-large);
}
// Weather Symbol Icons
.weatherIconCol {
width: var(--size-item-large);
height: var(--size-item-large);
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
align-self: center;
}
.weatherIcon {
width: var(--size-item-large);
height: auto;
vertical-align: middle;
@media (prefers-contrast) {
-moz-context-properties: fill, stroke;
fill: currentColor;
stroke: currentColor;
}
&.iconId1 {
content: url('chrome://browser/skin/weather/sunny.svg');
// height: var(--size-item-large);
}
&.iconId2 {
content: url('chrome://browser/skin/weather/mostly-sunny.svg');
// height: var(--size-item-large);
}
&:is(.iconId3, .iconId4, .iconId6) {
content: url('chrome://browser/skin/weather/partly-sunny.svg');
// height: var(--size-item-large);
}
&.iconId5 {
content: url('chrome://browser/skin/weather/hazy-sunshine.svg');
// height: var(--size-item-large);
}
&:is(.iconId7, .iconId8) {
content: url('chrome://browser/skin/weather/cloudy.svg');
}
&.iconId11 {
content: url('chrome://browser/skin/weather/fog.svg');
}
&.iconId12 {
content: url('chrome://browser/skin/weather/showers.svg');
}
&:is(.iconId13, .iconId14) {
content: url('chrome://browser/skin/weather/mostly-cloudy-with-showers.svg');
// height: var(--size-item-large);
}
&.iconId15 {
content: url('chrome://browser/skin/weather/thunderstorms.svg');
}
&:is(.iconId16, .iconId17) {
content: url('chrome://browser/skin/weather/mostly-cloudy-with-thunderstorms.svg');
}
&.iconId18 {
content: url('chrome://browser/skin/weather/rain.svg');
}
&:is(.iconId19, .iconId20, .iconId25) {
content: url('chrome://browser/skin/weather/flurries.svg');
}
&.iconId21 {
content: url('chrome://browser/skin/weather/partly-sunny-with-flurries.svg');
}
&:is(.iconId22, .iconId23) {
content: url('chrome://browser/skin/weather/snow.svg');
}
&:is(.iconId24, .iconId31) {
content: url('chrome://browser/skin/weather/ice.svg');
}
&:is(.iconId26, .iconId29) {
content: url('chrome://browser/skin/weather/freezing-rain.svg');
}
&.iconId30 {
content: url('chrome://browser/skin/weather/hot.svg');
}
&.iconId32 {
content: url('chrome://browser/skin/weather/windy.svg');
}
&.iconId33 {
content: url('chrome://browser/skin/weather/night-clear.svg');
}
&:is(.iconId34, .iconId35, .iconId36, .iconId38) {
content: url('chrome://browser/skin/weather/night-mostly-clear.svg');
}
&.iconId37 {
content: url('chrome://browser/skin/weather/night-hazy-moonlight.svg');
}
&:is(.iconId39, .iconId40) {
content: url('chrome://browser/skin/weather/night-partly-cloudy-with-showers.svg');
height: var(--size-item-large);
}
&:is(.iconId41, .iconId42) {
content: url('chrome://browser/skin/weather/night-partly-cloudy-with-thunderstorms.svg');
}
&:is(.iconId43, .iconId44) {
content: url('chrome://browser/skin/weather/night-mostly-cloudy-with-flurries.svg');
}
}

View file

@ -306,4 +306,100 @@ export const LinkMenuOptions = {
: LinkMenuOptions.EmptyItem(),
OpenInPrivateWindow: (site, index, eventSource, isEnabled) =>
isEnabled ? _OpenInPrivateWindow(site) : LinkMenuOptions.EmptyItem(),
ChangeWeatherLocation: () => ({
// type: "empty",
id: "newtab-weather-menu-change-location",
// icon: "search",
action: ac.OnlyToMain({
type: at.CHANGE_WEATHER_LOCATION,
data: { url: "https://mozilla.org" },
}),
}),
OpenWeatherDisplayMenu: () => ({
id: "newtab-weather-menu-weather-display",
// type: "empty",
// icon: "search",
action: ac.OnlyToMain({
type: at.OPEN_WEATHER_DISPLAY_MENU,
data: { url: "https://mozilla.org" },
}),
}),
ChangeWeatherDisplaySimple: () => ({
id: "newtab-weather-menu-change-weather-display-simple",
// type: "empty",
// icon: "search",
action: ac.OnlyToMain({
type: at.SET_PREF,
data: {
name: "weather.display",
value: "simple",
},
}),
}),
ChangeWeatherDisplayDetailed: () => ({
id: "newtab-weather-menu-change-weather-display-detailed",
// type: "empty",
// icon: "search",
action: ac.OnlyToMain({
type: at.SET_PREF,
data: {
name: "weather.display",
value: "detailed",
},
}),
}),
OpenChangeTemperatureUnits: () => ({
id: "newtab-weather-menu-temperature-units",
// type: "empty",
// icon: "search",
action: ac.OnlyToMain({
type: at.OPEN_CHANGE_TEMPERATURE_UNITS,
data: { url: "https://mozilla.org" },
}),
}),
ChangeTempUnitFahrenheit: () => ({
id: "newtab-weather-menu-change-temperature-units-fahrenheit",
// type: "empty",
// icon: "search",
action: ac.OnlyToMain({
type: at.SET_PREF,
data: {
name: "weather.temperatureUnits",
value: "f",
},
}),
}),
ChangeTempUnitCelsius: () => ({
id: "newtab-weather-menu-change-temperature-units-celsius",
// type: "empty",
// icon: "search",
action: ac.OnlyToMain({
type: at.SET_PREF,
data: {
name: "weather.temperatureUnits",
value: "c",
},
}),
}),
HideWeather: () => ({
id: "newtab-weather-menu-hide-weather",
// type: "empty",
// icon: "search",
action: ac.OnlyToMain({
type: at.SET_PREF,
data: {
name: "showWeather",
value: false,
},
}),
}),
OpenLearnMoreURL: site => ({
id: "newtab-weather-menu-learn-more",
// type: "empty",
// icon: "search",
action: ac.OnlyToMain({
type: at.OPEN_LINK,
data: { url: site.url },
}),
}),
};

View file

@ -149,6 +149,7 @@ input {
@import '../components/ConfirmDialog/ConfirmDialog';
@import '../components/CustomizeMenu/CustomizeMenu';
@import '../components/WallpapersSection/WallpapersSection';
@import '../components/Weather/Weather';
@import '../components/Card/Card';
@import '../components/CollapsibleSection/CollapsibleSection';
@import '../components/DiscoveryStreamAdmin/DiscoveryStreamAdmin';

View file

@ -70,6 +70,10 @@
background-image: url('chrome://global/skin/icons/info.svg');
}
&.icon-info-critical {
background-image: url('chrome://activity-stream/content/data/content/assets/glyph-info-critical-16.svg');
}
&.icon-help {
background-image: url('chrome://global/skin/icons/help.svg');
}
@ -167,6 +171,10 @@
background-image: url('chrome://activity-stream/content/data/content/assets/glyph-webextension-16.svg');
}
&.icon-weather {
background-image: url('chrome://browser/skin/weather/sunny.svg');
}
&.icon-highlights {
background-image: url('chrome://global/skin/icons/highlights.svg');
}

View file

@ -155,6 +155,9 @@ input {
.icon.icon-info {
background-image: url("chrome://global/skin/icons/info.svg");
}
.icon.icon-info-critical {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-info-critical-16.svg");
}
.icon.icon-help {
background-image: url("chrome://global/skin/icons/help.svg");
}
@ -235,6 +238,9 @@ input {
.icon.icon-webextension {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-webextension-16.svg");
}
.icon.icon-weather {
background-image: url("chrome://browser/skin/weather/sunny.svg");
}
.icon.icon-highlights {
background-image: url("chrome://global/skin/icons/highlights.svg");
}
@ -2009,6 +2015,333 @@ main section {
text-decoration: none;
}
:root {
--newtab-weather-content-font-size: 11px;
--newtab-weather-sponsor-font-size: 8px;
}
.weather {
font-size: var(--font-size-root);
position: absolute;
left: var(--space-xlarge);
top: var(--space-xlarge);
z-index: 1;
}
.weatherNotAvailable {
font-size: var(--newtab-weather-content-font-size);
color: var(--text-color-error);
display: flex;
align-items: center;
}
.weatherNotAvailable .icon {
fill: var(--icon-color-critical);
-moz-context-properties: fill;
}
.weatherCard {
margin-block-end: var(--space-xsmall);
display: flex;
flex-wrap: nowrap;
align-items: stretch;
border-radius: var(--border-radius-medium);
overflow: hidden;
}
.weatherCard:hover ~ .weatherSponsorText, .weatherCard:focus-within ~ .weatherSponsorText {
visibility: visible;
}
.weatherCard:focus-within {
overflow: visible;
}
.weatherCard:hover {
box-shadow: var(--box-shadow-10);
background: var(--background-color-box);
}
.weatherCard a {
color: var(--text-color);
}
.weatherSponsorText {
visibility: hidden;
font-size: var(--newtab-weather-sponsor-font-size);
color: var(--text-color-deemphasized);
}
.weatherInfoLink, .weatherButtonContextMenuWrapper {
appearance: none;
background-color: var(--background-color-ghost);
border: 0;
padding: var(--space-small);
cursor: pointer;
}
.weatherInfoLink:hover, .weatherButtonContextMenuWrapper:hover {
background-color: var(--button-background-color-ghost-hover);
}
.weatherInfoLink:hover::after, .weatherButtonContextMenuWrapper:hover::after {
background-color: transparent;
}
.weatherInfoLink:hover:active, .weatherButtonContextMenuWrapper:hover:active {
background-color: var(--button-background-color-ghost-active);
}
.weatherInfoLink:focus-visible, .weatherButtonContextMenuWrapper:focus-visible {
outline: var(--focus-outline);
}
@media (prefers-color-scheme: dark) {
.hasWallpaperDark .weatherInfoLink, .hasWallpaperDark .weatherButtonContextMenuWrapper {
background-color: rgba(35, 34, 43, 0.7);
}
.hasWallpaperDark .weatherInfoLink:hover, .hasWallpaperDark .weatherButtonContextMenuWrapper:hover {
background-color: var(--newtab-button-static-hover-background);
}
.hasWallpaperDark .weatherInfoLink:hover:active, .hasWallpaperDark .weatherButtonContextMenuWrapper:hover:active {
background-color: var(--newtab-button-static-active-background);
}
}
@media (prefers-contrast) and (prefers-color-scheme: dark) {
.hasWallpaperDark .weatherInfoLink, .hasWallpaperDark .weatherButtonContextMenuWrapper {
background-color: var(--background-color-box);
}
}
@media (prefers-color-scheme: light) {
.hasWallpaperLight .weatherInfoLink, .hasWallpaperLight .weatherButtonContextMenuWrapper {
background-color: rgba(255, 255, 255, 0.7);
}
.hasWallpaperLight .weatherInfoLink:hover, .hasWallpaperLight .weatherButtonContextMenuWrapper:hover {
background-color: var(--newtab-button-static-hover-background);
}
.hasWallpaperLight .weatherInfoLink:hover:active, .hasWallpaperLight .weatherButtonContextMenuWrapper:hover:active {
background-color: var(--newtab-button-static-active-background);
}
}
@media (prefers-contrast) and (prefers-color-scheme: light) {
.hasWallpaperLight .weatherInfoLink, .hasWallpaperLight .weatherButtonContextMenuWrapper {
background-color: var(--background-color-box);
}
}
.weatherInfoLink {
display: flex;
gap: var(--space-medium);
padding: var(--space-small) var(--space-medium);
border-radius: var(--border-radius-medium) 0 0 var(--border-radius-medium);
text-decoration: none;
color: var(--text-color);
min-width: 130px;
max-width: 190px;
text-overflow: ellipsis;
}
@media (min-width: 610px) {
.weatherInfoLink {
min-width: unset;
}
}
.weatherInfoLink:hover ~ .weatherButtonContextMenuWrapper::after {
background-color: transparent;
}
.weatherInfoLink:focus-visible {
border-radius: var(--border-radius-medium);
}
.weatherInfoLink:focus-visible ~ .weatherButtonContextMenuWrapper::after {
background-color: transparent;
}
.weatherButtonContextMenuWrapper {
position: relative;
cursor: pointer;
border-radius: 0 var(--border-radius-medium) var(--border-radius-medium) 0;
display: flex;
align-items: stretch;
width: 50px;
padding: 0;
}
.weatherButtonContextMenuWrapper::after {
content: "";
left: 0;
top: 10px;
height: calc(100% - 20px);
width: 1px;
background-color: var(--newtab-button-static-background);
display: block;
position: absolute;
z-index: 0;
}
@media (prefers-color-scheme: dark) {
.weatherButtonContextMenuWrapper::after {
background-color: var(--color-gray-70);
}
}
.weatherButtonContextMenuWrapper:hover::after {
background-color: transparent;
}
.weatherButtonContextMenuWrapper:focus-visible {
border-radius: var(--border-radius-medium);
}
.weatherButtonContextMenuWrapper:focus-visible::after {
background-color: transparent;
}
.weatherButtonContextMenu {
background-image: url("chrome://global/skin/icons/more.svg");
background-repeat: no-repeat;
background-size: var(--size-item-small) auto;
background-position: center;
background-color: transparent;
cursor: pointer;
fill: var(--icon-color);
-moz-context-properties: fill;
width: 100%;
height: 100%;
border: 0;
appearance: none;
min-width: var(--size-item-large);
}
.weatherText {
height: min-content;
}
.weatherCityRow, .weatherForecastRow, .weatherDetailedSummaryRow {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--space-small);
}
.weatherForecastRow {
text-transform: uppercase;
font-weight: var(--font-weight-bold);
}
.weatherCityRow {
color: var(--text-color-deemphasized);
}
.weatherCity {
text-overflow: ellipsis;
font-size: var(--font-size-small);
}
.weatherCityRow + .weatherDetailedSummaryRow {
margin-block-start: var(--space-xsmall);
}
.weatherDetailedSummaryRow {
font-size: var(--newtab-weather-content-font-size);
gap: var(--space-large);
}
.weatherHighLowTemps {
display: flex;
gap: var(--space-xxsmall);
text-transform: uppercase;
word-spacing: var(--space-xxsmall);
}
.weatherTextSummary {
text-align: center;
max-width: 90px;
}
.weatherTemperature {
font-size: var(--font-size-large);
}
.weatherIconCol {
width: var(--size-item-large);
height: var(--size-item-large);
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
align-self: center;
}
.weatherIcon {
width: var(--size-item-large);
height: auto;
vertical-align: middle;
}
@media (prefers-contrast) {
.weatherIcon {
-moz-context-properties: fill, stroke;
fill: currentColor;
stroke: currentColor;
}
}
.weatherIcon.iconId1 {
content: url("chrome://browser/skin/weather/sunny.svg");
}
.weatherIcon.iconId2 {
content: url("chrome://browser/skin/weather/mostly-sunny.svg");
}
.weatherIcon:is(.iconId3, .iconId4, .iconId6) {
content: url("chrome://browser/skin/weather/partly-sunny.svg");
}
.weatherIcon.iconId5 {
content: url("chrome://browser/skin/weather/hazy-sunshine.svg");
}
.weatherIcon:is(.iconId7, .iconId8) {
content: url("chrome://browser/skin/weather/cloudy.svg");
}
.weatherIcon.iconId11 {
content: url("chrome://browser/skin/weather/fog.svg");
}
.weatherIcon.iconId12 {
content: url("chrome://browser/skin/weather/showers.svg");
}
.weatherIcon:is(.iconId13, .iconId14) {
content: url("chrome://browser/skin/weather/mostly-cloudy-with-showers.svg");
}
.weatherIcon.iconId15 {
content: url("chrome://browser/skin/weather/thunderstorms.svg");
}
.weatherIcon:is(.iconId16, .iconId17) {
content: url("chrome://browser/skin/weather/mostly-cloudy-with-thunderstorms.svg");
}
.weatherIcon.iconId18 {
content: url("chrome://browser/skin/weather/rain.svg");
}
.weatherIcon:is(.iconId19, .iconId20, .iconId25) {
content: url("chrome://browser/skin/weather/flurries.svg");
}
.weatherIcon.iconId21 {
content: url("chrome://browser/skin/weather/partly-sunny-with-flurries.svg");
}
.weatherIcon:is(.iconId22, .iconId23) {
content: url("chrome://browser/skin/weather/snow.svg");
}
.weatherIcon:is(.iconId24, .iconId31) {
content: url("chrome://browser/skin/weather/ice.svg");
}
.weatherIcon:is(.iconId26, .iconId29) {
content: url("chrome://browser/skin/weather/freezing-rain.svg");
}
.weatherIcon.iconId30 {
content: url("chrome://browser/skin/weather/hot.svg");
}
.weatherIcon.iconId32 {
content: url("chrome://browser/skin/weather/windy.svg");
}
.weatherIcon.iconId33 {
content: url("chrome://browser/skin/weather/night-clear.svg");
}
.weatherIcon:is(.iconId34, .iconId35, .iconId36, .iconId38) {
content: url("chrome://browser/skin/weather/night-mostly-clear.svg");
}
.weatherIcon.iconId37 {
content: url("chrome://browser/skin/weather/night-hazy-moonlight.svg");
}
.weatherIcon:is(.iconId39, .iconId40) {
content: url("chrome://browser/skin/weather/night-partly-cloudy-with-showers.svg");
height: var(--size-item-large);
}
.weatherIcon:is(.iconId41, .iconId42) {
content: url("chrome://browser/skin/weather/night-partly-cloudy-with-thunderstorms.svg");
}
.weatherIcon:is(.iconId43, .iconId44) {
content: url("chrome://browser/skin/weather/night-mostly-cloudy-with-flurries.svg");
}
/* stylelint-disable max-nesting-depth */
.card-outer {
background: var(--newtab-background-color-secondary);

View file

@ -159,6 +159,9 @@ input {
.icon.icon-info {
background-image: url("chrome://global/skin/icons/info.svg");
}
.icon.icon-info-critical {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-info-critical-16.svg");
}
.icon.icon-help {
background-image: url("chrome://global/skin/icons/help.svg");
}
@ -239,6 +242,9 @@ input {
.icon.icon-webextension {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-webextension-16.svg");
}
.icon.icon-weather {
background-image: url("chrome://browser/skin/weather/sunny.svg");
}
.icon.icon-highlights {
background-image: url("chrome://global/skin/icons/highlights.svg");
}
@ -2013,6 +2019,333 @@ main section {
text-decoration: none;
}
:root {
--newtab-weather-content-font-size: 11px;
--newtab-weather-sponsor-font-size: 8px;
}
.weather {
font-size: var(--font-size-root);
position: absolute;
left: var(--space-xlarge);
top: var(--space-xlarge);
z-index: 1;
}
.weatherNotAvailable {
font-size: var(--newtab-weather-content-font-size);
color: var(--text-color-error);
display: flex;
align-items: center;
}
.weatherNotAvailable .icon {
fill: var(--icon-color-critical);
-moz-context-properties: fill;
}
.weatherCard {
margin-block-end: var(--space-xsmall);
display: flex;
flex-wrap: nowrap;
align-items: stretch;
border-radius: var(--border-radius-medium);
overflow: hidden;
}
.weatherCard:hover ~ .weatherSponsorText, .weatherCard:focus-within ~ .weatherSponsorText {
visibility: visible;
}
.weatherCard:focus-within {
overflow: visible;
}
.weatherCard:hover {
box-shadow: var(--box-shadow-10);
background: var(--background-color-box);
}
.weatherCard a {
color: var(--text-color);
}
.weatherSponsorText {
visibility: hidden;
font-size: var(--newtab-weather-sponsor-font-size);
color: var(--text-color-deemphasized);
}
.weatherInfoLink, .weatherButtonContextMenuWrapper {
appearance: none;
background-color: var(--background-color-ghost);
border: 0;
padding: var(--space-small);
cursor: pointer;
}
.weatherInfoLink:hover, .weatherButtonContextMenuWrapper:hover {
background-color: var(--button-background-color-ghost-hover);
}
.weatherInfoLink:hover::after, .weatherButtonContextMenuWrapper:hover::after {
background-color: transparent;
}
.weatherInfoLink:hover:active, .weatherButtonContextMenuWrapper:hover:active {
background-color: var(--button-background-color-ghost-active);
}
.weatherInfoLink:focus-visible, .weatherButtonContextMenuWrapper:focus-visible {
outline: var(--focus-outline);
}
@media (prefers-color-scheme: dark) {
.hasWallpaperDark .weatherInfoLink, .hasWallpaperDark .weatherButtonContextMenuWrapper {
background-color: rgba(35, 34, 43, 0.7);
}
.hasWallpaperDark .weatherInfoLink:hover, .hasWallpaperDark .weatherButtonContextMenuWrapper:hover {
background-color: var(--newtab-button-static-hover-background);
}
.hasWallpaperDark .weatherInfoLink:hover:active, .hasWallpaperDark .weatherButtonContextMenuWrapper:hover:active {
background-color: var(--newtab-button-static-active-background);
}
}
@media (prefers-contrast) and (prefers-color-scheme: dark) {
.hasWallpaperDark .weatherInfoLink, .hasWallpaperDark .weatherButtonContextMenuWrapper {
background-color: var(--background-color-box);
}
}
@media (prefers-color-scheme: light) {
.hasWallpaperLight .weatherInfoLink, .hasWallpaperLight .weatherButtonContextMenuWrapper {
background-color: rgba(255, 255, 255, 0.7);
}
.hasWallpaperLight .weatherInfoLink:hover, .hasWallpaperLight .weatherButtonContextMenuWrapper:hover {
background-color: var(--newtab-button-static-hover-background);
}
.hasWallpaperLight .weatherInfoLink:hover:active, .hasWallpaperLight .weatherButtonContextMenuWrapper:hover:active {
background-color: var(--newtab-button-static-active-background);
}
}
@media (prefers-contrast) and (prefers-color-scheme: light) {
.hasWallpaperLight .weatherInfoLink, .hasWallpaperLight .weatherButtonContextMenuWrapper {
background-color: var(--background-color-box);
}
}
.weatherInfoLink {
display: flex;
gap: var(--space-medium);
padding: var(--space-small) var(--space-medium);
border-radius: var(--border-radius-medium) 0 0 var(--border-radius-medium);
text-decoration: none;
color: var(--text-color);
min-width: 130px;
max-width: 190px;
text-overflow: ellipsis;
}
@media (min-width: 610px) {
.weatherInfoLink {
min-width: unset;
}
}
.weatherInfoLink:hover ~ .weatherButtonContextMenuWrapper::after {
background-color: transparent;
}
.weatherInfoLink:focus-visible {
border-radius: var(--border-radius-medium);
}
.weatherInfoLink:focus-visible ~ .weatherButtonContextMenuWrapper::after {
background-color: transparent;
}
.weatherButtonContextMenuWrapper {
position: relative;
cursor: pointer;
border-radius: 0 var(--border-radius-medium) var(--border-radius-medium) 0;
display: flex;
align-items: stretch;
width: 50px;
padding: 0;
}
.weatherButtonContextMenuWrapper::after {
content: "";
left: 0;
top: 10px;
height: calc(100% - 20px);
width: 1px;
background-color: var(--newtab-button-static-background);
display: block;
position: absolute;
z-index: 0;
}
@media (prefers-color-scheme: dark) {
.weatherButtonContextMenuWrapper::after {
background-color: var(--color-gray-70);
}
}
.weatherButtonContextMenuWrapper:hover::after {
background-color: transparent;
}
.weatherButtonContextMenuWrapper:focus-visible {
border-radius: var(--border-radius-medium);
}
.weatherButtonContextMenuWrapper:focus-visible::after {
background-color: transparent;
}
.weatherButtonContextMenu {
background-image: url("chrome://global/skin/icons/more.svg");
background-repeat: no-repeat;
background-size: var(--size-item-small) auto;
background-position: center;
background-color: transparent;
cursor: pointer;
fill: var(--icon-color);
-moz-context-properties: fill;
width: 100%;
height: 100%;
border: 0;
appearance: none;
min-width: var(--size-item-large);
}
.weatherText {
height: min-content;
}
.weatherCityRow, .weatherForecastRow, .weatherDetailedSummaryRow {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--space-small);
}
.weatherForecastRow {
text-transform: uppercase;
font-weight: var(--font-weight-bold);
}
.weatherCityRow {
color: var(--text-color-deemphasized);
}
.weatherCity {
text-overflow: ellipsis;
font-size: var(--font-size-small);
}
.weatherCityRow + .weatherDetailedSummaryRow {
margin-block-start: var(--space-xsmall);
}
.weatherDetailedSummaryRow {
font-size: var(--newtab-weather-content-font-size);
gap: var(--space-large);
}
.weatherHighLowTemps {
display: flex;
gap: var(--space-xxsmall);
text-transform: uppercase;
word-spacing: var(--space-xxsmall);
}
.weatherTextSummary {
text-align: center;
max-width: 90px;
}
.weatherTemperature {
font-size: var(--font-size-large);
}
.weatherIconCol {
width: var(--size-item-large);
height: var(--size-item-large);
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
align-self: center;
}
.weatherIcon {
width: var(--size-item-large);
height: auto;
vertical-align: middle;
}
@media (prefers-contrast) {
.weatherIcon {
-moz-context-properties: fill, stroke;
fill: currentColor;
stroke: currentColor;
}
}
.weatherIcon.iconId1 {
content: url("chrome://browser/skin/weather/sunny.svg");
}
.weatherIcon.iconId2 {
content: url("chrome://browser/skin/weather/mostly-sunny.svg");
}
.weatherIcon:is(.iconId3, .iconId4, .iconId6) {
content: url("chrome://browser/skin/weather/partly-sunny.svg");
}
.weatherIcon.iconId5 {
content: url("chrome://browser/skin/weather/hazy-sunshine.svg");
}
.weatherIcon:is(.iconId7, .iconId8) {
content: url("chrome://browser/skin/weather/cloudy.svg");
}
.weatherIcon.iconId11 {
content: url("chrome://browser/skin/weather/fog.svg");
}
.weatherIcon.iconId12 {
content: url("chrome://browser/skin/weather/showers.svg");
}
.weatherIcon:is(.iconId13, .iconId14) {
content: url("chrome://browser/skin/weather/mostly-cloudy-with-showers.svg");
}
.weatherIcon.iconId15 {
content: url("chrome://browser/skin/weather/thunderstorms.svg");
}
.weatherIcon:is(.iconId16, .iconId17) {
content: url("chrome://browser/skin/weather/mostly-cloudy-with-thunderstorms.svg");
}
.weatherIcon.iconId18 {
content: url("chrome://browser/skin/weather/rain.svg");
}
.weatherIcon:is(.iconId19, .iconId20, .iconId25) {
content: url("chrome://browser/skin/weather/flurries.svg");
}
.weatherIcon.iconId21 {
content: url("chrome://browser/skin/weather/partly-sunny-with-flurries.svg");
}
.weatherIcon:is(.iconId22, .iconId23) {
content: url("chrome://browser/skin/weather/snow.svg");
}
.weatherIcon:is(.iconId24, .iconId31) {
content: url("chrome://browser/skin/weather/ice.svg");
}
.weatherIcon:is(.iconId26, .iconId29) {
content: url("chrome://browser/skin/weather/freezing-rain.svg");
}
.weatherIcon.iconId30 {
content: url("chrome://browser/skin/weather/hot.svg");
}
.weatherIcon.iconId32 {
content: url("chrome://browser/skin/weather/windy.svg");
}
.weatherIcon.iconId33 {
content: url("chrome://browser/skin/weather/night-clear.svg");
}
.weatherIcon:is(.iconId34, .iconId35, .iconId36, .iconId38) {
content: url("chrome://browser/skin/weather/night-mostly-clear.svg");
}
.weatherIcon.iconId37 {
content: url("chrome://browser/skin/weather/night-hazy-moonlight.svg");
}
.weatherIcon:is(.iconId39, .iconId40) {
content: url("chrome://browser/skin/weather/night-partly-cloudy-with-showers.svg");
height: var(--size-item-large);
}
.weatherIcon:is(.iconId41, .iconId42) {
content: url("chrome://browser/skin/weather/night-partly-cloudy-with-thunderstorms.svg");
}
.weatherIcon:is(.iconId43, .iconId44) {
content: url("chrome://browser/skin/weather/night-mostly-cloudy-with-flurries.svg");
}
/* stylelint-disable max-nesting-depth */
.card-outer {
background: var(--newtab-background-color-secondary);

View file

@ -155,6 +155,9 @@ input {
.icon.icon-info {
background-image: url("chrome://global/skin/icons/info.svg");
}
.icon.icon-info-critical {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-info-critical-16.svg");
}
.icon.icon-help {
background-image: url("chrome://global/skin/icons/help.svg");
}
@ -235,6 +238,9 @@ input {
.icon.icon-webextension {
background-image: url("chrome://activity-stream/content/data/content/assets/glyph-webextension-16.svg");
}
.icon.icon-weather {
background-image: url("chrome://browser/skin/weather/sunny.svg");
}
.icon.icon-highlights {
background-image: url("chrome://global/skin/icons/highlights.svg");
}
@ -2009,6 +2015,333 @@ main section {
text-decoration: none;
}
:root {
--newtab-weather-content-font-size: 11px;
--newtab-weather-sponsor-font-size: 8px;
}
.weather {
font-size: var(--font-size-root);
position: absolute;
left: var(--space-xlarge);
top: var(--space-xlarge);
z-index: 1;
}
.weatherNotAvailable {
font-size: var(--newtab-weather-content-font-size);
color: var(--text-color-error);
display: flex;
align-items: center;
}
.weatherNotAvailable .icon {
fill: var(--icon-color-critical);
-moz-context-properties: fill;
}
.weatherCard {
margin-block-end: var(--space-xsmall);
display: flex;
flex-wrap: nowrap;
align-items: stretch;
border-radius: var(--border-radius-medium);
overflow: hidden;
}
.weatherCard:hover ~ .weatherSponsorText, .weatherCard:focus-within ~ .weatherSponsorText {
visibility: visible;
}
.weatherCard:focus-within {
overflow: visible;
}
.weatherCard:hover {
box-shadow: var(--box-shadow-10);
background: var(--background-color-box);
}
.weatherCard a {
color: var(--text-color);
}
.weatherSponsorText {
visibility: hidden;
font-size: var(--newtab-weather-sponsor-font-size);
color: var(--text-color-deemphasized);
}
.weatherInfoLink, .weatherButtonContextMenuWrapper {
appearance: none;
background-color: var(--background-color-ghost);
border: 0;
padding: var(--space-small);
cursor: pointer;
}
.weatherInfoLink:hover, .weatherButtonContextMenuWrapper:hover {
background-color: var(--button-background-color-ghost-hover);
}
.weatherInfoLink:hover::after, .weatherButtonContextMenuWrapper:hover::after {
background-color: transparent;
}
.weatherInfoLink:hover:active, .weatherButtonContextMenuWrapper:hover:active {
background-color: var(--button-background-color-ghost-active);
}
.weatherInfoLink:focus-visible, .weatherButtonContextMenuWrapper:focus-visible {
outline: var(--focus-outline);
}
@media (prefers-color-scheme: dark) {
.hasWallpaperDark .weatherInfoLink, .hasWallpaperDark .weatherButtonContextMenuWrapper {
background-color: rgba(35, 34, 43, 0.7);
}
.hasWallpaperDark .weatherInfoLink:hover, .hasWallpaperDark .weatherButtonContextMenuWrapper:hover {
background-color: var(--newtab-button-static-hover-background);
}
.hasWallpaperDark .weatherInfoLink:hover:active, .hasWallpaperDark .weatherButtonContextMenuWrapper:hover:active {
background-color: var(--newtab-button-static-active-background);
}
}
@media (prefers-contrast) and (prefers-color-scheme: dark) {
.hasWallpaperDark .weatherInfoLink, .hasWallpaperDark .weatherButtonContextMenuWrapper {
background-color: var(--background-color-box);
}
}
@media (prefers-color-scheme: light) {
.hasWallpaperLight .weatherInfoLink, .hasWallpaperLight .weatherButtonContextMenuWrapper {
background-color: rgba(255, 255, 255, 0.7);
}
.hasWallpaperLight .weatherInfoLink:hover, .hasWallpaperLight .weatherButtonContextMenuWrapper:hover {
background-color: var(--newtab-button-static-hover-background);
}
.hasWallpaperLight .weatherInfoLink:hover:active, .hasWallpaperLight .weatherButtonContextMenuWrapper:hover:active {
background-color: var(--newtab-button-static-active-background);
}
}
@media (prefers-contrast) and (prefers-color-scheme: light) {
.hasWallpaperLight .weatherInfoLink, .hasWallpaperLight .weatherButtonContextMenuWrapper {
background-color: var(--background-color-box);
}
}
.weatherInfoLink {
display: flex;
gap: var(--space-medium);
padding: var(--space-small) var(--space-medium);
border-radius: var(--border-radius-medium) 0 0 var(--border-radius-medium);
text-decoration: none;
color: var(--text-color);
min-width: 130px;
max-width: 190px;
text-overflow: ellipsis;
}
@media (min-width: 610px) {
.weatherInfoLink {
min-width: unset;
}
}
.weatherInfoLink:hover ~ .weatherButtonContextMenuWrapper::after {
background-color: transparent;
}
.weatherInfoLink:focus-visible {
border-radius: var(--border-radius-medium);
}
.weatherInfoLink:focus-visible ~ .weatherButtonContextMenuWrapper::after {
background-color: transparent;
}
.weatherButtonContextMenuWrapper {
position: relative;
cursor: pointer;
border-radius: 0 var(--border-radius-medium) var(--border-radius-medium) 0;
display: flex;
align-items: stretch;
width: 50px;
padding: 0;
}
.weatherButtonContextMenuWrapper::after {
content: "";
left: 0;
top: 10px;
height: calc(100% - 20px);
width: 1px;
background-color: var(--newtab-button-static-background);
display: block;
position: absolute;
z-index: 0;
}
@media (prefers-color-scheme: dark) {
.weatherButtonContextMenuWrapper::after {
background-color: var(--color-gray-70);
}
}
.weatherButtonContextMenuWrapper:hover::after {
background-color: transparent;
}
.weatherButtonContextMenuWrapper:focus-visible {
border-radius: var(--border-radius-medium);
}
.weatherButtonContextMenuWrapper:focus-visible::after {
background-color: transparent;
}
.weatherButtonContextMenu {
background-image: url("chrome://global/skin/icons/more.svg");
background-repeat: no-repeat;
background-size: var(--size-item-small) auto;
background-position: center;
background-color: transparent;
cursor: pointer;
fill: var(--icon-color);
-moz-context-properties: fill;
width: 100%;
height: 100%;
border: 0;
appearance: none;
min-width: var(--size-item-large);
}
.weatherText {
height: min-content;
}
.weatherCityRow, .weatherForecastRow, .weatherDetailedSummaryRow {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--space-small);
}
.weatherForecastRow {
text-transform: uppercase;
font-weight: var(--font-weight-bold);
}
.weatherCityRow {
color: var(--text-color-deemphasized);
}
.weatherCity {
text-overflow: ellipsis;
font-size: var(--font-size-small);
}
.weatherCityRow + .weatherDetailedSummaryRow {
margin-block-start: var(--space-xsmall);
}
.weatherDetailedSummaryRow {
font-size: var(--newtab-weather-content-font-size);
gap: var(--space-large);
}
.weatherHighLowTemps {
display: flex;
gap: var(--space-xxsmall);
text-transform: uppercase;
word-spacing: var(--space-xxsmall);
}
.weatherTextSummary {
text-align: center;
max-width: 90px;
}
.weatherTemperature {
font-size: var(--font-size-large);
}
.weatherIconCol {
width: var(--size-item-large);
height: var(--size-item-large);
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
align-self: center;
}
.weatherIcon {
width: var(--size-item-large);
height: auto;
vertical-align: middle;
}
@media (prefers-contrast) {
.weatherIcon {
-moz-context-properties: fill, stroke;
fill: currentColor;
stroke: currentColor;
}
}
.weatherIcon.iconId1 {
content: url("chrome://browser/skin/weather/sunny.svg");
}
.weatherIcon.iconId2 {
content: url("chrome://browser/skin/weather/mostly-sunny.svg");
}
.weatherIcon:is(.iconId3, .iconId4, .iconId6) {
content: url("chrome://browser/skin/weather/partly-sunny.svg");
}
.weatherIcon.iconId5 {
content: url("chrome://browser/skin/weather/hazy-sunshine.svg");
}
.weatherIcon:is(.iconId7, .iconId8) {
content: url("chrome://browser/skin/weather/cloudy.svg");
}
.weatherIcon.iconId11 {
content: url("chrome://browser/skin/weather/fog.svg");
}
.weatherIcon.iconId12 {
content: url("chrome://browser/skin/weather/showers.svg");
}
.weatherIcon:is(.iconId13, .iconId14) {
content: url("chrome://browser/skin/weather/mostly-cloudy-with-showers.svg");
}
.weatherIcon.iconId15 {
content: url("chrome://browser/skin/weather/thunderstorms.svg");
}
.weatherIcon:is(.iconId16, .iconId17) {
content: url("chrome://browser/skin/weather/mostly-cloudy-with-thunderstorms.svg");
}
.weatherIcon.iconId18 {
content: url("chrome://browser/skin/weather/rain.svg");
}
.weatherIcon:is(.iconId19, .iconId20, .iconId25) {
content: url("chrome://browser/skin/weather/flurries.svg");
}
.weatherIcon.iconId21 {
content: url("chrome://browser/skin/weather/partly-sunny-with-flurries.svg");
}
.weatherIcon:is(.iconId22, .iconId23) {
content: url("chrome://browser/skin/weather/snow.svg");
}
.weatherIcon:is(.iconId24, .iconId31) {
content: url("chrome://browser/skin/weather/ice.svg");
}
.weatherIcon:is(.iconId26, .iconId29) {
content: url("chrome://browser/skin/weather/freezing-rain.svg");
}
.weatherIcon.iconId30 {
content: url("chrome://browser/skin/weather/hot.svg");
}
.weatherIcon.iconId32 {
content: url("chrome://browser/skin/weather/windy.svg");
}
.weatherIcon.iconId33 {
content: url("chrome://browser/skin/weather/night-clear.svg");
}
.weatherIcon:is(.iconId34, .iconId35, .iconId36, .iconId38) {
content: url("chrome://browser/skin/weather/night-mostly-clear.svg");
}
.weatherIcon.iconId37 {
content: url("chrome://browser/skin/weather/night-hazy-moonlight.svg");
}
.weatherIcon:is(.iconId39, .iconId40) {
content: url("chrome://browser/skin/weather/night-partly-cloudy-with-showers.svg");
height: var(--size-item-large);
}
.weatherIcon:is(.iconId41, .iconId42) {
content: url("chrome://browser/skin/weather/night-partly-cloudy-with-thunderstorms.svg");
}
.weatherIcon:is(.iconId43, .iconId44) {
content: url("chrome://browser/skin/weather/night-mostly-cloudy-with-flurries.svg");
}
/* stylelint-disable max-nesting-depth */
.card-outer {
background: var(--newtab-background-color-secondary);

View file

@ -1754,6 +1754,102 @@ const LinkMenuOptions = {
: LinkMenuOptions.EmptyItem(),
OpenInPrivateWindow: (site, index, eventSource, isEnabled) =>
isEnabled ? _OpenInPrivateWindow(site) : LinkMenuOptions.EmptyItem(),
ChangeWeatherLocation: () => ({
// type: "empty",
id: "newtab-weather-menu-change-location",
// icon: "search",
action: actionCreators.OnlyToMain({
type: actionTypes.CHANGE_WEATHER_LOCATION,
data: { url: "https://mozilla.org" },
}),
}),
OpenWeatherDisplayMenu: () => ({
id: "newtab-weather-menu-weather-display",
// type: "empty",
// icon: "search",
action: actionCreators.OnlyToMain({
type: actionTypes.OPEN_WEATHER_DISPLAY_MENU,
data: { url: "https://mozilla.org" },
}),
}),
ChangeWeatherDisplaySimple: () => ({
id: "newtab-weather-menu-change-weather-display-simple",
// type: "empty",
// icon: "search",
action: actionCreators.OnlyToMain({
type: actionTypes.SET_PREF,
data: {
name: "weather.display",
value: "simple",
},
}),
}),
ChangeWeatherDisplayDetailed: () => ({
id: "newtab-weather-menu-change-weather-display-detailed",
// type: "empty",
// icon: "search",
action: actionCreators.OnlyToMain({
type: actionTypes.SET_PREF,
data: {
name: "weather.display",
value: "detailed",
},
}),
}),
OpenChangeTemperatureUnits: () => ({
id: "newtab-weather-menu-temperature-units",
// type: "empty",
// icon: "search",
action: actionCreators.OnlyToMain({
type: actionTypes.OPEN_CHANGE_TEMPERATURE_UNITS,
data: { url: "https://mozilla.org" },
}),
}),
ChangeTempUnitFahrenheit: () => ({
id: "newtab-weather-menu-change-temperature-units-fahrenheit",
// type: "empty",
// icon: "search",
action: actionCreators.OnlyToMain({
type: actionTypes.SET_PREF,
data: {
name: "weather.temperatureUnits",
value: "f",
},
}),
}),
ChangeTempUnitCelsius: () => ({
id: "newtab-weather-menu-change-temperature-units-celsius",
// type: "empty",
// icon: "search",
action: actionCreators.OnlyToMain({
type: actionTypes.SET_PREF,
data: {
name: "weather.temperatureUnits",
value: "c",
},
}),
}),
HideWeather: () => ({
id: "newtab-weather-menu-hide-weather",
// type: "empty",
// icon: "search",
action: actionCreators.OnlyToMain({
type: actionTypes.SET_PREF,
data: {
name: "showWeather",
value: false,
},
}),
}),
OpenLearnMoreURL: site => ({
id: "newtab-weather-menu-learn-more",
// type: "empty",
// icon: "search",
action: actionCreators.OnlyToMain({
type: actionTypes.OPEN_LINK,
data: { url: site.url },
}),
}),
};
;// CONCATENATED MODULE: ./content-src/components/LinkMenu/LinkMenu.jsx
@ -9522,6 +9618,151 @@ class _Search extends (external_React_default()).PureComponent {
const Search_Search = (0,external_ReactRedux_namespaceObject.connect)(state => ({
Prefs: state.Prefs
}))(_Search);
;// CONCATENATED MODULE: ./content-src/components/Weather/Weather.jsx
/* 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/. */
class _Weather extends (external_React_default()).PureComponent {
constructor(props) {
super(props);
this.state = {
contextMenuKeyboard: false,
showContextMenu: false,
url: "https://example.com"
};
this.onClick = this.onClick.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
this.onUpdate = this.onUpdate.bind(this);
}
openContextMenu(isKeyBoard) {
if (this.props.onUpdate) {
this.props.onUpdate(true);
}
this.setState({
showContextMenu: true,
contextMenuKeyboard: isKeyBoard
});
}
onClick(event) {
event.preventDefault();
this.openContextMenu(false, event);
}
onKeyDown(event) {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
this.openContextMenu(true, event);
}
}
onUpdate(showContextMenu) {
if (this.props.onUpdate) {
this.props.onUpdate(showContextMenu);
}
this.setState({
showContextMenu
});
}
render() {
// Check if weather should be rendered
const isWeatherEnabled = this.props.Prefs.values["system.showWeather"];
if (!isWeatherEnabled) {
return false;
}
const {
showContextMenu
} = this.state;
const WEATHER_SUGGESTION = this.props.Weather.suggestions?.[0];
const {
className,
index,
dispatch,
eventSource,
shouldSendImpressionStats
} = this.props;
const {
props
} = this;
const isContextMenuOpen = this.state.activeCard === index;
const outerClassName = ["weather", className, isContextMenuOpen && "active", props.placeholder && "placeholder"].filter(v => v).join(" ");
const showDetailedView = this.props.Prefs.values["weather.display"] === "detailed";
// Note: The temperature units/display options will become secondary menu items
const WEATHER_SOURCE_CONTEXT_MENU_OPTIONS = [...(this.props.Prefs.values["weather.locationSearchEnabled"] ? ["ChangeWeatherLocation"] : []), ...(this.props.Prefs.values["weather.temperatureUnits"] === "f" ? ["ChangeTempUnitCelsius"] : ["ChangeTempUnitFahrenheit"]), ...(this.props.Prefs.values["weather.display"] === "simple" ? ["ChangeWeatherDisplayDetailed"] : ["ChangeWeatherDisplaySimple"]), "HideWeather", "OpenLearnMoreURL"];
// Only return the widget if we have data. Otherwise, show error state
if (WEATHER_SUGGESTION) {
return /*#__PURE__*/external_React_default().createElement("div", {
className: outerClassName
}, /*#__PURE__*/external_React_default().createElement("div", {
className: "weatherCard"
}, /*#__PURE__*/external_React_default().createElement("a", {
"data-l10n-id": "newtab-weather-see-forecast",
"data-l10n-args": "{\"provider\": \"AccuWeather\"}",
href: WEATHER_SUGGESTION.forecast.url,
className: "weatherInfoLink"
}, /*#__PURE__*/external_React_default().createElement("div", {
className: "weatherIconCol"
}, /*#__PURE__*/external_React_default().createElement("span", {
className: `weatherIcon iconId${WEATHER_SUGGESTION.current_conditions.icon_id}`
})), /*#__PURE__*/external_React_default().createElement("div", {
className: "weatherText"
}, /*#__PURE__*/external_React_default().createElement("div", {
className: "weatherForecastRow"
}, /*#__PURE__*/external_React_default().createElement("span", {
className: "weatherTemperature"
}, WEATHER_SUGGESTION.current_conditions.temperature[this.props.Prefs.values["weather.temperatureUnits"]], "\xB0", this.props.Prefs.values["weather.temperatureUnits"])), /*#__PURE__*/external_React_default().createElement("div", {
className: "weatherCityRow"
}, /*#__PURE__*/external_React_default().createElement("span", {
className: "weatherCity"
}, WEATHER_SUGGESTION.city_name)), showDetailedView ? /*#__PURE__*/external_React_default().createElement("div", {
className: "weatherDetailedSummaryRow"
}, /*#__PURE__*/external_React_default().createElement("div", {
className: "weatherHighLowTemps"
}, /*#__PURE__*/external_React_default().createElement("span", null, WEATHER_SUGGESTION.forecast.high[this.props.Prefs.values["weather.temperatureUnits"]], "\xB0", this.props.Prefs.values["weather.temperatureUnits"]), /*#__PURE__*/external_React_default().createElement("span", null, "\u2022"), /*#__PURE__*/external_React_default().createElement("span", null, WEATHER_SUGGESTION.forecast.low[this.props.Prefs.values["weather.temperatureUnits"]], "\xB0", this.props.Prefs.values["weather.temperatureUnits"])), /*#__PURE__*/external_React_default().createElement("span", {
className: "weatherTextSummary"
}, WEATHER_SUGGESTION.current_conditions.summary)) : null)), /*#__PURE__*/external_React_default().createElement("div", {
className: "weatherButtonContextMenuWrapper"
}, /*#__PURE__*/external_React_default().createElement("button", {
"aria-haspopup": "true",
onKeyDown: this.onKeyDown,
onClick: this.onClick,
"data-l10n-id": "newtab-menu-section-tooltip",
className: "weatherButtonContextMenu"
}, showContextMenu ? /*#__PURE__*/external_React_default().createElement(LinkMenu, {
dispatch: dispatch,
index: index,
source: eventSource,
onUpdate: this.onUpdate,
options: WEATHER_SOURCE_CONTEXT_MENU_OPTIONS,
site: {
url: "https://support.mozilla.org/kb/customize-items-on-firefox-new-tab-page"
},
link: "https://support.mozilla.org/kb/customize-items-on-firefox-new-tab-page",
shouldSendImpressionStats: shouldSendImpressionStats
}) : null))), /*#__PURE__*/external_React_default().createElement("span", {
"data-l10n-id": "newtab-weather-sponsored",
"data-l10n-args": "{\"provider\": \"AccuWeather\"}",
className: "weatherSponsorText"
}));
}
return /*#__PURE__*/external_React_default().createElement("div", {
className: outerClassName
}, /*#__PURE__*/external_React_default().createElement("div", {
className: "weatherNotAvailable"
}, /*#__PURE__*/external_React_default().createElement("span", {
className: "icon icon-small-spacer icon-info-critical"
}), " ", /*#__PURE__*/external_React_default().createElement("span", {
"data-l10n-id": "newtab-weather-error-not-available"
})));
}
}
const Weather_Weather = (0,external_ReactRedux_namespaceObject.connect)(state => ({
Weather: state.Weather,
Prefs: state.Prefs
}))(_Weather);
;// CONCATENATED MODULE: ./content-src/components/Base/Base.jsx
function Base_extends() { Base_extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return Base_extends.apply(this, arguments); }
/* This Source Code Form is subject to the terms of the Mozilla Public
@ -9538,6 +9779,7 @@ function Base_extends() { Base_extends = Object.assign ? Object.assign.bind() :
const Base_VISIBLE = "visible";
const Base_VISIBILITY_CHANGE_EVENT = "visibilitychange";
const PrefsButton = ({
@ -9761,6 +10003,14 @@ class BaseContent extends (external_React_default()).PureComponent {
const darkWallpaper = wallpaperList.find(wp => wp.title === prefs["newtabWallpapers.wallpaper-dark"]) || "";
__webpack_require__.g.document?.body.style.setProperty(`--newtab-wallpaper-light`, `url(${lightWallpaper?.wallpaperUrl || ""})`);
__webpack_require__.g.document?.body.style.setProperty(`--newtab-wallpaper-dark`, `url(${darkWallpaper?.wallpaperUrl || ""})`);
// Add helper class to body if user has a wallpaper selected
if (lightWallpaper) {
__webpack_require__.g.document?.body.classList.add("hasWallpaperLight");
}
if (darkWallpaper) {
__webpack_require__.g.document?.body.classList.add("hasWallpaperDark");
}
}
}
render() {
@ -9777,6 +10027,7 @@ class BaseContent extends (external_React_default()).PureComponent {
const prefs = props.Prefs.values;
const activeWallpaper = prefs[`newtabWallpapers.wallpaper-${this.state.colorMode}`];
const wallpapersEnabled = prefs["newtabWallpapers.enabled"];
const weatherEnabled = prefs.showWeather;
const {
pocketConfig
} = prefs;
@ -9839,7 +10090,7 @@ class BaseContent extends (external_React_default()).PureComponent {
locale: props.App.locale,
mayHaveSponsoredStories: mayHaveSponsoredStories,
firstVisibleTimestamp: this.state.firstVisibleTimestamp
})) : /*#__PURE__*/external_React_default().createElement(Sections_Sections, null)), /*#__PURE__*/external_React_default().createElement(ConfirmDialog, null), wallpapersEnabled && this.renderWallpaperAttribution())));
})) : /*#__PURE__*/external_React_default().createElement(Sections_Sections, null)), /*#__PURE__*/external_React_default().createElement(ConfirmDialog, null), wallpapersEnabled && this.renderWallpaperAttribution()), /*#__PURE__*/external_React_default().createElement("aside", null, weatherEnabled && /*#__PURE__*/external_React_default().createElement(ErrorBoundary, null, /*#__PURE__*/external_React_default().createElement(Weather_Weather, null)))));
}
}
BaseContent.defaultProps = {

View file

@ -0,0 +1,6 @@
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="17" viewBox="0 0 16 17" fill="none">
<path d="M7.625 16C6.64009 16 5.66482 15.806 4.75487 15.4291C3.84493 15.0522 3.01814 14.4997 2.3217 13.8033C1.62526 13.1069 1.07281 12.2801 0.695904 11.3701C0.318993 10.4602 0.125 9.48491 0.125 8.5C0.125 7.51509 0.318993 6.53982 0.695904 5.62987C1.07281 4.71993 1.62526 3.89314 2.3217 3.1967C3.01814 2.50026 3.84493 1.94781 4.75487 1.5709C5.66482 1.19399 6.64009 1 7.625 1C9.61412 1 11.5218 1.79018 12.9283 3.1967C14.3348 4.60322 15.125 6.51088 15.125 8.5C15.125 10.4891 14.3348 12.3968 12.9283 13.8033C11.5218 15.2098 9.61412 16 7.625 16ZM8.25 5.125C8.25 4.95924 8.18415 4.80027 8.06694 4.68306C7.94973 4.56585 7.79076 4.5 7.625 4.5C7.45924 4.5 7.30027 4.56585 7.18306 4.68306C7.06585 4.80027 7 4.95924 7 5.125V9.563C7 9.72876 7.06585 9.88773 7.18306 10.0049C7.30027 10.1222 7.45924 10.188 7.625 10.188C7.79076 10.188 7.94973 10.1222 8.06694 10.0049C8.18415 9.88773 8.25 9.72876 8.25 9.563V5.125ZM8.25 11.5L8 11.25H7.25L7 11.5V12.25L7.25 12.5H8L8.25 12.25V11.5Z" fill="context-fill"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -188,6 +188,15 @@ module.exports = function (config) {
functions: 0,
branches: 0,
},
/**
* Weather.jsx is tested via an xpcshell test
*/
"content-src/components/Weather/*.jsx": {
statements: 0,
lines: 0,
functions: 0,
branches: 0,
},
"content-src/components/DiscoveryStreamAdmin/*.jsx": {
statements: 0,
lines: 0,

View file

@ -6,7 +6,6 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
MerinoClient: "resource:///modules/MerinoClient.sys.mjs",
clearTimeout: "resource://gre/modules/Timer.sys.mjs",
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
});
@ -17,6 +16,8 @@ import {
const MERINO_PROVIDER = "accuweather";
const WEATHER_ENABLED = "browser.newtabpage.activity-stream.showWeather";
const WEATHER_ENABLED_SYS =
"browser.newtabpage.activity-stream.system.showWeather";
const WEATHER_QUERY = "browser.newtabpage.activity-stream.weather.query";
@ -38,9 +39,8 @@ export class WeatherFeed {
isEnabled() {
return (
(Services.prefs.getBoolPref(WEATHER_ENABLED) &&
lazy.NimbusFeatures.pocketNewtab.getVariable("newtabWeatherEnabled")) ||
false
Services.prefs.getBoolPref(WEATHER_ENABLED) &&
Services.prefs.getBoolPref(WEATHER_ENABLED_SYS)
);
}
@ -134,6 +134,12 @@ export class WeatherFeed {
case at.PREF_CHANGED:
if (action.data.name === "weather.query") {
await this.fetch();
} else if (
action.data.name === "showWeather" &&
action.data.value &&
this.isEnabled()
) {
await this.fetch();
}
break;
}

View file

@ -117,6 +117,73 @@ test_newtab({
},
});
test_newtab({
async before({ pushPrefs }) {
await pushPrefs(
["browser.newtabpage.activity-stream.system.showWeather", true],
["browser.newtabpage.activity-stream.showWeather", false]
);
},
test: async function test_render_customizeMenuWeather() {
// Weather Widget Fecthing
function getWeatherWidget() {
return content.document.querySelector(`.weather`);
}
function promiseWeatherShown() {
return ContentTaskUtils.waitForMutationCondition(
content.document.querySelector("aside"),
{ childList: true, subtree: true },
() => getWeatherWidget()
);
}
const WEATHER_PREF = "browser.newtabpage.activity-stream.showWeather";
await ContentTaskUtils.waitForCondition(
() => content.document.querySelector(".personalize-button"),
"Wait for prefs button to load on the newtab page"
);
let customizeButton = content.document.querySelector(".personalize-button");
customizeButton.click();
let defaultPos = "matrix(1, 0, 0, 1, 0, 0)";
await ContentTaskUtils.waitForCondition(
() =>
content.getComputedStyle(
content.document.querySelector(".customize-menu")
).transform === defaultPos,
"Customize Menu should be visible on screen"
);
// Test that clicking the weather toggle will make the
// weather widget appear on the newtab page.
//
// We waive XRay wrappers because we want to call the click()
// method defined on the toggle from this context.
let weatherSwitch = Cu.waiveXrays(
content.document.querySelector("#weather-section moz-toggle")
);
Assert.ok(
!Services.prefs.getBoolPref(WEATHER_PREF),
"Weather pref is turned off"
);
Assert.ok(!getWeatherWidget(), "Weather widget is not rendered");
let sectionShownPromise = promiseWeatherShown();
weatherSwitch.click();
await sectionShownPromise;
Assert.ok(getWeatherWidget(), "Weather widget is rendered");
},
async after() {
Services.prefs.clearUserPref(
"browser.newtabpage.activity-stream.showWeather"
);
},
});
test_newtab({
test: async function test_open_close_customizeMenu() {
const EventUtils = ContentTaskUtils.getEventUtils(content);

View file

@ -861,7 +861,7 @@ pocketNewtab:
description: >-
Turns on and off the weather widget in home and newtab
fallbackPref: >-
browser.newtabpage.activity-stream.feeds.system.showWeather
browser.newtabpage.activity-stream.system.showWeather
recsPersonalized:
type: boolean
fallbackPref: >-