Material-UI - Can I selectively enable Button ripple? - reactjs

I know I can turn off all ripples by putting this in the theme object:
components: {
MuiButtonBase: {
defaultProps: {
disableRipple: true,
},
},
},
I know I can turn it off for everything except all IconButtons with the following:
components: {
MuiButtonBase: {
defaultProps: {
disableRipple: true,
},
},
MuiIconButton: {
defaultProps: {
disableRipple: false,
},
},
},
I know I can turn it off each component individually with this JSX:
<Button disableRipple>
Only one rippleless!
</Button>
But is there any way to turn the ripple off globally and just enable it individually?
For example, something that would do what the following fake code suggests:
<Button enableRipple>
Only one rippled!
</Button>
🤔

Okay its this easy:
<Button disableRipple={false}>
Only one rippled!
</Button>

Related

Storybook does not infer controls for Kendo-UI (React) Components

I'm in the process of creating an internal component library using Storybook and Kendo React and I'm having to explicitly define argTypes for controls for every prop in order to get them to give me anything but a string input (with the sole exception of booleans).
Here's a sample of one of my stories:
import React from "react";
import { ComponentStory, ComponentMeta } from "#storybook/react";
import { TestAvatar } from "../../components/Avatar/TestAvatar";
export default {
title: "Atomic Components/Test Avatar",
component: TestAvatar,
} as ComponentMeta<typeof TestAvatar>;
const Template: ComponentStory<typeof TestAvatar> = ({ ...args }: any) => (
<TestAvatar {...args} />
);
export const Standard = Template.bind({});
Standard.args = {
border: false,
themeColor: "base",
fillMode: "solid",
rounded: "medium",
size: "medium",
type: undefined,
};
Standard.argTypes = {
themeColor: {
options: ["base", "primary", "secondary", "tertiary", "success", "error"],
control: { type: "select" },
},
rounded: {
options: ["small", "medium", "large", "full", null],
control: { type: "select" },
},
size: {
options: ["small", "medium", "large"],
control: { type: "select" },
},
fillMode: {
options: ["solid", "outline"],
control: { type: "radio" },
},
type: {
options: ["image", "text", "icon"],
control: { type: "select" },
},
};
I have all the standard addons, although I haven't created any typescript options in main.js - I've tried, using info I got from this question but they don't seem to make any difference.
I've tried a few things, using Story and Meta from Storybook instead of the newer ComponentStory and ComponentMeta, I've tried extending the AvatarProps interface and I have also tried using the Kendo AvatarProps interface directly, but I can't find a way around it.
I suspect it's to do with how the Kendo library is typed.
I want to have to avoid creating controls for every single component I'm creating - does anyone have any ideas on how I can circumvent that amount of unnecessary work?

Conditionally disable select option on Storybook control

I have a React component with this props:
type MyComponentProps =
| {
orientation: 'horizontal';
labelPosition: 'labelRight' | 'labelDown';
}
| {
orientation: 'vertical';
labelPosition: 'labelRight';
};
When the prop orientation is vertical, it should only accept labelRight as the value for labelPosition (labelDown is only accepted when using the horizontal version).
I also have a story documentating each prop:
export default {
component: MyComponent,
argTypes: {{
orientation: {
control: {
type: 'select',
options: ['horizontal', 'vertical'],
},
},
labelPosition: {
control: {
type: 'select',
options: ['right', 'bottom'],
},
},
}}
}
Is it possible to conditionally disable an option in Storybook? I tried with conditional controls but it disables the whole control.

Change ripple effect duration in MUI React

I want to modify ripple effect duration in theme file
I've tried things like
const theme = createMuiTheme({
props: {
MuiButtonBase: {
TouchRippleProps: {
animationDuration: '5s',
transitionDuration: '5s',
},
classes: {
root: 'CustomizeTouchRipple'
}
},
}
})
Not working.. but class is added
Edit:
I found its a constant in TouchRipple component
https://github.com/mui-org/material-ui/blob/c06a88d24235685dd769c1a3df82152ded17a6ca/packages/material-ui/src/ButtonBase/TouchRipple.js#L8
From the source, the class name where the animationDuration is set is MuiTouchRipple-rippleVisible. You just need to override that value:
V5
<Button
sx={{
'&& .MuiTouchRipple-rippleVisible': {
animationDuration: '200ms',
},
}}
>
V4
const theme = createTheme({
overrides: {
MuiButton: {
root: {
'& .MuiTouchRipple-rippleVisible': {
animationDuration: '200ms',
},
},
},
// if you want to change the ripple duration of all components that have ripple
// effect (e.g. Button, CardActionArea, FAB...)
MuiTouchRipple: {
rippleVisible: {
animationDuration: '200ms',
},
},
},
});

Tailwind css background gradient not applying

My tailwind background gradient is not being applied
here is my html code:
<div>
<button className="bg-gradient-to-r from-primary-orange via-secondary-orange to-lighter-orange w-4/5 p-4 mt-10 rounded">Search Flights</button>
</div>
My tailwind.config.js:
module.exports = {
purge: [],
darkMode: false, // or 'media' or 'class'
theme: {
backgroundColor: theme => ({
...theme('colors'),
'primary-orange': '#FF8C00',
'secondary-orange':'#FFA500',
'lighter-orange':'#FFD700'
})
},
variants: {
extend: {},
},
plugins: [],
}
Running my react script with post-css so all the other colors I add to tailwind.config are working once I generate the post-css just not the gradient.
Any idea why?
Thanks
Use the extend section of your tailwind.config.js file if you'd like to extend the default color palette rather than override it. Then add gradientColorStops attribute to it where you can write your custom colors.
module.exports = {
purge: [],
darkMode: false,
theme: {
extend: {
gradientColorStops: theme => ({
'primary': '#FF8C00',
'secondary': '#FFA500',
'danger': '#FFD700',
}),
},
},
variants: {
extend: {},
},
plugins: [],
}

