-
Notifications
You must be signed in to change notification settings - Fork 512
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(article-footer): redesign footer + add feedback buttons (#10625)
* refactor(article-footer): rename Metadata => ArticleFooter * enhance(article-footer): apply new structure * fix(article-footer): consolidate CSS * refactor(article-footer): extract Contribute component * feat(article-footer): add feedback buttons * feat(article-footer): add background image * fix(article-footer): avoid layout shift on vote * refactor(document): move article-footer out of main-page-content * chore(article-footer): replace pipe by bullet * feat(article-footer): add feedback form on "No" vote * refactor(article-footer): rename article-footer-{content-container => inner} * chore(article-footer): crop background image * chore(article-footer): move background-image to inner container * chore(article-footer): remove label padding * chore(article-footer): remove button margin * fix(article-footer): restrict label style * chore(article-footer): add consistent 0.5rem margin * chore(article-footer): always show Contribute link * fix(article-footer): adjust space between thanks and contribute * feat(ui): redefine background-tertiary * fix(article-footer): use SVG element + --background-tertiary * chore(article-footer): use single heading * chore(article-footer): remove periods from GitHub links * chore(article-footer): reuse thumbs key to reuse queries
- Loading branch information
Showing
14 changed files
with
282 additions
and
127 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
105 changes: 105 additions & 0 deletions
105
client/src/document/organisms/article-footer/index.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
@use "../../../ui/vars" as *; | ||
|
||
.article-footer { | ||
background-color: var(--background-secondary); | ||
border: 1px solid var(--border-primary); | ||
border-radius: var(--elem-radius); | ||
box-shadow: var(--shadow-01); | ||
margin: 0; | ||
padding: 1rem; | ||
|
||
@media screen and (max-width: $screen-md) { | ||
margin: 3rem; | ||
// Reduce space to article content. | ||
margin-top: 0; | ||
} | ||
|
||
.article-footer-inner { | ||
margin: 0 auto; | ||
max-width: 1440px; | ||
width: 100%; | ||
|
||
.svg-container { | ||
position: relative; | ||
|
||
svg { | ||
height: auto; | ||
position: absolute; | ||
right: 0; | ||
top: 0; | ||
width: 25%; | ||
} | ||
} | ||
|
||
h2 { | ||
margin-top: 0; | ||
padding: 0; | ||
} | ||
|
||
.feedback { | ||
border: none; | ||
margin: 0; | ||
margin-bottom: 0.25rem; | ||
padding: 0; | ||
|
||
> label { | ||
display: block; | ||
margin-bottom: 0.25rem; | ||
} | ||
|
||
.thank-you { | ||
display: block; | ||
margin-bottom: calc(2.75rem + 2px); | ||
} | ||
|
||
.button-container { | ||
// Ensure both buttons take minimal width. | ||
display: inline-flex; | ||
gap: 0.75rem; | ||
margin: 0.25rem 0; | ||
} | ||
|
||
button { | ||
// Ensure both buttons have same size. | ||
flex: 1; | ||
min-width: 0; | ||
|
||
&:not(:hover) { | ||
--button-bg: var(--background-secondary); | ||
--button-color: var(--text-primary); | ||
} | ||
|
||
&.yes { | ||
--button-bg-hover: var(--text-primary-green); | ||
} | ||
|
||
&.no { | ||
--button-bg-hover: var(--text-primary-red); | ||
} | ||
} | ||
|
||
.button-wrap { | ||
display: flex; | ||
// Increase space between icon and button label. | ||
gap: 0.5rem; | ||
padding: 1rem; | ||
} | ||
|
||
.radio-container { | ||
align-items: center; | ||
display: flex; | ||
gap: 0.25rem; | ||
margin: 0.25rem 0; | ||
} | ||
} | ||
|
||
.contribute { | ||
margin-top: 0.25rem; | ||
} | ||
|
||
.last-modified-date { | ||
margin-bottom: 0; | ||
margin-top: 3rem; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import { useState } from "react"; | ||
import { Button } from "../../../ui/atoms/button"; | ||
import { OnGitHubLink } from "../../on-github"; | ||
import { ReactComponent as ArticleFooterSVG } from "../../../assets/article-footer/article-footer.svg"; | ||
import "./index.scss"; | ||
import { useGleanClick } from "../../../telemetry/glean-context"; | ||
import { ARTICLE_FOOTER, THUMBS } from "../../../telemetry/constants"; | ||
|
||
export function LastModified({ value, locale }) { | ||
if (!value) { | ||
return <span>Last modified date not known</span>; | ||
} | ||
const date = new Date(value); | ||
// Justification for these is to match historically | ||
const dateStringOptions: Intl.DateTimeFormatOptions = { | ||
year: "numeric", | ||
month: "short", | ||
day: "numeric", | ||
}; | ||
return ( | ||
<> | ||
This page was last modified on{" "} | ||
<time dateTime={value} suppressHydrationWarning> | ||
{date.toLocaleString(locale, dateStringOptions)} | ||
</time> | ||
</> | ||
); | ||
} | ||
|
||
export function Authors({ url }) { | ||
return <a href={`${url}/contributors.txt`}>MDN contributors</a>; | ||
} | ||
|
||
enum ArticleFooterView { | ||
Vote, | ||
Feedback, | ||
Thanks, | ||
} | ||
|
||
type FeedbackReason = "outdated" | "incomplete" | "code_examples" | "other"; | ||
|
||
const FEEDBACK_REASONS: Required<Record<FeedbackReason, string>> = { | ||
outdated: "Content is out of date", | ||
incomplete: "Missing information", | ||
code_examples: "Code examples not working as expected", | ||
other: "Other", | ||
}; | ||
|
||
export function ArticleFooter({ doc, locale }) { | ||
const [view, setView] = useState<ArticleFooterView>(ArticleFooterView.Vote); | ||
const [reason, setReason] = useState<FeedbackReason>(); | ||
|
||
const gleanClick = useGleanClick(); | ||
|
||
function handleVote(value: boolean) { | ||
setView(value ? ArticleFooterView.Thanks : ArticleFooterView.Feedback); | ||
// Reusing Thumbs' key to be able to reuse queries. | ||
gleanClick(`${THUMBS}: ${ARTICLE_FOOTER} -> ${Number(value)}`); | ||
} | ||
|
||
function handleFeedback() { | ||
setView(ArticleFooterView.Thanks); | ||
gleanClick(`${ARTICLE_FOOTER}: feedback -> ${reason}`); | ||
} | ||
|
||
return ( | ||
<aside className="article-footer"> | ||
<div className="article-footer-inner"> | ||
<div className="svg-container"> | ||
<ArticleFooterSVG role="none" /> | ||
</div> | ||
<h2>Help improve MDN</h2> | ||
|
||
<fieldset className="feedback"> | ||
{view === ArticleFooterView.Vote ? ( | ||
<> | ||
<label>Was this page helpful to you?</label> | ||
<div className="button-container"> | ||
<Button | ||
icon="thumbs-up" | ||
extraClasses="yes" | ||
onClickHandler={() => handleVote(true)} | ||
> | ||
Yes | ||
</Button> | ||
<Button | ||
icon="thumbs-down" | ||
extraClasses="no" | ||
onClickHandler={() => handleVote(false)} | ||
> | ||
No | ||
</Button> | ||
</div> | ||
</> | ||
) : view === ArticleFooterView.Feedback ? ( | ||
<> | ||
<label>Why was this page not helpful to you?</label> | ||
{Object.entries(FEEDBACK_REASONS).map(([key, label]) => ( | ||
<div className="radio-container" key={key}> | ||
<input | ||
type="radio" | ||
id={`reason_${key}`} | ||
name="reason" | ||
value={key} | ||
checked={reason === key} | ||
onChange={(event) => | ||
setReason(event.target.value as FeedbackReason) | ||
} | ||
/> | ||
<label htmlFor={`reason_${key}`}>{label}</label> | ||
</div> | ||
))} | ||
<div className="button-container"> | ||
<Button | ||
type="primary" | ||
isDisabled={!reason} | ||
onClickHandler={() => handleFeedback()} | ||
> | ||
Submit | ||
</Button> | ||
</div> | ||
</> | ||
) : ( | ||
<span className="thank-you">Thank you for your feedback! ❤️</span> | ||
)} | ||
</fieldset> | ||
|
||
<Contribute /> | ||
<p className="last-modified-date"> | ||
<LastModified value={doc.modified} locale={locale} /> by{" "} | ||
<Authors url={doc.mdn_url} />. | ||
</p> | ||
{doc.isActive && <OnGitHubLink doc={doc} />} | ||
</div> | ||
</aside> | ||
); | ||
} | ||
|
||
function Contribute() { | ||
return ( | ||
<> | ||
<a | ||
className="contribute" | ||
href="https://github.com/mdn/content/blob/main/CONTRIBUTING.md" | ||
title={`This will take you to our contribution guidelines on GitHub.`} | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
> | ||
Learn how to contribute | ||
</a> | ||
. | ||
</> | ||
); | ||
} |
Oops, something went wrong.