Skip to content

Commit

Permalink
Merge branch 'main' into focused-product-layout
Browse files Browse the repository at this point in the history
  • Loading branch information
stephenjwatkins committed Nov 2, 2023
2 parents b48e11e + 40bd4a3 commit 705d0f1
Show file tree
Hide file tree
Showing 37 changed files with 736 additions and 346 deletions.
5 changes: 5 additions & 0 deletions .changeset/nasty-cups-trade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@easypost/easy-ui": minor
---

feat(SearchNav): support PrimaryCTAItem and Title components
5 changes: 5 additions & 0 deletions .changeset/three-bulldogs-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@easypost/easy-ui": minor
---

feat: support base styles for internal UnstyledButton component
31 changes: 21 additions & 10 deletions documentation/specs/SearchNav.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ A `SearchNav` is a navigation bar focused on handling dense information interact

`SearchNav` will be made up of sub-component containers. At the top level, the `SearchNav` serves as the container for the logo, dropdown, search input, and CTAs. The logo and dropdown will be grouped into a `SearchNav.LogoGroup` container. The search input will be wrapped by a `SearchNav.Search` container. The CTAs will be wrapped by a `SearchNav.CTAGroup` container.

`SearchNav.LogoGroup` will be comprised of `SearchNav.Logo`, a minimal wrapper for the consumer provided logo, and `SearchNav.Selector`. `SearchNav.Selector` will be built using React Aria's `useSelect`, `useListBox`, `usePopover`, `useOption` and `HiddenSelect`. To help manage state, it will also rely on React Stately's `useSelectState`.
`SearchNav.LogoGroup` will be comprised of `SearchNav.Logo`, a minimal wrapper for the consumer provided logo, `SearchNav.Title`, and `SearchNav.Selector`. `SearchNav.Selector` will be built using React Aria's `useSelect`, `useListBox`, `usePopover`, `useOption` and `HiddenSelect`. To help manage state, it will also rely on React Stately's `useSelectState`.

`SearchNav.CTAGroup` will render individual CTAs via `SearchNav.CTAItem`, which will make use of Easy UI's `UnstyledButton` component.
`SearchNav.CTAGroup` will render a primary CTA, `SearchNav.PrimaryCTAItem`, and a secondary CTA, `SearchNav.SecondaryCTAItem`; both will make use of Easy UI's `UnstyledButton` component.

`SearchNav` will also need to handle a unique configuration for smaller devices. Although it won't be exposed to consumers directly,this will be accomplished via a `SearchNavMobile` component, which will be responsible for rendering a clickable hamburger and search icon. The hamburger icon will effectively be a trigger to render a menu comprised of `SearchNav.Selector` and the CTAs in `SearchNav.CTAGroup`. The clickable search icon will render the contents of `SearchNav.Search` and a right aligned close button.

Expand Down Expand Up @@ -78,22 +78,29 @@ export type SelectorProps<T> = AriaSelectProps<T> &

export type CTAGroupProps = {
/**
* The children of the <SearchNav.CTAGroup> element. Should include <SearchNav.CTAItem> elements.
* The children of the <SearchNav.CTAGroup> element. Should include <SearchNav.SecondaryCTAItem>
* elements and <SearchNav.PrimaryCTAItem>
*/
children: ReactNode;
};

export type CTAItemProps = AriaButtonProps<"button"> & {
/**
* Icon symbol SVG source from @easypost/easy-ui-icons.
*/
symbol?: IconSymbol;
/**
* Text content to display.
*/
label: string;
};

export type PrimaryCTAItemProps = CTAItemProps;

