diff --git a/AzurePipelines/pr-preview.yml b/AzurePipelines/pr-preview.yml index 23cd740693..99d782f77a 100644 --- a/AzurePipelines/pr-preview.yml +++ b/AzurePipelines/pr-preview.yml @@ -80,8 +80,12 @@ steps: az webapp deployment slot create \ --name $(webapp.name) \ --resource-group $(webapp.resourceGroup) \ - --slot pr-preview-$(webapp.slotName) \ - --configuration-source $(webapp.name) + --slot pr-preview-$(webapp.slotName) + az webapp identity assign \ + -g $(webapp.resourceGroup) \ + -n $(webapp.name) \ + --slot pr-preview-$(webapp.slotName) \ + --identities /subscriptions/19bddd49-8e73-4699-930d-74baa7e5751e/resourcegroups/AlphasiteAppserviceRG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/ID-SCLabs-dev - task: AzureCLI@2 displayName: 'Deploy pr image to new slot' @@ -90,11 +94,6 @@ steps: scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | - az webapp identity assign \ - -g $(webapp.resourceGroup) \ - -n $(webapp.name) \ - --slot pr-preview-$(webapp.slotName) \ - --identities /subscriptions/19bddd49-8e73-4699-930d-74baa7e5751e/resourcegroups/AlphasiteAppserviceRG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/ID-SCLabs-dev az webapp config container set \ --docker-custom-image-name alphasitecrdev.azurecr.io/$(azureContainerRegistry.repository):$(azureContainerRegistry.tag) \ --name $(webapp.name) \ diff --git a/components/atoms/ProjectInfo.js b/components/atoms/ProjectInfo.js index 295086f4fd..963c7c0ff2 100644 --- a/components/atoms/ProjectInfo.js +++ b/components/atoms/ProjectInfo.js @@ -1,22 +1,17 @@ import PropTypes from "prop-types"; -import { useTranslation } from "next-i18next"; -import { useState } from "react"; -import { HelpIcon } from "@dts-stn/service-canada-design-system"; +import { HelpIcon } from "../design-system/HelpIcon"; export function ProjectInfo(props) { - const { t } = useTranslation("common"); - const [showInfo, setShowInfo] = useState(false); - return ( <>
{props.termStarted}

- {!props.dateStarted ? undefined : props.dateStarted.substring(0, 10)} + {props.dateStarted && props.dateStarted.substring(0, 10)}

{props.termEnded}

- {!props.dateEnded ? undefined : props.dateEnded.substring(0, 10)} + {props.dateEnded && props.dateEnded.substring(0, 10)}

{props.termStage} diff --git a/components/design-system/Button.jsx b/components/design-system/Button.jsx new file mode 100644 index 0000000000..65eaeb3d84 --- /dev/null +++ b/components/design-system/Button.jsx @@ -0,0 +1,148 @@ +import PropTypes from "prop-types"; +import { Image } from "./Image"; + +export function Button(props) { + const style = "btn-" + props.styling; + return props.href === "no ref" ? ( + + ) : ( + + {props.icon && !props.iconEnd ? ( + {props.iconAltText} + ) : undefined} + {props.text} + {props.children} + {props.icon && props.iconEnd ? ( +
+ {props.iconAltText} +
+ ) : undefined} +
+ ); +} + +Button.defaultProps = { + id: "btn1", + styling: "supertask", + text: "default", + href: "no ref", +}; + +Button.propTypes = { + /** + * Identify which button being clicked + */ + id: PropTypes.string.isRequired, + + /** + * User must input one of the follow button styles to apply + * to their button. To apply the link style, the user must + * also add a value to the href prop + */ + styling: PropTypes.oneOf([ + "supertask", + "primary", + "secondary", + "danger", + "link", + "none", + ]), + + /** + * The text that the button will display + */ + text: PropTypes.string.isRequired, + + /** + * This will add a img inside the button when needed + */ + icon: PropTypes.string, + + /** + * Alt text for icon added to button + */ + iconAltText: PropTypes.string, + + /** + * This is for placing an icon at the end of the component + */ + iconEnd: PropTypes.bool, + + /** + * Use when button redirects to a new page. + * Automatically applies the Link styling + */ + href: PropTypes.string, + + /** + * the type of the button + */ + type: PropTypes.oneOf(["submit", "reset", "button"]), + + /** + * Callback for a click event on the button + */ + onClick: PropTypes.func, + + /** + * bool to disable a button + */ + disabled: PropTypes.bool, + + /** + * css overrides for button + */ + className: PropTypes.string, + + /** + * additional attributes for button + */ + attributes: PropTypes.object, + + /** + * any other elements you want to add to the button + */ + children: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.element, + PropTypes.arrayOf(PropTypes.element), + ]), +}; diff --git a/components/design-system/CTA.jsx b/components/design-system/CTA.jsx new file mode 100644 index 0000000000..ed94ccc6d8 --- /dev/null +++ b/components/design-system/CTA.jsx @@ -0,0 +1,38 @@ +import { Button } from "./Button"; +import { Link } from "./Link"; +import { Image } from "./Image"; + +export function CTA({ + heading, + body, + ButtonProps, + LinkProps, + containerClass = "", +}) { + return ( +
+
+
+ icon +
+
+
+
+
+
+

+ {heading} +

+

{body}

+
+
+
+ ); +} diff --git a/components/design-system/Collapse.jsx b/components/design-system/Collapse.jsx new file mode 100644 index 0000000000..394e77ec1c --- /dev/null +++ b/components/design-system/Collapse.jsx @@ -0,0 +1,52 @@ +import PropTypes from "prop-types"; + +export function Collapse(props) { + const { id, title, children } = props; + return ( +
+ + {title} + +
+ {children} +
+
+ ); +} + +Collapse.defaultProps = { + id: "defaultAccordion", +}; + +Collapse.propTypes = { + /** + * component id + */ + id: PropTypes.string, + + /** + * Title of the collapsible header + */ + title: PropTypes.string, + + /** + * code passed in to fill the expanded area. + */ + children: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.element, + PropTypes.arrayOf(PropTypes.element), + ]), + + /** + * Test id for unit test + */ + dataTestId: PropTypes.string, +}; diff --git a/components/design-system/ContextualAlert.jsx b/components/design-system/ContextualAlert.jsx new file mode 100644 index 0000000000..7552cb757a --- /dev/null +++ b/components/design-system/ContextualAlert.jsx @@ -0,0 +1,117 @@ +import PropTypes from "prop-types"; + +export function ContextualAlert(props) { + const warning_img = "/warning_img.svg"; + const danger_img = "/danger_img.svg"; + const info_img = "/info_img.svg"; + const success_img = "/success_img.svg"; + + const { + message_heading, + message_body, + id, + type, + alert_icon_id, + alert_icon_alt_text, + asHtml, + whiteBG, + } = props; + + const alert_type = + type === "warning" + ? warning_img + : type === "danger" + ? danger_img + : type === "info" + ? info_img + : success_img; + + const alert_color = + type === "warning" + ? "border-specific-orange-orange50" + : type === "danger" + ? "border-specific-red-red50b" + : type === "info" + ? "border-specific-cyan-cyan50" + : "border-specific-green-green50a"; + + let white_BG = whiteBG ? "bg-multi-neutrals-white" : " "; + + return ( +
+
+ {/* change back to image component once fixed */} + {alert_icon_alt_text} +
+
+ {asHtml ? ( +

+ ) : ( +

{message_heading}

+ )} + {asHtml ? ( +
+ ) : ( +
{message_body}
+ )} +
+
+ ); +} + +ContextualAlert.propTypes = { + /** + * component id + */ + id: PropTypes.string.isRequired, + + /** + * alert type + */ + type: PropTypes.oneOf(["warning", "info", "success", "danger"]).isRequired, + + /** + * id for the alert icon + */ + alert_icon_id: PropTypes.string.isRequired, + + /** + * Alternate text for the alert icon + */ + alert_icon_alt_text: PropTypes.string.isRequired, + + /** + * heading text + */ + message_heading: PropTypes.string.isRequired, + + /** + * body text + */ + message_body: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.element, + PropTypes.arrayOf(PropTypes.element), + ]).isRequired, + + /** + * HTML toggle. Determines if text is parsed as plain text or HTML. + */ + asHtml: PropTypes.bool, + + /** + * If true the background will be white, default is transparent. + */ + whiteBG: PropTypes.bool, +}; diff --git a/components/design-system/Footer.jsx b/components/design-system/Footer.jsx new file mode 100644 index 0000000000..c4cfaeac88 --- /dev/null +++ b/components/design-system/Footer.jsx @@ -0,0 +1,144 @@ +/* eslint-disable no-prototype-builtins */ +import PropTypes from "prop-types"; +import { MainBand } from "./MainBand"; +import { SubFooterBand } from "./SubFooterBand"; + +let landscapeLinkKeys = [ + "contacts", + "departments", + "about", + "jobs", + "taxes", + "canadaAndWorld", + "immigration", + "environment", + "finance", + "travel", + "nationalSecurity", + "innovation", + "business", + "culture", + "indigenous", + "benefit", + "policing", + "veterans", + "health", + "transport", + "youth", +]; + +export function Footer(props) { + const { + error, + lang, + id, + isAuthenticated, + brandLinks, + target, + onClick, + btnLink, + } = props; + + return ( +
+
+
+ +
+
+ +
+ ); +} + +Footer.defaultProps = { + lang: "en", + contactLink: "https://www.canada.ca/en/contact.html", + withMainBand: true, +}; + +Footer.propTypes = { + /** + * id of this component + */ + id: PropTypes.string.isRequired, + /** + * isAuthenticated: bool to switch between authenticated and non authenticated menus + **/ + isAuthenticated: PropTypes.bool, + /** + * Switch between english and french footer. Pass in "en" or "fr" + */ + lang: PropTypes.oneOf(["en", "fr"]), + /** + * Add the path Link to the top of your page for the "to the Top" button in mobile + */ + btnLink: PropTypes.string.isRequired, + + /** + * containerClass: Customized container class name. If pass a existing class name, then 'ds-container' will be + * replaced by the passed in class name. + **/ + containerClass: PropTypes.string, + + /** + * If true will display the error page version of the footer component + */ + error: PropTypes.bool, + + /** + * Allow user to pass in their own contact link + */ + contactLink: PropTypes.string, + + /** + * List of links to display on the footer + */ + brandLinks: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string, + text: PropTypes.string, + href: PropTypes.string, + }) + ), + + /** + * Specifies where to open the linked document + */ + target: PropTypes.string, + + /** + * Handle onclick on the footer media link + */ + onClick: PropTypes.func, + + /** + * List of menu items to display in dropdown with links + */ + contextualBandLinks: PropTypes.arrayOf( + PropTypes.shape({ + key: PropTypes.string, + text: PropTypes.string, + link: PropTypes.string, + }) + ), +}; diff --git a/components/design-system/Heading.jsx b/components/design-system/Heading.jsx new file mode 100644 index 0000000000..ae6b7a7fa9 --- /dev/null +++ b/components/design-system/Heading.jsx @@ -0,0 +1,54 @@ +import PropTypes from "prop-types"; + +export function Heading(props) { + const { title, fromLink, fromText, id, className } = props; + + return ( + <> +

+ {title} +

+ {fromLink && fromText && ( +

+ From: + + {fromText} + +

+ )} + + ); +} + +Heading.propTypes = { + /** + * The text / title that will be displayed as heading + */ + title: PropTypes.string.isRequired, + /** + * Link that should be dispayed under the main heading level + */ + fromLink: PropTypes.string, + /** + * Text that will be displyed as text link + */ + fromText: PropTypes.string, + /** + * css overrides for button + */ + className: PropTypes.string, + /** + * To identify the heading element + */ + id: PropTypes.string.isRequired, + /** + * Test id for unit test + */ + dataTestId: PropTypes.string, +}; diff --git a/components/design-system/HelpIcon.jsx b/components/design-system/HelpIcon.jsx new file mode 100644 index 0000000000..13ff975c73 --- /dev/null +++ b/components/design-system/HelpIcon.jsx @@ -0,0 +1,67 @@ +import { useState } from "react"; +import { Modal } from "react-bootstrap"; +import { Button } from "./Button"; + +export const HelpIcon = ({ title, body, lang }) => { + const [showModal, setShowModal] = useState(false); + const handleClose = () => setShowModal(false); + const handleShow = () => setShowModal(true); + + return ( + <> + + + {showModal && ( +
+ + +

{title}

+ +
+ +

+ + +

+ )} + + ); +}; diff --git a/components/design-system/Image.jsx b/components/design-system/Image.jsx new file mode 100644 index 0000000000..0058e9aa81 --- /dev/null +++ b/components/design-system/Image.jsx @@ -0,0 +1,38 @@ +import PropTypes from "prop-types"; + +export function Image(props) { + const { src, alt, rounded = "", className = "" } = props; + + return ( + <> + {alt} + + ); +} + +Image.propTypes = { + /** + * The text that the image tag will display + */ + id: PropTypes.string, + + /** + * Alternate text will be displayed in place of an image. + */ + alt: PropTypes.string, + + /** + * Identify source of image + */ + src: PropTypes.string.isRequired, + + /** + * css overrides for image + */ + className: PropTypes.string, + + /** + * Enables rounded corners for image + */ + rounded: PropTypes.string, +}; diff --git a/components/design-system/Link.jsx b/components/design-system/Link.jsx new file mode 100644 index 0000000000..20a944786e --- /dev/null +++ b/components/design-system/Link.jsx @@ -0,0 +1,146 @@ +/* eslint-disable jsx-a11y/anchor-is-valid */ +import PropTypes from "prop-types"; + +export function Link(props) { + //Styling for links based on Figma Design + let basicStyle = ""; + switch (props.linkStyle) { + case "basicStyleWithEmphasis": + basicStyle = + "underline text-multi-blue-blue70b font-body text-browserh5 font-bold text-mobileh5 leading-33px hover:text-multi-blue-blue50b"; + break; + case "titleLink": + basicStyle = + "underline text-multi-blue-blue70b font-header text-browserh5 leading-23px font-bold hover:text-multi-blue-blue50b"; + break; + case "smfooterBlue": + basicStyle = + "text-multi-blue-blue70b font-body leading-20px text-browserh7 hover:underline"; + break; + case "smfooterWhite": + basicStyle = + "text-multi-neutrals-white font-body text-browserh7 leading-20px font-regular hover:text-multi-neutrals-white hover:underline focus:ring-1 focus:ring-white"; + break; + case "smBreadcrumbs": + basicStyle = + "text-multi-blue-blue70b font-body text-browserh8 leading-23px font-regular hover:text-multi-blue-blue50b"; + break; + case "cardActionLink": + basicStyle = + "text-multi-blue-blue70b font-body text-browserh5 underline leading-28px font-regular hover:text-multi-blue-blue50b"; + break; + default: + basicStyle = + "underline text-multi-blue-blue70b font-body text-browserh5 leading-33px hover:text-multi-blue-blue50b"; + break; + } + + const Component = props.component || "a"; + + function onKeyDown() { + true; + } + + return Component !== "a" ? ( + + + {/* */} + + {props.text} + + {/* */} + + {props.abbr} + + + + ) : ( + + {/* */} + + {props.text} + + {/* */} + + {props.abbr} + + + ); +} + +Link.defaultProps = { + target: "_self", + href: "#", +}; + +Link.propTypes = { + /** + * The text that Text Link will display + */ + text: PropTypes.string, + /** + * Abbrivation for text + */ + abbr: PropTypes.string, + /** + * Style link as a Text Link when there's a href + */ + href: PropTypes.string, + /** + * Target attribute to tell the browser where the linked document should be loaded. + */ + target: PropTypes.string, + /** + * Identify which Text Link being clicked + */ + id: PropTypes.string.isRequired, + /** + * Lang attribute for links that do not match the language of the top level document + */ + lang: PropTypes.string, + /** + * css overrides for Link + */ + className: PropTypes.string, + + /** + * For tracking on click of forms for analytics + */ + analyticsTracking: PropTypes.bool, + + /** + * use ariaLabel to provide more descriptive text for a link (screen reader friendly) + */ + ariaLabel: PropTypes.string, + + /** + * Allow user to use configurable component, default is html anchor tag + */ + component: PropTypes.elementType, +}; diff --git a/components/design-system/MainBand.jsx b/components/design-system/MainBand.jsx new file mode 100644 index 0000000000..0972deb1d5 --- /dev/null +++ b/components/design-system/MainBand.jsx @@ -0,0 +1,43 @@ +import { Link } from "./Link"; +import { useTranslation } from "next-i18next"; + +export function MainBand(props) { + const { t } = useTranslation("common"); + + return ( + <> +

+ {t("footerTitle")} +

+ + + ); +} diff --git a/components/design-system/SubFooterBand.jsx b/components/design-system/SubFooterBand.jsx new file mode 100644 index 0000000000..fd45ff9be3 --- /dev/null +++ b/components/design-system/SubFooterBand.jsx @@ -0,0 +1,88 @@ +import { Link } from "./Link"; +import { Image } from "./Image"; + +export function SubFooterBand(props) { + return ( +
+
+ {props.error ? ( + + ) : ( +
+ +
+ )} +
+ Symbol of the Government of Canada +
+
+
+ ); +} diff --git a/components/organisms/Layout.js b/components/organisms/Layout.js index 9443c8ddcf..f0d72d4ac3 100644 --- a/components/organisms/Layout.js +++ b/components/organisms/Layout.js @@ -1,6 +1,6 @@ import PropTypes from "prop-types"; import { Banner } from "../atoms/Banner"; -import { Footer } from "@dts-stn/service-canada-design-system"; +// import { Footer } from "@dts-stn/service-canada-design-system"; import { PhaseBanner } from "./PhaseBanner"; import { ReportAProblem } from "./ReportAProblem"; import Link from "next/link"; @@ -8,6 +8,8 @@ import { useTranslation } from "next-i18next"; import { DateModified } from "../atoms/DateModified"; import { Breadcrumb } from "../atoms/Breadcrumb"; +import { Footer } from "../design-system/Footer"; + /** * Component which defines the layout of the page for all screen sizes */ @@ -33,16 +35,15 @@ export const Layout = ({ return (
-