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

ModeSwitcher and BreadcrumbNavigation draft #1542

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
9 changes: 9 additions & 0 deletions easy-ui-react/src/ModeSwitcher/ModeSwitcher.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from "react";
import { Canvas, Meta, ArgTypes } from "@storybook/blocks";
import * as ModeSwitcherStories from "./ModeSwitcher.stories";

<Meta of={ModeSwitcherStories} />

## Test

<Canvas of={ModeSwitcherStories.Test} />
29 changes: 29 additions & 0 deletions easy-ui-react/src/ModeSwitcher/ModeSwitcher.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@use "../styles/common" as *;
@use "../styles/unstyled";

.trigger {
@include unstyled.button;
display: flex;
align-items: center;
justify-content: space-between;
gap: design-token("space.1");
padding: design-token("space.2");
border: design-token("shape.border_width.1") solid
theme-token("color.neutral.300");
border-radius: design-token("shape.border_radius.md");
cursor: pointer;
width: 100%;
max-width: 160px;
}

.triggerPopoverOpen {
border-color: theme-token("color.neutral.800");
}

.popover {
border: design-token("shape.border_width.1") solid
theme-token("color.neutral.300");
border-radius: design-token("shape.border_radius.md");
background-color: white;
padding: design-token("space.2");
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are some open design questions i have regarding the yellow color thats given in figma and what we have available in easy-ui and also the spacing within the popover / radio items

16 changes: 16 additions & 0 deletions easy-ui-react/src/ModeSwitcher/ModeSwitcher.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Meta, StoryObj } from "@storybook/react";
import React from "react";
import { FakeTopLevelForgeModeSwitcher } from "./ModeSwitcher";

type Story = StoryObj<typeof FakeTopLevelForgeModeSwitcher>;

const meta: Meta<typeof FakeTopLevelForgeModeSwitcher> = {
title: "Components/ModeSwitcher",
component: FakeTopLevelForgeModeSwitcher,
};

export default meta;

export const Test: Story = {
render: () => <FakeTopLevelForgeModeSwitcher />,
};
144 changes: 144 additions & 0 deletions easy-ui-react/src/ModeSwitcher/ModeSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React, { useState, createContext, useContext, ReactNode } from "react";
import { Button, Dialog, DialogTrigger, Popover } from "react-aria-components";
import ExpandMoreIcon400 from "@easypost/easy-ui-icons/ExpandMore400";
import { Text } from "../Text";
import {
RadioGroup,
RadioGroupItemProps,
useRadioGroupContext,
} from "../RadioGroup";
import { Icon } from "../Icon";
import { VerticalStack } from "../VerticalStack";
import { classNames } from "../utilities/css";

import styles from "./ModeSwitcher.module.scss";

// not the actual component

export function FakeTopLevelForgeModeSwitcher() {
const [mode, setMode] = useState("production");

return (
<FakeForgeComponent mode={mode as "production" | "test"}>
<ModeSwitcher onModeChange={setMode} />
</FakeForgeComponent>
);
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stephenjwatkins does this align with how we could potentially use this component? the top level Forge component accepts mode as a prop and the dispatch function is passed directly to ForgeLayout.ModeSwitcher ?

type FakeForgeComponentProps = {
mode: "test" | "production";
children: ReactNode;
};

export function FakeForgeComponent({
children,
mode,
}: FakeForgeComponentProps) {
const context = {
mode,
};
return (
<FakeForgeContext.Provider value={context}>
{children}
</FakeForgeContext.Provider>
);
}

type FakeForgeContextType = {
mode: "production" | "test";
};

export const FakeForgeContext = createContext<FakeForgeContextType | null>(
null,
);

export function useFakeForgeContext() {
const forgeContext = useContext(FakeForgeContext);
if (!forgeContext) {
throw new Error("boom");
}
return forgeContext;
}

// the actual component

export type ModeSwitcherProps = {
onModeChange?: (value: string) => void;
};

export function ModeSwitcher(props: ModeSwitcherProps) {
const { onModeChange } = props;
const state = useFakeForgeContext();
const [isOpen, setIsOpen] = useState(false);

return (
<DialogTrigger>
<Button
className={classNames(
styles.trigger,
isOpen && styles.triggerPopoverOpen,
)}
onPress={() => setIsOpen(true)}
>
<Text
variant="subtitle1"
color={state.mode === "test" ? "alert.600" : "positive.600"}
truncate
>
{state.mode === "test" ? "Test" : "Production"}
</Text>
<Icon symbol={ExpandMoreIcon400} size="md" color="neutral.500" />
</Button>
<Popover
className={styles.popover}
offset={2}
crossOffset={102}
isOpen={isOpen}
onOpenChange={setIsOpen}
>
<Dialog>
<VerticalStack gap="1">
<Text variant="subtitle1" color="neutral.800">
Instance Switcher
</Text>
<RadioGroup
aria-label="Select a mode"
onChange={onModeChange}
value={state.mode}
>
<ModeSwitcherRadioGroupItem value="test" />
<ModeSwitcherRadioGroupItem value="production" />
</RadioGroup>
</VerticalStack>
</Dialog>
</Popover>
</DialogTrigger>
);
}

function ModeSwitcherRadioGroupItem(
props: Omit<RadioGroupItemProps, "children">,
) {
const { value, ...restProps } = props;
const state = useRadioGroupContext();
const isSelected = value === state.selectedValue;
return (
<RadioGroup.Item value={value} {...restProps}>
<VerticalStack>
<Text
variant="subtitle2"
color={isSelected ? "primary.800" : "primary.700"}
>
{value === "test" ? "Test" : "Production"}
</Text>
<Text variant="overline">
View data using the{" "}
<Text color={value === "test" ? "warning.600" : "positive.600"}>
{value === "test" ? "TEST" : "PRODUCTION"} API{" "}
</Text>
keys
</Text>
</VerticalStack>
</RadioGroup.Item>
);
}
1 change: 1 addition & 0 deletions easy-ui-react/src/ModeSwitcher/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./ModeSwitcher";
Loading