Skip to content

Commit

Permalink
Create structure of the app
Browse files Browse the repository at this point in the history
  • Loading branch information
anagperal committed Jun 25, 2024
1 parent 3bb0eb6 commit decc797
Show file tree
Hide file tree
Showing 18 changed files with 465 additions and 113 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@emotion/react": "11.11.4",
"@emotion/styled": "11.11.5",
"@eyeseetea/d2-api": "1.14.0",
"@eyeseetea/d2-ui-components": "2.7.0",
"@eyeseetea/d2-ui-components": "v2.9.0-beta.2",
"@eyeseetea/feedback-component": "0.0.3",
"@material-ui/core": "4.12.4",
"@material-ui/icons": "4.11.3",
Expand Down
91 changes: 91 additions & 0 deletions src/webapp/components/form-page/FormPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React from "react";
import styled from "styled-components";

import i18n from "../../../utils/i18n";
import { Button } from "../button/Button";
import { Separator } from "../separator/Separator";

interface FormPageProps {
title?: string;
subtitle?: string;
saveLabel?: string;
cancelLabel?: string;
children: React.ReactNode;
onSave: () => void;
onCancel: () => void;
}

export const FormPage: React.FC<FormPageProps> = React.memo(
({ title, subtitle = "", saveLabel = "", cancelLabel = "", children, onSave, onCancel }) => {
return (
<StyledFormPage>
<Header>
<TitleContainer>
{title ? <Title>{title}</Title> : null}
{subtitle ? <Subtitle>{subtitle}</Subtitle> : null}
</TitleContainer>
<RequiredText>{i18n.t("Indicates required")}</RequiredText>
</Header>
<Content>{children}</Content>
<Footer>
<Separator margin="12px" />
<ButtonsFooter>
<Button onClick={onSave}>{saveLabel || i18n.t("Save")}</Button>
<Button onClick={onCancel} variant="outlined" color="secondary">
{cancelLabel || i18n.t("Cancel")}
</Button>
</ButtonsFooter>
</Footer>
</StyledFormPage>
);
}
);

const StyledFormPage = styled.div`
width: 100%;
`;

const Header = styled.div`
display: flex;
justify-content: space-between;
`;

const Content = styled.div`
width: 100%;
`;

const Footer = styled.div``;

const ButtonsFooter = styled.div`
margin-block-start: 48px;
display: flex;
gap: 48px;
`;

const TitleContainer = styled.div`
display: flex;
gap: 4px;
`;

const Title = styled.span`
color: ${props => props.theme.palette.common.grey700};
font-size: 0.875rem;
font-weight: 700;
`;

const Subtitle = styled.span`
color: ${props => props.theme.palette.common.grey700};
font-size: 0.875rem;
font-weight: 400;
`;

const RequiredText = styled.span`
color: ${props => props.theme.palette.common.black};
font-size: 0.875rem;
font-weight: 700;
&::before {
content: "*";
color: ${props => props.theme.palette.common.red};
margin-inline-end: 4px;
}
`;
66 changes: 66 additions & 0 deletions src/webapp/components/form-section/FormSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from "react";
import styled from "styled-components";
import { IconInfo24 } from "@dhis2/ui";

import { Separator } from "../separator/Separator";
import { IconButton } from "../icon-button/IconButton";

interface FormSectionProps {
title: string;
children: React.ReactNode;
hasSeparator?: boolean;
onClickInfo?: () => void;
direction?: "row" | "column";
}

export const FormSection: React.FC<FormSectionProps> = React.memo(
({ title, hasSeparator = false, children, onClickInfo, direction = "row" }) => {
return (
<FormSectionContainer>
{hasSeparator && <Separator margin="12px" />}
<Container direction={direction}>
<TitleContainer>
<RequiredText>{title}</RequiredText>
{onClickInfo && <IconButton icon={<IconInfo24 />} onClick={onClickInfo} />}
</TitleContainer>
<FormContainer>{children}</FormContainer>
</Container>
</FormSectionContainer>
);
}
);

const FormSectionContainer = styled.div`
width: 100%;
margin-block-end: 24px;
`;

const Container = styled.div<{ direction: string }>`
display: flex;
flex-direction: ${props => props.direction};
width: 100%;
gap: ${props => (props.direction === "row" ? "48px" : "24px")};
align-items: ${props => (props.direction === "row" ? "center" : "flex-start")};
`;

const TitleContainer = styled.div`
display: flex;
align-items: center;
gap: 4px;
`;

const FormContainer = styled.div`
width: 100%;
`;

const RequiredText = styled.span`
color: ${props => props.theme.palette.common.black};
font-size: 0.875rem;
font-weight: 700;
white-space: nowrap;
&::after {
content: "*";
color: ${props => props.theme.palette.common.red};
margin-inline-start: 4px;
}
`;
24 changes: 24 additions & 0 deletions src/webapp/components/icon-button/IconButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from "react";
import { IconButton as MUIIconButton } from "@material-ui/core";
import styled from "styled-components";

