Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import styled from "@emotion/styled";
import { Button } from "@mui/material";

export const StyledButton = styled(Button)`
align-self: center;
text-transform: none;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { JSX } from "react";
import { BUTTON_PROPS } from "../../../../../../../../styles/common/mui/button";
import { useTerraSetUpUI } from "../../../../../../../../terra/setUpUI/provider/hook";
import { STEPS_REGION_ID } from "../../constants";
import { StyledButton } from "./button.styles";
import { ButtonProps } from "./types";

export const Button = ({
collapsible = false,
}: ButtonProps): JSX.Element | null => {
const { isOpen, onChange } = useTerraSetUpUI();

if (!collapsible) return null;

return (
<StyledButton
aria-controls={STEPS_REGION_ID}
aria-expanded={isOpen}
color={isOpen ? BUTTON_PROPS.COLOR.SECONDARY : BUTTON_PROPS.COLOR.PRIMARY}
onClick={onChange}
variant={BUTTON_PROPS.VARIANT.CONTAINED}
>
{isOpen ? "Save & continue later" : "Continue"}
Comment thread
frano-m marked this conversation as resolved.
</StyledButton>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface ButtonProps {
collapsible?: boolean;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
import styled from "@emotion/styled";
import { SectionContent as DXSectionContent } from "../../terraSetUpForm.styles";
import { PALETTE } from "../../../../../../../../styles/common/constants/palette";
import { sectionPadding } from "../../../../../../../common/Section/section.styles";

export const SectionContent = styled(DXSectionContent)`
export const Section = styled("div")`
${sectionPadding};
background-color: ${PALETTE.COMMON_WHITE};
border-top: 1px solid ${PALETTE.SMOKE_MAIN};
display: grid;
gap: 16px;
grid-template-columns: auto 1fr;
`;

