Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Publisher][Design Update] Agency Settings feature #1336

Merged
merged 5 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,6 @@ yalc.lock

# ESLint Cache
.eslintcache

#Webstorm config settings
.idea
3 changes: 3 additions & 0 deletions common/assets/close-icon-lg.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 16 additions & 3 deletions common/components/Button/Button.styled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const Button = styled.div<{
noSidePadding?: boolean;
noTopBottomPadding?: boolean;
noHover?: boolean;
agencySettingsConfigs?: boolean;
}>`
${({ size }) => (size ? typography.sizeCSS[size] : typography.body)};
display: flex;
Expand All @@ -46,7 +47,8 @@ export const Button = styled.div<{
border-radius: 3px;
gap: 8px;
white-space: nowrap;
min-width: 80px;
min-width: ${({ agencySettingsConfigs }) =>
agencySettingsConfigs ? "unset" : "80px"};
pointer-events: ${({ disabled }) => disabled && "none"};
background-color: ${({ buttonColor, disabled }) => {
if (disabled) return palette.highlight.grey3;
Expand All @@ -66,7 +68,17 @@ export const Button = styled.div<{
}
return "none";
}};
padding: ${({ size }) => (size === "medium" ? "16px 32px" : "10px 15px")};

padding: ${({ size, agencySettingsConfigs }) => {
if (agencySettingsConfigs) {
return "0";
}
if (size) {
return "16px 32px";
}
return "10px 15px";
}};

${({ noSidePadding }) =>
noSidePadding && "padding-left: 0; padding-right: 0;"}
${({ noTopBottomPadding }) =>
Expand All @@ -79,8 +91,9 @@ export const Button = styled.div<{
return `color: ${palette.solid.darkblue}`;
}
}};
${({ buttonColor, noHover }) => {
${({ buttonColor, noHover, agencySettingsConfigs }) => {
if (buttonColor) return "opacity: 0.8;";
if (agencySettingsConfigs) return "opacity: unset";
return !noHover && `background-color: ${palette.solid.lightgrey2};`;
}}
a {
Expand Down
3 changes: 3 additions & 0 deletions common/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export type ButtonProps = {
noTopBottomPadding?: boolean;
noHover?: boolean;
tooltipMsg?: string;
agencySettingsConfigs?: boolean;
};

export function Button({
Expand All @@ -54,6 +55,7 @@ export function Button({
noTopBottomPadding,
noHover,
tooltipMsg,
agencySettingsConfigs,
}: ButtonProps) {
return (
<Styled.ButtonWrapper id={id}>
Expand All @@ -67,6 +69,7 @@ export function Button({
noSidePadding={noSidePadding}
noTopBottomPadding={noTopBottomPadding}
noHover={noHover}
agencySettingsConfigs={agencySettingsConfigs}
>
{label}
</Styled.Button>
Expand Down
5 changes: 3 additions & 2 deletions common/components/Input/Input.styled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,9 @@ export const NewInputLabel = styled.label<{ error: boolean }>`
`};
`;