export type SecondaryCTAItemProps = CTAItemProps & {
/**
* Icon symbol SVG source from @easypost/easy-ui-icons.
*/
symbol?: IconSymbol;
/**
* Hides label on desktop.
* @default false
*/
hideLabelOnDesktop?: boolean;
/**
Expand Down Expand Up @@ -145,9 +152,13 @@ function App() {
<SearchComponent />
</SearchNav.Search>
<SearchNav.CTAGroup>
<SearchNav.CTAItem symbol={Campaign} key="Campaign" label="Optional" />
<SearchNav.CTAItem symbol={Help} key="Help" label="Optional" />
<SearchNav.CTAItem
<SearchNav.SecondaryCTAItem
symbol={Campaign}
key="Campaign"
label="Optional"
/>
<SearchNav.SecondaryCTAItem symbol={Help} key="Help" label="Optional" />
<SearchNav.SecondaryCTAItem
symbol={Brightness5}
key="Brightness"
label="Toggle theme"
Expand Down
2 changes: 1 addition & 1 deletion easy-ui-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"@testing-library/user-event": "^14.5.1",
"@types/react-is": "^18.2.3",
"@types/react-syntax-highlighter": "^15.5.9",
"@vitejs/plugin-react": "^4.1.0",
"@vitejs/plugin-react": "^4.1.1",
"glob": "^10.2.5",
"jsdom": "^22.1.0",
"react": "^18.2.0",
Expand Down
1 change: 0 additions & 1 deletion easy-ui-react/src/Button/_mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
}

@mixin variantFilled {
border: 0;
background-color: component-token("button", "resting.color");
color: component-token("button", "filled.font.color");

Expand Down
6 changes: 0 additions & 6 deletions easy-ui-react/src/CodeBlock/LanguageMenu.module.scss

This file was deleted.

4 changes: 1 addition & 3 deletions easy-ui-react/src/CodeBlock/LanguageMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import { Menu } from "../Menu";
import { Text } from "../Text";
import { UnstyledButton } from "../UnstyledButton";

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

export type LanguageMenuProps = {
/**
* List of languages.
Expand Down Expand Up @@ -41,7 +39,7 @@ export function LanguageMenu(props: LanguageMenuProps) {
return (
<Menu>
<Menu.Trigger>
<UnstyledButton className={styles.button}>
<UnstyledButton>
<HorizontalStack blockAlign="center" gap="1" wrap={false}>
<Text variant="body1">
{friendlySnippetLanguageNames[language]}
Expand Down
1 change: 0 additions & 1 deletion easy-ui-react/src/InputField/InputField.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
border: design-token("shape.border_width.1") solid
component-token("inputfield", "color.border.resting");
border-left: none;
margin: 0;
padding: calc(
#{design-token("space.1.5")} - #{design-token("shape.border_width.1")}
);
Expand Down
2 changes: 1 addition & 1 deletion easy-ui-react/src/Menu/MenuSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function MenuSectionContent<T>({
return (
<>
{section.key !== state.collection.getFirstKey() &&
section.props.children && (
section.nextKey !== state.collection.getLastKey() && (
<li {...separatorProps} className={styles.separator} />
)}
<li {...itemProps}>
Expand Down
1 change: 0 additions & 1 deletion easy-ui-react/src/Notification/Notification.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
}

.closeButton {
border: 0;
padding: design-token("space.0");
/* prettier-ignore */
margin: design-token("space.0.5") design-token("space.1") design-token("space.0.5") design-token("space.2");
Expand Down
2 changes: 0 additions & 2 deletions easy-ui-react/src/ProductLayout/HelpMenu.module.scss
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
@use "../styles/common" as *;
@use "../styles/unstyled";

.button {
@include unstyled.button;
display: inline-flex;
cursor: pointer;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
@use "../styles/common" as *;
@use "../styles/unstyled";
@use "mixins" as ProductLayout;

.ProductLayoutHeader {
Expand Down Expand Up @@ -28,7 +27,6 @@
}

.logoMenuBtn {
@include unstyled.button;
display: inline-flex;
cursor: pointer;
}
Expand Down
2 changes: 0 additions & 2 deletions easy-ui-react/src/SearchNav/CTAGroup.module.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
@use "../styles/common" as *;
@use "../styles/unstyled";

.ctaGroup {
@include component-token("cta-group", "max-height", 24px);
Expand Down Expand Up @@ -30,6 +29,5 @@
}

.menuBtn {
@include unstyled.button;
color: component-token("cta-group", "menu-color");
}
44 changes: 31 additions & 13 deletions easy-ui-react/src/SearchNav/CTAGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { ReactNode, useMemo, Fragment, ReactElement } from "react";
import React, { ReactNode, Fragment, ReactElement } from "react";
import Help from "@easypost/easy-ui-icons/Help";
import { Separator } from "./Separator";
import { Menu } from "../Menu";
Expand All @@ -7,31 +7,37 @@ import { Icon } from "../Icon";
import { UnstyledButton } from "../UnstyledButton";
import { useInternalSearchNavContext } from "./context";
import { classNames } from "../utilities/css";
import { flattenChildren, getFlattenedKey } from "../utilities/react";
import { getFlattenedKey } from "../utilities/react";

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

export type CTAGroupProps = {
/**
* The children of the <SearchNav.CTAGroup> element. Should include <SearchNav.CTAItem> elements.
* The children of the <SearchNav.CTAGroup> element. Should include <SearchNav.SecondaryCTAItem>
* elements and <SearchNav.PrimaryCTAItem>
*/
children: ReactNode;
};

export function CTAGroup(props: CTAGroupProps) {
const { children } = props;
const { menuOverlayProps, ctaMenuSymbol } = useInternalSearchNavContext();
/**
*
* @privateRemarks
* This component doesn't directly use children and instead
* reads the nodes it renders from context. This is so we can
* efficiently share the same data across various configurations.
*
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function CTAGroup(_props: CTAGroupProps) {
const { menuOverlayProps, ctaMenuSymbol, primaryCTAItem, secondaryCTAItems } =
useInternalSearchNavContext();

const items = useMemo(() => {
return flattenChildren(children);
}, [children]);

const totalItems = items.length;
const totalItems = secondaryCTAItems?.length || 0;

return (
<>
<div className={classNames(styles.ctaGroup, styles.ctaGroupExpanded)}>
{items.map((item, index) => {
{secondaryCTAItems?.map((item, index) => {
const isLastChild = index === totalItems - 1;
const itemEle = item as ReactElement;
return (
Expand All @@ -41,6 +47,12 @@ export function CTAGroup(props: CTAGroupProps) {
</Fragment>
);
})}
{primaryCTAItem && (
<>
<Separator group="cta" />
{primaryCTAItem}
</>
)}
</div>
<div className={classNames(styles.ctaGroup, styles.ctaGroupMenu)}>
<Menu>
Expand All @@ -52,7 +64,7 @@ export function CTAGroup(props: CTAGroupProps) {
</Menu.Trigger>
<Menu.Overlay placement="bottom right" {...menuOverlayProps}>
<Menu.Section aria-label="Nav actions">
{items.map((item) => {
{secondaryCTAItems?.map((item) => {
const itemEle = item as ReactElement;
return (
<Menu.Item
Expand All @@ -67,6 +79,12 @@ export function CTAGroup(props: CTAGroupProps) {
</Menu.Section>
</Menu.Overlay>
</Menu>
{primaryCTAItem && (
<>
<Separator group="cta" />
{primaryCTAItem}
</>
)}
</div>
</>
);
Expand Down
2 changes: 0 additions & 2 deletions easy-ui-react/src/SearchNav/CondensedSearchNav.module.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
@use "../styles/common" as *;
@use "../styles/unstyled";

.condensed {
display: flex;
Expand Down Expand Up @@ -28,7 +27,6 @@
"search-color",
theme-token("color.text")
);
@include unstyled.button;
}

.menuBtn {
Expand Down
47 changes: 27 additions & 20 deletions easy-ui-react/src/SearchNav/CondensedSearchNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Close from "@easypost/easy-ui-icons/Close";
import MenuSymbol from "@easypost/easy-ui-icons/Menu";
import Search from "@easypost/easy-ui-icons/Search";
import { Menu } from "../Menu";
import { HorizontalStack } from "../HorizontalStack";
import { Text } from "../Text";
import { UnstyledButton } from "../UnstyledButton";
import { Icon } from "../Icon";
Expand All @@ -15,23 +16,24 @@ import { getFlattenedKey } from "../utilities/react";
/**
* @privateRemarks
* Renders a left aligned menu button and right aligned search button.
* The menu options come from `SearchNav.Selector` and `SearchNav.CTAGroup`.
* On small screens, this effectively replaces `SearchNav`.
* The menu options come from `<SearchNav.Selector>` and `<SearchNav.CTAGroup>`.
* On small screens, this effectively replaces `<SearchNav>`.
*/
export function CondensedSearchNav() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isSearchOpen, setIsSearchOpen] = useState(false);

const {
searchNode,
selectChildren,
ctaGroupChildren,
selectLabel,
search,
selectorChildren,
secondaryCTAItems,
primaryCTAItem,
selectorLabel,
menuOverlayProps,
} = useInternalSearchNavContext();

const hasMenuToShow = !!selectChildren || !!ctaGroupChildren;
const hasSearchToShow = searchNode !== null;
const hasMenuToShow = !!selectorChildren || !!secondaryCTAItems;
const hasSearchToShow = !!search;

return (
<div className={classNames(styles.condensed)}>
Expand All @@ -51,8 +53,8 @@ export function CondensedSearchNav() {
</UnstyledButton>
</Menu.Trigger>
<Menu.Overlay placement="bottom left" {...menuOverlayProps}>
<Menu.Section aria-label={selectLabel}>
{selectChildren?.map((item) => {
<Menu.Section aria-label={selectorLabel}>
{selectorChildren?.map((item) => {
const itemEle = item as ReactElement;
return (
<Menu.Item key={getFlattenedKey(itemEle.key)}>
Expand All @@ -62,7 +64,7 @@ export function CondensedSearchNav() {
})}
</Menu.Section>
<Menu.Section aria-label="Nav actions">
{ctaGroupChildren?.map((item) => {
{secondaryCTAItems?.map((item) => {
const itemEle = item as ReactElement;
return (
<Menu.Item
Expand All @@ -77,20 +79,25 @@ export function CondensedSearchNav() {
</Menu.Section>
</Menu.Overlay>
</Menu>
{searchNode && (
<UnstyledButton
className={classNames(styles.btn, styles.searchBtn)}
onPress={() => setIsSearchOpen((prev) => !prev)}
>
<Icon symbol={Search} />
<Text visuallyHidden>search</Text>
</UnstyledButton>
{(search || primaryCTAItem) && (
<HorizontalStack gap="2">
{search && (
<UnstyledButton
className={classNames(styles.btn, styles.searchBtn)}
onPress={() => setIsSearchOpen((prev) => !prev)}
>
<Icon symbol={Search} />
<Text visuallyHidden>search</Text>
</UnstyledButton>
)}
{primaryCTAItem}
</HorizontalStack>
)}
</>
) : (
hasSearchToShow && (
<div className={classNames(styles.condensedSearch)}>
{searchNode}
{search}
<UnstyledButton
className={classNames(styles.btn)}
onPress={() => setIsSearchOpen((prev) => !prev)}
Expand Down
Loading

0 comments on commit 705d0f1

Please sign in to comment.