Skip to content

Commit d1b8e44

Browse files
authored
feat(ui): chonkify segmentedControl (#89589)
1 parent e07587f commit d1b8e44

File tree

4 files changed

+244
-85
lines changed

4 files changed

+244
-85
lines changed

static/app/components/core/button/index.chonk.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ function chonkSizeMapping(size: ButtonProps['size']): ChonkButtonSize {
5151
}
5252

5353
export function getChonkButtonStyles(
54-
p: ButtonProps & {theme: DO_NOT_USE_ChonkTheme}
54+
p: Pick<ButtonProps, 'size' | 'priority' | 'busy' | 'disabled' | 'borderless'> & {
55+
theme: DO_NOT_USE_ChonkTheme;
56+
}
5557
): StrictCSSObject {
5658
const type = chonkPriorityToType(p.priority);
5759
const size = chonkSizeMapping(p.size);
@@ -150,7 +152,7 @@ export function getChonkButtonStyles(
150152
},
151153
},
152154

153-
'&:active, &[aria-expanded="true"]': {
155+
'&:active, &[aria-expanded="true"], &[aria-checked="true"]': {
154156
'&::after': {
155157
transform: 'translateY(0px)',
156158
},
@@ -159,7 +161,7 @@ export function getChonkButtonStyles(
159161
},
160162
},
161163

162-
'&:disabled': {
164+
'&:disabled, &[aria-disabled="true"]': {
163165
'&::after': {
164166
transform: 'translateY(0px)',
165167
},
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import type {DO_NOT_USE_ChonkTheme} from '@emotion/react';
2+
3+
import {getChonkButtonStyles} from 'sentry/components/core/button/index.chonk';
4+
import type {FormSize} from 'sentry/utils/theme';
5+
import {chonkStyled} from 'sentry/utils/theme/theme.chonk';
6+
7+
export type Priority = 'default' | 'primary';
8+
9+
export const ChonkStyledGroupWrap = chonkStyled('div')<{
10+
priority: Priority;
11+
size: FormSize;
12+
}>`
13+
position: relative;
14+
display: inline-grid;
15+
grid-auto-flow: column;
16+
min-width: 0;
17+
18+
${p => p.theme.form[p.size]}
19+
20+
& > label:first-child {
21+
border-top-right-radius: 0;
22+
border-bottom-right-radius: 0;
23+
border-right: 0;
24+
}
25+
26+
& > label:not(:first-child):not(:last-child) {
27+
border-radius: 0;
28+
}
29+
30+
& > label:last-child {
31+
border-top-left-radius: 0;
32+
border-bottom-left-radius: 0;
33+
}
34+
35+
/* don't turn off border if the 2nd element is also the last element */
36+
& > label:last-child:not(:nth-child(2)) {
37+
border-left: 0;
38+
}
39+
`;
40+
41+
export const ChonkStyledSegmentWrap = chonkStyled('label')<{
42+
isSelected: boolean;
43+
priority: Priority;
44+
size: FormSize;
45+
isDisabled?: boolean;
46+
}>`
47+
position: relative;
48+
display: flex;
49+
align-items: center;
50+
margin: 0;
51+
cursor: ${p => (p.isDisabled ? 'default' : 'pointer')};
52+
min-height: 0;
53+
min-width: 0;
54+
z-index: ${p => (p.isSelected ? 1 : undefined)};
55+
56+
${p => p.theme.buttonPadding[p.size]}
57+
font-weight: ${p => p.theme.fontWeightNormal};
58+
59+
${p => ({...getChonkButtonStyles({...p, disabled: p.isDisabled, priority: p.isSelected && p.priority === 'primary' ? 'primary' : 'default'})})}
60+
61+
&:has(input:focus-visible) {
62+
${p => p.theme.focusRing};
63+
64+
/* Hide fallback ring when :has works */
65+
span {
66+
box-shadow: none !important;
67+
}
68+
}
69+
70+
/* Fallback ring (for Firefox, where :has doesn't work) */
71+
input:focus-visible + span {
72+
${({theme}) => theme.focusRing};
73+
}
74+
`;
75+
76+
export const ChonkStyledVisibleLabel = chonkStyled('span')<{
77+
isSelected: boolean;
78+
priority: Priority;
79+
}>`
80+
${p => p.theme.overflowEllipsis}
81+
user-select: none;
82+
font-weight: ${p => p.theme.fontWeightNormal};
83+
text-align: center;
84+
color: ${p => getTextColor(p)};
85+
`;
86+
87+
function getTextColor({
88+
isSelected,
89+
priority,
90+
theme,
91+
}: {
92+
isSelected: boolean;
93+
priority: Priority;
94+
theme: DO_NOT_USE_ChonkTheme;
95+
isDisabled?: boolean;
96+
}) {
97+
if (isSelected) {
98+
return priority === 'default' ? theme.colors.blue500 : undefined;
99+
}
100+
101+
return theme.subText;
102+
}

static/app/components/core/segmentedControl/index.stories.tsx

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import storyBook from 'sentry/stories/storyBook';
1010
export default storyBook('SegmentedControl', story => {
1111
story('Controlled Value', () => {
1212
const [value, setValue] = useState('two');
13+
const [value2, setValue2] = useState('one');
1314
return (
1415
<Fragment>
1516
<p>
@@ -19,11 +20,30 @@ export default storyBook('SegmentedControl', story => {
1920
manually.
2021
</p>
2122
<p>selected={value}</p>
22-
<SegmentedControl value={value} onChange={setValue}>
23-
<SegmentedControl.Item key="one">One</SegmentedControl.Item>
24-
<SegmentedControl.Item key="two">Two</SegmentedControl.Item>
25-
<SegmentedControl.Item key="three">Three</SegmentedControl.Item>
26-
</SegmentedControl>
23+
<SideBySide>
24+
<SegmentedControl value={value} onChange={setValue} priority="default">
25+
<SegmentedControl.Item key="one">One</SegmentedControl.Item>
26+
<SegmentedControl.Item key="two">Two</SegmentedControl.Item>
27+
<SegmentedControl.Item key="three">Three</SegmentedControl.Item>
28+
</SegmentedControl>
29+
<SegmentedControl value={value} onChange={setValue} priority="primary">
30+
<SegmentedControl.Item key="one">One</SegmentedControl.Item>
31+
<SegmentedControl.Item key="two">Two</SegmentedControl.Item>
32+
<SegmentedControl.Item key="three">Three</SegmentedControl.Item>
33+
</SegmentedControl>
34+
</SideBySide>
35+
36+
<p>selected={value2}</p>
37+
<SideBySide>
38+
<SegmentedControl value={value2} onChange={setValue2} priority="default">
39+
<SegmentedControl.Item key="one">One</SegmentedControl.Item>
40+
<SegmentedControl.Item key="two">Two</SegmentedControl.Item>
41+
</SegmentedControl>
42+
<SegmentedControl value={value2} onChange={setValue2} priority="primary">
43+
<SegmentedControl.Item key="one">One</SegmentedControl.Item>
44+
<SegmentedControl.Item key="two">Two</SegmentedControl.Item>
45+
</SegmentedControl>
46+
</SideBySide>
2747
</Fragment>
2848
);
2949
});
@@ -90,13 +110,13 @@ export default storyBook('SegmentedControl', story => {
90110
const [value, setValue] = useState('two');
91111
return (
92112
<SegmentedControl value={value} onChange={setValue}>
93-
<SegmentedControl.Item key="one" tooltip="One">
113+
<SegmentedControl.Item key="one" {...props}>
94114
One
95115
</SegmentedControl.Item>
96116
<SegmentedControl.Item key="two" {...props}>
97117
Two
98118
</SegmentedControl.Item>
99-
<SegmentedControl.Item key="three" tooltip="Three">
119+
<SegmentedControl.Item key="three" {...props}>
100120
Three
101121
</SegmentedControl.Item>
102122
</SegmentedControl>

0 commit comments

Comments
 (0)