Skip to content

Commit

Permalink
Merge pull request #134 from cosmology-tech/feat/improve-core
Browse files Browse the repository at this point in the history
Feat/improve core
  • Loading branch information
yyyyaaa authored Dec 5, 2023
2 parents 12402dd + b4d67a7 commit 4ebe9ee
Show file tree
Hide file tree
Showing 18 changed files with 1,470 additions and 724 deletions.
4 changes: 1 addition & 3 deletions packages/react/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ const config: StorybookConfig = {
docs: {
autodocs: "tag",
},
core: {
builder: "@storybook/builder-vite", // 👈 The builder enabled here.
},
core: {},
async viteFinal(config, { configType }) {
// return the customized config
if (configType === "PRODUCTION") {
Expand Down
17 changes: 8 additions & 9 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,13 @@
"@parcel/reporter-bundle-analyzer": "^2.10.2",
"@parcel/transformer-typescript-tsc": "^2.10.2",
"@parcel/transformer-typescript-types": "^2.10.2",
"@storybook/addon-essentials": "^7.5.3",
"@storybook/addon-interactions": "^7.5.3",
"@storybook/addon-links": "^7.5.3",
"@storybook/addon-viewport": "^7.5.3",
"@storybook/blocks": "^7.5.3",
"@storybook/builder-vite": "^7.5.3",
"@storybook/react": "^7.5.3",
"@storybook/react-vite": "^7.5.3",
"@storybook/addon-essentials": "^7.6.3",
"@storybook/addon-interactions": "^7.6.3",
"@storybook/addon-links": "^7.6.3",
"@storybook/addon-viewport": "^7.6.3",
"@storybook/blocks": "^7.6.3",
"@storybook/react": "^7.6.3",
"@storybook/react-vite": "^7.6.3",
"@storybook/testing-library": "^0.2.2",
"@types/react": "^18.2.34",
"@vanilla-extract/parcel-transformer": "^1.0.0",
Expand All @@ -124,7 +123,7 @@
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"storybook": "^7.5.3",
"storybook": "^7.6.3",
"type-fest": "^4.2.0",
"vite-plugin-replace": "^0.1.1",
"vite-tsconfig-paths": "^4.2.1"
Expand Down
13 changes: 11 additions & 2 deletions packages/react/scaffolds/select-option/select-option.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ import { baseButton } from "@/ui/button/button.css";
import { SelectContext } from "../select/select.context";

export interface SelectOptionProps {
optionKey: string;
label: string;
children?: React.ReactNode;
className?: string;
}

export default function SelectOption(props: SelectOptionProps) {
const { activeIndex, selectedIndex, getItemProps, handleSelect } =
const { activeIndex, selectedItem, getItemProps, handleSelect } =
React.useContext(SelectContext);

const { ref, index } = useListItem({ label: props.label });

const selectedIndex = selectedItem?.index ?? null;
const isActive = activeIndex === index;
const isSelected = selectedIndex === index;

Expand All @@ -25,6 +27,8 @@ export default function SelectOption(props: SelectOptionProps) {
ref={ref}
role="option"
aria-selected={isActive && isSelected}
data-select-key={props.optionKey}
data-select-label={props.label}
tabIndex={isActive ? 0 : -1}
className={clx(baseButton, props.className)}
style={{
Expand All @@ -34,7 +38,12 @@ export default function SelectOption(props: SelectOptionProps) {
textAlign: "left",
}}
{...getItemProps({
onClick: () => handleSelect(index),
onClick: () =>
handleSelect({
key: props.optionKey,
label: props.label,
index,
}),
})}
>
<ListItem isActive={isActive}>{props.children ?? props.label}</ListItem>
Expand Down
10 changes: 8 additions & 2 deletions packages/react/scaffolds/select/select.context.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import React from "react";
import { useInteractions } from "@floating-ui/react";

export type Item = {
key: string;
label: string;
index: number;
};

export interface SelectContextValue {
activeIndex: number | null;
selectedIndex: number | null;
selectedItem: Item | null;
getItemProps: ReturnType<typeof useInteractions>["getItemProps"];
handleSelect: (index: number | null) => void;
handleSelect: (selectedItem: Item | null) => void;
}

export const SelectContext = React.createContext<SelectContextValue>(
Expand Down
86 changes: 64 additions & 22 deletions packages/react/scaffolds/select/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
listboxStyle,
selectFullWidth,
} from "./select.css";
import { SelectContext } from "./select.context";
import { Item, SelectContext, SelectContextValue } from "./select.context";

const DEFAULT_LIST_WIDTH = "220";

Expand Down Expand Up @@ -80,7 +80,9 @@ export interface SelectProps {
size?: "sm" | "md" | "lg";
label?: React.ReactNode;
placeholder?: string;
onSelectItem?: (index: number | null) => void;
defaultSelectedItem?: Item;
selectedIndex?: number;
onSelectItem?: (item: Item | null) => void;
children?: React.ReactNode;
className?: string;
}
Expand All @@ -93,9 +95,11 @@ export default function Select(props: SelectProps) {
const [pointer, setPointer] = React.useState(false);
const isTypingRef = React.useRef<boolean>(false);

const [selectedItem, setSelectedItem] = React.useState<Item | null>(null);
const [activeIndex, setActiveIndex] = React.useState<number | null>(null);
const [selectedIndex, setSelectedIndex] = React.useState<number | null>(null);
const [selectedLabel, setSelectedLabel] = React.useState<string | null>(null);
const isControlled = React.useRef<boolean>(
typeof props.selectedIndex !== "undefined"
);

const { refs, floatingStyles, context } = useFloating({
placement: "bottom-start",
Expand All @@ -111,35 +115,64 @@ export default function Select(props: SelectProps) {
const wrapperRef = React.useRef<HTMLDivElement>(null);
const labelsRef = React.useRef<Array<string | null>>([]);

const handleSelect = React.useCallback((index: number | null) => {
setSelectedIndex(index);
const handleSelect = React.useCallback((item: Item | null) => {
setSelectedItem(item);
setIsOpen(false);
if (index !== null) {
if (typeof props.onSelectItem === "function") {
props.onSelectItem(index);
}
setSelectedLabel(labelsRef.current[index]);

if (item !== null && typeof props.onSelectItem === "function") {
props.onSelectItem(item);
}
}, []);

const handleSelectIndex = React.useCallback(
(index: number | null) => {
const element = elementsRef[index];
if (!element) return;

const optionKey = element.dataset.selectKey;
const optionLabel = element.dataset.selectLabel;

handleSelect({
index,
key: optionKey,
label: optionLabel,
});
},
[elementsRef, handleSelect]
);

React.useEffect(() => {
if (!!props.defaultSelectedItem) {
handleSelect(props.defaultSelectedItem);
}
}, []);

React.useEffect(() => {
// Controlled usage
if (isControlled.current) {
handleSelectIndex(props.selectedIndex);
}
}, [props.selectedIndex]);

function handleTypeaheadMatch(index: number | null) {
if (isOpen) {
setActiveIndex(index);
} else {
handleSelect(index);
handleSelectIndex(index);
}
}

const listNav = useListNavigation(context, {
listRef: elementsRef,
activeIndex,
selectedIndex,
selectedIndex: selectedItem?.index,
onNavigate: setActiveIndex,
});

const typeahead = useTypeahead(context, {
listRef: labelsRef,
activeIndex,
selectedIndex,
selectedIndex: selectedItem?.index,
onMatch: handleTypeaheadMatch,
});
const click = useClick(context);
Expand All @@ -150,14 +183,14 @@ export default function Select(props: SelectProps) {
[listNav, typeahead, click, dismiss, role]
);

const selectContext = React.useMemo(
const selectContext: SelectContextValue = React.useMemo(
() => ({
activeIndex,
selectedIndex,
selectedItem,
getItemProps,
handleSelect,
}),
[activeIndex, selectedIndex, getItemProps, handleSelect]
[activeIndex, selectedItem, getItemProps, handleSelect]
);

return (
Expand All @@ -170,12 +203,21 @@ export default function Select(props: SelectProps) {
)}
>
{props.label ? (
<FieldLabel htmlFor={props.id} label={props.label} size={props.size} />
<FieldLabel
htmlFor={props.id}
label={props.label}
size={props.size}
attributes={{
marginBottom: "$4",
}}
/>
) : null}

<div ref={refs.setReference}>
<SelectButton
placeholder={selectedLabel || props.placeholder || "Select an option"}
placeholder={
selectedItem?.label || props.placeholder || "Select an option"
}
_css={{
width: props.width
? typeof props.width === "number"
Expand Down Expand Up @@ -214,16 +256,16 @@ export default function Select(props: SelectProps) {
setPointer(false);

if (e.key === "Enter" && activeIndex !== null) {
handleSelect(activeIndex);
return handleSelectIndex(activeIndex);
}

if (e.key === " " && !isTypingRef.current) {
e.preventDefault();
return e.preventDefault();
}
},
onKeyUp(e) {
if (e.key === " " && !isTypingRef.current) {
handleSelect(activeIndex);
handleSelectIndex(activeIndex);
}
},
onPointerMove() {
Expand Down
28 changes: 28 additions & 0 deletions packages/react/stories/Callout.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";

import Callout from "../src/ui/callout";

const meta: Meta<typeof Callout> = {
component: Callout,
title: "Callout",
tags: ["autodocs"],
argTypes: {},
};

export default meta;

type Story = StoryObj<typeof meta>;

export const Primary: Story = {
args: {
title: "Visually important content",
intent: "info",
iconName: "lock",
children:
"You will need to undelegate in order for your staked assets to be liquid again. This process will take 14 days to complete.",
attributes: {
width: "600px",
},
},
};
44 changes: 28 additions & 16 deletions packages/react/stories/inputs/select.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import * as React from "react";
import type { Meta, StoryObj } from "@storybook/react";

import Select from "../../src/ui/select";
Expand All @@ -20,21 +20,33 @@ type Story = StoryObj<typeof meta>;
export const Default: Story = {
render: () => (
<Box width="30" height="700px">
<Select width={300} label="Favorite Animal">
<SelectOption label="Red Panda">Red Panda</SelectOption>
<SelectOption label="Cat">Cat</SelectOption>
<SelectOption label="Dog">Dog</SelectOption>
<SelectOption label="Aardvark">Aardvark</SelectOption>
<SelectOption label="Kangaroo">Kangaroo</SelectOption>
<SelectOption label="Snake">Snake</SelectOption>
<SelectOption label="Kangaroo 2">Kangaroo 2</SelectOption>
<SelectOption label="Snake 2">Snake 2</SelectOption>
<SelectOption label="Kangaroo 3">Kangaroo 3</SelectOption>
<SelectOption label="Snake 3">Snake 3</SelectOption>
<SelectOption label="Kangaroo 4">Kangaroo 4</SelectOption>
<SelectOption label="Snake 4">Snake 4</SelectOption>
<SelectOption label="Kangaroo 5">Kangaroo 5</SelectOption>
<SelectOption label="Snake 5">Snake 5</SelectOption>
<Select
width={300}
defaultSelectedItem={{
key: "Dog",
label: "Dog",
index: 2,
}}
label="Favorite Animal"
>
<SelectOption optionKey="Red Panda" label="Red Panda">
Red Panda
</SelectOption>
<SelectOption optionKey="Cat" label="Cat">
Cat
</SelectOption>
<SelectOption optionKey="Dog" label="Dog">
Dog
</SelectOption>
<SelectOption optionKey="Aardvark" label="Aardvark">
Aardvark
</SelectOption>
<SelectOption optionKey="Kangaroo" label="Kangaroo">
Kangaroo
</SelectOption>
<SelectOption optionKey="Snake" label="Snake">
Snake
</SelectOption>
</Select>
</Box>
),
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export { default as Link } from "./ui/link";
export { default as Stack } from "./ui/stack";
export { default as Center } from "./ui/center";
export { default as Icon } from "./ui/icon";
export { default as Callout } from "./ui/callout";
export type { IconProps, IconName } from "./ui/icon/icon.types";
export { default as Text } from "./ui/text";
export type { TextProps } from "./ui/text/text.types";
Expand Down
4 changes: 3 additions & 1 deletion src/models/system.model.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export enum Intent {
None = "",
None = "none",
Info = "info",
Success = "warning",
Warning = "success",
Expand All @@ -10,6 +10,8 @@ export const intents = Object.entries(Intent).map(
([key, value]: [string, string]) => ({ key, value })
);

export type IntentValues = "none" | "info" | "warning" | "success" | "error";

export type ThemeVariant = "light" | "dark";

export type ModePreference = ThemeVariant | "system";
Expand Down
Loading

0 comments on commit 4ebe9ee

Please sign in to comment.