Rendering Material-UI icons from an array

Background info:
I'm using react and material-ui.
To keep the code clean, I populate menu items from a const array, like so:
const menuItems = [
{ label: "Home", path: "/home" },
{ label: "Accounts", path: "/accounts" },
{ label: "Organizations", path: "/organizations" },
];
Each item in the array is an object containing a label and a redirect path. I map over the items when rendering. Very basic.
Problem:
I would like to include a material-ui icon component in the menuItems array so the icon can be rendered next to the label. But I can't find a way to reference the icons by a name string
https://material-ui.com/components/material-icons/
Possible solutions:
put the icon component into a string:
{ label: "Accounts", path: "/accounts" }, icon: "<AccountBox/>"} but then I somehow need to evaluate the string into jsx. I don't know how.
Make a react functional component which renders a different icon depending on a prop, for example: <IconSwitch icon = {"accountIcon"} /> and hard-code different icons inside the RFC. Not pretty, but should work.
Punt and use different icons such as svg icons or font icons that can referenced by a name string.
Any suggestions on how to do this?
Thanks
Icon Font
You can use the Icon component. https://material-ui.com/components/icons/#icon-font-icons
To use an icon simply wrap the icon name (font ligature) with the Icon component, for example:
import Icon from '#material-ui/core/Icon';
<Icon>star</Icon>
https://codesandbox.io/s/material-demo-forked-sj66h?file=/demo.tsx
Assuming you set up your menu items with the appropriate icon ligatures:
const menuItems = [
{ label: "Home", path: "/home", icon: "home" },
{ label: "Accounts", path: "/accounts", icon: "account_circle" },
{ label: "Organizations", path: "/organizations", icon: "settings" }
];
Then you can map over them:
{menuItems.map(({ label, icon }) => {
return (
<span key={label}>
{label} <Icon>{icon}</Icon>
</span>
);
})}
SVG Icons
If you want to use SVG icons instead of basic icons, I'd recommend pulling only the SVG icons you plan to use in order to allow the icons you aren't using to be tree-shaken from the resulting bundle. The ability to tree shake is a good reason to use SVG icons over font icons.
import { Home, AccountCircle, Settings, AddCircle } from "#material-ui/icons";
If you want to allow user input of all icons or aren't aware ahead of time which icons will be displayed, you can import everything from #material-ui/icons as in Jonathan's answer.
If you aren't putting the list of icons into something that needs to be able to be stringified (i.e. Redux/sent through an API call) then you can just directly put the icons into the array and render them:
const menuItems: MenuItem[] = [
{ label: "Home", path: "/home", icon: <Home /> },
{ label: "Accounts", path: "/accounts", icon: <AccountCircle /> },
{ label: "Organizations", path: "/organizations", icon: <Settings /> }
];
// Rendering:
{menuItems.map(({ label, icon }) => {
return (
<span key={label}>
{label} {icon}
</span>
);
})}
If you are going to put the Icons somewhere that needs to be stringified, the above won't work, so I'd recommend putting the icons you want to use into an object to map them. That way you have a string to icon map.
Example: https://codesandbox.io/s/material-icons-svg-udcv3?file=/demo.tsx
import { Home, AccountCircle, Settings, AddCircle } from "#material-ui/icons";
const icons = {
Home,
AccountCircle,
Settings
};
In the case of the example above (i.e. rendering the icons from an array)
interface MenuItem {
label: string;
path: string;
icon: keyof typeof icons;
}
const menuItems: MenuItem[] = [
{ label: "Home", path: "/home", icon: "Home" },
{ label: "Accounts", path: "/accounts", icon: "AccountCircle" },
{ label: "Organizations", path: "/organizations", icon: "Settings" }
];
// Rendering:
{menuItems.map(({ label, icon }) => {
const Icon = icons[icon];
return (
<span key={label}>
{label} <Icon />
</span>
);
})}
You can import all from #material-ui/icons and than create an Icon component dynamically:
import React from 'react'
import * as icons from '#material-ui/icons'
interface MenuItem {
label: string,
icon: keyof typeof icons,
path: string
}
export function Menu() {
const menuItems: MenuItem[] = [
{ label: 'Home', path: './home', icon: 'Home' },
{ label: 'Accounts', path: './accounts', icon: 'AccountCircle' },
{ label: 'Organizations', path: './organizations', icon: 'Settings' }
]
return (
<>
{menuItems.map(menuItem => {
const Icon = icons[menuItem.icon]
return (
<span key={menuItem.path}>
{menuItem.label} <Icon />
</span>
)
})}
</>
)
}
// I have better way to avoid all of this other hustle .
// 1: Make Every icon in Array which is in Jsx from to simple name.
// Ex:
[
{ Name: "New", Icon: <HomeIcon /> },
{ Name: "JS Mastery", Icon: <CodeIcon /> },
{ Name: "Coding", Icon: <CodeIcon /> },
{ Name: "ReactJS", Icon: <CodeIcon /> },
{ Name: "NextJS", Icon: <CodeIcon /> },
]
to
[
({ Name: "New", Icon: HomeIcon },
{ Name: "JS Mastery", Icon: CodeIcon },
{ Name: "Coding", Icon: CodeIcon },
{ Name: "ReactJS", Icon: CodeIcon })
];
// 2: Remember Using Object keys name as capital ,here:- "Name , Icon" not "name , icon".
// 3: Now Simply use : -
{
categories.map(({ Name, Icon }) => (
<button key={Name}>
<span>{Name}</span>
<span> {<Icon/>} </span>
</button>
));
}
//use icon in this cleaver way

Resources