Closed ever-dev closed 1 year ago
The workaround is to create a new component using styled
.
import { InputAdornment } from '@mui/material';
import type { InputAdornmentProps } from '@mui/material';
import { styled } from '@mui/material/styles';
const BorderedInputAdornment = styled(InputAdornment)<InputAdornmentProps>(({ theme }) => ({
'&.MuiInputAdornment-root.MuiInputAdornment-positionStart': {
borderRight: '1px solid',
borderRightColor: theme.palette.divider,
height: '100%',
'& > p': {
paddingRight: 8,
},
},
'&.MuiInputAdornment-root.MuiInputAdornment-positionEnd': {
borderLeft: '1px solid',
borderLeftColor: theme.palette.divider,
height: '100%',
'& > p': {
paddingLeft: 8,
},
},
}));
export default BorderedInputAdornment;
But I don't want to have a separate component while it's possible to do by overriding component.
I looked at the source code and I was wondering how to effectively use AdditionalProps
.
I changed node_modules/@mui/material/InputAdornment/InputAdornment.d.ts
file to the following
import * as React from 'react';
import { SxProps } from '@mui/system';
import { OverridableComponent, OverrideProps } from '../OverridableComponent';
import { Theme } from '..';
import { InputAdornmentClasses } from './inputAdornmentClasses';
export interface InputAdornmentAdditonalPropsOverride {}
export interface InputAdornmentTypeMap<
AdditionalProps = InputAdornmentAdditonalPropsOverride,
DefaultComponent extends React.ElementType = 'div',
> {
props: AdditionalProps & {
/**
* Override or extend the styles applied to the component.
*/
classes?: Partial<InputAdornmentClasses>;
/**
* The content of the component, normally an `IconButton` or string.
*/
children?: React.ReactNode;
/**
* Disable pointer events on the root.
* This allows for the content of the adornment to focus the `input` on click.
* @default false
*/
disablePointerEvents?: boolean;
/**
* If children is a string then disable wrapping in a Typography component.
* @default false
*/
disableTypography?: boolean;
/**
* The position this adornment should appear relative to the `Input`.
*/
position: 'start' | 'end';
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx?: SxProps<Theme>;
/**
* The variant to use.
* Note: If you are using the `TextField` component or the `FormControl` component
* you do not have to set this manually.
*/
variant?: 'standard' | 'outlined' | 'filled';
};
defaultComponent: DefaultComponent;
}
/**
*
* Demos:
*
* - [Text Field](https://mui.com/material-ui/react-text-field/)
*
* API:
*
* - [InputAdornment API](https://mui.com/material-ui/api/input-adornment/)
*/
declare const InputAdornment: OverridableComponent<InputAdornmentTypeMap>;
export type InputAdornmentProps<
RootComponent extends React.ElementType = InputAdornmentTypeMap['defaultComponent'],
AdditionalProps = InputAdornmentAdditonalPropsOverride,
> = OverrideProps<InputAdornmentTypeMap<AdditionalProps, RootComponent>, RootComponent> & {
component?: React.ElementType;
};
export default InputAdornment;
Basically, I introduced InputAdornmentAdditonalPropsOverride
and used it for AdditionalProps
for both InputAdornmentTypeMap
and InputAdornmentProps
.
Then I override that interface in my mui-override.d.ts
declare module '@mui/material' {
interface InputAdornmentAdditonalPropsOverride {
showBorder?: true;
}
}
TypeScript was happy with it.
I'm not sure if there's a way already to use AdditionalProps
effectively.
If it doesn't, I think we should bring this kind of props override to every MUI component so that we can effectively add new props and customize styles.
I'm pretty sure it's not only myself who wants this.
I was doing some research and found this StackOverflow question without an answer. https://stackoverflow.com/questions/73281320/how-to-extend-mui-5-typography-with-custom-props-typescript
@ever-dev You are applying the variant incorrectly which you showed in the description. Follow this documentation on how to create a new component variant. See - https://codesandbox.io/s/thirsty-mcnulty-r4tmlf?file=/src/App.tsx. There's a TypeScript error because we don't provide the interface InputAdornmentPropsVariantOverrides
to override the inbuilt variants. That's a separate issue.
As for your question, if you want to have your own props you will have to create a new component wrapping the Material UI component.
@ZeeshanTamboli I think there's a confusion. I'm not asking to extend the variant, but I'm asking how to extend props. BTW, it's not a new functionality, it's already working without TypeScript. I just want the team to add TypeScript support for this functionality. Also, I proposed a solution here. just wondering if the MUI team is considering including that.
I'm asking how to extend props.
You can create your own wrapper component and extend the Material UI's interface with your custom interface for types. Why not this?
Can you provide your use cases examples in the form of CodeSandboxes so that we are one the same page? The issue template is a good starting point.
Im having a similar issue in Tab
where I need to add a new prop ( dense
). Like Ever-dev said: I have no issues adding the prop itself but the type definitions are created in a way where I can't extend them and I have to redefine them which means that I will have to watch for any changes to these types moving forward. Can you tell me what AdditionalProps
is meant to do? I had the same thought as Ever-dev where extending that AdditionalProps
interface would allow me to tap into those types without stomping on the originals.
Ive also tried module augmentation on the TabOwnProps
interface and it doesnt seem to be working:
declare module '@mui/material/Tab' {
interface TabOwnProps {
/**
'Applies the dense prop to the Tab component.'
*/
dense?: boolean;
}
}
Is there any reason that the above wouldnt work? Its an exported interface so I assumed there wouldnt be any issues
@mogrady88 Does this work for you - https://codesandbox.io/p/sandbox/white-wave-fxqrlq?file=%2Fsrc%2FApp.tsx%3A44%2C2? AdditionalProps
is the second generic type parameter you can use for custom props without having to redefine or extending your custom interface with Material UI component props.
That gets me a lot closer. I think I can come up with the rest based off of your example. Thank you!
Actually @ZeeshanTamboli using TabProps
isnt 1-1 with the original definition. Right now I'm creating a CustomTab
in the way you've done in your codesandbox and it looks like the use of component
with external
isnt working because external
doesnt seem to be included in TabProps
. You can see this if you add external to your props destructuring like so:
const { dense, external, ...other } = props;
Actually @ZeeshanTamboli using
TabProps
isnt 1-1 with the original definition. Right now I'm creating aCustomTab
in the way you've done in your codesandbox and it looks like the use ofcomponent
withexternal
isnt working becauseexternal
doesnt seem to be included inTabProps
. You can see this if you add external to your props destructuring like so:const { dense, external, ...other } = props;
@mogrady88 I am not quite understanding you. It would be better to provide a CodeSandbox. Have you added the external
prop to the TabProps
type like:
function CustomTab(
props: TabProps<"div", { dense?: boolean; external?: boolean }>
...
...
CodeSandbox - https://codesandbox.io/p/sandbox/white-wave-fxqrlq?file=%2Fsrc%2FApp.tsx.
I can do that but I shouldnt have to. Here is a codesandbox that shows the issue.
TabProps
isnt the same as ExtendButtonBase<TabTypeMap>
. The external prop is included in the original type but wrapping my component and using TabProps
doesnt include it. So the actual Tab
component can take different props than TabProps
includes.
You only have to pay attention to CustomTab
. Take a look at the props destructuring based off of the TabProps
and then look at the actual <Tab ... />
component and see that it accepts props that TabProps
doesnt. If Im using TabProps
I would expect it to include everything the original type definition for Tab
does.
There are also other missing types. Im getting reports that the to
prop isnt supported under this new TabProps
type. There seems to be a major mismatch between TabProps
and the const Tab: ExtendButtonBase<TabTypeMap>
I will be out of town for the next few weeks but Ill check this thread when Im back in office. Thank you for your help in this matter!
@mogrady88 To enable the href
prop, you need to use an anchor element because the href
property is not applicable to divs. Use TabProps<"a", { dense?: boolean }>
for this. Regarding the external
prop, it's not an HTML attribute, so it causes an error.
CodeSandbox link: https://codesandbox.io/p/sandbox/shy-lake-rjnvdr?file=%2Fsrc%2FApp.tsx%3A49%2C7
I’ve been working on a project where I encountered an issue while customizing Material UI's Typography
component. Specifically, I added a custom property textStrokeWidth
to control the stroke width of text. Here's an outline of the implementation and the issue I'm facing.
I add a typography component and added a new property textStrokeWidth
:
import { Typography } from '@mui/material'
import React from 'react'
export const Home = (): React.ReactNode => {
return (
<Typography variant='outlineText' fontSize={80} textStrokeWidth={3}>
Home Component
</Typography>
)
}
In my theme configuration, I extended Material UI's theme to handle this property:
import { type Theme, type Components, type PaletteColor } from '@mui/material/styles'
export const dataDisplayCustomizations: Components<Theme> = {
MuiTypography: {
styleOverrides: {
root: ({ ownerState: { textStrokeWidth } }) => ({
WebkitTextStrokeWidth: isNaN(textStrokeWidth as number)
? textStrokeWidth
: `${textStrokeWidth}px`,
}),
outlineText: ({ ownerState: { color = 'secondary' }, theme }) => {
const textColor = {
textPrimary: theme.palette.text.primary,
textSecondary: theme.palette.text.secondary,
textDisabled: theme.palette.text.disabled,
}
let muiColor = textColor[color as keyof typeof textColor]
if (muiColor === undefined) {
muiColor =
(theme.palette[color as keyof typeof theme.palette] as PaletteColor)?.main ??
theme.palette.text.primary
}
return { color: 'transparent', WebkitTextStrokeColor: muiColor }
},
},
variants: [{ props: { noWrap: true }, style: { maxWidth: '100%' } }],
defaultProps: {
variantMapping: {
superTitle: 'h1',
outlineText: 'span',
},
classes: {
superTitle: 'MuiTypography-superTitle',
outlineText: 'MuiTypography-outlineText',
},
},
},
}
'use client'
import { createTheme } from '@mui/material'
import { esES } from '@mui/material/locale'
import { esES as esESX } from '@mui/x-date-pickers/locales'
import type {} from '@mui/x-date-pickers/themeAugmentation'
import {
dataDisplayCustomizations,
inputsCustomizations,
navigationCustomizations,
surfacesCustomizations,
} from './Customizations'
export const theme = createTheme(
{
components: {
...dataDisplayCustomizations,
...inputsCustomizations,
...navigationCustomizations,
...surfacesCustomizations,
},
palette: {...},
shape: { borderRadius: 10 },
typography: {
outlineText: {
font: 'inherit',
color: 'transparent',
WebkitTextStrokeWidth: '1.5px',
WebkitTextStrokeColor: '#2B2D2E',
},
// ... other typography
},
},
esES,
esESX
)
This setup works well, and it correctly applies the style -webkit-text-stroke-width: 3px;, as expected.
To handle the custom property textStrokeWidth
in TypeScript, I extended the TypographyOwnProps
interface to avoid typing errors:
// src/interfaces/@mui/material/Typography.d.ts
import {
type TypographyClasses as MuiTypographyClasses,
type TypographyOwnProps as MuiTypographyOwnProps,
} from '@mui/material/Typography'
export declare module '@mui/material/Typography' {
interface TypographyPropsVariantOverrides {
superTitle: true
outlineText: true
}
interface TypographyPropsColorOverrides {
gray: true
}
interface TypographyClasses extends MuiTypographyClasses {
superTitle: string
outlineText: string
}
interface TypographyOwnProps extends MuiTypographyOwnProps {
textStrokeWidth?:
| '-moz-initial'
| 'inherit'
| 'initial'
| 'revert'
| 'revert-layer'
| 'unset'
| (string & {})
| number
}
}
This solution resolves the TypeScript error, allowing me to use textStrokeWidth
without issues. Everything works fine, and I get the desired visual result without having to create a fully custom typography component.
Despite everything working visually, I’ve noticed an issue in the console. React is throwing the following warning:
React does not recognize the `textStrokeWidth` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `textstrokewidth` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
As a result, the DOM is rendering textstrokewidth="3"
, which shouldn’t happen. Since textStrokeWidth
is not a valid HTML attribute, it should be omitted from the DOM. However, it's still being rendered, causing unnecessary clutter and a React warning.
I’m wondering if there is an existing way in Material UI to prevent this attribute from appearing in the DOM, similar to how fontSize
is handled. I attempted to trace how fontSize
is managed internally by Material UI since it’s excluded from the DOM, but I couldn’t determine the exact mechanism.
Ideally, I’d like to exclude the custom textStrokeWidth
property from being rendered in the DOM, without creating a custom typography component. A solution like a hypothetical mappingIgnoreProps
attribute would be perfect, where we could list attributes to be ignored during rendering.
Duplicates
Latest version
Summary 💡
I want to add new props to MUI components without getting TypeError.
Examples 🌈
No response
Motivation 🔦
I wanted to add a border to the InputAdornment component with the
showBorder
prop.I tried to add
And used
It shows the border in the application, but TypeScript compiler throws an TypeError that
showBorder
doesn't exist.