diff --git a/packages/building-blocks/i18n/en-US.yml b/packages/building-blocks/i18n/en-US.yml
new file mode 100644
index 000000000..153b1e162
--- /dev/null
+++ b/packages/building-blocks/i18n/en-US.yml
@@ -0,0 +1,4 @@
+otpUi:
+ buildingBlocks:
+ alert:
+ expand: Expand
diff --git a/packages/building-blocks/i18n/fr.yml b/packages/building-blocks/i18n/fr.yml
new file mode 100644
index 000000000..87a926396
--- /dev/null
+++ b/packages/building-blocks/i18n/fr.yml
@@ -0,0 +1,4 @@
+otpUi:
+ buildingBlocks:
+ alert:
+ expand: Développer
diff --git a/packages/building-blocks/package.json b/packages/building-blocks/package.json
index 9ee6fc64d..c33248d0a 100644
--- a/packages/building-blocks/package.json
+++ b/packages/building-blocks/package.json
@@ -14,6 +14,7 @@
},
"peerDependencies": {
"react": "^18.2.0",
+ "react-animate-height": "^3.0.4",
"styled-components": "^5.3.0"
},
"repository": {
@@ -27,5 +28,8 @@
"bugs": {
"url": "https://github.com/opentripplanner/otp-ui/issues"
},
- "gitHead": "0af1b7cda60bd4252b219dcf893e01c2acb2ed5d"
+ "gitHead": "0af1b7cda60bd4252b219dcf893e01c2acb2ed5d",
+ "dependencies": {
+ "@styled-icons/bootstrap": "^10.47.0"
+ }
}
diff --git a/packages/building-blocks/src/alert/Alert.tsx b/packages/building-blocks/src/alert/Alert.tsx
new file mode 100644
index 000000000..7a5f4eef6
--- /dev/null
+++ b/packages/building-blocks/src/alert/Alert.tsx
@@ -0,0 +1,136 @@
+import React, { ReactChild, useState } from "react";
+import styled from "styled-components";
+import AnimateHeight from "react-animate-height";
+import { ChevronUp } from "@styled-icons/bootstrap/ChevronUp";
+import { Bell } from "@styled-icons/bootstrap/Bell";
+import { StyledIcon } from "@styled-icons/styled-icon";
+import { useIntl } from "react-intl";
+import blue from "../colors/blue";
+
+interface Props {
+ alertHeader: string;
+ alertSubheader?: string;
+ backgroundColor?: string;
+ collapsible?: boolean;
+ children?: ReactChild;
+ Icon?: StyledIcon;
+}
+
+const AlertContainer = styled.div<{
+ backgroundColor: string;
+ collapsible: boolean;
+ expandAlert: boolean;
+}>`
+ background-color: ${props =>
+ props.backgroundColor ? props.backgroundColor : blue[50]};
+ display: grid;
+ grid-template-columns: 50px auto auto;
+ grid-template-rows: minmax(25px, auto) auto;
+ max-width: 715px;
+ padding: 1.5em 2em;
+
+ svg {
+ align-self: center;
+ grid-column: 1;
+ grid-row: 1 / span 2;
+ justify-self: start;
+ }
+
+ button {
+ background: transparent;
+ border: none;
+ width: 40px;
+
+ svg {
+ transform: ${props => !props.expandAlert && "rotate(180deg)"};
+ transition: all 0.2s ease-in;
+ }
+ }
+
+ @media (max-width: 550px) {
+ grid-template-columns: 40px auto auto;
+ padding: 1.25em 1.25em;
+ }
+`;
+
+const ButtonContainer = styled.span`
+ align-items: center;
+ display: flex;
+ justify-content: center;
+ justify-self: right;
+`;
+
+const AlertHeader = styled.span`
+ align-self: center;
+ font-weight: 700;
+ grid-column: 2;
+`;
+
+const AlertSubheader = styled(AlertHeader)`
+ font-weight: 400;
+ margin-top: 0.3em;
+`;
+
+const AlertContent = styled.div`
+ grid-column: 2;
+ grid-row: 3;
+`;
+
+const ContentPadding = styled.div<{
+ collapsible: boolean;
+}>`
+ margin-top: ${props => (props.collapsible ? "1em" : ".5em")};
+`;
+
+const Alert = ({
+ alertHeader,
+ alertSubheader,
+ backgroundColor,
+ children,
+ collapsible,
+ Icon = Bell
+}: Props): JSX.Element => {
+ const [expandAlert, setExpandAlert] = useState(false);
+ const intl = useIntl();
+ const label = intl.formatMessage({
+ id: "otpUi.buildingBlocks.alert.expand",
+ defaultMessage: "Expand"
+ });
+ return (
+