interface IconButtonProps {
icon: React.ReactNode;
disabled?: boolean;
onClick: () => void;
}

export const IconButton: React.FC<IconButtonProps> = React.memo(
({ icon, disabled = false, onClick }) => {
return (
<StyledIconButton disabled={disabled} onClick={onClick}>
{icon}
</StyledIconButton>
);
}
);

const StyledIconButton = styled(MUIIconButton)`
color: ${props => props.theme.palette.icon.color};
padding: 0;
`;
2 changes: 1 addition & 1 deletion src/webapp/components/layout/main-content/MainContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const SubTitle = styled.span`
`;

const PageContent = styled.div`
margin-block-start: 64px;
margin-block-start: 48px;
`;

const Main = styled.main`
Expand Down
2 changes: 1 addition & 1 deletion src/webapp/components/notice-box/NoticeBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export const NoticeBox: React.FC<NoticeBoxProps> = React.memo(({ title, children
const Container = styled.div`
display: flex;
flex-direction: column;
width: 100%;
padding-inline: 16px;
padding-block: 12px;
gap: 8px;
Expand All @@ -44,6 +43,7 @@ const TitleContainer = styled.div`
`;

const Content = styled.div`
margin-inline-start: 32px;
font-size: 0.875rem;
font-weight: 400;
color: ${props => props.theme.palette.text.primary};
Expand Down
87 changes: 87 additions & 0 deletions src/webapp/components/section/Section.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React from "react";
import styled from "styled-components";

import i18n from "../../../utils/i18n";
import { Separator } from "../separator/Separator";

interface SectionProps {
title?: string;
lastUpdated?: string;
children: React.ReactNode;
headerButtom?: React.ReactNode;
hasSeparator?: boolean;
titleVariant?: "primary" | "secondary";
}

export const Section: React.FC<SectionProps> = React.memo(
({
title = "",
lastUpdated = "",
headerButtom,
hasSeparator = false,
children,
titleVariant = "primary",
}) => {
return (
<SectionContainer $hasSeparator={hasSeparator}>
{hasSeparator && <Separator />}
<Header>
<TitleContainer>
{title ? <Title $titleVariant={titleVariant}>{title}</Title> : null}
{lastUpdated ? (
<LastUpdatedContainer>
<LastUpdatedTitle>{i18n.t("Last updated: ")}</LastUpdatedTitle>
{lastUpdated}
</LastUpdatedContainer>
) : null}
</TitleContainer>
{headerButtom ? <div>{headerButtom}</div> : null}
</Header>
<Content>{children}</Content>
</SectionContainer>
);
}
);

const SectionContainer = styled.section<{ $hasSeparator?: boolean }>`
width: 100%;
margin-block-end: ${props => (props.$hasSeparator ? "0" : "24px")};
`;

const Header = styled.div`
display: flex;
justify-content: space-between;
`;

const TitleContainer = styled.div`
display: flex;
flex-direction: column;
`;

const Title = styled.span<{ $titleVariant: string }>`
color: ${props =>
props.$titleVariant === "secondary"
? props.theme.palette.common.grey800
: props.theme.palette.common.black};
font-size: 1.25rem;
font-weight: 400;
`;

const LastUpdatedContainer = styled.div`
display: flex;
gap: 4px;
color: ${props => props.theme.palette.common.grey600};
font-size: 0.875rem;
font-weight: 400;
margin-block-start: 12px;
`;

const LastUpdatedTitle = styled.span`
color: ${props => props.theme.palette.common.grey600};
font-size: 0.875rem;
font-weight: 700;
`;

const Content = styled.div`
margin-block-start: 20px;
`;
17 changes: 17 additions & 0 deletions src/webapp/components/separator/Separator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from "react";
import styled from "styled-components";

interface SeparatorProps {
margin?: string;
}

export const Separator: React.FC<SeparatorProps> = React.memo(({ margin = "" }) => {
return <StyledSeparator margin={margin} />;
});

const StyledSeparator = styled.hr<{ margin?: string }>`
border: none;
height: 1px;
background-color: ${props => props.theme.palette.common.grey4};
margin-block: ${props => props.margin || "24px"};
`;
3 changes: 1 addition & 2 deletions src/webapp/pages/app/__tests__/App.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import App from "../App";
import { getTestContext } from "../../../../utils/tests";
import { Provider } from "@dhis2/app-runtime";

// TODO: fix
describe("App", () => {
it.skip("renders the feedback component", async () => {
it("renders the feedback component", async () => {
const view = getView();

expect(await view.findByText("Send feedback")).toBeInTheDocument();
Expand Down
Loading

0 comments on commit decc797

Please sign in to comment.