Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ui v2: select #722

Merged
merged 11 commits into from
Dec 3, 2020
176 changes: 139 additions & 37 deletions frontend/packages/core/src/Input/select.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,159 @@
import React from "react";
import * as React from "react";
import styled from "@emotion/styled";
import type { SelectProps as MuiSelectProps } from "@material-ui/core";
import {
FormControl as MuiFormControl,
FormHelperText as MuiFormHelperText,
InputLabel as MuiInputLabel,
MenuItem,
Select as MuiSelect,
} from "@material-ui/core";
import styled from "styled-components";

const FormControl = styled(MuiFormControl)`
${({ theme, ...props }) => `
display: flex;
min-width: fit-content;
width: ${props["data-max-width"] || "500px"};
.MuiInput-underline:after {
border-bottom: 2px solid ${theme.palette.accent.main};
}
`}
`;
import ErrorIcon from "@material-ui/icons/Error";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";

const InputLabel = styled(MuiInputLabel)`
${({ theme }) => `
&& {
color: ${theme.palette.text.primary};
}
position: relative;
`}
`;
const StyledFormControl = styled(MuiFormControl)({
"label + .MuiInput-formControl": {
marginTop: "20px",
},
});

const StyledSelect = styled(MuiSelect)`
&& {
margin-top: 0px;
}
`;
const StyledFormHelperText = styled(MuiFormHelperText)({
verticalAlign: "middle",
display: "flex",
position: "relative",
fontSize: "12px",
lineHeight: "16px",
marginTop: "7px",
color: "grey",

"&.Mui-error": {
color: "#db3615",
},

svg: {
height: "16px",
width: "16px",
marginRight: "4px",
},
});

const StyledInputLabel = styled(MuiInputLabel)({
marginLeft: "2px",
fontWeight: 500,
fontSize: "14px",
transform: "scale(1)",
marginBottom: "10px",
color: "rgba(13, 16, 48, 0.6)",
"&.Mui-focused": {
color: "rgba(13, 16, 48, 0.6)",
},
"&.Mui-error": {
color: "#db3615",
},
});

const SelectIcon = (props: any) => (
<div {...props}>
<ExpandMoreIcon />
</div>
);

const BaseSelect = ({ ...props }: MuiSelectProps) => (
<MuiSelect
disableUnderline
fullWidth
IconComponent={SelectIcon}
MenuProps={{
style: { marginTop: "2px" },
danielhochman marked this conversation as resolved.
Show resolved Hide resolved
anchorOrigin: { vertical: "bottom", horizontal: "left" },
transformOrigin: { vertical: "top", horizontal: "left" },
getContentAnchorEl: null,
}}
{...props}
/>
);

const StyledSelect = styled(BaseSelect)({
padding: "0",

".MuiSelect-root": {
padding: "15px 60px 13px 16px",
borderRadius: "4px",
borderStyle: "solid",
borderWidth: "1px",
borderColor: "rgba(13, 16, 48, 0.38)",
height: "20px",
},

"&.Mui-focused > .MuiSelect-root": {
borderColor: "#3548d4",
},

"&.Mui-error > .MuiSelect-root": {
borderColor: "#db3615",
},

".MuiSelect-root.Mui-disabled": {
backgroundColor: "rgba(13, 16, 48, 0.12)",
},

".MuiSelect-icon": {
height: "100%",
width: "48px",
top: "unset",
transform: "unset",
display: "flex",
justifyContent: "center",
alignItems: "center",
boxSizing: "border-box",
},
danielhochman marked this conversation as resolved.
Show resolved Hide resolved

".MuiSelect-icon.Mui-disabled > svg": {
color: "rgba(13, 16, 48, 0.6)",
},

".MuiSelect-icon > svg": {
color: "rgba(13, 16, 48, 0.6)",
position: "absolute",
},

"&.Mui-focused > .MuiSelect-icon > svg": {
color: "#0d1030",
},

".MuiSelect-icon.MuiSelect-iconOpen > svg": {
transform: "rotate(180deg)",
},

".MuiSelect-select:focus": {
backgroundColor: "inherit",
},
});