export const SectionContent = styled.div`
display: grid;
gap: 2px;
grid-row: 1;

.MuiTypography-body-400-2lines {
p {
margin: 0;
}
}

.MuiTypography-body-500 {
margin: 2px 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ import { StepIcon, Typography } from "@mui/material";
import { JSX, ReactNode } from "react";
import { TYPOGRAPHY_PROPS } from "../../../../../../../../styles/common/mui/typography";
import { FormStatusCompletedIcon } from "../../../../../../../common/CustomIcon/components/FormStatusCompletedIcon/formStatusCompletedIcon";
import {
Section,
SectionActions,
SectionStatus,
} from "../../terraSetUpForm.styles";
import { SectionContent } from "./formStep.styles";
import { SectionActions, SectionStatus } from "../../terraSetUpForm.styles";
import { Section, SectionContent } from "./formStep.styles";

export interface FormStepProps {
action: ReactNode;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* DOM id of the collapsible steps region. Used by the toggle button's
* `aria-controls` so assistive tech can associate the toggle with the
* region it expands/collapses.
*/
export const STEPS_REGION_ID = "terra-set-up-form-steps";
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ComponentProps } from "react";
import { AUTH_STATUS, AuthState } from "../../../../../../../auth/types/auth";
import { REQUEST_STATUS } from "../../../../../../../terra/types/common";
import { TerraProfileContextProps } from "../../../../../../../terra/types/context";
import { TerraSetUpForm } from "../terraSetUpForm";

/**
* Authenticated, settled auth state — used so `TerraSetUpForm` doesn't bail
* early on its `isAuthenticated` / status guards.
*/
export const MOCK_AUTH_STATE: AuthState = {
isAuthenticated: true,
status: AUTH_STATUS.SETTLED,
};

/**
* Terra profile statuses where every onboarding step is supported but not yet
* complete — produces three incomplete steps in the rendered form.
*/
export const MOCK_TERRA_PROFILE_INCOMPLETE: TerraProfileContextProps = {
terraNIHProfileLoginStatus: {
isSuccess: false,
isSupported: true,
requestStatus: REQUEST_STATUS.COMPLETED,
response: undefined,
},
terraProfileLoginStatus: {
isSuccess: false,
isSupported: true,
requestStatus: REQUEST_STATUS.COMPLETED,
response: undefined,
},
terraTOSLoginStatus: {
isSuccess: false,
isSupported: true,
requestStatus: REQUEST_STATUS.COMPLETED,
response: undefined,
},
};

export const DEFAULT_TERRA_SET_UP_FORM_ARGS: ComponentProps<
typeof TerraSetUpForm
> = {
collapsible: true,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ComponentProps } from "react";
import { TerraSetUpForm } from "../terraSetUpForm";

export const BOOLEAN_CONTROLS: (keyof ComponentProps<typeof TerraSetUpForm>)[] =
["collapsible"];
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Box } from "@mui/material";
import type { Meta, StoryObj } from "@storybook/nextjs-vite";
import { ComponentProps, JSX } from "react";
import { AuthContext } from "../../../../../../../auth/contexts/auth";
import { CONTROL_TYPE } from "../../../../../../../storybook/controls/types";
import { configureControls } from "../../../../../../../storybook/controls/utils";
import { TerraProfileContext } from "../../../../../../../terra/context";
import { TerraSetUpUIProvider } from "../../../../../../../terra/setUpUI/provider/provider";
import { TerraSetUpForm } from "../terraSetUpForm";
import {
DEFAULT_TERRA_SET_UP_FORM_ARGS,
MOCK_AUTH_STATE,
MOCK_TERRA_PROFILE_INCOMPLETE,
} from "./args";
import { BOOLEAN_CONTROLS } from "./constants";

const meta: Meta<typeof TerraSetUpForm> = {
argTypes: {
...configureControls<ComponentProps<typeof TerraSetUpForm>>(
BOOLEAN_CONTROLS,
CONTROL_TYPE.BOOLEAN,
),
},
component: TerraSetUpForm,
decorators: [
(Story): JSX.Element => (
<AuthContext.Provider
value={{
authDispatch: null,
authState: MOCK_AUTH_STATE,
service: undefined,
}}
>
<TerraProfileContext.Provider value={MOCK_TERRA_PROFILE_INCOMPLETE}>
<TerraSetUpUIProvider>
<Box m={8}>
<Story />
</Box>
</TerraSetUpUIProvider>
</TerraProfileContext.Provider>
</AuthContext.Provider>
),
],
parameters: {
layout: "fullscreen",
},
};

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: DEFAULT_TERRA_SET_UP_FORM_ARGS,
};

export const NotCollapsible: Story = {
args: { ...DEFAULT_TERRA_SET_UP_FORM_ARGS, collapsible: false },
};
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
import styled from "@emotion/styled";
import { Stack } from "@mui/material";
import { PALETTE } from "../../../../../../styles/common/constants/palette";
import { FluidPaper } from "../../../../../common/Paper/components/FluidPaper/fluidPaper";
import { sectionPadding } from "../../../../../common/Section/section.styles";

export const Section = styled("div")`
${sectionPadding};
export const StyledFluidPaper = styled(FluidPaper)`
background-color: ${PALETTE.COMMON_WHITE};
display: grid;
gap: 16px;
grid-template-columns: auto 1fr;
`;

export const SectionContent = styled("div")`
display: grid;
gap: 4px;
grid-row: 1;

.MuiTypography-body-400-2lines {
p {
margin: 0;
}
}
export const StyledStack = styled(Stack)`
${sectionPadding};
justify-content: space-between;
`;

export const SectionStatus = styled("div")`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Typography } from "@mui/material";
import { Collapse, Stack, Typography } from "@mui/material";
import { JSX } from "react";
import { useAuth } from "../../../../../../auth/hooks/useAuth";
import { AUTH_STATUS } from "../../../../../../auth/types/auth";
Expand All @@ -7,39 +7,47 @@ import {
OnboardingStatus,
useAuthenticationForm,
} from "../../../../../../hooks/authentication/terra/useAuthenticationForm";
import { STACK_PROPS } from "../../../../../../styles/common/mui/stack";
import { TYPOGRAPHY_PROPS } from "../../../../../../styles/common/mui/typography";
import {
FluidPaper,
GridPaper,
} from "../../../../../common/Paper/paper.styles";
import { useTerraSetUpUI } from "../../../../../../terra/setUpUI/provider/hook";
import { SectionTitle } from "../../../../../common/Section/components/SectionTitle/sectionTitle";
import { Button } from "./components/Button/button";
import { AcceptTerraTOS } from "./components/FormStep/components/AcceptTerraTOS/acceptTerraTOS";
import { ConnectTerraToNIHAccount } from "./components/FormStep/components/ConnectTerraToNIHAccount/connectTerraToNIHAccount";
import { CreateTerraAccount } from "./components/FormStep/components/CreateTerraAccount/createTerraAccount";
import { Section, SectionContent } from "./terraSetUpForm.styles";
import { STEPS_REGION_ID } from "./constants";
import { StyledFluidPaper, StyledStack } from "./terraSetUpForm.styles";
import { TerraSetUpFormProps } from "./types";

