1
0
mirror of https://github.com/bitwarden/server synced 2026-02-13 15:04:03 +00:00

[PM-30610] Break shared components into AC versions

This commit is contained in:
Jimmy Vo
2026-02-12 17:23:56 -05:00
parent d7a6da2274
commit 4a318413bb
4 changed files with 254 additions and 1 deletions

View File

@@ -4,6 +4,9 @@
"components/mj-bw-simple-hero",
"components/mj-bw-icon-row",
"components/mj-bw-learn-more-footer",
"emails/AdminConsole/components/mj-bw-inviter-info"
"emails/AdminConsole/components/mj-bw-inviter-info",
"emails/AdminConsole/components/mj-bw-ac-hero",
"emails/AdminConsole/components/mj-bw-ac-icon-row",
"emails/AdminConsole/components/mj-bw-ac-learn-more-footer"
]
}

View File

@@ -0,0 +1,92 @@
const { BodyComponent } = require("mjml-core");
class MjBwAcHero extends BodyComponent {
static dependencies = {
// Tell the validator which tags are allowed as our component's parent
"mj-column": ["mj-bw-ac-hero"],
"mj-wrapper": ["mj-bw-ac-hero"],
// Tell the validator which tags are allowed as our component's children
"mj-bw-ac-hero": [],
};
static allowedAttributes = {
"img-src": "string", // REQUIRED: Source for the image displayed in the right-hand side of the blue header area
title: "string", // REQUIRED: large text stating primary purpose of the email
"button-text": "string", // OPTIONAL: text to display in the button
"button-url": "string", // OPTIONAL: URL to navigate to when the button is clicked
"sub-title": "string", // OPTIONAL: smaller text providing additional context for the title
};
static defaultAttributes = {};
componentHeadStyle = breakpoint => {
return `
@media only screen and (max-width:${breakpoint}) {
.mj-bw-ac-hero-responsive-img {
display: none !important;
}
}
`
}
render() {
const buttonElement = this.getAttribute("button-text") && this.getAttribute("button-url") ?
`<mj-button
href="${this.getAttribute("button-url")}"
background-color="#fff"
color="#1A41AC"
border-radius="20px"
align="left"
inner-padding="12px 24px"
>
${this.getAttribute("button-text")}
</mj-button
>` : "";
const subTitleElement = this.getAttribute("sub-title") ?
`<mj-text color="#fff" padding-top="0" padding-bottom="0">
<h2 style="font-weight: normal; font-size: 16px; line-height: 0px">
${this.getAttribute("sub-title")}
</h2>
</mj-text>` : "";
return this.renderMJML(
`
<mj-section
full-width="full-width"
background-color="#175ddc"
border-radius="4px 4px 0px 0px"
>
<mj-column width="70%">
<mj-image
align="left"
src="https://bitwarden.com/images/logo-horizontal-white.png"
width="150px"
height="30px"
></mj-image>
<mj-text color="#fff" padding-top="0" padding-bottom="0">
<h1 style="font-weight: 400; font-size: 24px; line-height: 32px">
${this.getAttribute("title")}
</h1>
` +
subTitleElement +
`
</mj-text>` +
buttonElement +
`
</mj-column>
<mj-column width="30%" vertical-align="bottom">
<mj-image
src="${this.getAttribute("img-src")}"
alt=""
width="155px"
padding="0px 20px 0px 0px"
align="right"
css-class="mj-bw-ac-hero-responsive-img"
/>
</mj-column>
</mj-section>
`,
);
}
}
module.exports = MjBwAcHero;

View File

