diff --git a/src/components/Button/Button.stories.js b/src/components/Button/Button.stories.js index 0e6ead51..a44d5264 100644 --- a/src/components/Button/Button.stories.js +++ b/src/components/Button/Button.stories.js @@ -13,7 +13,7 @@ export const Supertask = Template.bind({}); export const SupertaskIcon = Template.bind({}); export const Primary = Template.bind({}); export const Secondary = Template.bind({}); -export const DangerDisabled = Template.bind({}); +export const Danger = Template.bind({}); export const Link = Template.bind({}); Supertask.args = { @@ -47,12 +47,11 @@ Secondary.args = { styling: "secondary", }; -DangerDisabled.args = { +Danger.args = { id: "danger", text: "Danger Button", + iconEnd: false, styling: "danger", - disabled: true, - iconAltText: "disabled", }; Link.args = { diff --git a/src/components/Button/Button.test.js b/src/components/Button/Button.test.js index 5d813146..95aa5961 100644 --- a/src/components/Button/Button.test.js +++ b/src/components/Button/Button.test.js @@ -6,13 +6,7 @@ import React from "react"; import { render, screen } from "@testing-library/react"; import "@testing-library/jest-dom/extend-expect"; import { axe, toHaveNoViolations } from "jest-axe"; -import { - Primary, - Secondary, - DangerDisabled, - Link, - Supertask, -} from "./Button.stories"; +import { Primary, Secondary, Danger, Link, Supertask } from "./Button.stories"; expect.extend(toHaveNoViolations); @@ -43,10 +37,8 @@ describe("Button", () => { }); it("renders danger", () => { - render(); - expect(screen.getByRole("button")).toHaveTextContent( - DangerDisabled.args.text - ); + render(); + expect(screen.getByRole("button")).toHaveTextContent(Danger.args.text); expect(screen.getByRole("button")).toHaveClass("ds-btn-danger"); }); diff --git a/src/components/ContextualAlert/ContextualAlert.js b/src/components/ContextualAlert/ContextualAlert.js index 9c38e04b..0d7a3349 100644 --- a/src/components/ContextualAlert/ContextualAlert.js +++ b/src/components/ContextualAlert/ContextualAlert.js @@ -50,19 +50,19 @@ export function ContextualAlert(props) { > {asHtml ? (

) : ( -

{message_heading}

+

{message_heading}

)} {asHtml ? (
) : ( -
{message_body}
+
{message_body}
)}
diff --git a/src/components/FormErrorSummary/FormErrorSummary.js b/src/components/FormErrorSummary/FormErrorSummary.js new file mode 100644 index 00000000..7d9cab4c --- /dev/null +++ b/src/components/FormErrorSummary/FormErrorSummary.js @@ -0,0 +1,75 @@ +import PropTypes from "prop-types"; +import React from "react"; +import { ContextualAlert } from "../ContextualAlert/ContextualAlert"; + +export function FormErrorSummary(props) { + const { + message_heading, + id, + alert_icon_id, + alert_icon_alt_text, + error_list, + whiteBG, + } = props; + + return ( + + {error_list.map(({ line, id }, i) => ( + +
  • +

    + {line} +

    +
  • +
    + ))} + , + ]} + /> + ); +} + +FormErrorSummary.propTypes = { + /** + * component id + */ + id: PropTypes.string.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, + + /** + * An array of plaintext error messages and corresponding DOM ids for anchor links + */ + + error_list: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string, + line: PropTypes.string, + }) + ).isRequired, + /** + * If true the background will be white, default is transparent. + */ + whiteBG: PropTypes.bool, +}; diff --git a/src/components/FormErrorSummary/FormErrorSummary.stories.js b/src/components/FormErrorSummary/FormErrorSummary.stories.js new file mode 100644 index 00000000..35c0898c --- /dev/null +++ b/src/components/FormErrorSummary/FormErrorSummary.stories.js @@ -0,0 +1,30 @@ +import { FormErrorSummary } from "./FormErrorSummary"; +export default { + title: "Components/FormErrorSummary", + component: FormErrorSummary, +}; + +const Template = (args) => ; + +export const Default = Template.bind({}); + +Default.args = { + id: "formerrors", + error_list: [ + { line: "Last name is required", id: "last_name" }, + { + line: "Email address must be in the format of example@email.com", + id: "email", + }, + { line: "Password must include both numbers and letters", id: "password" }, + { + line: "A valid postal code is required for your selected city", + id: "postal_code", + }, + ], + message_heading: + "The form could not be submitted because 4 errors were found", + alert_icon_alt_text: "danger icon", + alert_icon_id: "danger icon", + whiteBG: false, +}; diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index e319942f..d0bb615d 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -26,6 +26,7 @@ export function Header(props) { topnavProps, useParentContainer, customLink, + dataGcAnalyticsCustomClickInstitutionVariable, } = props; const containerClass = useParentContainer ? "" : "ds-container"; @@ -63,7 +64,7 @@ export function Header(props) { locale={locale} customLink={customLink} dataGcAnalyticsCustomClickInstitutionVariable={ - props.dataGcAnalyticsCustomClickInstitutionVariable + dataGcAnalyticsCustomClickInstitutionVariable } /> @@ -85,7 +86,7 @@ export function Header(props) { customLink={customLink} locale={locale} dataGcAnalyticsCustomClickInstitutionVariable={ - props.dataGcAnalyticsCustomClickInstitutionVariable + dataGcAnalyticsCustomClickInstitutionVariable } /> @@ -99,7 +100,7 @@ export function Header(props) { customLink={customLink} locale={locale} dataGcAnalyticsCustomClickInstitutionVariable={ - props.dataGcAnalyticsCustomClickInstitutionVariable + dataGcAnalyticsCustomClickInstitutionVariable } /> @@ -112,7 +113,7 @@ export function Header(props) { menuList={menuProps.menuList} onSignOut={menuProps.onSignOut} dataGcAnalyticsCustomClickInstitutionVariable={ - props.dataGcAnalyticsCustomClickInstitutionVariable + dataGcAnalyticsCustomClickInstitutionVariable } /> )} @@ -253,6 +254,11 @@ Header.propTypes = { }) ), + /** + * Prefix for Adobe Analytics tag + */ + dataGcAnalyticsCustomClickInstitutionVariable: PropTypes.string, + /** * Test id for unit test */ diff --git a/src/components/Header/Header.stories.js b/src/components/Header/Header.stories.js index c8c238a5..4b09dc5b 100644 --- a/src/components/Header/Header.stories.js +++ b/src/components/Header/Header.stories.js @@ -12,6 +12,7 @@ export const FrenchAuth = Template.bind({}); EnglishAuth.args = { id: "header", + dataGcAnalyticsCustomClickInstitutionVariable: "Institution", lang: "en", menuProps: { menuList: [ @@ -49,6 +50,7 @@ EnglishAuth.args = { FrenchAuth.args = { id: "header", + dataGcAnalyticsCustomClickInstitutionVariable: "Institution", lang: "fr", menuProps: { menuList: [ diff --git a/src/components/Menu/Menu.js b/src/components/Menu/Menu.js index 5b636e9d..d130f4f1 100644 --- a/src/components/Menu/Menu.js +++ b/src/components/Menu/Menu.js @@ -11,8 +11,16 @@ import FR from "../../translations/fr.json"; * Menu component */ export function Menu(props) { - const { onClick, isAuthenticated, menuList, lang, onSignOut, demoBuffer } = - props; + const { + onClick, + isAuthenticated, + menuList, + lang, + onSignOut, + demoBuffer, + dataGcAnalyticsCustomClickInstitutionVariable, + } = props; + const [showDropdown, setShowDropdown] = useState(false); const dropdown = useRef(null); @@ -53,11 +61,7 @@ export function Menu(props) { onClick={() => setShowDropdown((e) => !e)} data-gc-analytics-customclick={`${ props.dataGcAnalyticsCustomClickInstitutionVariable - }:${ - showDropdown - ? "Menu Contract-Diminuer Menu" - : "Expand Menu-Etendre Menu" - }`} + }:${showDropdown ? "Menu Contract" : "Expand Menu"}`} aria-haspopup="true" data-testid="menuButton" aria-expanded={showDropdown} @@ -119,7 +123,7 @@ export function Menu(props) { index === 0 ? "ds-border-none" : "ds-border-t-2" } ds-font-body ds-flex ds-items-center ds-h-[55px] ds-px-4 hover:ds-text-[#0535D2] focus:ds-outline-none ds-ring-offset-2 focus:ds-ring-2 ds-ring-[#0535D2] ds-rounded-sm focus:ds-border-none`} onClick={element.showIcon ? onSignOut : onClick} - data-gc-analytics-customclick={`${props.dataGcAnalyticsCustomClickInstitutionVariable}:Menu-${element.value}`} + data-gc-analytics-customclick={`${props.dataGcAnalyticsCustomClickInstitutionVariable}:Menu-${element.id}`} > {element.showIcon && ( {element.showIcon && (