Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd3bf506

Browse files
authored
chore(site): convert more components from Emotion to TailwindCSS (#19719)
## Changes made- Patched React `CSSProperties` type to add support for custom CSSproperties- Updated several of the components in the `components` directory toTailwind- Updated most of the `WorkspacePageBuildView` component to Tailwind toaccount for CSS specificity changes- Updated `Search` to address accessibility violation and removed allMUI logic- Updated `Search` stories (added new story, decoupled all stories fromsingle decorator)- Updated `autoFocus` behavior in `SearchField`- Updated the styling for `WorkspacePageBuildView` to make sure the tabshad enough padding- Fixed layout effect in `WorkspacePageBuildView` to fire correctly
1 parent679179f commitd3bf506

File tree

16 files changed

+280
-432
lines changed

16 files changed

+280
-432
lines changed

‎site/src/@types/react.d.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
declare module"react"{
2+
interfaceCSSProperties{
3+
[key: `--${string}`]:string|number|undefined;
4+
}
5+
}
6+
7+
export{};

‎site/src/components/Filter/SelectFilter.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ export const SelectFilter: FC<SelectFilterProps> = ({
5252
<SelectMenuTrigger>
5353
<SelectMenuButton
5454
startIcon={selectedOption?.startIcon}
55-
css={{flexBasis:width,flexGrow:1}}
56-
className="shrink-0"
55+
className="shrink-0 grow"
56+
style={{flexBasis:width}}
5757
aria-label={label}
5858
>
5959
{selectedOption?.label??placeholder}

‎site/src/components/MultiSelectCombobox/MultiSelectCombobox.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ export const MultiSelectCombobox = forwardRef<
506506
<Badge
507507
key={option.value}
508508
className={cn(
509-
"data-[disabled]:bg-content-disabled data-[disabled]:text-surface-tertiarydata-[disabled]:hover:bg-content-disabled",
509+
"data-[disabled]:bg-content-disabled data-[disabled]:text-surface-tertiary data-[disabled]:hover:bg-content-disabled",
510510
"data-[fixed]:bg-content-disabled data-[fixed]:text-surface-tertiary data-[fixed]:hover:bg-surface-secondary",
511511
badgeClassName,
512512
)}
Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,46 @@
11
importtype{Meta,StoryObj}from"@storybook/react-vite";
2-
import{Search,SearchInput}from"./Search";
2+
import{Search,SearchEmpty,SearchInput}from"./Search";
33

44
constmeta:Meta<typeofSearchInput>={
55
title:"components/Search",
66
component:SearchInput,
7-
decorators:[
8-
(Story)=>(
9-
<Search>
10-
<Story/>
11-
</Search>
12-
),
13-
],
147
};
158

169
exportdefaultmeta;
1710
typeStory=StoryObj<typeofSearchInput>;
1811

19-
exportconstExample:Story={};
12+
exportconstExample:Story={
13+
render:(props)=>(
14+
<Search>
15+
<SearchInput{...props}/>
16+
</Search>
17+
),
18+
};
2019

21-
exportconstWithPlaceholder:Story={
20+
exportconstWithCustomPlaceholder:Story={
2221
args:{
2322
label:"uwu",
2423
placeholder:"uwu",
2524
},
25+
render:(props)=>(
26+
<Search>
27+
<SearchInput{...props}/>
28+
</Search>
29+
),
30+
};
31+
32+
exportconstWithSearchEmpty:Story={
33+
args:{
34+
label:"I crave the certainty of steel",
35+
placeholder:"Alas, I am empty",
36+
},
37+
render:(props)=>(
38+
<divclassName="flex flex-col gap-2">
39+
<Search>
40+
<SearchInput{...props}/>
41+
</Search>
42+
43+
<SearchEmpty/>
44+
</div>
45+
),
2646
};
Lines changed: 28 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
importtype{Interpolation,Theme}from"@emotion/react";
2-
// biome-ignore lint/style/noRestrictedImports: use it to have the component prop
3-
importBox,{typeBoxProps}from"@mui/material/Box";
4-
importvisuallyHiddenfrom"@mui/utils/visuallyHidden";
51
import{SearchIcon}from"lucide-react";
62
importtype{FC,HTMLAttributes,InputHTMLAttributes,Ref}from"react";
3+
import{cn}from"utils/cn";
74

8-
interfaceSearchPropsextendsOmit<BoxProps,"ref">{
9-
$$ref?:Ref<unknown>;
5+
interfaceSearchPropsextendsHTMLAttributes<HTMLDivElement>{
6+
ref?:Ref<HTMLDivElement>;
107
}
118

129
/**
@@ -18,100 +15,63 @@ interface SearchProps extends Omit<BoxProps, "ref"> {
1815
* </Search>
1916
* ```
2017
*/
21-
exportconstSearch:FC<SearchProps>=({ children, $$ref, ...boxProps})=>{
18+
exportconstSearch:FC<SearchProps>=({
19+
children,
20+
ref,
21+
className,
22+
...props
23+
})=>{
2224
return(
23-
<Boxref={$$ref}{...boxProps}css={SearchStyles.container}>
24-
<SearchIconclassName="size-icon-xs"css={SearchStyles.icon}/>
25+
<div
26+
ref={ref}
27+
{...props}
28+
className={cn(
29+
"flex items-center h-10 pl-4 border-0 border-b border-solid border-border",
30+
className,
31+
)}
32+
>
33+
<SearchIconclassName="size-icon-xs text-sm text-content-secondary"/>
2534
{children}
26-
</Box>
35+
</div>
2736
);
2837
};
2938

30-
constSearchStyles={
31-
container:(theme)=>({
32-
display:"flex",
33-
alignItems:"center",
34-
paddingLeft:16,
35-
height:40,
36-
borderBottom:`1px solid${theme.palette.divider}`,
37-
}),
38-
39-
icon:(theme)=>({
40-
fontSize:14,
41-
color:theme.palette.text.secondary,
42-
}),
43-
}satisfiesRecord<string,Interpolation<Theme>>;
44-
4539
typeSearchInputProps=InputHTMLAttributes<HTMLInputElement>&{
4640
label?:string;
47-
$$ref?:Ref<HTMLInputElement>;
41+
ref?:Ref<HTMLInputElement>;
4842
};
4943

5044
exportconstSearchInput:FC<SearchInputProps>=({
5145
label,
52-
$$ref,
46+
ref,
47+
id,
5348
...inputProps
5449
})=>{
5550
return(
5651
<>
57-
<labelcss={{ ...visuallyHidden}}htmlFor={inputProps.id}>
52+
<labelclassName="sr-only"htmlFor={id}>
5853
{label}
5954
</label>
6055
<input
61-
ref={$$ref}
62-
tabIndex={-1}
56+
ref={ref}
57+
id={id}
58+
tabIndex={0}
6359
type="text"
6460
placeholder="Search..."
65-
css={SearchInputStyles.input}
61+
className="text-inherit h-full border-0 bg-transparent grow basis-0 outline-none pl-4 placeholder:text-content-secondary"
6662
{...inputProps}
6763
/>
6864
</>
6965
);
7066
};
7167

72-
constSearchInputStyles={
73-
input:(theme)=>({
74-
color:"inherit",
75-
height:"100%",
76-
border:0,
77-
background:"none",
78-
flex:1,
79-
marginLeft:16,
80-
outline:0,
81-
"&::placeholder":{
82-
color:theme.palette.text.secondary,
83-
},
84-
}),
85-
}satisfiesRecord<string,Interpolation<Theme>>;
86-
8768
exportconstSearchEmpty:FC<HTMLAttributes<HTMLDivElement>>=({
8869
children="Not found",
8970
...props
9071
})=>{
9172
return(
92-
<divcss={SearchEmptyStyles.empty}{...props}>
73+
<divclassName="text-sm text-content-secondary text-center py-2"{...props}>
9374
{children}
9475
</div>
9576
);
9677
};
97-
98-
constSearchEmptyStyles={
99-
empty:(theme)=>({
100-
fontSize:13,
101-
color:theme.palette.text.secondary,
102-
textAlign:"center",
103-
paddingTop:8,
104-
paddingBottom:8,
105-
}),
106-
}satisfiesRecord<string,Interpolation<Theme>>;
107-
108-
/**
109-
* Reusable styles for consumers of the base components
110-
*/
111-
exportconstsearchStyles={
112-
content:{
113-
width:320,
114-
padding:0,
115-
borderRadius:4,
116-
},
117-
}satisfiesRecord<string,Interpolation<Theme>>;

‎site/src/components/SearchField/SearchField.tsx‎

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,49 @@
1-
import{useTheme}from"@emotion/react";
21
importIconButtonfrom"@mui/material/IconButton";
32
importInputAdornmentfrom"@mui/material/InputAdornment";
43
importTextField,{typeTextFieldProps}from"@mui/material/TextField";
54
importTooltipfrom"@mui/material/Tooltip";
6-
importvisuallyHiddenfrom"@mui/utils/visuallyHidden";
5+
import{useEffectEvent}from"hooks/hookPolyfills";
76
import{SearchIcon,XIcon}from"lucide-react";
8-
import{typeFC,useEffect,useRef}from"react";
7+
import{typeFC,useLayoutEffect,useRef}from"react";
98

109
exporttypeSearchFieldProps=Omit<TextFieldProps,"onChange">&{
1110
onChange:(query:string)=>void;
1211
autoFocus?:boolean;
1312
};
1413

1514
exportconstSearchField:FC<SearchFieldProps>=({
16-
value="",
15+
InputProps,
1716
onChange,
17+
value="",
1818
autoFocus=false,
19-
InputProps,
2019
...textFieldProps
2120
})=>{
22-
consttheme=useTheme();
23-
constinputRef=useRef<HTMLInputElement>(null);
24-
25-
useEffect(()=>{
21+
// MUI's autoFocus behavior is wonky. If you set autoFocus=true, the
22+
// component will keep getting focus on every single render, even if there
23+
// are other input elements on screen. We want this to be one-time logic
24+
constinputRef=useRef<HTMLInputElement|null>(null);
25+
constfocusOnMount=useEffectEvent(():void=>{
2626
if(autoFocus){
2727
inputRef.current?.focus();
2828
}
2929
});
30+
useLayoutEffect(()=>{
31+
focusOnMount();
32+
},[focusOnMount]);
3033

3134
return(
3235
<TextField
33-
// Specifying `minWidth` so that the text box can't shrink so much
36+
inputRef={inputRef}
37+
// Specifying min width so that the text box can't shrink so much
3438
// that it becomes un-clickable as we add more filter controls
35-
css={{minWidth:"280px"}}
39+
className="min-w-[280px]"
3640
size="small"
3741
value={value}
3842
onChange={(e)=>onChange(e.target.value)}
39-
inputRef={inputRef}
4043
InputProps={{
4144
startAdornment:(
4245
<InputAdornmentposition="start">
43-
<SearchIcon
44-
className="size-icon-xs"
45-
css={{
46-
color:theme.palette.text.secondary,
47-
}}
48-
/>
46+
<SearchIconclassName="size-icon-xs text-content-secondary"/>
4947
</InputAdornment>
5048
),
5149
endAdornment:value!==""&&(
@@ -58,7 +56,7 @@ export const SearchField: FC<SearchFieldProps> = ({
5856
}}
5957
>
6058
<XIconclassName="size-icon-xs"/>
61-
<spancss={{ ...visuallyHidden}}>Clear search</span>
59+
<spanclassName="sr-only">Clear search</span>
6260
</IconButton>
6361
</Tooltip>
6462
</InputAdornment>

‎site/src/components/SelectMenu/SelectMenu.tsx‎

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ import {
2222
}from"react";
2323
import{cn}from"utils/cn";
2424

25-
constSIDE_PADDING=16;
26-
2725
exportconstSelectMenu=Popover;
2826

2927
exportconstSelectMenuTrigger=PopoverTrigger;
@@ -37,13 +35,20 @@ type SelectMenuButtonProps = ButtonProps & {
3735
exportconstSelectMenuButton=forwardRef<
3836
HTMLButtonElement,
3937
SelectMenuButtonProps
40-
>((props,ref)=>{
41-
const{ startIcon, ...restProps}=props;
38+
>(({ className, startIcon, children, ...props},ref)=>{
4239
return(
43-
<Buttonvariant="outline"size="lg"ref={ref}{...restProps}>
40+
<Button
41+
variant="outline"
42+
size="lg"
43+
ref={ref}
44+
// Shrink padding right slightly to account for visual weight of
45+
// the chevron
46+
className={cn("flex flex-row gap-2 pr-1.5",className)}
47+
{...props}
48+
>
4449
{startIcon}
4550
<spanclassName="text-left block overflow-hidden text-ellipsis flex-grow">
46-
{props.children}
51+
{children}
4752
</span>
4853
<ChevronDownIcon/>
4954
</Button>
@@ -55,22 +60,7 @@ export const SelectMenuSearch: FC<SearchFieldProps> = (props) => {
5560
<SearchField
5661
fullWidth
5762
size="medium"
58-
css={(theme)=>({
59-
borderBottom:`1px solid${theme.palette.divider}`,
60-
"& input":{
61-
fontSize:14,
62-
},
63-
"& fieldset":{
64-
border:0,
65-
borderRadius:0,
66-
},
67-
"& .MuiInputBase-root":{
68-
padding:`12px${SIDE_PADDING}px`,
69-
},
70-
"& .MuiInputAdornment-positionStart":{
71-
marginRight:SIDE_PADDING,
72-
},
73-
})}
63+
className="border border-solid border-border [&_input]:text-sm [&_fieldset]:border-0 [&_fieldset]:rounded-none [&_.MuiInputBase-root]:px-4 [&_.MuiInputBase-root]:py-3"
7464
{...props}
7565
inputProps={{autoFocus:true, ...props.inputProps}}
7666
/>

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp