TypeScript Reassign InputHTMLAttributes - reactjs

i think i know the answer to this already but i'm effectively trying to overwrite a React InputHTMLAttribute when using an interface with styled-components
import { InputHTMLAttributes } from 'react'
import styled from 'styled-components'
export interface IInputStyles
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
/** The size of the input */
size?: 'large' | 'small'
}
export const StyledInput = styled.input<IInputStyles>({}, ({ size }) => ({
height: size === 'large' ? 48 : 32,
}))
i always get var size: never
Is there a way to reassign size or does that defeat the point of type safety?
"typescript": "^3.9.7",

i believe you cannot overwrite HTML attributes like you want, but what you can do is:
export interface IInputStyles extends InputHTMLAttributes<HTMLInputElement> {
large?: boolean;
}
export const StyledInput = styled.input.attrs<IInputStyles>((props) => ({
style: {
height: (props.large ? "48" : "32") + "px",
},
}))<IInputStyles>`
...other css styles...
`;
So, if you create a StyledInput with large attribute, the height should be 48px;
<StyledInput large />;
else the height should be 32px;
<StyledInput />;

Related

React Styled components, how to get props type of component wchich returns different elements

I decided to change the styling in the project from css modules to styled components.
I had a problem with one component, which, depending on the props, returns different elements - a button or a link.
here is code of it:
//type
export type ButtonProps = UIComponentProps &
React.ComponentPropsWithoutRef<"button"> & {
type?: "button";
};
export type AnchorProps = UIComponentProps &
React.ComponentPropsWithoutRef<"a"> & {
type: "link";
external?: boolean;
};
export type CustomButtonProps = ButtonProps | AnchorProps;
//component
import Link from "next/link";
import { CustomButtonProps } from "../Button";
const Button = (props: CustomButtonProps) => {
if (props.type === "link") {
// eslint-disable-next-line #typescript-eslint/no-unused-vars
const { type: _, external, ...anchorProps } = props;
const { children, disabled, href } = anchorProps;
if (disabled) {
return <button disabled={true}>{children}</button>;
} else {
return external ? (
<a {...anchorProps}>{children}</a>
) : (
<Link {...anchorProps} href={href || "#"}>
{children}
</Link>
);
}
}
const { children } = props;
return <button {...props}>{children}</button>;
};
export default Button;
When I used css modules, to change the styles, I just sent the className which became at the end of the list of classes and overlapped the previous styles
<Button className={styles.heroButton}>{HeaderText.heroButton}</Button>
// if pass 'type={"link"}' it become link el
Now i use styled components extend functionality
export const HeroButton = styled(Button)`
width: 60px;
height: 60px;
#media screen and (max-width: 425px) {
width: 50px;
height: 50px;
}
`;
If i extend a component that returns a single element all is working fine
But with Button multi component typescript not getting types
Is there a way to get types and not seperate multi component to two different?

How to set customized color for a Text component with styled-components?

I want to set a Text component's color dynamically. When there is a disable condition then change to another color.
import React from "react";
import styled, { css } from "styled-components";
type TextType = {
text: string;
disable: string;
};
export const Text: React.FC<TextType> = ({ text, disable }) => {
return <TextStyle disable>{text}</TextStyle>;
};
const TextStyle = styled.span`
${({ theme }) => css`
color: ${(props) =>
props.disable ? theme.color.text : theme.color.disable};
`}
`;
The props.disable got error
Property 'disable' does not exist on type 'ThemeProps<DefaultTheme>'. [2339]
The theme is
export const theme = {
color: {
text: black,
disable: blue,
},
};
I want to use it as
<Text disable text={text} />
Or
<Text text={text} disable={disable} />
Is it necessary to define a disable property in theme const?
i think it is because the first argument in your styled component Text is 'theme', while it should be props. so in your case there is a confusion between theme and props.
try this see if it works :
import React from "react";
import styled, { css } from "styled-components";
type TextType = {
text: string;
disable: string;
};
export const Text: React.FC<TextType> = ({ text, disable }) => {
return <TextStyle disable={disable} >{text}</TextStyle>;
};
const TextStyle = styled.span`
${(props) => css`
color: ${ props.disable ? props.theme.color.text : props.theme.color.disable};
`}
`;
Also, the disable prop that you are passing in your component is not the same as the disable property in your theme. If you do not wish to have a different name for each as to avoid confusion, may be you should at least say that the disable you are passing into your component as a prop is of type boolean, like so :
type TextType = {
text: string;
disable: boolean;
};

How to define props on framer-motion styled-component? (PSA)