interface SelectOption {
label: string;
value?: string;
}

export interface SelectProps {
export interface SelectProps extends Pick<MuiSelectProps, "disabled" | "error"> {
defaultOption?: number;
helperText?: string;
label?: string;
maxWidth?: string;
name: string;
options: SelectOption[];
onChange: (value: string) => void;
}

const Select: React.FC<SelectProps> = ({
export const Select = ({
defaultOption = 0,
disabled,
error,
helperText,
label,
maxWidth,
name,
options,
onChange,
}) => {
}: SelectProps) => {
if (options.length === 0) {
return null;
}
Expand All @@ -74,22 +173,25 @@ const Select: React.FC<SelectProps> = ({
}, []);

return (
<FormControl key={name} data-max-width={maxWidth}>
<InputLabel shrink color="secondary">
{label}
</InputLabel>
<StyledFormControl key={name} fullWidth disabled={disabled} error={error}>
{label && <StyledInputLabel>{label}</StyledInputLabel>}
<StyledSelect
value={options[selectedIdx]?.value || options[selectedIdx].label}
onChange={updateSelectedOption}
fullWidth
>
{options.map(option => (
<MenuItem key={option.label} value={option?.value || option.label}>
{option.label}
</MenuItem>
))}
</StyledSelect>
</FormControl>
{helperText && (
<StyledFormHelperText>
{error && <ErrorIcon />}
{helperText}
</StyledFormHelperText>
)}
</StyledFormControl>
);
};

Expand Down
49 changes: 27 additions & 22 deletions frontend/packages/core/src/Input/stories/select.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from "react";
import * as React from "react";
import type { Meta } from "@storybook/react";

import type { SelectProps } from "../select";
import Select from "../select";
import { Select } from "../select";

export default {
title: "Core/Input/Select",
Expand All @@ -13,46 +13,51 @@ export default {
},
} as Meta;

const Template = (props: SelectProps) => <Select name="demo" {...props} />;
const Template = (props: SelectProps) => <Select name="storybookDemo" {...props} />;

export const Primary = Template.bind({});
Primary.args = {
label: "My Label",
options: [
{
label: "option 1",
label: "Option 1",
},
{
label: "option 2",
label: "Option 2",
},
],
};

export const Disabled = Template.bind({});
Disabled.args = {
...Primary.args,
disabled: true,
};

export const Error = Template.bind({});
Error.args = {
...Primary.args,
error: true,
helperText: "There was a problem!",
};

export const CustomValues = Template.bind({});
CustomValues.args = {
...Primary.args,
options: [
{
label: "option 1",
value: "value 1",
label: "Option 1",
value: "VALUE_ONE",
},
{
label: "option 2",
value: "value 2",
label: "Option 2",
value: "VALUE_TWO",
},
],
};

export const WithLabel = Template.bind({});
WithLabel.argTypes = {
defaultOption: {
control: {
type: "select",
options: Primary.args.options.map((_: any, i: number) => i),
},
},
};
WithLabel.args = {
export const WithoutLabel = Template.bind({});
WithoutLabel.args = {
...Primary.args,
defaultOption: 0,
label: "Please select one",
maxWidth: "100px",
label: null,
};
2 changes: 1 addition & 1 deletion frontend/packages/core/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { BaseWorkflowProps, WorkflowConfiguration } from "./AppProvider/workflow";
import CheckboxPanel from "./Input/checkbox";
import { RadioGroup } from "./Input/radio-group";
import Select from "./Input/select";
import { Select } from "./Input/select";
import { TextField } from "./Input/text-field";
import ClutchApp from "./AppProvider";
import { Button, ButtonGroup, ButtonProps, ClipboardButton } from "./button";
Expand Down