Skip to content

Commit f337bb8

Browse files
author
Dominic Nguyen
authored
Merge pull request #333 from storybookjs/select-focus-color
Select integration fixes
2 parents 674ebc9 + f3823f2 commit f337bb8

2 files changed

Lines changed: 104 additions & 31 deletions

File tree

src/components/Select.stories.tsx

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export default {
1111
component: UnstyledSelect,
1212
};
1313

14-
1514
export const Template = (args) => <Select {...args} />;
1615
Template.args = {
1716
value: 'value1',
@@ -23,7 +22,6 @@ Template.args = {
2322
};
2423
Template.story = { name: 'Playground' };
2524

26-
2725
const Form = styled.form`
2826
padding: 3em 12em;
2927
`;
@@ -49,7 +47,6 @@ const All = ({ appearance }) => (
4947
]}
5048
onChange={onChange}
5149
appearance={appearance}
52-
5350
/>
5451
<Select
5552
id="disabled"
@@ -139,6 +136,47 @@ export const Default = () => (
139136
</DarkForm>
140137
);
141138

139+
export const Stacked = () => (
140+
<Form>
141+
<UnstyledSelect
142+
id="default"
143+
value="value1"
144+
label="Animal"
145+
hideLabel
146+
options={[
147+
{ label: 'Default', value: 'value1' },
148+
{ label: 'Dog', value: 'value2' },
149+
]}
150+
onChange={onChange}
151+
stackLevel="top"
152+
/>
153+
<UnstyledSelect
154+
id="default1"
155+
value="value1"
156+
label="Animal"
157+
hideLabel
158+
options={[
159+
{ label: 'Default', value: 'value1' },
160+
{ label: 'Dog', value: 'value2' },
161+
]}
162+
onChange={onChange}
163+
stackLevel="middle"
164+
/>
165+
<UnstyledSelect
166+
id="default2"
167+
value="value1"
168+
label="Animal"
169+
hideLabel
170+
options={[
171+
{ label: 'Default', value: 'value1' },
172+
{ label: 'Dog', value: 'value2' },
173+
]}
174+
onChange={onChange}
175+
stackLevel="bottom"
176+
/>
177+
</Form>
178+
);
179+
142180
export const Tertiary = () => (
143181
<Form>
144182
<All appearance="tertiary" />

src/components/Select.tsx

Lines changed: 63 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ const LabelWrapper = styled.div<LabelProps>`
3434
`;
3535

3636
interface SelectProps {
37-
disabled: boolean;
38-
inProgress: boolean;
37+
disabled?: boolean;
38+
inProgress?: boolean;
3939
}
4040

4141
const Selector = styled.select<SelectProps>`
@@ -44,11 +44,12 @@ const Selector = styled.select<SelectProps>`
4444
border-radius: 0;
4545
font-size: ${typography.size.s2}px;
4646
line-height: 20px;
47-
padding: 10px 3em 10px 1em;
47+
padding: 10px 3em 10px 15px;
4848
position: relative;
4949
outline: none;
5050
width: 100%;
5151
margin: 0;
52+
display: block;
5253
5354
${(props) =>
5455
props.disabled &&
@@ -74,7 +75,7 @@ const SelectIcon = styled(Icon)``;
7475
const SelectSpinner = styled(Spinner)`
7576
right: 16px;
7677
left: auto;
77-
z-index: 1;
78+
z-index: 2;
7879
`;
7980

8081
const SelectError = styled.div`
@@ -89,36 +90,66 @@ const SelectError = styled.div`
8990
color: ${color.negative};
9091
line-height: 40px;
9192
padding-right: 2.75em;
93+
pointer-events: none;
9294
`;
9395

9496
interface WrapperProps {
95-
disabled: boolean;
9697
appearance: 'default' | 'tertiary';
9798
hasIcon: boolean;
9899
error: any;
99-
inProgress: boolean;
100+
disabled?: boolean;
101+
inProgress?: boolean;
102+
stackLevel?: 'top' | 'middle' | 'bottom';
100103
}
101104

105+
const getStackLevelStyling = (props: Pick<Props, 'stackLevel'>) => {
106+
const radius = 4;
107+
108+
const stackLevelDefinedStyling = css`
109+
position: relative;
110+
111+
&:focus {
112+
z-index: 2;
113+
}
114+
`;
115+
116+
switch (props.stackLevel) {
117+
case 'top':
118+
return css`
119+
border-top-left-radius: ${radius}px;
120+
border-top-right-radius: ${radius}px;
121+
border-bottom-left-radius: 0;
122+
border-bottom-right-radius: 0;
123+
${stackLevelDefinedStyling}
124+
`;
125+
case 'middle':
126+
return css`
127+
border-radius: 0px;
128+
margin-top: -1px;
129+
${stackLevelDefinedStyling}
130+
`;
131+
case 'bottom':
132+
return css`
133+
border-bottom-left-radius: ${radius}px;
134+
border-bottom-right-radius: ${radius}px;
135+
border-top-left-radius: 0;
136+
border-top-right-radius: 0;
137+
margin-top: -1px;
138+
${stackLevelDefinedStyling}
139+
`;
140+
default:
141+
return css`
142+
border-radius: ${radius}px;
143+
`;
144+
}
145+
};
146+
102147
const SelectWrapper = styled.span<WrapperProps>`
103148
display: inline-block;
104-
height: 40px;
105-
line-height: normal;
106-
overflow: hidden;
107149
position: relative;
108150
vertical-align: top;
109151
width: 100%;
110152
111-
transition: all 150ms ease-out;
112-
transform: translate3d(0, 0, 0);
113-
114-
&:hover {
115-
transform: translate3d(0, -1px, 0);
116-
}
117-
118-
&:active {
119-
transform: translate3d(0, 0, 0);
120-
}
121-
122153
&:before {
123154
content: '';
124155
bottom: 1px;
@@ -127,14 +158,13 @@ const SelectWrapper = styled.span<WrapperProps>`
127158
width: 2em;
128159
margin-left: 1px;
129160
position: absolute;
130-
z-index: 1;
161+
z-index: 3;
131162
pointer-events: none;
132-
border-radius: ${spacing.borderRadius.small}px;
133163
}
134164
135165
${Arrow} {
136166
position: absolute;
137-
z-index: 1;
167+
z-index: 3;
138168
pointer-events: none;
139169
height: 12px;
140170
margin-top: -6px;
@@ -148,7 +178,7 @@ const SelectWrapper = styled.span<WrapperProps>`
148178
149179
${Selector} {
150180
box-shadow: ${color.border} 0 0 0 1px inset;
151-
border-radius: 4px;
181+
${(props) => getStackLevelStyling(props)}
152182
}
153183
${Selector}:focus {
154184
box-shadow: ${color.secondary} 0 0 0 1px inset;
@@ -183,14 +213,14 @@ const SelectWrapper = styled.span<WrapperProps>`
183213
}
184214
${Arrow} {
185215
right: 0;
186-
top: 12px;
216+
top: 10px;
187217
}
188218
${SelectSpinner} {
189219
right: 0;
190220
}
191221
${SelectIcon} {
192222
left: 0;
193-
top: 12px;
223+
top: 10px;
194224
}
195225
${SelectError} {
196226
position: relative;
@@ -209,7 +239,7 @@ const SelectWrapper = styled.span<WrapperProps>`
209239
${Selector} + ${SelectIcon} {
210240
transition: all 150ms ease-out;
211241
position: absolute;
212-
top: ${props.appearance === 'tertiary' ? '12px' : '50%'};
242+
top: ${props.appearance === 'tertiary' ? '10px' : '50%'};
213243
left: ${props.appearance === 'tertiary' ? 0 : '0.8em'};
214244
height: 1em;
215245
width: 1em;
@@ -236,7 +266,9 @@ const SelectWrapper = styled.span<WrapperProps>`
236266
}
237267
238268
${Selector} + ${SelectIcon} {
239-
animation: ${jiggle} 700ms ease-out;
269+
${css`
270+
animation: ${jiggle} 700ms ease-out;
271+
`}
240272
path {
241273
fill: ${color.red};
242274
}
@@ -264,6 +296,7 @@ interface Props {
264296
className?: string;
265297
inProgress?: boolean;
266298
disabled?: boolean;
299+
stackLevel?: 'top' | 'middle' | 'bottom';
267300
}
268301

269302
export const Select: FunctionComponent<Props & ComponentProps<typeof Selector>> = ({
@@ -278,6 +311,7 @@ export const Select: FunctionComponent<Props & ComponentProps<typeof Selector>>
278311
className,
279312
inProgress = false,
280313
disabled = false,
314+
stackLevel = undefined,
281315
...other
282316
}) => {
283317
let spinnerId;
@@ -304,6 +338,7 @@ export const Select: FunctionComponent<Props & ComponentProps<typeof Selector>>
304338
error={error}
305339
disabled={disabled}
306340
inProgress={inProgress}
341+
stackLevel={stackLevel}
307342
>
308343
{!inProgress && <Arrow />}
309344
<Selector

0 commit comments

Comments
 (0)