-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: use own SelectButton
- Loading branch information
Showing
14 changed files
with
925 additions
and
2 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { useRef } from "react"; | ||
import { useOption } from "@mui/base/useOption"; | ||
import { OptionOwnProps } from "@mui/base/Option"; | ||
import { useForkRef } from "@mui/material/utils"; | ||
|
||
import { HvListItem, HvListItemProps } from "../ListContainer"; | ||
import { useDefaultProps } from "../hooks/useDefaultProps"; | ||
import { fixedForwardRef } from "../types/generic"; | ||
import { ExtractNames, createClasses } from "../utils/classes"; | ||
import { outlineStyles } from "../utils/focusUtils"; | ||
|
||
const { staticClasses, useClasses } = createClasses("HvOption", { | ||
root: {}, | ||
highlighted: { | ||
...outlineStyles, | ||
}, | ||
}); | ||
|
||
export { staticClasses as optionClasses }; | ||
|
||
export type HvOptionClasses = ExtractNames<typeof useClasses>; | ||
|
||
export interface HvOptionProps<OptionValue extends {}> | ||
extends Omit<HvListItemProps, "value" | "disabled">, | ||
Pick<OptionOwnProps<OptionValue>, "disabled" | "label" | "value"> { | ||
classes?: HvOptionClasses; | ||
} | ||
|
||
export const HvOption = fixedForwardRef(function HvOption< | ||
OptionValue extends {} | ||
>(props: HvOptionProps<OptionValue>, ref: React.Ref<HTMLLIElement>) { | ||
const { | ||
classes: classesProp, | ||
className, | ||
disabled = false, | ||
label, | ||
value, | ||
children, | ||
...others | ||
} = useDefaultProps("HvOption", props); | ||
const { classes, cx } = useClasses(classesProp); | ||
|
||
const optionRef = useRef<HTMLElement>(null); | ||
const rootRef = useForkRef(optionRef, ref); | ||
|
||
const computedLabel = | ||
label ?? | ||
(typeof children === "string" | ||
? children | ||
: optionRef.current?.textContent?.trim()); | ||
|
||
const { getRootProps, selected, highlighted } = useOption({ | ||
disabled, | ||
label: computedLabel, | ||
rootRef, | ||
value, | ||
}); | ||
|
||
return ( | ||
<HvListItem | ||
ref={ref} | ||
selected={selected} | ||
className={cx(classes.root, className, { | ||
[classes.highlighted]: highlighted, | ||
})} | ||
{...getRootProps()} | ||
{...others} | ||
> | ||
{children} | ||
</HvListItem> | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { forwardRef } from "react"; | ||
import { OptionGroup, OptionGroupProps } from "@mui/base/OptionGroup"; | ||
import { theme } from "@hitachivantara/uikit-styles"; | ||
|
||
import { ExtractNames, createClasses } from "../utils/classes"; | ||
import { useDefaultProps } from "../hooks/useDefaultProps"; | ||
|
||
const { staticClasses, useClasses } = createClasses("HvOptionGroup", { | ||
root: { | ||
listStyle: "none", | ||
...theme.typography.label, | ||
}, | ||
}); | ||
|
||
export { staticClasses as optionGroupClasses }; | ||
|
||
export type HvOptionGroupClasses = ExtractNames<typeof useClasses>; | ||
|
||
export interface HvOptionGroupProps extends OptionGroupProps { | ||
classes?: HvOptionGroupClasses; | ||
} | ||
|
||
export const HvOptionGroup = forwardRef<HTMLLIElement, HvOptionGroupProps>( | ||
(props, ref) => { | ||
const { | ||
className, | ||
classes: classesProp, | ||
...others | ||
} = useDefaultProps("HvOptionGroup", props); | ||
const { classes, cx } = useClasses(classesProp); | ||
|
||
return ( | ||
<OptionGroup | ||
ref={ref} | ||
className={cx(classes.root, className)} | ||
{...others} | ||
/> | ||
); | ||
} | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import { Decorator, Meta, StoryObj } from "@storybook/react"; | ||
import { | ||
HvSelect, | ||
HvSelectProps, | ||
HvOption, | ||
HvOptionGroup, | ||
} from "@hitachivantara/uikit-react-core"; | ||
|
||
import FormStory from "./stories/Form"; | ||
import FormStoryRaw from "./stories/Form?raw"; | ||
import ControlledStory from "./stories/Controlled"; | ||
import ControlledStoryRaw from "./stories/Controlled?raw"; | ||
|
||
const decorator: Decorator = (Story) => ( | ||
<div className="w-[300px] min-h-[300px]">{Story()}</div> | ||
); | ||
|
||
export default { | ||
title: "Components/Select", | ||
component: HvSelect, | ||
// @ts-expect-error https://github.com/storybookjs/storybook/issues/20782 | ||
subcomponents: { HvOption, HvOptionGroup }, | ||
} satisfies Meta<typeof HvSelect>; | ||
|
||
export const Main: StoryObj<HvSelectProps<{}, false>> = { | ||
args: { | ||
multiple: false, | ||
}, | ||
argTypes: {}, | ||
decorators: [decorator], | ||
render: (args) => { | ||
return ( | ||
<HvSelect | ||
required | ||
name="country" | ||
label="Country" | ||
description="Select your favorite country" | ||
placeholder="Select country" | ||
onChange={(evt, val) => console.log(val)} | ||
{...args} | ||
> | ||
<HvOptionGroup label="America"> | ||
<HvOption value="ar">Argentina</HvOption> | ||
<HvOption value="us">United States</HvOption> | ||
</HvOptionGroup> | ||
<HvOptionGroup label="Europe"> | ||
<HvOption value="bg">Belgium</HvOption> | ||
<HvOption value="pt">Portugal</HvOption> | ||
<HvOption value="pl">Poland</HvOption> | ||
<HvOption value="sp">Spain</HvOption> | ||
</HvOptionGroup> | ||
</HvSelect> | ||
); | ||
}, | ||
}; | ||
|
||
export const Variants: StoryObj<HvSelectProps<{}, false>> = { | ||
parameters: { | ||
docs: { | ||
description: { | ||
story: "Selects in their various form state variants.", | ||
}, | ||
}, | ||
}, | ||
decorators: [ | ||
(Story) => ( | ||
<div className="flex flex-wrap gap-sm [&>*]:w-[200px]">{Story()}</div> | ||
), | ||
], | ||
render: () => { | ||
return ( | ||
<> | ||
<HvSelect required label="Required"> | ||
<HvOption value="op">Option</HvOption> | ||
</HvSelect> | ||
<HvSelect disabled label="Disabled"> | ||
<HvOption value="op">Option</HvOption> | ||
</HvSelect> | ||
<HvSelect readOnly label="Read-only"> | ||
<HvOption value="op">Option</HvOption> | ||
</HvSelect> | ||
<HvSelect | ||
status="invalid" | ||
label="Invalid" | ||
statusMessage="This is always invalid" | ||
> | ||
<HvOption value="op">Option</HvOption> | ||
</HvSelect> | ||
</> | ||
); | ||
}, | ||
}; | ||
|
||
export const Form: StoryObj<HvSelectProps<{}, false>> = { | ||
parameters: { | ||
docs: { | ||
source: { code: FormStoryRaw }, | ||
description: { | ||
story: | ||
"To integrate `HvSelect` in a form, make sure you're giving it a `name`. <br />\ | ||
The value result will be the selected option's `value`, or a JSON of the selected values when multi-select is enabled. The value can be customized via the `getSerializedValue` prop.", | ||
}, | ||
}, | ||
}, | ||
decorators: [decorator], | ||
render: () => <FormStory />, | ||
}; | ||
|
||
export const Controlled: StoryObj<HvSelectProps<{}, false>> = { | ||
parameters: { | ||
docs: { | ||
source: { code: ControlledStoryRaw }, | ||
description: { | ||
story: | ||
"The value and open states of `HvSelect` can be controlled by using the `value`/`onChange` and `open`/`onOpenChange` props respectively.", | ||
}, | ||
}, | ||
}, | ||
decorators: [ | ||
(Story) => <div className="flex gap-sm min-h-[300px]">{Story()}</div>, | ||
], | ||
render: () => <ControlledStory />, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { theme } from "@hitachivantara/uikit-styles"; | ||
|
||
import { createClasses } from "../utils/classes"; | ||
|
||
export const { staticClasses, useClasses } = createClasses("HvSelect", { | ||
root: { | ||
position: "relative", | ||
"&$disabled,&$readOnly": { | ||
pointerEvents: "none", | ||
}, | ||
}, | ||
disabled: {}, | ||
readOnly: {}, | ||
invalid: { | ||
border: `1px solid ${theme.colors.negative}`, | ||
}, | ||
labelContainer: { | ||
display: "flex", | ||
alignItems: "flex-start", | ||
}, | ||
label: { | ||
display: "block", | ||
paddingBottom: 6, | ||
}, | ||
description: {}, | ||
select: {}, | ||
popper: { | ||
zIndex: theme.zIndices.popover, | ||
}, | ||
panel: { | ||
border: `1px solid ${theme.colors.secondary}`, | ||
marginTop: -1, | ||
marginBottom: -1, | ||
}, | ||
panelOpenedUp: { | ||
borderRadius: `${theme.radii.base} ${theme.radii.base} 0 0`, | ||
}, | ||
panelOpenedDown: { | ||
borderRadius: `0 0 ${theme.radii.base} ${theme.radii.base}`, | ||
}, | ||
error: {}, | ||
}); |
Oops, something went wrong.