@@ -0,0 +1,103 @@
const { BodyComponent } = require("mjml-core");
const BODY_TEXT_STYLES = `
font-family="'Helvetica Neue', Helvetica, Arial, sans-serif"
font-size="16px"
font-weight="400"
line-height="24px"
`;
class MjBwAcIconRow extends BodyComponent {
static dependencies = {
"mj-column": ["mj-bw-ac-icon-row"],
"mj-wrapper": ["mj-bw-ac-icon-row"],
"mj-bw-ac-icon-row": [],
};
static allowedAttributes = {
"icon-src": "string",
"icon-alt": "string",
"head-url-text": "string",
"head-url": "string",
text: "string",
"foot-url-text": "string",
"foot-url": "string",
};
static defaultAttributes = {};
headStyle = (breakpoint) => {
return `
@media only screen and (max-width:${breakpoint}) {
.mj-bw-ac-icon-row-text {
padding-left: 15px !important;
padding-right: 15px !important;
line-height: 20px;
}
.mj-bw-ac-icon-row-icon {
display: none !important;
width: 0 !important;
max-width: 0 !important;
}
.mj-bw-ac-icon-row-text-column {
width: 100% !important;
}
}
`;
};
render() {
const headAnchorElement =
this.getAttribute("head-url-text") && this.getAttribute("head-url")
? `
<mj-text css-class="mj-bw-ac-icon-row-text" padding="5px 10px 0px 10px" ${BODY_TEXT_STYLES}>
<a href="${this.getAttribute("head-url")}" class="link">
${this.getAttribute("head-url-text")}
<span style="text-decoration: none">
<img src="https://assets.bitwarden.com/email/v1/bwi-external-link-16px.png"
alt="External Link Icon"
width="16px"
style="vertical-align: middle;"
/>
</span>
</a>
</mj-text>`
: "";
const footAnchorElement =
this.getAttribute("foot-url-text") && this.getAttribute("foot-url")
? `<mj-text css-class="mj-bw-ac-icon-row-text" padding="0px" ${BODY_TEXT_STYLES}>
<a href="${this.getAttribute("foot-url")}" class="link">
${this.getAttribute("foot-url-text")}
</a>
</mj-text>`
: "";
return this.renderMJML(
`
<mj-section background-color="#fff" padding="0px 10px 24px 10px">
<mj-group css-class="mj-bw-ac-icon-row">
<mj-column width="15%" vertical-align="middle" css-class="mj-bw-ac-icon-row-icon">
<mj-image
src="${this.getAttribute("icon-src")}"
alt="${this.getAttribute("icon-alt")}"
width="48px"
padding="0px 10px 0px 5px"
border-radius="8px"
/>
</mj-column>
<mj-column width="85%" vertical-align="middle" css-class="mj-bw-ac-icon-row-text-column">
${headAnchorElement}
<mj-text css-class="mj-bw-ac-icon-row-text" padding="0px 0px 0px 0px" ${BODY_TEXT_STYLES}>
${this.getAttribute("text")}
</mj-text>
${footAnchorElement}
</mj-column>
</mj-group>
</mj-section>
`,
);
}
}
module.exports = MjBwAcIconRow;

View File

@@ -0,0 +1,55 @@
const { BodyComponent } = require("mjml-core");
class MjBwAcLearnMoreFooter extends BodyComponent {
static dependencies = {
// Tell the validator which tags are allowed as our component's parent
"mj-column": ["mj-bw-ac-learn-more-footer"],
"mj-wrapper": ["mj-bw-ac-learn-more-footer"],
// Tell the validator which tags are allowed as our component's children
"mj-bw-ac-learn-more-footer": [],
};
static allowedAttributes = {};
static defaultAttributes = {};
componentHeadStyle = (breakpoint) => {
return `
@media only screen and (max-width:${breakpoint}) {
.mj-bw-ac-learn-more-footer-responsive-img {
display: none !important;
}
}
`;
};
render() {
return this.renderMJML(
`
<mj-section border-radius="0px 0px 4px 4px" background-color="#F3F6F9" padding="14px 10px 14px 10px">
<mj-column width="70%">
<mj-text padding="10px 15px 10px 15px">
<p style="font-size: 18px; line-height: 28px; font-weight: 500; margin: 0 0 8px 0;">
Learn more about Bitwarden
</p>
<p style="font-size: 16px; line-height: 24px; margin: 0;">
Find user guides, product documentation, and videos on the
<a href="https://bitwarden.com/help/" class="link"> Bitwarden Help Center</a>.
</p>
</mj-text>
</mj-column>
<mj-column width="30%" vertical-align="bottom">
<mj-image
src="https://assets.bitwarden.com/email/v1/spot-community.png"
css-class="mj-bw-ac-learn-more-footer-responsive-img"
width="94px"
padding="0px 15px 0px 0px"
align="right"
/>
</mj-column>
</mj-section>
`,
);
}
}
module.exports = MjBwAcLearnMoreFooter;