Skip to content

Commit

Permalink
Merge pull request #23469 from storybookjs/charles/update-form-ui
Browse files Browse the repository at this point in the history
UI: Create new form elements in the new Core UI (Input, TextArea, Select)
  • Loading branch information
cdedreuille committed Jul 18, 2023
2 parents afdc58a + a164fd1 commit 53276b5
Show file tree
Hide file tree
Showing 15 changed files with 1,249 additions and 1 deletion.
1 change: 1 addition & 0 deletions code/ui/components/package.json
Expand Up @@ -67,6 +67,7 @@
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
"@radix-ui/react-select": "^1.2.2",
"@storybook/client-logger": "7.1.0",
"@storybook/csf": "^0.1.0",
"@storybook/global": "^5.0.0",
Expand Down
32 changes: 32 additions & 0 deletions code/ui/components/src/new/Input/Input.stories.tsx
@@ -0,0 +1,32 @@
import type { Meta, StoryObj } from '@storybook/react';

import { Input } from './Input';

const meta: Meta<typeof Input> = {
title: 'Input',
component: Input,
tags: ['autodocs'],
};

export default meta;
type Story = StoryObj<typeof Input>;

export const Base: Story = {
args: {
placeholder: 'Hello World',
},
};

export const Filled: Story = {
args: {
...Base.args,
value: 'Hello World',
},
};

export const Disabled: Story = {
args: {
...Base.args,
disabled: true,
},
};
55 changes: 55 additions & 0 deletions code/ui/components/src/new/Input/Input.tsx
@@ -0,0 +1,55 @@
import React, { forwardRef } from 'react';
import { styled } from '@storybook/theming';

interface InputProps {
disabled?: boolean;
placeholder?: string;
value?: string;
}

export const Input = forwardRef<HTMLInputElement, InputProps>(({ ...props }, ref) => {
return <StyledInput ref={ref} {...props} />;
});

Input.displayName = 'Input';

const StyledInput = styled.input(({ theme }) => ({
// resets
appearance: 'none',
border: '0 none',
display: 'block',
margin: ' 0',
position: 'relative',

// styles
width: '100%',
height: '32px',
transition: 'box-shadow 200ms ease-out, opacity 200ms ease-out',
color: theme.input.color,
background: theme.input.background,
boxShadow: `${theme.input.border} 0 0 0 1px inset`,
borderRadius: theme.input.borderRadius,
fontSize: theme.typography.size.s2 - 1,
padding: '6px 10px',
boxSizing: 'border-box',
lineHeight: '20px',

'&:focus': {
boxShadow: `${theme.color.secondary} 0 0 0 1px inset`,
outline: 'none',
},

'&[disabled]': {
cursor: 'not-allowed',
opacity: 0.5,
},

'&:-webkit-autofill': {
WebkitBoxShadow: `0 0 0 3em ${theme.color.lightest} inset`,
},

'&::placeholder': {
color: theme.textMutedColor,
opacity: 1,
},
}));
91 changes: 91 additions & 0 deletions code/ui/components/src/new/Select/Select.stories.tsx
@@ -0,0 +1,91 @@
import type { Meta, StoryObj } from '@storybook/react';
import React from 'react';

import { Select } from './Select';

const meta: Meta<typeof Select.Root> = {
title: 'Select',
component: Select.Root,
tags: ['autodocs'],
parameters: {
layout: 'centered',
},
};

export default meta;
type Story = StoryObj<typeof Select.Root>;