export const TerraSetUpForm = (): JSX.Element | null => {
export const TerraSetUpForm = ({
collapsible = false,
}: TerraSetUpFormProps): JSX.Element | null => {
const {
authState: { isAuthenticated, status },
} = useAuth();
const { isComplete, onboardingStatusByStep } = useAuthenticationForm();
const { isOpen } = useTerraSetUpUI();

if (!isAuthenticated) return null;
if (status === AUTH_STATUS.PENDING) return null;
return isComplete ? null : (
<FluidPaper>
<GridPaper>
<Section>
<SectionContent>
<SectionTitle title="Complete your setup" />
<Typography
color={TYPOGRAPHY_PROPS.COLOR.INK_LIGHT}
variant={TYPOGRAPHY_PROPS.VARIANT.BODY_400_2_LINES}
>
Follow these steps to unlock the full potential of the data
explorer.
</Typography>
</SectionContent>
</Section>
if (isComplete) return null;

return (
<StyledFluidPaper>
<StyledStack direction={STACK_PROPS.DIRECTION.ROW} useFlexGap>
<Stack gap={1} useFlexGap>
<SectionTitle title="Complete your setup" />
<Typography
color={TYPOGRAPHY_PROPS.COLOR.INK_LIGHT}
variant={TYPOGRAPHY_PROPS.VARIANT.BODY_400_2_LINES}
>
Follow these steps to unlock the full potential of the data
explorer.
</Typography>
</Stack>
<Button collapsible={collapsible} />
</StyledStack>
<Collapse id={STEPS_REGION_ID} in={collapsible ? isOpen : true}>
<CreateTerraAccount
active={isStepActive(
onboardingStatusByStep,
Expand Down Expand Up @@ -75,8 +83,8 @@ export const TerraSetUpForm = (): JSX.Element | null => {
step={ONBOARDING_STEP.NIH_ACCOUNT}
/>
)}
</GridPaper>
</FluidPaper>
</Collapse>
</StyledFluidPaper>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface TerraSetUpFormProps {
collapsible?: boolean;
}
11 changes: 11 additions & 0 deletions src/components/common/Collapse/provider/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createContext } from "react";
import { CollapseContextProps } from "./types";

/**
* Collapse context. Tracks an in/out boolean and a toggle callback for any
* collapsible UI surface that opts in via `CollapseProvider`.
*/
export const CollapseContext = createContext<CollapseContextProps>({
isIn: false,
onChange: () => undefined,
});
11 changes: 11 additions & 0 deletions src/components/common/Collapse/provider/hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useContext } from "react";
import { CollapseContext } from "./context";
import { CollapseContextProps } from "./types";

/**
* Returns collapse context.
* @returns collapse context.
*/
export const useCollapse = (): CollapseContextProps => {
return useContext(CollapseContext);
};
28 changes: 28 additions & 0 deletions src/components/common/Collapse/provider/provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { JSX, useCallback, useState } from "react";
import { CollapseContext } from "./context";
import { CollapseProviderProps } from "./types";

/**
* Holds in-memory collapse state for any UI that opts in. Exposes `isIn`
* and a toggle callback to descendants via `CollapseContext`. Accepts a
* render-prop child so the value can be passed to a sibling context without
* an extra hook consumer.
* @param props - Provider props.
* @param props.children - React children or a render function receiving the context value.
* @param props.initialState - Initial value for `isIn` (defaults to `false`).
* @returns Collapse provider component.
*/
export function CollapseProvider({
children,
initialState = false,
}: CollapseProviderProps): JSX.Element {
Comment thread
frano-m marked this conversation as resolved.
const [isIn, setIsIn] = useState<boolean>(initialState);

const onChange = useCallback(() => setIsIn((prev) => !prev), []);

return (
<CollapseContext.Provider value={{ isIn, onChange }}>
{typeof children === "function" ? children({ isIn, onChange }) : children}
</CollapseContext.Provider>
);
}
11 changes: 11 additions & 0 deletions src/components/common/Collapse/provider/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ReactNode } from "react";

export type CollapseContextProps = {
isIn: boolean;
onChange: () => void;
};

export type CollapseProviderProps = {
children: ReactNode | ((props: CollapseContextProps) => ReactNode);
initialState?: boolean;
};
Loading
Loading