PSA:
(not a question but I didn't see any answers to this on stack so here's the answer.)
Define props on a styled-components that wraps a motion, it's a little confusing how to define them when wrapping motion with styled().
how to define props according to styled components docs
import styled from 'styled-components';
import Header from './Header';
interface TitleProps {
readonly isActive: boolean;
}
const Title = styled.h1<TitleProps>`
color: ${(props) => (props.isActive ? props.theme.colors.main : props.theme.colors.secondary)};
`;
How to define the props in your code without motion:
import styled from "styled-components";
interface Props {
height?: number;
}
const Container = styled.div<Props>`
height: ${({ height }) => height};
`;
export default Container;
How to define the props in your code with motion:
import { HTMLMotionProps, motion } from "framer-motion";
import styled from "styled-components";
/**
* notice the props extend HTMLMotionProps<"div"> this is so all the default
* props are passed such as `onClick`
*/
interface Props extends HTMLMotionProps<"div"> {
height?: number;
}
//notice motion is called as a function instead of `motion.div`
const Container = styled(motion<Props>("div"))`
height: ${({ height }) => height};
`;
export default Container;
The problem is that you are defining your "motion div" wrong. It should be defined this way instead:
interface Props {
height?: number;
}
// motion is an object that gives you access to the html tags (like the div)
const Container = styled(motion.div)<Props>`
height: ${({ height }) => height};
`;
As you can see above, you just need to pass in motion.div as any other component, into the styled function.

Cannot create a toggle switch with React JSS (react-jss) [duplicate]

I have seen in a lot of the MUI code that they use pseudo selectors in their react styled components. I thought I would try to do it myself and I cannot get it to work. I'm not sure what I am doing wrong or if this is even possible.
I am trying to make some CSS that will offset this element against the fixed header.
import React from 'react';
import { createStyles, WithStyles, withStyles, Typography } from '#material-ui/core';
import { TypographyProps } from '#material-ui/core/Typography';
import GithubSlugger from 'github-slugger';
import Link from './link';
const styles = () =>
createStyles({
h: {
'&::before': {
content: 'some content',
display: 'block',
height: 60,
marginTop: -60
}
}
});
interface Props extends WithStyles<typeof styles>, TypographyProps {
children: string;
}
const AutolinkHeader = ({ classes, children, variant }: Props) => {
// I have to call new slugger here otherwise on a re-render it will append a 1
const slug = new GithubSlugger().slug(children);
return (
<Link to={`#${slug}`}>
<Typography classes={{ root: classes.h }} id={slug} variant={variant} children={children} />
</Link>
);
};
export default withStyles(styles)(AutolinkHeader);
I found out that the content attribute needed to be double quoted like this
const styles = () =>
createStyles({
h: {
'&::before': {
content: '"some content"',
display: 'block',
height: 60,
marginTop: -60
}
}
});
and then everything worked like expected
As #Eran Goldin said, check the value of your content property and make sure it's set to a string "". Odds are, you are doing something like this:
'&::before': {
content: '',
...
}
Which doesn't set the content property at all in the output stylesheet
.makeStyles-content-154:before {
content: ;
...
}
In Material-UI style object, the content of the string is the css value, including the double quote, to fix it simply write
'&::before': {
content: '""', // "''" will also work.
...
}
Without having to explicitly set a height
The above solutions require explicit height. If you want height to be dynamic, you can do something like this:
&::after {
content: '_'; // Any character can go here
visibility: hidden;
}

Cannot declare React context in typescript? [duplicate]

This question already has an answer here:
Cannot find namespace 'ctx' error when creating Context with react - typescript
(1 answer)
Closed 3 years ago.
i am trying to make a context with hooks in react in typescript like this:
interface SettingsStateContextProps {
sort: string;
type: string;
price: string;
option_full: boolean;
option_rated: boolean;
option_free: boolean;
}
export const SettingsStateContext = React.createContext<
Partial<SettingsStateContextProps>
>({});
export const SettingsStoreProvider:any = ({ reducer, initialState, children }):any => (
<SettingsStateContext.Provider // ERROR HERE
value={useReducer(reducer, initialState)}
children={children}
/>
);
but for some reason i don't know why I cannot declare SettingsStateContext.Provider? I am getting an error message with
'Cannot find namespace 'SettingsStateContext'
How do i fix this?
I'm not sure about your second export. There is how i use it:
1) Export your context to use it anywhere
import React, { useEffect } from 'react'
import { ThemeProvider } from 'styled-components'
// this is needed so that components using theme will see it's type
declare module 'styled-components' {
interface DefaultTheme extends Theme {}
}
interface Theme {
color: string
}
const defaultTheme = { color: 'red' }
export const ThemeContext = React.createContext<Partial<Theme>>({})
export const ThemeContextProvider = props => {
const [theme, setTheme] = useState<Theme>(defaultTheme)
return (
<ThemeContext.Provider value={{ theme }}>
{props.children}
</ThemeContext.Provider>
)
}
2) Wrap component where you want to get theme access (i use it one time at top level of app):
import React from 'react'
import { ThemeContextProvider, Input } from '#frontend'
export const App = () => {
return (
<ThemeContextProvider>
<Input />
</ThemeContextProvider>
)
}
Use it in component (example for shared component):
import styled from 'styled-components'
import React, { forwardRef } from 'react'
type MyProps = {
err?: boolean
}
export const Input = forwardRef<HTMLInputElement, MyProps>(({ err, ...rest }, ref) => {
return <StyledInput {...rest} ref={ref} />
})
const StyledInput = styled.input<MyProps>`
box-shadow: inset 0 0 0 1px ${({ err, theme }) => (err ? theme.badColor : theme.primaryColor)};
border-radius: 5px;
`
I'm seein' here you didn't put any square bracket after React.createContent, you should put write like this React.createContext(),
export const SettingsStateContext = React.createContext();
put your logic in squre bracket, I should work!
you can study here to learn more about!
Thanks!

Resources