export const Base: Story = {
args: {},
render: (_, { args }) => (
<div style={{ width: 400 }}>
<Select.Root {...args}>
<Select.Trigger>
<Select.Value placeholder="Select a fruit…" />
</Select.Trigger>
<Select.Content>
<Select.Item value="Avocado">Avocado</Select.Item>
<Select.Item value="Banana">Banana</Select.Item>
<Select.Item value="Bilberry">Bilberry</Select.Item>
<Select.Item value="Blackberry">Blackberry</Select.Item>
<Select.Item value="Blackcurrant">Blackcurrant</Select.Item>
<Select.Item value="Black sapote">Black sapote</Select.Item>
<Select.Item value="Blueberry">Blueberry</Select.Item>
<Select.Item value="Boysenberry">Boysenberry</Select.Item>
<Select.Item value="Breadfruit">Breadfruit</Select.Item>
<Select.Item value="Cacao">Cacao</Select.Item>
<Select.Item value="Cactus pear">Cactus pear</Select.Item>
<Select.Item value="Canistel">Canistel</Select.Item>
<Select.Item value="Catmon">Catmon</Select.Item>
<Select.Item value="Cempedak">Cempedak</Select.Item>
<Select.Item value="Cherimoya">Cherimoya</Select.Item>
<Select.Item value="Cherry">Cherry</Select.Item>
<Select.Item value="Chico fruit">Chico fruit</Select.Item>
<Select.Item value="Cloudberry">Cloudberry</Select.Item>
<Select.Item value="Coco de mer">Coco de mer</Select.Item>
<Select.Item value="Coconut">Coconut</Select.Item>
<Select.Item value="Crab apple">Crab apple</Select.Item>
<Select.Item value="Cranberry">Cranberry</Select.Item>
<Select.Item value="Currant">Currant</Select.Item>
<Select.Item value="Damson">Damson</Select.Item>
<Select.Item value="Date">Date</Select.Item>
<Select.Item value="Dragonfruit">Dragonfruit</Select.Item>
<Select.Item value="Durian">Durian</Select.Item>
<Select.Item value="Elderberry">Elderberry</Select.Item>
<Select.Item value="Feijoa">Feijoa</Select.Item>
<Select.Item value="Fig">Fig</Select.Item>
<Select.Item value="Finger Lime">Finger Lime</Select.Item>
<Select.Item value="Gac Fruit">Gac Fruit</Select.Item>
<Select.Item value="Goji berry">Goji berry</Select.Item>
<Select.Item value="Gooseberry">Gooseberry</Select.Item>
<Select.Item value="Grape">Grape</Select.Item>
<Select.Item value="Raisin">Raisin</Select.Item>
<Select.Item value="Grapefruit">Grapefruit</Select.Item>
<Select.Item value="Grewia asiatica">Grewia asiatica</Select.Item>
<Select.Item value="Guava">Guava</Select.Item>
<Select.Item value="Guyabano">Guyabano</Select.Item>
<Select.Item value="Hala Fruit">Hala Fruit</Select.Item>
<Select.Item value="Honeyberry">Honeyberry</Select.Item>
<Select.Item value="Huckleberry">Huckleberry</Select.Item>
<Select.Item value="Jabuticaba">Jabuticaba</Select.Item>
<Select.Item value="Jackfruit">Jackfruit</Select.Item>
<Select.Item value="Jambul">Jambul</Select.Item>
<Select.Item value="Japanese plum">Japanese plum</Select.Item>
<Select.Item value="Jostaberry">Jostaberry</Select.Item>
<Select.Item value="Jujube">Jujube</Select.Item>
<Select.Item value="Juniper berry">Juniper berry</Select.Item>
<Select.Item value="Kaffir Lime">Kaffir Lime</Select.Item>
<Select.Item value="Kiwano">Kiwano</Select.Item>
<Select.Item value="Kiwifruit">Kiwifruit</Select.Item>
<Select.Item value="Kumquat">Kumquat</Select.Item>
<Select.Item value="Lanzones">Lanzones</Select.Item>
<Select.Item value="Lemon">Lemon</Select.Item>
<Select.Item value="Lime">Lime</Select.Item>
<Select.Item value="Loganberry">Loganberry</Select.Item>
<Select.Item value="Longan">Longan</Select.Item>
<Select.Item value="Loquat">Loquat</Select.Item>
</Select.Content>
</Select.Root>
</div>
),
};
180 changes: 180 additions & 0 deletions code/ui/components/src/new/Select/Select.tsx
@@ -0,0 +1,180 @@
import * as React from 'react';
import * as SelectPrimitive from '@radix-ui/react-select';
import { styled } from '@storybook/theming';
import { ExpandAlt } from './icons/ExpandAlt';
import { Arrowup } from './icons/Arrowup';
import { Arrowdown } from './icons/Arrowdown';
import { Check } from './icons/Check';

