Skip to content

Commit

Permalink
Refactor onClose and add site docs
Browse files Browse the repository at this point in the history
  • Loading branch information
joshwooding committed Sep 6, 2024
1 parent d9cd70e commit 607bc20
Show file tree
Hide file tree
Showing 17 changed files with 239 additions and 195 deletions.
1 change: 0 additions & 1 deletion packages/lab/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"sideEffects": false,
"dependencies": {
"@floating-ui/react": "^0.26.5",
"@fluentui/react-overflow": "^9.0.19",
"@internationalized/date": "^3.0.0",
"@types/react-window": "^1.8.2",
"aria-hidden": "^1.1.1",
Expand Down
35 changes: 25 additions & 10 deletions packages/lab/src/tabs-next/TabNext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ import {

import { CloseIcon } from "@salt-ds/icons";
import tabCss from "./TabNext.css";
import { useTabs } from "./TabNextContext";
import { useTabstrip } from "./TabstripNextContext";

const withBaseName = makePrefixer("saltTabNext");

export interface TabNextProps extends ComponentPropsWithoutRef<"div"> {
/* Value prop is mandatory and must be unique in order for overflow to work. */
disabled?: boolean;
value: string;
onClose?: () => void;
closable?: boolean;
}

export const TabNext = forwardRef<HTMLDivElement, TabNextProps>(
Expand All @@ -35,7 +35,7 @@ export const TabNext = forwardRef<HTMLDivElement, TabNextProps>(
className,
disabled: disabledProp,
onClick,
onClose,
closable,
onKeyDown,
onFocus,
value,
Expand All @@ -51,10 +51,11 @@ export const TabNext = forwardRef<HTMLDivElement, TabNextProps>(
registerItem,
variant,
setSelected,
setActive,
selected,
focusInside,
handleClose,
} = useTabs();
} = useTabstrip();
const disabled = disabledProp;

const tabRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -87,25 +88,38 @@ export const TabNext = forwardRef<HTMLDivElement, TabNextProps>(

const handleFocus = () => {
setFocused(true);
setActive(value);
};

const handleBlur = () => {
setFocused(false);
};

const handleCloseButton = (event: MouseEvent<HTMLButtonElement>) => {
onClose?.();
const handleCloseButtonClick = (event: MouseEvent<HTMLButtonElement>) => {
handleClose(event, value);
event.stopPropagation();
};

const handleCloseButtonKeyDown = (
event: KeyboardEvent<HTMLButtonElement>,
) => {
if (event.key === "Enter" || event.key === " ") {
handleClose(event, value);
event.stopPropagation();
}
};

return (
<div
className={clsx(withBaseName(), withBaseName(variant), className)}
data-value={value}
aria-selected={selected === value || undefined}
aria-selected={selected === value}
aria-disabled={disabled}
tabIndex={selected === value ? 0 : -1}
tabIndex={
(focusInside && focused) || (selected === value && !focusInside)
? 0
: -1
}
ref={handleRef}
role="tab"
onClick={!disabled ? handleClick : undefined}
Expand All @@ -118,14 +132,15 @@ export const TabNext = forwardRef<HTMLDivElement, TabNextProps>(
<span className={withBaseName("label")} aria-hidden id={labelId}>
{children}
</span>
{onClose ? (
{closable ? (
<Button
aria-label="Dismiss tab"
id={closeButtonId}
aria-labelledby={clsx(closeButtonId, labelId)}
tabIndex={focused || (!focusInside && selected === value) ? 0 : -1}
variant="secondary"
onClick={handleCloseButton}
onClick={handleCloseButtonClick}
onKeyDown={handleCloseButtonKeyDown}
>
<CloseIcon aria-hidden />
</Button>
Expand Down
29 changes: 0 additions & 29 deletions packages/lab/src/tabs-next/TabNextContext.tsx

This file was deleted.

17 changes: 17 additions & 0 deletions packages/lab/src/tabs-next/TabNextPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useId } from "@salt-ds/core";
import { type ComponentPropsWithoutRef, forwardRef } from "react";

export interface TabNextPanelProps extends ComponentPropsWithoutRef<"div"> {}

export const TabNextPanel = forwardRef<HTMLDivElement, TabNextPanelProps>(
function TabNextPanel(props, ref) {
const { children, id: idProp, ...rest } = props;
const id = useId(idProp);

return (
<div id={id} ref={ref} role="tabpanel" {...rest}>
{children}
</div>
);
},
);
6 changes: 5 additions & 1 deletion packages/lab/src/tabs-next/TabOverflowList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,11 @@ export const TabOverflowList = forwardRef<HTMLDivElement, TabOverflowListProps>(
onBlur={handleBlur}
onClick={handleListClick}
ref={refs.setFloating}
style={{ left: x ?? 0, top: y ?? 0, position: strategy }}
style={
!hidden
? { left: x ?? 0, top: y ?? 0, position: strategy }
: undefined
}
tabIndex={-1}
>
<div className={withBaseName("listContainer")}>{children}</div>
Expand Down
13 changes: 13 additions & 0 deletions packages/lab/src/tabs-next/TabsNext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { ReactNode } from "react";

import { TabsNextContext } from "./TabsNextContext";

export interface TabsNextProps {
children: ReactNode;
}

export function TabsNext({ children }: TabsNextProps) {
return (
<TabsNextContext.Provider value={{}}>{children}</TabsNextContext.Provider>
);
}
14 changes: 14 additions & 0 deletions packages/lab/src/tabs-next/TabsNextContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createContext } from "@salt-ds/core";
import { useContext } from "react";

// biome-ignore lint/complexity/noBannedTypes: <explanation>
export type TabsNextContextValue = {};

export const TabsNextContext = createContext<TabsNextContextValue>(
"TabsNextContext",
{},
);

export function useTabsNext() {
return useContext(TabsNextContext);
}
21 changes: 17 additions & 4 deletions packages/lab/src/tabs-next/TabstripNext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import {
useState,
} from "react";

import { TabsContext } from "./TabNextContext";
import { TabOverflowList } from "./TabOverflowList";
import tabstripCss from "./TabstripNext.css";
import { TabstripNextContext } from "./TabstripNextContext";
import { useOverflow } from "./useOverflow";
import { useTabstrip } from "./useTabstrip";

Expand All @@ -35,6 +35,7 @@ export interface TabstripNextProps
/* The Tabs variant */
variant?: "main" | "inline";
onAdd?: () => void;
onClose?: (event: SyntheticEvent, value: string) => void;
}

export const TabstripNext = forwardRef<HTMLDivElement, TabstripNextProps>(
Expand All @@ -47,6 +48,7 @@ export const TabstripNext = forwardRef<HTMLDivElement, TabstripNextProps>(
value,
defaultValue,
onAdd,
onClose,
onChange,
onKeyDown,
style,
Expand All @@ -69,13 +71,15 @@ export const TabstripNext = forwardRef<HTMLDivElement, TabstripNextProps>(
registerItem,
setSelected,
selected,
setActive,
handleKeyDown,
items,
handleClose,
} = useTabstrip({
defaultSelected: defaultValue,
selected: value,
onChange,
onClose,
});

const [visible, hidden, isMeasuring] = useOverflow({
Expand Down Expand Up @@ -103,10 +107,19 @@ export const TabstripNext = forwardRef<HTMLDivElement, TabstripNextProps>(
variant,
setSelected,
selected,
setActive,
focusInside,
handleClose,
}),
[variant, setSelected, selected, registerItem, focusInside, handleClose],
[
variant,
setSelected,
selected,
registerItem,
focusInside,
handleClose,
setActive,
],
);

const tabstripStyle = {
Expand All @@ -115,7 +128,7 @@ export const TabstripNext = forwardRef<HTMLDivElement, TabstripNextProps>(
};

return (
<TabsContext.Provider value={contextValue}>
<TabstripNextContext.Provider value={contextValue}>
<div
role="tablist"
className={clsx(
Expand Down Expand Up @@ -150,7 +163,7 @@ export const TabstripNext = forwardRef<HTMLDivElement, TabstripNextProps>(
{hidden}
</TabOverflowList>
</div>
</TabsContext.Provider>
</TabstripNextContext.Provider>
);
},
);
34 changes: 34 additions & 0 deletions packages/lab/src/tabs-next/TabstripNextContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { createContext } from "@salt-ds/core";
import { type SyntheticEvent, useContext } from "react";

interface TabValue {
id: string;
element: HTMLElement;
}

export interface TabstripNextContextValue {
registerItem: (tab: TabValue) => void;
variant: "main" | "inline";
setSelected: (event: SyntheticEvent, id: string) => void;
setActive: (id: string) => void;
handleClose: (event: SyntheticEvent, id: string) => void;
selected?: string;
focusInside: boolean;
}

export const TabstripNextContext = createContext<TabstripNextContextValue>(
"TabstripNextContext",
{
registerItem: () => undefined,
variant: "main",
setSelected: () => undefined,
setActive: () => undefined,
handleClose: () => undefined,
selected: undefined,
focusInside: false,
},
);

export function useTabstrip() {
return useContext(TabstripNextContext);
}
1 change: 1 addition & 0 deletions packages/lab/src/tabs-next/useOverflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export function useOverflow({
tabs.length,
]);

// biome-ignore lint/correctness/useExhaustiveDependencies: we want to update when selected changes.
useIsomorphicLayoutEffect(() => {
updateOverflow();
}, [updateOverflow, selected]);
Expand Down
11 changes: 7 additions & 4 deletions packages/lab/src/tabs-next/useTabstrip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
useRef,
useState,
} from "react";
import type { TabstripNextProps } from "./TabstripNext";

export interface Item {
id: string;
Expand Down Expand Up @@ -107,7 +108,6 @@ function useCollection({ wrap }: UseCollectionProps) {
return items[newIndex]?.id;
},
getFirst: () => {
console.log(items);
return items[0]?.id;
},
getLast: () => {
Expand All @@ -121,10 +121,12 @@ export function useTabstrip({
selected: selectedProp,
defaultSelected,
onChange,
onClose,
}: {
selected?: string;
defaultSelected?: string;
onChange?: (event: SyntheticEvent, selected: string) => void;
selected?: TabstripNextProps["value"];
defaultSelected?: TabstripNextProps["defaultValue"];
onChange?: TabstripNextProps["onChange"];
onClose?: TabstripNextProps["onClose"];
}) {
const { registerItem, item, getNext, getPrevious, getFirst, getLast, items } =
useCollection({ wrap: true });
Expand Down Expand Up @@ -167,6 +169,7 @@ export function useTabstrip({
} else {
setActive(newActive);
}
onClose?.(event, id);
},
[getFirst, getNext, getPrevious, selected],
);
Expand Down
21 changes: 9 additions & 12 deletions packages/lab/stories/tabstrip-next/tabstrip-next.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,7 @@ LotsOfTabsTabstrip.args = {
],
};

export const Dismissable: TabstripStory = ({
width = 600,
...tabstripProps
}) => {
export const Closable: TabstripStory = ({ width = 600, ...tabstripProps }) => {
const [tabs, setTabs] = useState([
"Home",
"Transactions",
Expand All @@ -257,15 +254,15 @@ export const Dismissable: TabstripStory = ({

return (
<div style={{ width, minWidth: 0, maxWidth: "100%" }}>
<TabstripNext defaultValue={tabs[0]} {...tabstripProps}>
<TabstripNext
defaultValue={tabs[0]}
onClose={(_event, closedTab) => {
setTabs(tabs.filter((tab) => tab !== closedTab));
}}
{...tabstripProps}
>
{tabs.map((label) => (
<TabNext
value={label}
key={label}
onClose={() => {
setTabs(tabs.filter((tab) => tab !== label));
}}
>
<TabNext value={label} key={label} closable>
{label}
{label === "Transactions" && <Badge value={2} />}
</TabNext>
Expand Down
Loading

0 comments on commit 607bc20

Please sign in to comment.