export const ErrorMessage = styled.div`
export const ErrorMessage = styled.div<{ settingsCustomMargin?: boolean }>`
${typography.body}
color: ${palette.solid.red};
margin-top: -4px;
margin-top: ${({ settingsCustomMargin }) =>
settingsCustomMargin ? "-4px" : "8px"};
`;
12 changes: 10 additions & 2 deletions common/components/Input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import * as Styled from "./Input.styled";
import { InputTextSize, NotReportedIconTooltipProps } from "./types";

interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
label: string;
label?: string;
noBottomMargin?: boolean;
error?: FormError;
valueLabel?: string;
Expand All @@ -41,6 +41,8 @@ interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
textSize?: InputTextSize;
hideLabel?: boolean;
fullWidth?: boolean;
agencySettingsConfigs?: boolean;
settingsCustomMargin?: boolean;
}

export function Input({
Expand Down Expand Up @@ -197,6 +199,8 @@ export function NewInput({
metricKey,
hideLabel,
fullWidth,
agencySettingsConfigs,
settingsCustomMargin,
...props
}: InputProps) {
return (
Expand All @@ -219,7 +223,11 @@ export function NewInput({
error={Boolean(error)}
fullWidth={fullWidth}
/>
{error && <Styled.ErrorMessage>{error.message}</Styled.ErrorMessage>}
{error && (
<Styled.ErrorMessage settingsCustomMargin={settingsCustomMargin}>
{error.message}
</Styled.ErrorMessage>
)}
</Styled.NewInputWrapper>
);
}
62 changes: 57 additions & 5 deletions common/components/Modal/Modal.styled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export const InnerWrapper = styled.div<{
modalType?: ModalType;
centerText?: boolean;
customPadding?: string;
noBottomDiv?: boolean;
}>`
background-color: ${palette.solid.white};
width: 100%;
Expand All @@ -71,6 +72,11 @@ export const InnerWrapper = styled.div<{
${({ centerText }) => centerText && `align-items: center;`};
border-radius: 3px;
${centerTextCSS}
${({ noBottomDiv }) => {
if (noBottomDiv) {
return `& > div:last-child {display: none;}`;
}
}};
`;

export const Icon = styled.img`
Expand All @@ -79,21 +85,45 @@ export const Icon = styled.img`
margin-bottom: 24px;
`;

export const Title = styled.div<{ mediumTitle?: boolean }>`
export const Title = styled.div<{
mediumTitle?: boolean;
}>`
${({ mediumTitle }) =>
mediumTitle ? typography.sizeCSS.medium : typography.sizeCSS.large};
mediumTitle
? `${typography.sizeCSS.medium}`
: `${typography.sizeCSS.large}`};
margin-top: 16px;
margin-bottom: 16px;
a {
color: ${palette.solid.blue};
text-decoration: none;
}
`;

export const AgencySettingsAndJurisdictionsTitle = styled.div`
${typography.bodyEmphasized}
margin-bottom: 16px;
margin-top: 16px;
a {
color: ${palette.solid.blue};
text-decoration: none;
}
`;

export const Description = styled.div<{ centerText?: boolean }>`
export const Description = styled.div<{
centerText?: boolean;
unsetTextAlignment?: boolean;
}>`
display: flex;
flex-direction: column;
align-items: center;
align-items: ${({ centerText, unsetTextAlignment }) => {
if (centerText) {
return `align-items: center;`;
}
if (unsetTextAlignment) {
return `align-items: unset;`;
}
}};
${typography.sizeCSS.normal};
`;