const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<StyledTrigger ref={ref} {...props}>
{children}
<SelectPrimitive.Icon asChild>
<ExpandAlt size={12} />
</SelectPrimitive.Icon>
</StyledTrigger>
));
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;

const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Portal>
<StyledContent ref={ref} {...props}>
<StyledScrollUpButton>
<Arrowup size={12} />
</StyledScrollUpButton>
<StyledViewport>{children}</StyledViewport>
<StyledScrollDownButton>
<Arrowdown size={12} />
</StyledScrollDownButton>
</StyledContent>
</SelectPrimitive.Portal>
));
SelectContent.displayName = SelectPrimitive.Content.displayName;

const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => <SelectPrimitive.Label ref={ref} {...props} />);
SelectLabel.displayName = SelectPrimitive.Label.displayName;

const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<StyledItem ref={ref} {...props}>
<StyledItemIndicator>
<Check size={12} />
</StyledItemIndicator>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</StyledItem>
));
SelectItem.displayName = SelectPrimitive.Item.displayName;

const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => <SelectPrimitive.Separator ref={ref} {...props} />);
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;

export const Select = {
Root: SelectPrimitive.Root,
Group: SelectPrimitive.Group,
Value: SelectPrimitive.Value,
Trigger: SelectTrigger,
Content: SelectContent,
Label: SelectLabel,
Item: SelectItem,
Separator: SelectSeparator,
};

const StyledTrigger = styled(SelectPrimitive.Trigger)(({ theme }) => ({
all: 'unset',
display: 'flex',
width: '100%',
height: '32px',
alignItems: 'center',
justifyContent: 'space-between',
transition: 'box-shadow 200ms ease-out, opacity 200ms ease-out',
color: theme.input.color,
background: theme.input.background,
boxShadow: `${theme.input.border} 0 0 0 1px inset`,
borderRadius: theme.input.borderRadius,
fontSize: theme.typography.size.s2 - 1,
padding: '6px 10px',
boxSizing: 'border-box',
lineHeight: '20px',

'&:focus': {
boxShadow: `${theme.color.secondary} 0 0 0 1px inset`,
outline: 'none',
},

'&[disabled]': {
cursor: 'not-allowed',
opacity: 0.5,
},

'&[data-placeholder]': {
color: theme.textMutedColor,
},

'&:-webkit-autofill': {
WebkitBoxShadow: `0 0 0 3em ${theme.color.lightest} inset`,
},
}));

const StyledContent = styled(SelectPrimitive.Content)(({ theme }) => ({
boxSizing: 'border-box',
overflow: 'hidden',
backgroundColor: theme.input.background,
borderRadius: '6px',
border: theme.base === 'dark' ? `1px solid ${theme.input.border}` : '1px solid transparent',
width: '100%',
boxShadow:
'0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2)',
}));

const StyledViewport = styled(SelectPrimitive.Viewport)(() => ({
boxSizing: 'border-box',
width: '100%',
padding: '5px',
}));

const StyledScrollUpButton = styled(SelectPrimitive.ScrollUpButton)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '25px',
backgroundColor: theme.input.background,
color: theme.input.color,
cursor: 'default',
}));

const StyledScrollDownButton = styled(SelectPrimitive.ScrollDownButton)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '25px',
backgroundColor: theme.input.background,
color: theme.input.color,
cursor: 'default',
}));

const StyledItem = styled(SelectPrimitive.Item)(({ theme }) => ({
fontSize: '13px',
lineHeight: 1,
color: theme.input.color,
borderRadius: '3px',
display: 'flex',
alignItems: 'center',
height: '25px',
padding: '0 35px 0 25px',
position: 'relative',
userSelect: 'none',

'&[data-disabled]': {
color: 'red',
pointerEvents: 'none',
},

'&[data-highlighted]': {
outline: 'none',
backgroundColor: theme.barSelectedColor,
color: theme.barBg,
},
}));

const StyledItemIndicator = styled(SelectPrimitive.ItemIndicator)(() => ({
position: 'absolute',
left: 0,
width: '25px',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
}));

0 comments on commit 53276b5

Please sign in to comment.