Expand All @@ -104,7 +134,6 @@ export const ButtonsContainer = styled.div<{
width: 100%;
display: flex;
gap: 12px;

${({ modalType, centerButtons }) => {
if (centerButtons) return "justify-content: center; margin-top: 72px;";
if (modalType) return "justify-content: space-between; margin-top: 72px;";
Expand All @@ -116,3 +145,26 @@ export const ButtonsContainer = styled.div<{
margin-left: auto;
}
`;

export const UnsavedChangesButtonsContainer = styled.div`
width: 100%;
display: flex;
justify-content: end;
gap: 16px;
margin-top: 24px;
& > div:first-child {
div {
border: none;
}
}
`;

export const ModalTitleWrapper = styled.div<{
typographyBodyEmphasized?: boolean;
}>`
display: flex;
flex-direction: row;
justify-content: space-between;
${({ typographyBodyEmphasized }) =>
typographyBodyEmphasized ? `${typography.bodyEmphasized}` : ""};
`;
83 changes: 67 additions & 16 deletions common/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import React, { useEffect } from "react";
import { createPortal } from "react-dom";

import alertIcon from "../../assets/alert-icon.png";
import xCloseLg from "../../assets/close-icon-lg.svg";
import successIcon from "../../assets/success-icon.png";
import warningIcon from "../../assets/warning-icon.svg";
import { Button, ButtonColor } from "../Button";
Expand All @@ -36,6 +37,12 @@ type ModalProps = Partial<{
mediumTitle: boolean;
customPadding?: string;
children?: React.ReactNode;
onClickClose?: () => void;
agencySettingsConfigs?: boolean;
unsavedChangesConfigs?: boolean;
jurisdictionsSettingsConfigs?: boolean;
agencySettingsAndJurisdictionsTitleConfigs?: boolean;
noBottomDiv?: boolean;
}>;

export function Modal({
Expand All @@ -49,6 +56,11 @@ export function Modal({
mediumTitle,
customPadding,
children,
onClickClose,
unsavedChangesConfigs,
jurisdictionsSettingsConfigs,
agencySettingsAndJurisdictionsTitleConfigs,
noBottomDiv,
}: ModalProps) {
const primaryButtonColor = (): ButtonColor => {
if (modalType === "alert") return "red";
Expand All @@ -71,28 +83,67 @@ export function Modal({
modalType={modalType}
centerText={centerText}
customPadding={customPadding}
noBottomDiv={noBottomDiv}
>
{modalType === "success" && <Styled.Icon src={successIcon} alt="" />}
{modalType === "warning" && <Styled.Icon src={warningIcon} alt="" />}
{modalType === "alert" && <Styled.Icon src={alertIcon} alt="" />}
<Styled.Title mediumTitle={mediumTitle}>{title}</Styled.Title>
<Styled.Description>{description}</Styled.Description>
<Styled.ButtonsContainer modalType={modalType}>
{buttons?.map((button, index) => (
<Styled.ModalTitleWrapper typographyBodyEmphasized>
{agencySettingsAndJurisdictionsTitleConfigs && (
<Styled.AgencySettingsAndJurisdictionsTitle>
{title}
</Styled.AgencySettingsAndJurisdictionsTitle>
)}
{!agencySettingsAndJurisdictionsTitleConfigs && (
<Styled.Title mediumTitle={mediumTitle}>{title}</Styled.Title>
)}
{onClickClose && (
<Button
label={button.label}
onClick={button.onClick}
borderColor={
index === buttons.length - 1 ? undefined : "lightgrey"
}
buttonColor={
index === buttons.length - 1
? primaryButtonColor()
: undefined
}
label={<img src={xCloseLg} alt="Close Button" />}
onClick={onClickClose}
agencySettingsConfigs
/>
))}
</Styled.ButtonsContainer>
)}
</Styled.ModalTitleWrapper>
<Styled.Description unsetTextAlignment={jurisdictionsSettingsConfigs}>
{description}
</Styled.Description>
{!unsavedChangesConfigs && (
<Styled.ButtonsContainer modalType={modalType}>
{buttons?.map((button, index) => (
<Button
label={button.label}
onClick={button.onClick}
borderColor={
index === buttons.length - 1 ? undefined : "lightgrey"
}
buttonColor={
index === buttons.length - 1
? primaryButtonColor()
: undefined
}
/>
))}
</Styled.ButtonsContainer>
)}
{unsavedChangesConfigs && (
<Styled.UnsavedChangesButtonsContainer>
{buttons?.map((button, index) => (
<Button
label={button.label}
onClick={button.onClick}
borderColor={
index === buttons.length - 1 ? undefined : "lightgrey"
}
buttonColor={
index === buttons.length - 1
? primaryButtonColor()
: undefined
}
/>
))}
</Styled.UnsavedChangesButtonsContainer>
)}
</Styled.InnerWrapper>
)}
</Styled.OuterWrapper>
Expand Down
5 changes: 5 additions & 0 deletions common/utils/helperUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,11 @@ export const validateEmail = (email: string) => {
);
};

export const validateAgencyURL = (url: string) => {
const urlRegex = /^(?:(?:https?|ftp):\/\/)?[\w.-]+\.[a-zA-Z]{2,}(?:\/\S*)?$/;
return urlRegex.test(url);
};

/**
* Updates a set of selections by either adding or removing a specified item.
* @param prevSet - The previous set of selected items to update
Expand Down
28 changes: 28 additions & 0 deletions publisher/src/components/Global/GlobalStyles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2023 Recidiviz, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

import { typography } from "@justice-counts/common/components/GlobalStyles";
import styled from "styled-components/macro";

export const GlobalTitle = styled.div`
${typography.sizeCSS.title};
`;

export const GlobalDescription = styled.div`
${typography.paragraph};
margin-bottom: 24px;
`;
Loading
Loading