diff --git a/examples/storybook/.storybook/main.js b/examples/storybook/.storybook/main.js new file mode 100644 index 00000000000000..62fd47fa002bb6 --- /dev/null +++ b/examples/storybook/.storybook/main.js @@ -0,0 +1,12 @@ +module.exports = { + stories: ['../stories/**/*.stories.(ts|tsx|js|jsx)'], + addons: ['@storybook/addon-actions', '@storybook/addon-links', '@storybook/addon-docs'], + typescript: { + check: false, + checkOptions: {}, + reactDocgen: 'react-docgen-typescript', + reactDocgenTypescriptOptions: { + propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true), + }, + }, +}; diff --git a/examples/storybook/README.md b/examples/storybook/README.md new file mode 100644 index 00000000000000..2624620d1a94fa --- /dev/null +++ b/examples/storybook/README.md @@ -0,0 +1,10 @@ +# Storybook example + +The idea of this example is to provide you with a ready to go storybook containing all the demos we have in the documentation, so that you can see how theme changes affect the overall component look and feel. + +# How to use + +``` +yarn +yarn storybook +``` diff --git a/examples/storybook/package.json b/examples/storybook/package.json new file mode 100644 index 00000000000000..f195b87dd2db4f --- /dev/null +++ b/examples/storybook/package.json @@ -0,0 +1,27 @@ +{ + "name": "storybook", + "version": "1.0.0", + "main": "index.js", + "author": "sakulstra", + "license": "MIT", + "dependencies": { + "@material-ui/core": "^4.9.14", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "^4.0.0-alpha.53", + "@storybook/addon-docs": "^5.3.18", + "material-table": "^1.57.2", + "notistack": "^0.9.16" + }, + "devDependencies": { + "@babel/core": "^7.9.6", + "@storybook/addon-actions": "^6.0.0-beta.12", + "@storybook/addon-links": "^6.0.0-beta.12", + "@storybook/addons": "^6.0.0-beta.12", + "@storybook/react": "^6.0.0-beta.12", + "babel-loader": "^8.1.0" + }, + "scripts": { + "storybook": "start-storybook -p 6006", + "build-storybook": "build-storybook" + } +} diff --git a/examples/storybook/scripts/csf-transform.js b/examples/storybook/scripts/csf-transform.js new file mode 100644 index 00000000000000..e8b0f845c37068 --- /dev/null +++ b/examples/storybook/scripts/csf-transform.js @@ -0,0 +1,29 @@ +// play around with ast https://astexplorer.net +// https://github.com/benjamn/ast-types/ + +module.exports = (fileInfo, api) => { + const j = api.jscodeshift; + + const root = j(fileInfo.source); + const partials = fileInfo.path.replace('.tsx', '').split('/'); + const component = partials[partials.length - 2]; + const test = partials[partials.length - 1]; + + // make export a names export + root.find(j.ExportDefaultDeclaration).replaceWith((p) => { + return j.exportDeclaration(false, p.node.declaration); + }); + + // add default export + root + .get() + .node.program.body.push( + j.exportDefaultDeclaration( + j.objectExpression([ + j.property('init', j.identifier('title'), j.literal(`Material-ui|${component}|${test}`)), + ]), + ), + ); + + return root.toSource(); +}; diff --git a/examples/storybook/scripts/prepare.sh b/examples/storybook/scripts/prepare.sh new file mode 100644 index 00000000000000..7e65bf187f7767 --- /dev/null +++ b/examples/storybook/scripts/prepare.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) +cd "$parent_path" + +# copy files +rm -rf ../stories/material-ui +cp -R ../../../docs/src/pages/components ../stories/material-ui + +# remove unnecessary files +find ../stories/material-ui -name "*.md" -type f -delete +find ../stories/material-ui -name "*.js" -type f -delete + +# rename files +find ../stories/material-ui -name "*.tsx" -exec bash -c 'mv "$1" "${1%.tsx}".stories.tsx' - '{}' \; + +# alter files +npx jscodeshift -t ./csf-transform.js --extensions=ts,tsx --parser=tsx ../stories/material-ui/**/*.tsx + +# remove stuff that is broken atm :) +rm ../stories/material-ui/grid/InteractiveGrid.stories.tsx # references component from docs +rm ../stories/material-ui/grid-list -rf # because i removed tile-list.js +rm ../stories/material-ui/hidden/BreakpointDown.stories.tsx # current jscodeshift breaks hoc exports like export default withWidth()(BreakpointDown); +rm ../stories/material-ui/hidden/BreakpointOnly.stories.tsx # ^^ +rm ../stories/material-ui/hidden/BreakpointUp.stories.tsx # ^^ +rm ../stories/material-ui/hidden/GridIntegration.stories.tsx # ^^ +rm ../stories/material-ui/steppers/SwipeableTextMobileStepper.stories.tsx # export default Sth currently breaks as well diff --git a/examples/storybook/stories/0-Welcome.stories.tsx b/examples/storybook/stories/0-Welcome.stories.tsx new file mode 100644 index 00000000000000..c76f45f3b4be46 --- /dev/null +++ b/examples/storybook/stories/0-Welcome.stories.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { linkTo } from '@storybook/addon-links'; +import { Welcome } from '@storybook/react/demo'; + +export default { + title: 'Welcome', + component: Welcome, +}; + +export const ToStorybook = () => ; + +ToStorybook.story = { + name: 'to Storybook', +}; diff --git a/examples/storybook/stories/material-ui/alert/ActionAlerts.stories.tsx b/examples/storybook/stories/material-ui/alert/ActionAlerts.stories.tsx new file mode 100644 index 00000000000000..4473e8c3c4c2ff --- /dev/null +++ b/examples/storybook/stories/material-ui/alert/ActionAlerts.stories.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Alert from '@material-ui/lab/Alert'; +import Button from '@material-ui/core/Button'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function ActionAlerts() { + const classes = useStyles(); + + return ( +
+ {}}>This is a success alert — check it out! + + UNDO + + } + > + This is a success alert — check it out! + +
+ ); +} + +export default { + title: 'Material-ui|alert|ActionAlerts.stories', +}; diff --git a/examples/storybook/stories/material-ui/alert/ColorAlerts.stories.tsx b/examples/storybook/stories/material-ui/alert/ColorAlerts.stories.tsx new file mode 100644 index 00000000000000..3cbd4b2cfcb07a --- /dev/null +++ b/examples/storybook/stories/material-ui/alert/ColorAlerts.stories.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Alert from '@material-ui/lab/Alert'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function ColorAlerts() { + const classes = useStyles(); + + return ( +
+ + This is a success alert — check it out! + +
+ ); +} + +export default { + title: "Material-ui|alert|ColorAlerts.stories" +}; diff --git a/examples/storybook/stories/material-ui/alert/DescriptionAlerts.stories.tsx b/examples/storybook/stories/material-ui/alert/DescriptionAlerts.stories.tsx new file mode 100644 index 00000000000000..99cdaf18a0182b --- /dev/null +++ b/examples/storybook/stories/material-ui/alert/DescriptionAlerts.stories.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import { Alert, AlertTitle } from '@material-ui/lab'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function DescriptionAlerts() { + const classes = useStyles(); + + return ( +
+ + Error + This is an error alert — check it out! + + + Warning + This is a warning alert — check it out! + + + Info + This is an info alert — check it out! + + + Success + This is a success alert — check it out! + +
+ ); +} + +export default { + title: "Material-ui|alert|DescriptionAlerts.stories" +}; diff --git a/examples/storybook/stories/material-ui/alert/FilledAlerts.stories.tsx b/examples/storybook/stories/material-ui/alert/FilledAlerts.stories.tsx new file mode 100644 index 00000000000000..ca4eaba606d6a5 --- /dev/null +++ b/examples/storybook/stories/material-ui/alert/FilledAlerts.stories.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Alert from '@material-ui/lab/Alert'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function SimpleAlerts() { + const classes = useStyles(); + + return ( +
+ + This is an error alert — check it out! + + + This is a warning alert — check it out! + + + This is an info alert — check it out! + + + This is a success alert — check it out! + +
+ ); +} + +export default { + title: "Material-ui|alert|FilledAlerts.stories" +}; diff --git a/examples/storybook/stories/material-ui/alert/IconAlerts.stories.tsx b/examples/storybook/stories/material-ui/alert/IconAlerts.stories.tsx new file mode 100644 index 00000000000000..63a4e8bb1c389c --- /dev/null +++ b/examples/storybook/stories/material-ui/alert/IconAlerts.stories.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Alert from '@material-ui/lab/Alert'; +import CheckIcon from '@material-ui/icons/Check'; +import CheckCircleOutlineIcon from '@material-ui/icons/CheckCircleOutline'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function IconAlerts() { + const classes = useStyles(); + + return ( +
+ } severity="success"> + This is a success alert — check it out! + + }}> + This is a success alert — check it out! + + + This is a success alert — check it out! + +
+ ); +} + +export default { + title: "Material-ui|alert|IconAlerts.stories" +}; diff --git a/examples/storybook/stories/material-ui/alert/OutlinedAlerts.stories.tsx b/examples/storybook/stories/material-ui/alert/OutlinedAlerts.stories.tsx new file mode 100644 index 00000000000000..12721298261430 --- /dev/null +++ b/examples/storybook/stories/material-ui/alert/OutlinedAlerts.stories.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Alert from '@material-ui/lab/Alert'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function SimpleAlerts() { + const classes = useStyles(); + + return ( +
+ + This is an error alert — check it out! + + + This is a warning alert — check it out! + + + This is an info alert — check it out! + + + This is a success alert — check it out! + +
+ ); +} + +export default { + title: "Material-ui|alert|OutlinedAlerts.stories" +}; diff --git a/examples/storybook/stories/material-ui/alert/SimpleAlerts.stories.tsx b/examples/storybook/stories/material-ui/alert/SimpleAlerts.stories.tsx new file mode 100644 index 00000000000000..26de606746a48e --- /dev/null +++ b/examples/storybook/stories/material-ui/alert/SimpleAlerts.stories.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Alert from '@material-ui/lab/Alert'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function SimpleAlerts() { + const classes = useStyles(); + + return ( +
+ This is an error alert — check it out! + This is a warning alert — check it out! + This is an info alert — check it out! + This is a success alert — check it out! +
+ ); +} + +export default { + title: "Material-ui|alert|SimpleAlerts.stories" +}; diff --git a/examples/storybook/stories/material-ui/alert/TransitionAlerts.stories.tsx b/examples/storybook/stories/material-ui/alert/TransitionAlerts.stories.tsx new file mode 100644 index 00000000000000..629d9540f7521d --- /dev/null +++ b/examples/storybook/stories/material-ui/alert/TransitionAlerts.stories.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Alert from '@material-ui/lab/Alert'; +import IconButton from '@material-ui/core/IconButton'; +import Collapse from '@material-ui/core/Collapse'; +import Button from '@material-ui/core/Button'; +import CloseIcon from '@material-ui/icons/Close'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function TransitionAlerts() { + const classes = useStyles(); + const [open, setOpen] = React.useState(true); + + return ( +
+ + { + setOpen(false); + }} + > + + + } + > + Close me! + + + +
+ ); +} + +export default { + title: "Material-ui|alert|TransitionAlerts.stories" +}; diff --git a/examples/storybook/stories/material-ui/app-bar/BackToTop.stories.tsx b/examples/storybook/stories/material-ui/app-bar/BackToTop.stories.tsx new file mode 100644 index 00000000000000..2333dc1c1e58d6 --- /dev/null +++ b/examples/storybook/stories/material-ui/app-bar/BackToTop.stories.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import useScrollTrigger from '@material-ui/core/useScrollTrigger'; +import Box from '@material-ui/core/Box'; +import Container from '@material-ui/core/Container'; +import Fab from '@material-ui/core/Fab'; +import KeyboardArrowUpIcon from '@material-ui/icons/KeyboardArrowUp'; +import Zoom from '@material-ui/core/Zoom'; + +interface Props { + /** + * Injected by the documentation to work in an iframe. + * You won't need it on your project. + */ + window?: () => Window; + children: React.ReactElement; +} + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + position: 'fixed', + bottom: theme.spacing(2), + right: theme.spacing(2), + }, + }), +); + +function ScrollTop(props: Props) { + const { children, window } = props; + const classes = useStyles(); + // Note that you normally won't need to set the window ref as useScrollTrigger + // will default to window. + // This is only being set here because the demo is in an iframe. + const trigger = useScrollTrigger({ + target: window ? window() : undefined, + disableHysteresis: true, + threshold: 100, + }); + + const handleClick = (event: React.MouseEvent) => { + const anchor = ((event.target as HTMLDivElement).ownerDocument || document).querySelector( + '#back-to-top-anchor', + ); + + if (anchor) { + anchor.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + }; + + return ( + +
+ {children} +
+
+ ); +} + +export function BackToTop(props: Props) { + return ( + + + + + Scroll to see button + + + + + + {[...new Array(12)] + .map( + () => `Cras mattis consectetur purus sit amet fermentum. +Cras justo odio, dapibus ac facilisis in, egestas eget quam. +Morbi leo risus, porta ac consectetur ac, vestibulum at eros. +Praesent commodo cursus magna, vel scelerisque nisl consectetur et.`, + ) + .join('\n')} + + + + + + + + + ); +} + +export default { + title: "Material-ui|app-bar|BackToTop.stories" +}; diff --git a/examples/storybook/stories/material-ui/app-bar/BottomAppBar.stories.tsx b/examples/storybook/stories/material-ui/app-bar/BottomAppBar.stories.tsx new file mode 100644 index 00000000000000..153f7a9b43fc8e --- /dev/null +++ b/examples/storybook/stories/material-ui/app-bar/BottomAppBar.stories.tsx @@ -0,0 +1,150 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import Toolbar from '@material-ui/core/Toolbar'; +import Typography from '@material-ui/core/Typography'; +import IconButton from '@material-ui/core/IconButton'; +import Paper from '@material-ui/core/Paper'; +import Fab from '@material-ui/core/Fab'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemAvatar from '@material-ui/core/ListItemAvatar'; +import ListItemText from '@material-ui/core/ListItemText'; +import ListSubheader from '@material-ui/core/ListSubheader'; +import Avatar from '@material-ui/core/Avatar'; +import MenuIcon from '@material-ui/icons/Menu'; +import AddIcon from '@material-ui/icons/Add'; +import SearchIcon from '@material-ui/icons/Search'; +import MoreIcon from '@material-ui/icons/MoreVert'; + +const messages = [ + { + id: 1, + primary: 'Brunch this week?', + secondary: "I'll be in the neighbourhood this week. Let's grab a bite to eat", + person: '/static/images/avatar/5.jpg', + }, + { + id: 2, + primary: 'Birthday Gift', + secondary: `Do you have a suggestion for a good present for John on his work + anniversary. I am really confused & would love your thoughts on it.`, + person: '/static/images/avatar/1.jpg', + }, + { + id: 3, + primary: 'Recipe to try', + secondary: 'I am try out this new BBQ recipe, I think this might be amazing', + person: '/static/images/avatar/2.jpg', + }, + { + id: 4, + primary: 'Yes!', + secondary: 'I have the tickets to the ReactConf for this year.', + person: '/static/images/avatar/3.jpg', + }, + { + id: 5, + primary: "Doctor's Appointment", + secondary: 'My appointment for the doctor was rescheduled for next Saturday.', + person: '/static/images/avatar/4.jpg', + }, + { + id: 6, + primary: 'Discussion', + secondary: `Menus that are generated by the bottom app bar (such as a bottom + navigation drawer or overflow menu) open as bottom sheets at a higher elevation + than the bar.`, + person: '/static/images/avatar/5.jpg', + }, + { + id: 7, + primary: 'Summer BBQ', + secondary: `Who wants to have a cookout this weekend? I just got some furniture + for my backyard and would love to fire up the grill.`, + person: '/static/images/avatar/1.jpg', + }, +]; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + text: { + padding: theme.spacing(2, 2, 0), + }, + paper: { + paddingBottom: 50, + }, + list: { + marginBottom: theme.spacing(2), + }, + subheader: { + backgroundColor: theme.palette.background.paper, + }, + appBar: { + top: 'auto', + bottom: 0, + }, + grow: { + flexGrow: 1, + }, + fabButton: { + position: 'absolute', + zIndex: 1, + top: -30, + left: 0, + right: 0, + margin: '0 auto', + }, + }), +); + +export function BottomAppBar() { + const classes = useStyles(); + + return ( + + + + + Inbox + + + {messages.map(({ id, primary, secondary, person }) => ( + + {id === 1 && Today} + {id === 3 && Yesterday} + + + + + + + + ))} + + + + + + + + + + +
+ + + + + + + + + + ); +} + +export default { + title: "Material-ui|app-bar|BottomAppBar.stories" +}; diff --git a/examples/storybook/stories/material-ui/app-bar/ButtonAppBar.stories.tsx b/examples/storybook/stories/material-ui/app-bar/ButtonAppBar.stories.tsx new file mode 100644 index 00000000000000..28242fc97ea7ea --- /dev/null +++ b/examples/storybook/stories/material-ui/app-bar/ButtonAppBar.stories.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; +import IconButton from '@material-ui/core/IconButton'; +import MenuIcon from '@material-ui/icons/Menu'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + }, + menuButton: { + marginRight: theme.spacing(2), + }, + title: { + flexGrow: 1, + }, + }), +); + +export function ButtonAppBar() { + const classes = useStyles(); + + return ( +
+ + + + + + + News + + + + +
+ ); +} + +export default { + title: "Material-ui|app-bar|ButtonAppBar.stories" +}; diff --git a/examples/storybook/stories/material-ui/app-bar/DenseAppBar.stories.tsx b/examples/storybook/stories/material-ui/app-bar/DenseAppBar.stories.tsx new file mode 100644 index 00000000000000..ded9806f61b06a --- /dev/null +++ b/examples/storybook/stories/material-ui/app-bar/DenseAppBar.stories.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import Typography from '@material-ui/core/Typography'; +import IconButton from '@material-ui/core/IconButton'; +import MenuIcon from '@material-ui/icons/Menu'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + }, + menuButton: { + marginRight: theme.spacing(2), + }, + }), +); + +export function DenseAppBar() { + const classes = useStyles(); + + return ( +
+ + + + + + + Photos + + + +
+ ); +} + +export default { + title: "Material-ui|app-bar|DenseAppBar.stories" +}; diff --git a/examples/storybook/stories/material-ui/app-bar/ElevateAppBar.stories.tsx b/examples/storybook/stories/material-ui/app-bar/ElevateAppBar.stories.tsx new file mode 100644 index 00000000000000..e5d00f597ab846 --- /dev/null +++ b/examples/storybook/stories/material-ui/app-bar/ElevateAppBar.stories.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import Typography from '@material-ui/core/Typography'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import useScrollTrigger from '@material-ui/core/useScrollTrigger'; +import Box from '@material-ui/core/Box'; +import Container from '@material-ui/core/Container'; + +interface Props { + /** + * Injected by the documentation to work in an iframe. + * You won't need it on your project. + */ + window?: () => Window; + children: React.ReactElement; +} + +function ElevationScroll(props: Props) { + const { children, window } = props; + // Note that you normally won't need to set the window ref as useScrollTrigger + // will default to window. + // This is only being set here because the demo is in an iframe. + const trigger = useScrollTrigger({ + disableHysteresis: true, + threshold: 0, + target: window ? window() : undefined, + }); + + return React.cloneElement(children, { + elevation: trigger ? 4 : 0, + }); +} + +export function ElevateAppBar(props: Props) { + return ( + + + + + + Scroll to Elevate App Bar + + + + + + + {[...new Array(12)] + .map( + () => `Cras mattis consectetur purus sit amet fermentum. +Cras justo odio, dapibus ac facilisis in, egestas eget quam. +Morbi leo risus, porta ac consectetur ac, vestibulum at eros. +Praesent commodo cursus magna, vel scelerisque nisl consectetur et.`, + ) + .join('\n')} + + + + ); +} + +export default { + title: "Material-ui|app-bar|ElevateAppBar.stories" +}; diff --git a/examples/storybook/stories/material-ui/app-bar/HideAppBar.stories.tsx b/examples/storybook/stories/material-ui/app-bar/HideAppBar.stories.tsx new file mode 100644 index 00000000000000..20792ceed4a80c --- /dev/null +++ b/examples/storybook/stories/material-ui/app-bar/HideAppBar.stories.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import Typography from '@material-ui/core/Typography'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import useScrollTrigger from '@material-ui/core/useScrollTrigger'; +import Box from '@material-ui/core/Box'; +import Container from '@material-ui/core/Container'; +import Slide from '@material-ui/core/Slide'; + +interface Props { + /** + * Injected by the documentation to work in an iframe. + * You won't need it on your project. + */ + window?: () => Window; + children: React.ReactElement; +} + +function HideOnScroll(props: Props) { + const { children, window } = props; + // Note that you normally won't need to set the window ref as useScrollTrigger + // will default to window. + // This is only being set here because the demo is in an iframe. + const trigger = useScrollTrigger({ target: window ? window() : undefined }); + + return ( + + {children} + + ); +} + +export function HideAppBar(props: Props) { + return ( + + + + + + Scroll to Hide App Bar + + + + + + + {[...new Array(12)] + .map( + () => `Cras mattis consectetur purus sit amet fermentum. +Cras justo odio, dapibus ac facilisis in, egestas eget quam. +Morbi leo risus, porta ac consectetur ac, vestibulum at eros. +Praesent commodo cursus magna, vel scelerisque nisl consectetur et.`, + ) + .join('\n')} + + + + ); +} + +export default { + title: "Material-ui|app-bar|HideAppBar.stories" +}; diff --git a/examples/storybook/stories/material-ui/app-bar/MenuAppBar.stories.tsx b/examples/storybook/stories/material-ui/app-bar/MenuAppBar.stories.tsx new file mode 100644 index 00000000000000..6da95782ddf36f --- /dev/null +++ b/examples/storybook/stories/material-ui/app-bar/MenuAppBar.stories.tsx @@ -0,0 +1,102 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import Typography from '@material-ui/core/Typography'; +import IconButton from '@material-ui/core/IconButton'; +import MenuIcon from '@material-ui/icons/Menu'; +import AccountCircle from '@material-ui/icons/AccountCircle'; +import Switch from '@material-ui/core/Switch'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormGroup from '@material-ui/core/FormGroup'; +import MenuItem from '@material-ui/core/MenuItem'; +import Menu from '@material-ui/core/Menu'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + }, + menuButton: { + marginRight: theme.spacing(2), + }, + title: { + flexGrow: 1, + }, + }), +); + +export function MenuAppBar() { + const classes = useStyles(); + const [auth, setAuth] = React.useState(true); + const [anchorEl, setAnchorEl] = React.useState(null); + const open = Boolean(anchorEl); + + const handleChange = (event: React.ChangeEvent) => { + setAuth(event.target.checked); + }; + + const handleMenu = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + return ( +
+ + } + label={auth ? 'Logout' : 'Login'} + /> + + + + + + + + Photos + + {auth && ( +
+ + + + + Profile + My account + +
+ )} +
+
+
+ ); +} + +export default { + title: "Material-ui|app-bar|MenuAppBar.stories" +}; diff --git a/examples/storybook/stories/material-ui/app-bar/PrimarySearchAppBar.stories.tsx b/examples/storybook/stories/material-ui/app-bar/PrimarySearchAppBar.stories.tsx new file mode 100644 index 00000000000000..e3cf6968e6ac14 --- /dev/null +++ b/examples/storybook/stories/material-ui/app-bar/PrimarySearchAppBar.stories.tsx @@ -0,0 +1,238 @@ +import React from 'react'; +import { fade, makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import IconButton from '@material-ui/core/IconButton'; +import Typography from '@material-ui/core/Typography'; +import InputBase from '@material-ui/core/InputBase'; +import Badge from '@material-ui/core/Badge'; +import MenuItem from '@material-ui/core/MenuItem'; +import Menu from '@material-ui/core/Menu'; +import MenuIcon from '@material-ui/icons/Menu'; +import SearchIcon from '@material-ui/icons/Search'; +import AccountCircle from '@material-ui/icons/AccountCircle'; +import MailIcon from '@material-ui/icons/Mail'; +import NotificationsIcon from '@material-ui/icons/Notifications'; +import MoreIcon from '@material-ui/icons/MoreVert'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + grow: { + flexGrow: 1, + }, + menuButton: { + marginRight: theme.spacing(2), + }, + title: { + display: 'none', + [theme.breakpoints.up('sm')]: { + display: 'block', + }, + }, + search: { + position: 'relative', + borderRadius: theme.shape.borderRadius, + backgroundColor: fade(theme.palette.common.white, 0.15), + '&:hover': { + backgroundColor: fade(theme.palette.common.white, 0.25), + }, + marginRight: theme.spacing(2), + marginLeft: 0, + width: '100%', + [theme.breakpoints.up('sm')]: { + marginLeft: theme.spacing(3), + width: 'auto', + }, + }, + searchIcon: { + padding: theme.spacing(0, 2), + height: '100%', + position: 'absolute', + pointerEvents: 'none', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + inputRoot: { + color: 'inherit', + }, + inputInput: { + padding: theme.spacing(1, 1, 1, 0), + // vertical padding + font size from searchIcon + paddingLeft: `calc(1em + ${theme.spacing(4)}px)`, + transition: theme.transitions.create('width'), + width: '100%', + [theme.breakpoints.up('md')]: { + width: '20ch', + }, + }, + sectionDesktop: { + display: 'none', + [theme.breakpoints.up('md')]: { + display: 'flex', + }, + }, + sectionMobile: { + display: 'flex', + [theme.breakpoints.up('md')]: { + display: 'none', + }, + }, + }), +); + +export function PrimarySearchAppBar() { + const classes = useStyles(); + const [anchorEl, setAnchorEl] = React.useState(null); + const [mobileMoreAnchorEl, setMobileMoreAnchorEl] = React.useState(null); + + const isMenuOpen = Boolean(anchorEl); + const isMobileMenuOpen = Boolean(mobileMoreAnchorEl); + + const handleProfileMenuOpen = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleMobileMenuClose = () => { + setMobileMoreAnchorEl(null); + }; + + const handleMenuClose = () => { + setAnchorEl(null); + handleMobileMenuClose(); + }; + + const handleMobileMenuOpen = (event: React.MouseEvent) => { + setMobileMoreAnchorEl(event.currentTarget); + }; + + const menuId = 'primary-search-account-menu'; + const renderMenu = ( + + Profile + My account + + ); + + const mobileMenuId = 'primary-search-account-menu-mobile'; + const renderMobileMenu = ( + + + + + + + +

Messages

+
+ + + + + + +

Notifications

+
+ + + + +

Profile

+
+
+ ); + + return ( +
+ + + + + + + Material-UI + +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + {renderMobileMenu} + {renderMenu} +
+ ); +} + +export default { + title: "Material-ui|app-bar|PrimarySearchAppBar.stories" +}; diff --git a/examples/storybook/stories/material-ui/app-bar/ProminentAppBar.stories.tsx b/examples/storybook/stories/material-ui/app-bar/ProminentAppBar.stories.tsx new file mode 100644 index 00000000000000..7e6cf900ab8daa --- /dev/null +++ b/examples/storybook/stories/material-ui/app-bar/ProminentAppBar.stories.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import IconButton from '@material-ui/core/IconButton'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles } from '@material-ui/core/styles'; +import MenuIcon from '@material-ui/icons/Menu'; +import SearchIcon from '@material-ui/icons/Search'; +import MoreIcon from '@material-ui/icons/MoreVert'; + +const useStyles = makeStyles((theme) => ({ + root: { + flexGrow: 1, + }, + menuButton: { + marginRight: theme.spacing(2), + }, + toolbar: { + minHeight: 128, + alignItems: 'flex-start', + paddingTop: theme.spacing(1), + paddingBottom: theme.spacing(2), + }, + title: { + flexGrow: 1, + alignSelf: 'flex-end', + }, +})); + +export function ProminentAppBar() { + const classes = useStyles(); + + return ( +
+ + + + + + + Material-UI + + + + + + + + + +
+ ); +} + +export default { + title: "Material-ui|app-bar|ProminentAppBar.stories" +}; diff --git a/examples/storybook/stories/material-ui/app-bar/SearchAppBar.stories.tsx b/examples/storybook/stories/material-ui/app-bar/SearchAppBar.stories.tsx new file mode 100644 index 00000000000000..6c5b22a52451d7 --- /dev/null +++ b/examples/storybook/stories/material-ui/app-bar/SearchAppBar.stories.tsx @@ -0,0 +1,107 @@ +import React from 'react'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import IconButton from '@material-ui/core/IconButton'; +import Typography from '@material-ui/core/Typography'; +import InputBase from '@material-ui/core/InputBase'; +import { createStyles, fade, Theme, makeStyles } from '@material-ui/core/styles'; +import MenuIcon from '@material-ui/icons/Menu'; +import SearchIcon from '@material-ui/icons/Search'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + }, + menuButton: { + marginRight: theme.spacing(2), + }, + title: { + flexGrow: 1, + display: 'none', + [theme.breakpoints.up('sm')]: { + display: 'block', + }, + }, + search: { + position: 'relative', + borderRadius: theme.shape.borderRadius, + backgroundColor: fade(theme.palette.common.white, 0.15), + '&:hover': { + backgroundColor: fade(theme.palette.common.white, 0.25), + }, + marginLeft: 0, + width: '100%', + [theme.breakpoints.up('sm')]: { + marginLeft: theme.spacing(1), + width: 'auto', + }, + }, + searchIcon: { + padding: theme.spacing(0, 2), + height: '100%', + position: 'absolute', + pointerEvents: 'none', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + inputRoot: { + color: 'inherit', + }, + inputInput: { + padding: theme.spacing(1, 1, 1, 0), + // vertical padding + font size from searchIcon + paddingLeft: `calc(1em + ${theme.spacing(4)}px)`, + transition: theme.transitions.create('width'), + width: '100%', + [theme.breakpoints.up('sm')]: { + width: '12ch', + '&:focus': { + width: '20ch', + }, + }, + }, + }), +); + +export function SearchAppBar() { + const classes = useStyles(); + + return ( +
+ + + + + + + Material-UI + +
+
+ +
+ +
+
+
+
+ ); +} + +export default { + title: "Material-ui|app-bar|SearchAppBar.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/Asynchronous.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/Asynchronous.stories.tsx new file mode 100644 index 00000000000000..1c2d8dffb6e5f8 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/Asynchronous.stories.tsx @@ -0,0 +1,88 @@ +// *https://www.registers.service.gov.uk/registers/country/use-the-api* +import fetch from 'cross-fetch'; +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import CircularProgress from '@material-ui/core/CircularProgress'; + +interface CountryType { + name: string; +} + +function sleep(delay = 0) { + return new Promise((resolve) => { + setTimeout(resolve, delay); + }); +} + +export function Asynchronous() { + const [open, setOpen] = React.useState(false); + const [options, setOptions] = React.useState([]); + const loading = open && options.length === 0; + + React.useEffect(() => { + let active = true; + + if (!loading) { + return undefined; + } + + (async () => { + const response = await fetch('https://country.register.gov.uk/records.json?page-size=5000'); + await sleep(1e3); // For demo purposes. + const countries = await response.json(); + + if (active) { + setOptions(Object.keys(countries).map((key) => countries[key].item[0]) as CountryType[]); + } + })(); + + return () => { + active = false; + }; + }, [loading]); + + React.useEffect(() => { + if (!open) { + setOptions([]); + } + }, [open]); + + return ( + { + setOpen(true); + }} + onClose={() => { + setOpen(false); + }} + getOptionSelected={(option, value) => option.name === value.name} + getOptionLabel={(option) => option.name} + options={options} + loading={loading} + renderInput={(params) => ( + + {loading ? : null} + {params.InputProps.endAdornment} + + ), + }} + /> + )} + /> + ); +} + +export default { + title: "Material-ui|autocomplete|Asynchronous.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/CheckboxesTags.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/CheckboxesTags.stories.tsx new file mode 100644 index 00000000000000..dcb7820b35ac54 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/CheckboxesTags.stories.tsx @@ -0,0 +1,78 @@ +/* eslint-disable no-use-before-define */ + +import React from 'react'; +import Checkbox from '@material-ui/core/Checkbox'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; +import CheckBoxIcon from '@material-ui/icons/CheckBox'; + +const icon = ; +const checkedIcon = ; + +export function CheckboxesTags() { + return ( + option.title} + renderOption={(option, { selected }) => ( + + + {option.title} + + )} + style={{ width: 500 }} + renderInput={(params) => ( + + )} + /> + ); +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { title: 'The Shawshank Redemption', year: 1994 }, + { title: 'The Godfather', year: 1972 }, + { title: 'The Godfather: Part II', year: 1974 }, + { title: 'The Dark Knight', year: 2008 }, + { title: '12 Angry Men', year: 1957 }, + { title: "Schindler's List", year: 1993 }, + { title: 'Pulp Fiction', year: 1994 }, + { title: 'The Lord of the Rings: The Return of the King', year: 2003 }, + { title: 'The Good, the Bad and the Ugly', year: 1966 }, + { title: 'Fight Club', year: 1999 }, + { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 }, + { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 }, + { title: 'Forrest Gump', year: 1994 }, + { title: 'Inception', year: 2010 }, + { title: 'The Lord of the Rings: The Two Towers', year: 2002 }, + { title: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { title: 'Goodfellas', year: 1990 }, + { title: 'The Matrix', year: 1999 }, + { title: 'Seven Samurai', year: 1954 }, + { title: 'Star Wars: Episode IV - A New Hope', year: 1977 }, + { title: 'City of God', year: 2002 }, + { title: 'Se7en', year: 1995 }, + { title: 'The Silence of the Lambs', year: 1991 }, + { title: "It's a Wonderful Life", year: 1946 }, + { title: 'Life Is Beautiful', year: 1997 }, + { title: 'The Usual Suspects', year: 1995 }, + { title: 'Léon: The Professional', year: 1994 }, + { title: 'Spirited Away', year: 2001 }, + { title: 'Saving Private Ryan', year: 1998 }, + { title: 'Once Upon a Time in the West', year: 1968 }, + { title: 'American History X', year: 1998 }, + { title: 'Interstellar', year: 2014 }, +]; + +export default { + title: "Material-ui|autocomplete|CheckboxesTags.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/ComboBox.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/ComboBox.stories.tsx new file mode 100644 index 00000000000000..fc82b8e63feea0 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/ComboBox.stories.tsx @@ -0,0 +1,124 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete from '@material-ui/lab/Autocomplete'; + +export function ComboBox() { + return ( + option.title} + style={{ width: 300 }} + renderInput={(params) => } + /> + ); +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { title: 'The Shawshank Redemption', year: 1994 }, + { title: 'The Godfather', year: 1972 }, + { title: 'The Godfather: Part II', year: 1974 }, + { title: 'The Dark Knight', year: 2008 }, + { title: '12 Angry Men', year: 1957 }, + { title: "Schindler's List", year: 1993 }, + { title: 'Pulp Fiction', year: 1994 }, + { title: 'The Lord of the Rings: The Return of the King', year: 2003 }, + { title: 'The Good, the Bad and the Ugly', year: 1966 }, + { title: 'Fight Club', year: 1999 }, + { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 }, + { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 }, + { title: 'Forrest Gump', year: 1994 }, + { title: 'Inception', year: 2010 }, + { title: 'The Lord of the Rings: The Two Towers', year: 2002 }, + { title: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { title: 'Goodfellas', year: 1990 }, + { title: 'The Matrix', year: 1999 }, + { title: 'Seven Samurai', year: 1954 }, + { title: 'Star Wars: Episode IV - A New Hope', year: 1977 }, + { title: 'City of God', year: 2002 }, + { title: 'Se7en', year: 1995 }, + { title: 'The Silence of the Lambs', year: 1991 }, + { title: "It's a Wonderful Life", year: 1946 }, + { title: 'Life Is Beautiful', year: 1997 }, + { title: 'The Usual Suspects', year: 1995 }, + { title: 'Léon: The Professional', year: 1994 }, + { title: 'Spirited Away', year: 2001 }, + { title: 'Saving Private Ryan', year: 1998 }, + { title: 'Once Upon a Time in the West', year: 1968 }, + { title: 'American History X', year: 1998 }, + { title: 'Interstellar', year: 2014 }, + { title: 'Casablanca', year: 1942 }, + { title: 'City Lights', year: 1931 }, + { title: 'Psycho', year: 1960 }, + { title: 'The Green Mile', year: 1999 }, + { title: 'The Intouchables', year: 2011 }, + { title: 'Modern Times', year: 1936 }, + { title: 'Raiders of the Lost Ark', year: 1981 }, + { title: 'Rear Window', year: 1954 }, + { title: 'The Pianist', year: 2002 }, + { title: 'The Departed', year: 2006 }, + { title: 'Terminator 2: Judgment Day', year: 1991 }, + { title: 'Back to the Future', year: 1985 }, + { title: 'Whiplash', year: 2014 }, + { title: 'Gladiator', year: 2000 }, + { title: 'Memento', year: 2000 }, + { title: 'The Prestige', year: 2006 }, + { title: 'The Lion King', year: 1994 }, + { title: 'Apocalypse Now', year: 1979 }, + { title: 'Alien', year: 1979 }, + { title: 'Sunset Boulevard', year: 1950 }, + { title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', year: 1964 }, + { title: 'The Great Dictator', year: 1940 }, + { title: 'Cinema Paradiso', year: 1988 }, + { title: 'The Lives of Others', year: 2006 }, + { title: 'Grave of the Fireflies', year: 1988 }, + { title: 'Paths of Glory', year: 1957 }, + { title: 'Django Unchained', year: 2012 }, + { title: 'The Shining', year: 1980 }, + { title: 'WALL·E', year: 2008 }, + { title: 'American Beauty', year: 1999 }, + { title: 'The Dark Knight Rises', year: 2012 }, + { title: 'Princess Mononoke', year: 1997 }, + { title: 'Aliens', year: 1986 }, + { title: 'Oldboy', year: 2003 }, + { title: 'Once Upon a Time in America', year: 1984 }, + { title: 'Witness for the Prosecution', year: 1957 }, + { title: 'Das Boot', year: 1981 }, + { title: 'Citizen Kane', year: 1941 }, + { title: 'North by Northwest', year: 1959 }, + { title: 'Vertigo', year: 1958 }, + { title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 }, + { title: 'Reservoir Dogs', year: 1992 }, + { title: 'Braveheart', year: 1995 }, + { title: 'M', year: 1931 }, + { title: 'Requiem for a Dream', year: 2000 }, + { title: 'Amélie', year: 2001 }, + { title: 'A Clockwork Orange', year: 1971 }, + { title: 'Like Stars on Earth', year: 2007 }, + { title: 'Taxi Driver', year: 1976 }, + { title: 'Lawrence of Arabia', year: 1962 }, + { title: 'Double Indemnity', year: 1944 }, + { title: 'Eternal Sunshine of the Spotless Mind', year: 2004 }, + { title: 'Amadeus', year: 1984 }, + { title: 'To Kill a Mockingbird', year: 1962 }, + { title: 'Toy Story 3', year: 2010 }, + { title: 'Logan', year: 2017 }, + { title: 'Full Metal Jacket', year: 1987 }, + { title: 'Dangal', year: 2016 }, + { title: 'The Sting', year: 1973 }, + { title: '2001: A Space Odyssey', year: 1968 }, + { title: "Singin' in the Rain", year: 1952 }, + { title: 'Toy Story', year: 1995 }, + { title: 'Bicycle Thieves', year: 1948 }, + { title: 'The Kid', year: 1921 }, + { title: 'Inglourious Basterds', year: 2009 }, + { title: 'Snatch', year: 2000 }, + { title: '3 Idiots', year: 2009 }, + { title: 'Monty Python and the Holy Grail', year: 1975 }, +]; + +export default { + title: "Material-ui|autocomplete|ComboBox.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/ControllableStates.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/ControllableStates.stories.tsx new file mode 100644 index 00000000000000..be17b05a75ab24 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/ControllableStates.stories.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete from '@material-ui/lab/Autocomplete'; + +const options = ['Option 1', 'Option 2']; + +export function ControllableStates() { + const [value, setValue] = React.useState(options[0]); + const [inputValue, setInputValue] = React.useState(''); + + return ( +
+
{`value: ${value !== null ? `'${value}'` : 'null'}`}
+
{`inputValue: '${inputValue}'`}
+
+ { + setValue(newValue); + }} + inputValue={inputValue} + onInputChange={(event, newInputValue) => { + setInputValue(newInputValue); + }} + id="controllable-states-demo" + options={options} + style={{ width: 300 }} + renderInput={(params) => } + /> +
+ ); +} + +export default { + title: "Material-ui|autocomplete|ControllableStates.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/CountrySelect.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/CountrySelect.stories.tsx new file mode 100644 index 00000000000000..5ba47b93fbfd80 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/CountrySelect.stories.tsx @@ -0,0 +1,321 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import { makeStyles } from '@material-ui/core/styles'; + +// ISO 3166-1 alpha-2 +// ⚠️ No support for IE 11 +function countryToFlag(isoCode: string) { + return typeof String.fromCodePoint !== 'undefined' + ? isoCode + .toUpperCase() + .replace(/./g, (char) => String.fromCodePoint(char.charCodeAt(0) + 127397)) + : isoCode; +} + +const useStyles = makeStyles({ + option: { + fontSize: 15, + '& > span': { + marginRight: 10, + fontSize: 18, + }, + }, +}); + +export function CountrySelect() { + const classes = useStyles(); + + return ( + option.label} + renderOption={(option) => ( + + {countryToFlag(option.code)} + {option.label} ({option.code}) +{option.phone} + + )} + renderInput={(params) => ( + + )} + /> + ); +} + +interface CountryType { + code: string; + label: string; + phone: string; +} + +// From https://bitbucket.org/atlassian/atlaskit-mk-2/raw/4ad0e56649c3e6c973e226b7efaeb28cb240ccb0/packages/core/select/src/data/countries.js +const countries = [ + { code: 'AD', label: 'Andorra', phone: '376' }, + { code: 'AE', label: 'United Arab Emirates', phone: '971' }, + { code: 'AF', label: 'Afghanistan', phone: '93' }, + { code: 'AG', label: 'Antigua and Barbuda', phone: '1-268' }, + { code: 'AI', label: 'Anguilla', phone: '1-264' }, + { code: 'AL', label: 'Albania', phone: '355' }, + { code: 'AM', label: 'Armenia', phone: '374' }, + { code: 'AO', label: 'Angola', phone: '244' }, + { code: 'AQ', label: 'Antarctica', phone: '672' }, + { code: 'AR', label: 'Argentina', phone: '54' }, + { code: 'AS', label: 'American Samoa', phone: '1-684' }, + { code: 'AT', label: 'Austria', phone: '43' }, + { code: 'AU', label: 'Australia', phone: '61', suggested: true }, + { code: 'AW', label: 'Aruba', phone: '297' }, + { code: 'AX', label: 'Alland Islands', phone: '358' }, + { code: 'AZ', label: 'Azerbaijan', phone: '994' }, + { code: 'BA', label: 'Bosnia and Herzegovina', phone: '387' }, + { code: 'BB', label: 'Barbados', phone: '1-246' }, + { code: 'BD', label: 'Bangladesh', phone: '880' }, + { code: 'BE', label: 'Belgium', phone: '32' }, + { code: 'BF', label: 'Burkina Faso', phone: '226' }, + { code: 'BG', label: 'Bulgaria', phone: '359' }, + { code: 'BH', label: 'Bahrain', phone: '973' }, + { code: 'BI', label: 'Burundi', phone: '257' }, + { code: 'BJ', label: 'Benin', phone: '229' }, + { code: 'BL', label: 'Saint Barthelemy', phone: '590' }, + { code: 'BM', label: 'Bermuda', phone: '1-441' }, + { code: 'BN', label: 'Brunei Darussalam', phone: '673' }, + { code: 'BO', label: 'Bolivia', phone: '591' }, + { code: 'BR', label: 'Brazil', phone: '55' }, + { code: 'BS', label: 'Bahamas', phone: '1-242' }, + { code: 'BT', label: 'Bhutan', phone: '975' }, + { code: 'BV', label: 'Bouvet Island', phone: '47' }, + { code: 'BW', label: 'Botswana', phone: '267' }, + { code: 'BY', label: 'Belarus', phone: '375' }, + { code: 'BZ', label: 'Belize', phone: '501' }, + { code: 'CA', label: 'Canada', phone: '1', suggested: true }, + { code: 'CC', label: 'Cocos (Keeling) Islands', phone: '61' }, + { code: 'CD', label: 'Congo, Democratic Republic of the', phone: '243' }, + { code: 'CF', label: 'Central African Republic', phone: '236' }, + { code: 'CG', label: 'Congo, Republic of the', phone: '242' }, + { code: 'CH', label: 'Switzerland', phone: '41' }, + { code: 'CI', label: "Cote d'Ivoire", phone: '225' }, + { code: 'CK', label: 'Cook Islands', phone: '682' }, + { code: 'CL', label: 'Chile', phone: '56' }, + { code: 'CM', label: 'Cameroon', phone: '237' }, + { code: 'CN', label: 'China', phone: '86' }, + { code: 'CO', label: 'Colombia', phone: '57' }, + { code: 'CR', label: 'Costa Rica', phone: '506' }, + { code: 'CU', label: 'Cuba', phone: '53' }, + { code: 'CV', label: 'Cape Verde', phone: '238' }, + { code: 'CW', label: 'Curacao', phone: '599' }, + { code: 'CX', label: 'Christmas Island', phone: '61' }, + { code: 'CY', label: 'Cyprus', phone: '357' }, + { code: 'CZ', label: 'Czech Republic', phone: '420' }, + { code: 'DE', label: 'Germany', phone: '49', suggested: true }, + { code: 'DJ', label: 'Djibouti', phone: '253' }, + { code: 'DK', label: 'Denmark', phone: '45' }, + { code: 'DM', label: 'Dominica', phone: '1-767' }, + { code: 'DO', label: 'Dominican Republic', phone: '1-809' }, + { code: 'DZ', label: 'Algeria', phone: '213' }, + { code: 'EC', label: 'Ecuador', phone: '593' }, + { code: 'EE', label: 'Estonia', phone: '372' }, + { code: 'EG', label: 'Egypt', phone: '20' }, + { code: 'EH', label: 'Western Sahara', phone: '212' }, + { code: 'ER', label: 'Eritrea', phone: '291' }, + { code: 'ES', label: 'Spain', phone: '34' }, + { code: 'ET', label: 'Ethiopia', phone: '251' }, + { code: 'FI', label: 'Finland', phone: '358' }, + { code: 'FJ', label: 'Fiji', phone: '679' }, + { code: 'FK', label: 'Falkland Islands (Malvinas)', phone: '500' }, + { code: 'FM', label: 'Micronesia, Federated States of', phone: '691' }, + { code: 'FO', label: 'Faroe Islands', phone: '298' }, + { code: 'FR', label: 'France', phone: '33', suggested: true }, + { code: 'GA', label: 'Gabon', phone: '241' }, + { code: 'GB', label: 'United Kingdom', phone: '44' }, + { code: 'GD', label: 'Grenada', phone: '1-473' }, + { code: 'GE', label: 'Georgia', phone: '995' }, + { code: 'GF', label: 'French Guiana', phone: '594' }, + { code: 'GG', label: 'Guernsey', phone: '44' }, + { code: 'GH', label: 'Ghana', phone: '233' }, + { code: 'GI', label: 'Gibraltar', phone: '350' }, + { code: 'GL', label: 'Greenland', phone: '299' }, + { code: 'GM', label: 'Gambia', phone: '220' }, + { code: 'GN', label: 'Guinea', phone: '224' }, + { code: 'GP', label: 'Guadeloupe', phone: '590' }, + { code: 'GQ', label: 'Equatorial Guinea', phone: '240' }, + { code: 'GR', label: 'Greece', phone: '30' }, + { code: 'GS', label: 'South Georgia and the South Sandwich Islands', phone: '500' }, + { code: 'GT', label: 'Guatemala', phone: '502' }, + { code: 'GU', label: 'Guam', phone: '1-671' }, + { code: 'GW', label: 'Guinea-Bissau', phone: '245' }, + { code: 'GY', label: 'Guyana', phone: '592' }, + { code: 'HK', label: 'Hong Kong', phone: '852' }, + { code: 'HM', label: 'Heard Island and McDonald Islands', phone: '672' }, + { code: 'HN', label: 'Honduras', phone: '504' }, + { code: 'HR', label: 'Croatia', phone: '385' }, + { code: 'HT', label: 'Haiti', phone: '509' }, + { code: 'HU', label: 'Hungary', phone: '36' }, + { code: 'ID', label: 'Indonesia', phone: '62' }, + { code: 'IE', label: 'Ireland', phone: '353' }, + { code: 'IL', label: 'Israel', phone: '972' }, + { code: 'IM', label: 'Isle of Man', phone: '44' }, + { code: 'IN', label: 'India', phone: '91' }, + { code: 'IO', label: 'British Indian Ocean Territory', phone: '246' }, + { code: 'IQ', label: 'Iraq', phone: '964' }, + { code: 'IR', label: 'Iran, Islamic Republic of', phone: '98' }, + { code: 'IS', label: 'Iceland', phone: '354' }, + { code: 'IT', label: 'Italy', phone: '39' }, + { code: 'JE', label: 'Jersey', phone: '44' }, + { code: 'JM', label: 'Jamaica', phone: '1-876' }, + { code: 'JO', label: 'Jordan', phone: '962' }, + { code: 'JP', label: 'Japan', phone: '81', suggested: true }, + { code: 'KE', label: 'Kenya', phone: '254' }, + { code: 'KG', label: 'Kyrgyzstan', phone: '996' }, + { code: 'KH', label: 'Cambodia', phone: '855' }, + { code: 'KI', label: 'Kiribati', phone: '686' }, + { code: 'KM', label: 'Comoros', phone: '269' }, + { code: 'KN', label: 'Saint Kitts and Nevis', phone: '1-869' }, + { code: 'KP', label: "Korea, Democratic People's Republic of", phone: '850' }, + { code: 'KR', label: 'Korea, Republic of', phone: '82' }, + { code: 'KW', label: 'Kuwait', phone: '965' }, + { code: 'KY', label: 'Cayman Islands', phone: '1-345' }, + { code: 'KZ', label: 'Kazakhstan', phone: '7' }, + { code: 'LA', label: "Lao People's Democratic Republic", phone: '856' }, + { code: 'LB', label: 'Lebanon', phone: '961' }, + { code: 'LC', label: 'Saint Lucia', phone: '1-758' }, + { code: 'LI', label: 'Liechtenstein', phone: '423' }, + { code: 'LK', label: 'Sri Lanka', phone: '94' }, + { code: 'LR', label: 'Liberia', phone: '231' }, + { code: 'LS', label: 'Lesotho', phone: '266' }, + { code: 'LT', label: 'Lithuania', phone: '370' }, + { code: 'LU', label: 'Luxembourg', phone: '352' }, + { code: 'LV', label: 'Latvia', phone: '371' }, + { code: 'LY', label: 'Libya', phone: '218' }, + { code: 'MA', label: 'Morocco', phone: '212' }, + { code: 'MC', label: 'Monaco', phone: '377' }, + { code: 'MD', label: 'Moldova, Republic of', phone: '373' }, + { code: 'ME', label: 'Montenegro', phone: '382' }, + { code: 'MF', label: 'Saint Martin (French part)', phone: '590' }, + { code: 'MG', label: 'Madagascar', phone: '261' }, + { code: 'MH', label: 'Marshall Islands', phone: '692' }, + { code: 'MK', label: 'Macedonia, the Former Yugoslav Republic of', phone: '389' }, + { code: 'ML', label: 'Mali', phone: '223' }, + { code: 'MM', label: 'Myanmar', phone: '95' }, + { code: 'MN', label: 'Mongolia', phone: '976' }, + { code: 'MO', label: 'Macao', phone: '853' }, + { code: 'MP', label: 'Northern Mariana Islands', phone: '1-670' }, + { code: 'MQ', label: 'Martinique', phone: '596' }, + { code: 'MR', label: 'Mauritania', phone: '222' }, + { code: 'MS', label: 'Montserrat', phone: '1-664' }, + { code: 'MT', label: 'Malta', phone: '356' }, + { code: 'MU', label: 'Mauritius', phone: '230' }, + { code: 'MV', label: 'Maldives', phone: '960' }, + { code: 'MW', label: 'Malawi', phone: '265' }, + { code: 'MX', label: 'Mexico', phone: '52' }, + { code: 'MY', label: 'Malaysia', phone: '60' }, + { code: 'MZ', label: 'Mozambique', phone: '258' }, + { code: 'NA', label: 'Namibia', phone: '264' }, + { code: 'NC', label: 'New Caledonia', phone: '687' }, + { code: 'NE', label: 'Niger', phone: '227' }, + { code: 'NF', label: 'Norfolk Island', phone: '672' }, + { code: 'NG', label: 'Nigeria', phone: '234' }, + { code: 'NI', label: 'Nicaragua', phone: '505' }, + { code: 'NL', label: 'Netherlands', phone: '31' }, + { code: 'NO', label: 'Norway', phone: '47' }, + { code: 'NP', label: 'Nepal', phone: '977' }, + { code: 'NR', label: 'Nauru', phone: '674' }, + { code: 'NU', label: 'Niue', phone: '683' }, + { code: 'NZ', label: 'New Zealand', phone: '64' }, + { code: 'OM', label: 'Oman', phone: '968' }, + { code: 'PA', label: 'Panama', phone: '507' }, + { code: 'PE', label: 'Peru', phone: '51' }, + { code: 'PF', label: 'French Polynesia', phone: '689' }, + { code: 'PG', label: 'Papua New Guinea', phone: '675' }, + { code: 'PH', label: 'Philippines', phone: '63' }, + { code: 'PK', label: 'Pakistan', phone: '92' }, + { code: 'PL', label: 'Poland', phone: '48' }, + { code: 'PM', label: 'Saint Pierre and Miquelon', phone: '508' }, + { code: 'PN', label: 'Pitcairn', phone: '870' }, + { code: 'PR', label: 'Puerto Rico', phone: '1' }, + { code: 'PS', label: 'Palestine, State of', phone: '970' }, + { code: 'PT', label: 'Portugal', phone: '351' }, + { code: 'PW', label: 'Palau', phone: '680' }, + { code: 'PY', label: 'Paraguay', phone: '595' }, + { code: 'QA', label: 'Qatar', phone: '974' }, + { code: 'RE', label: 'Reunion', phone: '262' }, + { code: 'RO', label: 'Romania', phone: '40' }, + { code: 'RS', label: 'Serbia', phone: '381' }, + { code: 'RU', label: 'Russian Federation', phone: '7' }, + { code: 'RW', label: 'Rwanda', phone: '250' }, + { code: 'SA', label: 'Saudi Arabia', phone: '966' }, + { code: 'SB', label: 'Solomon Islands', phone: '677' }, + { code: 'SC', label: 'Seychelles', phone: '248' }, + { code: 'SD', label: 'Sudan', phone: '249' }, + { code: 'SE', label: 'Sweden', phone: '46' }, + { code: 'SG', label: 'Singapore', phone: '65' }, + { code: 'SH', label: 'Saint Helena', phone: '290' }, + { code: 'SI', label: 'Slovenia', phone: '386' }, + { code: 'SJ', label: 'Svalbard and Jan Mayen', phone: '47' }, + { code: 'SK', label: 'Slovakia', phone: '421' }, + { code: 'SL', label: 'Sierra Leone', phone: '232' }, + { code: 'SM', label: 'San Marino', phone: '378' }, + { code: 'SN', label: 'Senegal', phone: '221' }, + { code: 'SO', label: 'Somalia', phone: '252' }, + { code: 'SR', label: 'Suriname', phone: '597' }, + { code: 'SS', label: 'South Sudan', phone: '211' }, + { code: 'ST', label: 'Sao Tome and Principe', phone: '239' }, + { code: 'SV', label: 'El Salvador', phone: '503' }, + { code: 'SX', label: 'Sint Maarten (Dutch part)', phone: '1-721' }, + { code: 'SY', label: 'Syrian Arab Republic', phone: '963' }, + { code: 'SZ', label: 'Swaziland', phone: '268' }, + { code: 'TC', label: 'Turks and Caicos Islands', phone: '1-649' }, + { code: 'TD', label: 'Chad', phone: '235' }, + { code: 'TF', label: 'French Southern Territories', phone: '262' }, + { code: 'TG', label: 'Togo', phone: '228' }, + { code: 'TH', label: 'Thailand', phone: '66' }, + { code: 'TJ', label: 'Tajikistan', phone: '992' }, + { code: 'TK', label: 'Tokelau', phone: '690' }, + { code: 'TL', label: 'Timor-Leste', phone: '670' }, + { code: 'TM', label: 'Turkmenistan', phone: '993' }, + { code: 'TN', label: 'Tunisia', phone: '216' }, + { code: 'TO', label: 'Tonga', phone: '676' }, + { code: 'TR', label: 'Turkey', phone: '90' }, + { code: 'TT', label: 'Trinidad and Tobago', phone: '1-868' }, + { code: 'TV', label: 'Tuvalu', phone: '688' }, + { code: 'TW', label: 'Taiwan, Province of China', phone: '886' }, + { code: 'TZ', label: 'United Republic of Tanzania', phone: '255' }, + { code: 'UA', label: 'Ukraine', phone: '380' }, + { code: 'UG', label: 'Uganda', phone: '256' }, + { code: 'US', label: 'United States', phone: '1', suggested: true }, + { code: 'UY', label: 'Uruguay', phone: '598' }, + { code: 'UZ', label: 'Uzbekistan', phone: '998' }, + { code: 'VA', label: 'Holy See (Vatican City State)', phone: '379' }, + { code: 'VC', label: 'Saint Vincent and the Grenadines', phone: '1-784' }, + { code: 'VE', label: 'Venezuela', phone: '58' }, + { code: 'VG', label: 'British Virgin Islands', phone: '1-284' }, + { code: 'VI', label: 'US Virgin Islands', phone: '1-340' }, + { code: 'VN', label: 'Vietnam', phone: '84' }, + { code: 'VU', label: 'Vanuatu', phone: '678' }, + { code: 'WF', label: 'Wallis and Futuna', phone: '681' }, + { code: 'WS', label: 'Samoa', phone: '685' }, + { code: 'XK', label: 'Kosovo', phone: '383' }, + { code: 'YE', label: 'Yemen', phone: '967' }, + { code: 'YT', label: 'Mayotte', phone: '262' }, + { code: 'ZA', label: 'South Africa', phone: '27' }, + { code: 'ZM', label: 'Zambia', phone: '260' }, + { code: 'ZW', label: 'Zimbabwe', phone: '263' }, +]; + +export default { + title: "Material-ui|autocomplete|CountrySelect.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/CustomizedHook.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/CustomizedHook.stories.tsx new file mode 100644 index 00000000000000..2e59808e1d0f2f --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/CustomizedHook.stories.tsx @@ -0,0 +1,287 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import useAutocomplete from '@material-ui/lab/useAutocomplete'; +import NoSsr from '@material-ui/core/NoSsr'; +import CheckIcon from '@material-ui/icons/Check'; +import CloseIcon from '@material-ui/icons/Close'; +import styled from 'styled-components'; + +const Label = styled('label')` + padding: 0 0 4px; + line-height: 1.5; + display: block; +`; + +const InputWrapper = styled('div')` + width: 300px; + border: 1px solid #d9d9d9; + background-color: #fff; + border-radius: 4px; + padding: 1px; + display: flex; + flex-wrap: wrap; + + &:hover { + border-color: #40a9ff; + } + + &.focused { + border-color: #40a9ff; + box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); + } + + & input { + font-size: 14px; + height: 30px; + box-sizing: border-box; + padding: 4px 6px; + width: 0; + min-width: 30px; + flex-grow: 1; + border: 0; + margin: 0; + outline: 0; + } +`; + +const Tag = styled(({ label, onDelete, ...props }) => ( +
+ {label} + +
+))` + display: flex; + align-items: center; + height: 24px; + margin: 2px; + line-height: 22px; + background-color: #fafafa; + border: 1px solid #e8e8e8; + border-radius: 2px; + box-sizing: content-box; + padding: 0 4px 0 10px; + outline: 0; + overflow: hidden; + + &:focus { + border-color: #40a9ff; + background-color: #e6f7ff; + } + + & span { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + & svg { + font-size: 12px; + cursor: pointer; + padding: 4px; + } +`; + +const Listbox = styled('ul')` + width: 300px; + margin: 2px 0 0; + padding: 0; + position: absolute; + list-style: none; + background-color: #fff; + overflow: auto; + max-height: 250px; + border-radius: 4px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + z-index: 1; + + & li { + padding: 5px 12px; + display: flex; + + & span { + flex-grow: 1; + } + + & svg { + color: transparent; + } + } + + & li[aria-selected='true'] { + background-color: #fafafa; + font-weight: 600; + + & svg { + color: #1890ff; + } + } + + & li[data-focus='true'] { + background-color: #e6f7ff; + cursor: pointer; + + & svg { + color: #000; + } + } +`; + +export function CustomizedHook() { + const { + getRootProps, + getInputLabelProps, + getInputProps, + getTagProps, + getListboxProps, + getOptionProps, + groupedOptions, + value, + focused, + setAnchorEl, + } = useAutocomplete({ + id: 'customized-hook-demo', + defaultValue: [top100Films[1]], + multiple: true, + options: top100Films, + getOptionLabel: (option) => option.title, + }); + + return ( + +
+
+ + + {value.map((option: FilmOptionType, index: number) => ( + + ))} + + +
+ {groupedOptions.length > 0 ? ( + + {groupedOptions.map((option, index) => ( +
  • + {option.title} + +
  • + ))} +
    + ) : null} +
    +
    + ); +} + +interface FilmOptionType { + title: string; + year: number; +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { title: 'The Shawshank Redemption', year: 1994 }, + { title: 'The Godfather', year: 1972 }, + { title: 'The Godfather: Part II', year: 1974 }, + { title: 'The Dark Knight', year: 2008 }, + { title: '12 Angry Men', year: 1957 }, + { title: "Schindler's List", year: 1993 }, + { title: 'Pulp Fiction', year: 1994 }, + { title: 'The Lord of the Rings: The Return of the King', year: 2003 }, + { title: 'The Good, the Bad and the Ugly', year: 1966 }, + { title: 'Fight Club', year: 1999 }, + { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 }, + { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 }, + { title: 'Forrest Gump', year: 1994 }, + { title: 'Inception', year: 2010 }, + { title: 'The Lord of the Rings: The Two Towers', year: 2002 }, + { title: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { title: 'Goodfellas', year: 1990 }, + { title: 'The Matrix', year: 1999 }, + { title: 'Seven Samurai', year: 1954 }, + { title: 'Star Wars: Episode IV - A New Hope', year: 1977 }, + { title: 'City of God', year: 2002 }, + { title: 'Se7en', year: 1995 }, + { title: 'The Silence of the Lambs', year: 1991 }, + { title: "It's a Wonderful Life", year: 1946 }, + { title: 'Life Is Beautiful', year: 1997 }, + { title: 'The Usual Suspects', year: 1995 }, + { title: 'Léon: The Professional', year: 1994 }, + { title: 'Spirited Away', year: 2001 }, + { title: 'Saving Private Ryan', year: 1998 }, + { title: 'Once Upon a Time in the West', year: 1968 }, + { title: 'American History X', year: 1998 }, + { title: 'Interstellar', year: 2014 }, + { title: 'Casablanca', year: 1942 }, + { title: 'City Lights', year: 1931 }, + { title: 'Psycho', year: 1960 }, + { title: 'The Green Mile', year: 1999 }, + { title: 'The Intouchables', year: 2011 }, + { title: 'Modern Times', year: 1936 }, + { title: 'Raiders of the Lost Ark', year: 1981 }, + { title: 'Rear Window', year: 1954 }, + { title: 'The Pianist', year: 2002 }, + { title: 'The Departed', year: 2006 }, + { title: 'Terminator 2: Judgment Day', year: 1991 }, + { title: 'Back to the Future', year: 1985 }, + { title: 'Whiplash', year: 2014 }, + { title: 'Gladiator', year: 2000 }, + { title: 'Memento', year: 2000 }, + { title: 'The Prestige', year: 2006 }, + { title: 'The Lion King', year: 1994 }, + { title: 'Apocalypse Now', year: 1979 }, + { title: 'Alien', year: 1979 }, + { title: 'Sunset Boulevard', year: 1950 }, + { title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', year: 1964 }, + { title: 'The Great Dictator', year: 1940 }, + { title: 'Cinema Paradiso', year: 1988 }, + { title: 'The Lives of Others', year: 2006 }, + { title: 'Grave of the Fireflies', year: 1988 }, + { title: 'Paths of Glory', year: 1957 }, + { title: 'Django Unchained', year: 2012 }, + { title: 'The Shining', year: 1980 }, + { title: 'WALL·E', year: 2008 }, + { title: 'American Beauty', year: 1999 }, + { title: 'The Dark Knight Rises', year: 2012 }, + { title: 'Princess Mononoke', year: 1997 }, + { title: 'Aliens', year: 1986 }, + { title: 'Oldboy', year: 2003 }, + { title: 'Once Upon a Time in America', year: 1984 }, + { title: 'Witness for the Prosecution', year: 1957 }, + { title: 'Das Boot', year: 1981 }, + { title: 'Citizen Kane', year: 1941 }, + { title: 'North by Northwest', year: 1959 }, + { title: 'Vertigo', year: 1958 }, + { title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 }, + { title: 'Reservoir Dogs', year: 1992 }, + { title: 'Braveheart', year: 1995 }, + { title: 'M', year: 1931 }, + { title: 'Requiem for a Dream', year: 2000 }, + { title: 'Amélie', year: 2001 }, + { title: 'A Clockwork Orange', year: 1971 }, + { title: 'Like Stars on Earth', year: 2007 }, + { title: 'Taxi Driver', year: 1976 }, + { title: 'Lawrence of Arabia', year: 1962 }, + { title: 'Double Indemnity', year: 1944 }, + { title: 'Eternal Sunshine of the Spotless Mind', year: 2004 }, + { title: 'Amadeus', year: 1984 }, + { title: 'To Kill a Mockingbird', year: 1962 }, + { title: 'Toy Story 3', year: 2010 }, + { title: 'Logan', year: 2017 }, + { title: 'Full Metal Jacket', year: 1987 }, + { title: 'Dangal', year: 2016 }, + { title: 'The Sting', year: 1973 }, + { title: '2001: A Space Odyssey', year: 1968 }, + { title: "Singin' in the Rain", year: 1952 }, + { title: 'Toy Story', year: 1995 }, + { title: 'Bicycle Thieves', year: 1948 }, + { title: 'The Kid', year: 1921 }, + { title: 'Inglourious Basterds', year: 2009 }, + { title: 'Snatch', year: 2000 }, + { title: '3 Idiots', year: 2009 }, + { title: 'Monty Python and the Holy Grail', year: 1975 }, +]; + +export default { + title: "Material-ui|autocomplete|CustomizedHook.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/DisabledOptions.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/DisabledOptions.stories.tsx new file mode 100644 index 00000000000000..0e9070412565c0 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/DisabledOptions.stories.tsx @@ -0,0 +1,27 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete from '@material-ui/lab/Autocomplete'; + +export function DisabledOptions() { + return ( + option === timeSlots[0] || option === timeSlots[2]} + style={{ width: 300 }} + renderInput={(params) => ( + + )} + /> + ); +} + +// One time slot every 30 minutes. +const timeSlots = Array.from(new Array(24 * 2)).map( + (_, index) => `${index < 20 ? '0' : ''}${Math.floor(index / 2)}:${index % 2 === 0 ? '00' : '30'}`, +); + +export default { + title: "Material-ui|autocomplete|DisabledOptions.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/Filter.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/Filter.stories.tsx new file mode 100644 index 00000000000000..5e88eb6dffafba --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/Filter.stories.tsx @@ -0,0 +1,135 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete, { createFilterOptions } from '@material-ui/lab/Autocomplete'; + +const filterOptions = createFilterOptions({ + matchFrom: 'start', + stringify: (option: FilmOptionType) => option.title, +}); + +export function Filter() { + return ( + option.title} + filterOptions={filterOptions} + style={{ width: 300 }} + renderInput={(params) => } + /> + ); +} + +interface FilmOptionType { + title: string; + year: number; +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { title: 'The Shawshank Redemption', year: 1994 }, + { title: 'The Godfather', year: 1972 }, + { title: 'The Godfather: Part II', year: 1974 }, + { title: 'The Dark Knight', year: 2008 }, + { title: '12 Angry Men', year: 1957 }, + { title: "Schindler's List", year: 1993 }, + { title: 'Pulp Fiction', year: 1994 }, + { title: 'The Lord of the Rings: The Return of the King', year: 2003 }, + { title: 'The Good, the Bad and the Ugly', year: 1966 }, + { title: 'Fight Club', year: 1999 }, + { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 }, + { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 }, + { title: 'Forrest Gump', year: 1994 }, + { title: 'Inception', year: 2010 }, + { title: 'The Lord of the Rings: The Two Towers', year: 2002 }, + { title: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { title: 'Goodfellas', year: 1990 }, + { title: 'The Matrix', year: 1999 }, + { title: 'Seven Samurai', year: 1954 }, + { title: 'Star Wars: Episode IV - A New Hope', year: 1977 }, + { title: 'City of God', year: 2002 }, + { title: 'Se7en', year: 1995 }, + { title: 'The Silence of the Lambs', year: 1991 }, + { title: "It's a Wonderful Life", year: 1946 }, + { title: 'Life Is Beautiful', year: 1997 }, + { title: 'The Usual Suspects', year: 1995 }, + { title: 'Léon: The Professional', year: 1994 }, + { title: 'Spirited Away', year: 2001 }, + { title: 'Saving Private Ryan', year: 1998 }, + { title: 'Once Upon a Time in the West', year: 1968 }, + { title: 'American History X', year: 1998 }, + { title: 'Interstellar', year: 2014 }, + { title: 'Casablanca', year: 1942 }, + { title: 'City Lights', year: 1931 }, + { title: 'Psycho', year: 1960 }, + { title: 'The Green Mile', year: 1999 }, + { title: 'The Intouchables', year: 2011 }, + { title: 'Modern Times', year: 1936 }, + { title: 'Raiders of the Lost Ark', year: 1981 }, + { title: 'Rear Window', year: 1954 }, + { title: 'The Pianist', year: 2002 }, + { title: 'The Departed', year: 2006 }, + { title: 'Terminator 2: Judgment Day', year: 1991 }, + { title: 'Back to the Future', year: 1985 }, + { title: 'Whiplash', year: 2014 }, + { title: 'Gladiator', year: 2000 }, + { title: 'Memento', year: 2000 }, + { title: 'The Prestige', year: 2006 }, + { title: 'The Lion King', year: 1994 }, + { title: 'Apocalypse Now', year: 1979 }, + { title: 'Alien', year: 1979 }, + { title: 'Sunset Boulevard', year: 1950 }, + { title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', year: 1964 }, + { title: 'The Great Dictator', year: 1940 }, + { title: 'Cinema Paradiso', year: 1988 }, + { title: 'The Lives of Others', year: 2006 }, + { title: 'Grave of the Fireflies', year: 1988 }, + { title: 'Paths of Glory', year: 1957 }, + { title: 'Django Unchained', year: 2012 }, + { title: 'The Shining', year: 1980 }, + { title: 'WALL·E', year: 2008 }, + { title: 'American Beauty', year: 1999 }, + { title: 'The Dark Knight Rises', year: 2012 }, + { title: 'Princess Mononoke', year: 1997 }, + { title: 'Aliens', year: 1986 }, + { title: 'Oldboy', year: 2003 }, + { title: 'Once Upon a Time in America', year: 1984 }, + { title: 'Witness for the Prosecution', year: 1957 }, + { title: 'Das Boot', year: 1981 }, + { title: 'Citizen Kane', year: 1941 }, + { title: 'North by Northwest', year: 1959 }, + { title: 'Vertigo', year: 1958 }, + { title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 }, + { title: 'Reservoir Dogs', year: 1992 }, + { title: 'Braveheart', year: 1995 }, + { title: 'M', year: 1931 }, + { title: 'Requiem for a Dream', year: 2000 }, + { title: 'Amélie', year: 2001 }, + { title: 'A Clockwork Orange', year: 1971 }, + { title: 'Like Stars on Earth', year: 2007 }, + { title: 'Taxi Driver', year: 1976 }, + { title: 'Lawrence of Arabia', year: 1962 }, + { title: 'Double Indemnity', year: 1944 }, + { title: 'Eternal Sunshine of the Spotless Mind', year: 2004 }, + { title: 'Amadeus', year: 1984 }, + { title: 'To Kill a Mockingbird', year: 1962 }, + { title: 'Toy Story 3', year: 2010 }, + { title: 'Logan', year: 2017 }, + { title: 'Full Metal Jacket', year: 1987 }, + { title: 'Dangal', year: 2016 }, + { title: 'The Sting', year: 1973 }, + { title: '2001: A Space Odyssey', year: 1968 }, + { title: "Singin' in the Rain", year: 1952 }, + { title: 'Toy Story', year: 1995 }, + { title: 'Bicycle Thieves', year: 1948 }, + { title: 'The Kid', year: 1921 }, + { title: 'Inglourious Basterds', year: 2009 }, + { title: 'Snatch', year: 2000 }, + { title: '3 Idiots', year: 2009 }, + { title: 'Monty Python and the Holy Grail', year: 1975 }, +]; + +export default { + title: "Material-ui|autocomplete|Filter.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/FixedTags.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/FixedTags.stories.tsx new file mode 100644 index 00000000000000..96baf2088782d7 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/FixedTags.stories.tsx @@ -0,0 +1,147 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import Chip from '@material-ui/core/Chip'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete from '@material-ui/lab/Autocomplete'; + +export function FixedTags() { + const fixedOptions = [top100Films[6]]; + const [value, setValue] = React.useState([...fixedOptions, top100Films[13]]); + + return ( + { + setValue([ + ...fixedOptions, + ...newValue.filter((option) => fixedOptions.indexOf(option) === -1), + ]); + }} + options={top100Films} + getOptionLabel={(option) => option.title} + renderTags={(tagValue, getTagProps) => + tagValue.map((option, index) => ( + + )) + } + style={{ width: 500 }} + renderInput={(params) => ( + + )} + /> + ); +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { title: 'The Shawshank Redemption', year: 1994 }, + { title: 'The Godfather', year: 1972 }, + { title: 'The Godfather: Part II', year: 1974 }, + { title: 'The Dark Knight', year: 2008 }, + { title: '12 Angry Men', year: 1957 }, + { title: "Schindler's List", year: 1993 }, + { title: 'Pulp Fiction', year: 1994 }, + { title: 'The Lord of the Rings: The Return of the King', year: 2003 }, + { title: 'The Good, the Bad and the Ugly', year: 1966 }, + { title: 'Fight Club', year: 1999 }, + { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 }, + { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 }, + { title: 'Forrest Gump', year: 1994 }, + { title: 'Inception', year: 2010 }, + { title: 'The Lord of the Rings: The Two Towers', year: 2002 }, + { title: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { title: 'Goodfellas', year: 1990 }, + { title: 'The Matrix', year: 1999 }, + { title: 'Seven Samurai', year: 1954 }, + { title: 'Star Wars: Episode IV - A New Hope', year: 1977 }, + { title: 'City of God', year: 2002 }, + { title: 'Se7en', year: 1995 }, + { title: 'The Silence of the Lambs', year: 1991 }, + { title: "It's a Wonderful Life", year: 1946 }, + { title: 'Life Is Beautiful', year: 1997 }, + { title: 'The Usual Suspects', year: 1995 }, + { title: 'Léon: The Professional', year: 1994 }, + { title: 'Spirited Away', year: 2001 }, + { title: 'Saving Private Ryan', year: 1998 }, + { title: 'Once Upon a Time in the West', year: 1968 }, + { title: 'American History X', year: 1998 }, + { title: 'Interstellar', year: 2014 }, + { title: 'Casablanca', year: 1942 }, + { title: 'City Lights', year: 1931 }, + { title: 'Psycho', year: 1960 }, + { title: 'The Green Mile', year: 1999 }, + { title: 'The Intouchables', year: 2011 }, + { title: 'Modern Times', year: 1936 }, + { title: 'Raiders of the Lost Ark', year: 1981 }, + { title: 'Rear Window', year: 1954 }, + { title: 'The Pianist', year: 2002 }, + { title: 'The Departed', year: 2006 }, + { title: 'Terminator 2: Judgment Day', year: 1991 }, + { title: 'Back to the Future', year: 1985 }, + { title: 'Whiplash', year: 2014 }, + { title: 'Gladiator', year: 2000 }, + { title: 'Memento', year: 2000 }, + { title: 'The Prestige', year: 2006 }, + { title: 'The Lion King', year: 1994 }, + { title: 'Apocalypse Now', year: 1979 }, + { title: 'Alien', year: 1979 }, + { title: 'Sunset Boulevard', year: 1950 }, + { title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', year: 1964 }, + { title: 'The Great Dictator', year: 1940 }, + { title: 'Cinema Paradiso', year: 1988 }, + { title: 'The Lives of Others', year: 2006 }, + { title: 'Grave of the Fireflies', year: 1988 }, + { title: 'Paths of Glory', year: 1957 }, + { title: 'Django Unchained', year: 2012 }, + { title: 'The Shining', year: 1980 }, + { title: 'WALL·E', year: 2008 }, + { title: 'American Beauty', year: 1999 }, + { title: 'The Dark Knight Rises', year: 2012 }, + { title: 'Princess Mononoke', year: 1997 }, + { title: 'Aliens', year: 1986 }, + { title: 'Oldboy', year: 2003 }, + { title: 'Once Upon a Time in America', year: 1984 }, + { title: 'Witness for the Prosecution', year: 1957 }, + { title: 'Das Boot', year: 1981 }, + { title: 'Citizen Kane', year: 1941 }, + { title: 'North by Northwest', year: 1959 }, + { title: 'Vertigo', year: 1958 }, + { title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 }, + { title: 'Reservoir Dogs', year: 1992 }, + { title: 'Braveheart', year: 1995 }, + { title: 'M', year: 1931 }, + { title: 'Requiem for a Dream', year: 2000 }, + { title: 'Amélie', year: 2001 }, + { title: 'A Clockwork Orange', year: 1971 }, + { title: 'Like Stars on Earth', year: 2007 }, + { title: 'Taxi Driver', year: 1976 }, + { title: 'Lawrence of Arabia', year: 1962 }, + { title: 'Double Indemnity', year: 1944 }, + { title: 'Eternal Sunshine of the Spotless Mind', year: 2004 }, + { title: 'Amadeus', year: 1984 }, + { title: 'To Kill a Mockingbird', year: 1962 }, + { title: 'Toy Story 3', year: 2010 }, + { title: 'Logan', year: 2017 }, + { title: 'Full Metal Jacket', year: 1987 }, + { title: 'Dangal', year: 2016 }, + { title: 'The Sting', year: 1973 }, + { title: '2001: A Space Odyssey', year: 1968 }, + { title: "Singin' in the Rain", year: 1952 }, + { title: 'Toy Story', year: 1995 }, + { title: 'Bicycle Thieves', year: 1948 }, + { title: 'The Kid', year: 1921 }, + { title: 'Inglourious Basterds', year: 2009 }, + { title: 'Snatch', year: 2000 }, + { title: '3 Idiots', year: 2009 }, + { title: 'Monty Python and the Holy Grail', year: 1975 }, +]; + +export default { + title: "Material-ui|autocomplete|FixedTags.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/FreeSolo.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/FreeSolo.stories.tsx new file mode 100644 index 00000000000000..31ec0436b542c0 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/FreeSolo.stories.tsx @@ -0,0 +1,142 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete from '@material-ui/lab/Autocomplete'; + +export function FreeSolo() { + return ( +
    + option.title)} + renderInput={(params) => ( + + )} + /> + option.title)} + renderInput={(params) => ( + + )} + /> +
    + ); +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { title: 'The Shawshank Redemption', year: 1994 }, + { title: 'The Godfather', year: 1972 }, + { title: 'The Godfather: Part II', year: 1974 }, + { title: 'The Dark Knight', year: 2008 }, + { title: '12 Angry Men', year: 1957 }, + { title: "Schindler's List", year: 1993 }, + { title: 'Pulp Fiction', year: 1994 }, + { title: 'The Lord of the Rings: The Return of the King', year: 2003 }, + { title: 'The Good, the Bad and the Ugly', year: 1966 }, + { title: 'Fight Club', year: 1999 }, + { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 }, + { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 }, + { title: 'Forrest Gump', year: 1994 }, + { title: 'Inception', year: 2010 }, + { title: 'The Lord of the Rings: The Two Towers', year: 2002 }, + { title: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { title: 'Goodfellas', year: 1990 }, + { title: 'The Matrix', year: 1999 }, + { title: 'Seven Samurai', year: 1954 }, + { title: 'Star Wars: Episode IV - A New Hope', year: 1977 }, + { title: 'City of God', year: 2002 }, + { title: 'Se7en', year: 1995 }, + { title: 'The Silence of the Lambs', year: 1991 }, + { title: "It's a Wonderful Life", year: 1946 }, + { title: 'Life Is Beautiful', year: 1997 }, + { title: 'The Usual Suspects', year: 1995 }, + { title: 'Léon: The Professional', year: 1994 }, + { title: 'Spirited Away', year: 2001 }, + { title: 'Saving Private Ryan', year: 1998 }, + { title: 'Once Upon a Time in the West', year: 1968 }, + { title: 'American History X', year: 1998 }, + { title: 'Interstellar', year: 2014 }, + { title: 'Casablanca', year: 1942 }, + { title: 'City Lights', year: 1931 }, + { title: 'Psycho', year: 1960 }, + { title: 'The Green Mile', year: 1999 }, + { title: 'The Intouchables', year: 2011 }, + { title: 'Modern Times', year: 1936 }, + { title: 'Raiders of the Lost Ark', year: 1981 }, + { title: 'Rear Window', year: 1954 }, + { title: 'The Pianist', year: 2002 }, + { title: 'The Departed', year: 2006 }, + { title: 'Terminator 2: Judgment Day', year: 1991 }, + { title: 'Back to the Future', year: 1985 }, + { title: 'Whiplash', year: 2014 }, + { title: 'Gladiator', year: 2000 }, + { title: 'Memento', year: 2000 }, + { title: 'The Prestige', year: 2006 }, + { title: 'The Lion King', year: 1994 }, + { title: 'Apocalypse Now', year: 1979 }, + { title: 'Alien', year: 1979 }, + { title: 'Sunset Boulevard', year: 1950 }, + { title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', year: 1964 }, + { title: 'The Great Dictator', year: 1940 }, + { title: 'Cinema Paradiso', year: 1988 }, + { title: 'The Lives of Others', year: 2006 }, + { title: 'Grave of the Fireflies', year: 1988 }, + { title: 'Paths of Glory', year: 1957 }, + { title: 'Django Unchained', year: 2012 }, + { title: 'The Shining', year: 1980 }, + { title: 'WALL·E', year: 2008 }, + { title: 'American Beauty', year: 1999 }, + { title: 'The Dark Knight Rises', year: 2012 }, + { title: 'Princess Mononoke', year: 1997 }, + { title: 'Aliens', year: 1986 }, + { title: 'Oldboy', year: 2003 }, + { title: 'Once Upon a Time in America', year: 1984 }, + { title: 'Witness for the Prosecution', year: 1957 }, + { title: 'Das Boot', year: 1981 }, + { title: 'Citizen Kane', year: 1941 }, + { title: 'North by Northwest', year: 1959 }, + { title: 'Vertigo', year: 1958 }, + { title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 }, + { title: 'Reservoir Dogs', year: 1992 }, + { title: 'Braveheart', year: 1995 }, + { title: 'M', year: 1931 }, + { title: 'Requiem for a Dream', year: 2000 }, + { title: 'Amélie', year: 2001 }, + { title: 'A Clockwork Orange', year: 1971 }, + { title: 'Like Stars on Earth', year: 2007 }, + { title: 'Taxi Driver', year: 1976 }, + { title: 'Lawrence of Arabia', year: 1962 }, + { title: 'Double Indemnity', year: 1944 }, + { title: 'Eternal Sunshine of the Spotless Mind', year: 2004 }, + { title: 'Amadeus', year: 1984 }, + { title: 'To Kill a Mockingbird', year: 1962 }, + { title: 'Toy Story 3', year: 2010 }, + { title: 'Logan', year: 2017 }, + { title: 'Full Metal Jacket', year: 1987 }, + { title: 'Dangal', year: 2016 }, + { title: 'The Sting', year: 1973 }, + { title: '2001: A Space Odyssey', year: 1968 }, + { title: "Singin' in the Rain", year: 1952 }, + { title: 'Toy Story', year: 1995 }, + { title: 'Bicycle Thieves', year: 1948 }, + { title: 'The Kid', year: 1921 }, + { title: 'Inglourious Basterds', year: 2009 }, + { title: 'Snatch', year: 2000 }, + { title: '3 Idiots', year: 2009 }, + { title: 'Monty Python and the Holy Grail', year: 1975 }, +]; + +export default { + title: "Material-ui|autocomplete|FreeSolo.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/FreeSoloCreateOption.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/FreeSoloCreateOption.stories.tsx new file mode 100644 index 00000000000000..6daedca5ea2684 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/FreeSoloCreateOption.stories.tsx @@ -0,0 +1,183 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete, { createFilterOptions } from '@material-ui/lab/Autocomplete'; + +const filter = createFilterOptions(); + +export function FreeSoloCreateOption() { + const [value, setValue] = React.useState(null); + + return ( + { + if (typeof newValue === 'string') { + setValue({ + title: newValue, + }); + } else if (newValue && newValue.inputValue) { + // Create a new value from the user input + setValue({ + title: newValue.inputValue, + }); + } else { + setValue(newValue); + } + }} + filterOptions={(options, params) => { + const filtered = filter(options, params); + + // Suggest the creation of a new value + if (params.inputValue !== '') { + filtered.push({ + inputValue: params.inputValue, + title: `Add "${params.inputValue}"`, + }); + } + + return filtered; + }} + selectOnFocus + clearOnBlur + handleHomeEndKeys + id="free-solo-with-text-demo" + options={top100Films} + getOptionLabel={(option) => { + // Value selected with enter, right from the input + if (typeof option === 'string') { + return option; + } + // Add "xxx" option created dynamically + if (option.inputValue) { + return option.inputValue; + } + // Regular option + return option.title; + }} + renderOption={(option) => option.title} + style={{ width: 300 }} + freeSolo + renderInput={(params) => ( + + )} + /> + ); +} + +interface FilmOptionType { + inputValue?: string; + title: string; + year?: number; +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films: FilmOptionType[] = [ + { title: 'The Shawshank Redemption', year: 1994 }, + { title: 'The Godfather', year: 1972 }, + { title: 'The Godfather: Part II', year: 1974 }, + { title: 'The Dark Knight', year: 2008 }, + { title: '12 Angry Men', year: 1957 }, + { title: "Schindler's List", year: 1993 }, + { title: 'Pulp Fiction', year: 1994 }, + { title: 'The Lord of the Rings: The Return of the King', year: 2003 }, + { title: 'The Good, the Bad and the Ugly', year: 1966 }, + { title: 'Fight Club', year: 1999 }, + { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 }, + { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 }, + { title: 'Forrest Gump', year: 1994 }, + { title: 'Inception', year: 2010 }, + { title: 'The Lord of the Rings: The Two Towers', year: 2002 }, + { title: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { title: 'Goodfellas', year: 1990 }, + { title: 'The Matrix', year: 1999 }, + { title: 'Seven Samurai', year: 1954 }, + { title: 'Star Wars: Episode IV - A New Hope', year: 1977 }, + { title: 'City of God', year: 2002 }, + { title: 'Se7en', year: 1995 }, + { title: 'The Silence of the Lambs', year: 1991 }, + { title: "It's a Wonderful Life", year: 1946 }, + { title: 'Life Is Beautiful', year: 1997 }, + { title: 'The Usual Suspects', year: 1995 }, + { title: 'Léon: The Professional', year: 1994 }, + { title: 'Spirited Away', year: 2001 }, + { title: 'Saving Private Ryan', year: 1998 }, + { title: 'Once Upon a Time in the West', year: 1968 }, + { title: 'American History X', year: 1998 }, + { title: 'Interstellar', year: 2014 }, + { title: 'Casablanca', year: 1942 }, + { title: 'City Lights', year: 1931 }, + { title: 'Psycho', year: 1960 }, + { title: 'The Green Mile', year: 1999 }, + { title: 'The Intouchables', year: 2011 }, + { title: 'Modern Times', year: 1936 }, + { title: 'Raiders of the Lost Ark', year: 1981 }, + { title: 'Rear Window', year: 1954 }, + { title: 'The Pianist', year: 2002 }, + { title: 'The Departed', year: 2006 }, + { title: 'Terminator 2: Judgment Day', year: 1991 }, + { title: 'Back to the Future', year: 1985 }, + { title: 'Whiplash', year: 2014 }, + { title: 'Gladiator', year: 2000 }, + { title: 'Memento', year: 2000 }, + { title: 'The Prestige', year: 2006 }, + { title: 'The Lion King', year: 1994 }, + { title: 'Apocalypse Now', year: 1979 }, + { title: 'Alien', year: 1979 }, + { title: 'Sunset Boulevard', year: 1950 }, + { + title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', + year: 1964, + }, + { title: 'The Great Dictator', year: 1940 }, + { title: 'Cinema Paradiso', year: 1988 }, + { title: 'The Lives of Others', year: 2006 }, + { title: 'Grave of the Fireflies', year: 1988 }, + { title: 'Paths of Glory', year: 1957 }, + { title: 'Django Unchained', year: 2012 }, + { title: 'The Shining', year: 1980 }, + { title: 'WALL·E', year: 2008 }, + { title: 'American Beauty', year: 1999 }, + { title: 'The Dark Knight Rises', year: 2012 }, + { title: 'Princess Mononoke', year: 1997 }, + { title: 'Aliens', year: 1986 }, + { title: 'Oldboy', year: 2003 }, + { title: 'Once Upon a Time in America', year: 1984 }, + { title: 'Witness for the Prosecution', year: 1957 }, + { title: 'Das Boot', year: 1981 }, + { title: 'Citizen Kane', year: 1941 }, + { title: 'North by Northwest', year: 1959 }, + { title: 'Vertigo', year: 1958 }, + { title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 }, + { title: 'Reservoir Dogs', year: 1992 }, + { title: 'Braveheart', year: 1995 }, + { title: 'M', year: 1931 }, + { title: 'Requiem for a Dream', year: 2000 }, + { title: 'Amélie', year: 2001 }, + { title: 'A Clockwork Orange', year: 1971 }, + { title: 'Like Stars on Earth', year: 2007 }, + { title: 'Taxi Driver', year: 1976 }, + { title: 'Lawrence of Arabia', year: 1962 }, + { title: 'Double Indemnity', year: 1944 }, + { title: 'Eternal Sunshine of the Spotless Mind', year: 2004 }, + { title: 'Amadeus', year: 1984 }, + { title: 'To Kill a Mockingbird', year: 1962 }, + { title: 'Toy Story 3', year: 2010 }, + { title: 'Logan', year: 2017 }, + { title: 'Full Metal Jacket', year: 1987 }, + { title: 'Dangal', year: 2016 }, + { title: 'The Sting', year: 1973 }, + { title: '2001: A Space Odyssey', year: 1968 }, + { title: "Singin' in the Rain", year: 1952 }, + { title: 'Toy Story', year: 1995 }, + { title: 'Bicycle Thieves', year: 1948 }, + { title: 'The Kid', year: 1921 }, + { title: 'Inglourious Basterds', year: 2009 }, + { title: 'Snatch', year: 2000 }, + { title: '3 Idiots', year: 2009 }, + { title: 'Monty Python and the Holy Grail', year: 1975 }, +]; + +export default { + title: "Material-ui|autocomplete|FreeSoloCreateOption.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/FreeSoloCreateOptionDialog.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/FreeSoloCreateOptionDialog.stories.tsx new file mode 100644 index 00000000000000..82bc9137c6b612 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/FreeSoloCreateOptionDialog.stories.tsx @@ -0,0 +1,252 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Dialog from '@material-ui/core/Dialog'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogActions from '@material-ui/core/DialogActions'; +import Button from '@material-ui/core/Button'; +import Autocomplete, { createFilterOptions } from '@material-ui/lab/Autocomplete'; + +const filter = createFilterOptions(); + +export function FreeSoloCreateOptionDialog() { + const [value, setValue] = React.useState(null); + const [open, toggleOpen] = React.useState(false); + + const handleClose = () => { + setDialogValue({ + title: '', + year: '', + }); + toggleOpen(false); + }; + + const [dialogValue, setDialogValue] = React.useState({ + title: '', + year: '', + }); + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + setValue({ + title: dialogValue.title, + year: parseInt(dialogValue.year, 10), + }); + handleClose(); + }; + + return ( + + { + if (typeof newValue === 'string') { + // timeout to avoid instant validation of the dialog's form. + setTimeout(() => { + toggleOpen(true); + setDialogValue({ + title: newValue, + year: '', + }); + }); + } else if (newValue && newValue.inputValue) { + toggleOpen(true); + setDialogValue({ + title: newValue.inputValue, + year: '', + }); + } else { + setValue(newValue); + } + }} + filterOptions={(options, params) => { + const filtered = filter(options, params) as FilmOptionType[]; + + if (params.inputValue !== '') { + filtered.push({ + inputValue: params.inputValue, + title: `Add "${params.inputValue}"`, + }); + } + + return filtered; + }} + id="free-solo-dialog-demo" + options={top100Films} + getOptionLabel={(option) => { + // e.g value selected with enter, right from the input + if (typeof option === 'string') { + return option; + } + if (option.inputValue) { + return option.inputValue; + } + return option.title; + }} + selectOnFocus + clearOnBlur + handleHomeEndKeys + renderOption={(option) => option.title} + style={{ width: 300 }} + freeSolo + renderInput={(params) => ( + + )} + /> + +
    + Add a new film + + + Did you miss any film in our list? Please, add it! + + setDialogValue({ ...dialogValue, title: event.target.value })} + label="title" + type="text" + /> + setDialogValue({ ...dialogValue, year: event.target.value })} + label="year" + type="number" + /> + + + + + +
    +
    +
    + ); +} + +interface FilmOptionType { + inputValue?: string; + title: string; + year?: number; +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films: FilmOptionType[] = [ + { title: 'The Shawshank Redemption', year: 1994 }, + { title: 'The Godfather', year: 1972 }, + { title: 'The Godfather: Part II', year: 1974 }, + { title: 'The Dark Knight', year: 2008 }, + { title: '12 Angry Men', year: 1957 }, + { title: "Schindler's List", year: 1993 }, + { title: 'Pulp Fiction', year: 1994 }, + { title: 'The Lord of the Rings: The Return of the King', year: 2003 }, + { title: 'The Good, the Bad and the Ugly', year: 1966 }, + { title: 'Fight Club', year: 1999 }, + { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 }, + { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 }, + { title: 'Forrest Gump', year: 1994 }, + { title: 'Inception', year: 2010 }, + { title: 'The Lord of the Rings: The Two Towers', year: 2002 }, + { title: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { title: 'Goodfellas', year: 1990 }, + { title: 'The Matrix', year: 1999 }, + { title: 'Seven Samurai', year: 1954 }, + { title: 'Star Wars: Episode IV - A New Hope', year: 1977 }, + { title: 'City of God', year: 2002 }, + { title: 'Se7en', year: 1995 }, + { title: 'The Silence of the Lambs', year: 1991 }, + { title: "It's a Wonderful Life", year: 1946 }, + { title: 'Life Is Beautiful', year: 1997 }, + { title: 'The Usual Suspects', year: 1995 }, + { title: 'Léon: The Professional', year: 1994 }, + { title: 'Spirited Away', year: 2001 }, + { title: 'Saving Private Ryan', year: 1998 }, + { title: 'Once Upon a Time in the West', year: 1968 }, + { title: 'American History X', year: 1998 }, + { title: 'Interstellar', year: 2014 }, + { title: 'Casablanca', year: 1942 }, + { title: 'City Lights', year: 1931 }, + { title: 'Psycho', year: 1960 }, + { title: 'The Green Mile', year: 1999 }, + { title: 'The Intouchables', year: 2011 }, + { title: 'Modern Times', year: 1936 }, + { title: 'Raiders of the Lost Ark', year: 1981 }, + { title: 'Rear Window', year: 1954 }, + { title: 'The Pianist', year: 2002 }, + { title: 'The Departed', year: 2006 }, + { title: 'Terminator 2: Judgment Day', year: 1991 }, + { title: 'Back to the Future', year: 1985 }, + { title: 'Whiplash', year: 2014 }, + { title: 'Gladiator', year: 2000 }, + { title: 'Memento', year: 2000 }, + { title: 'The Prestige', year: 2006 }, + { title: 'The Lion King', year: 1994 }, + { title: 'Apocalypse Now', year: 1979 }, + { title: 'Alien', year: 1979 }, + { title: 'Sunset Boulevard', year: 1950 }, + { + title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', + year: 1964, + }, + { title: 'The Great Dictator', year: 1940 }, + { title: 'Cinema Paradiso', year: 1988 }, + { title: 'The Lives of Others', year: 2006 }, + { title: 'Grave of the Fireflies', year: 1988 }, + { title: 'Paths of Glory', year: 1957 }, + { title: 'Django Unchained', year: 2012 }, + { title: 'The Shining', year: 1980 }, + { title: 'WALL·E', year: 2008 }, + { title: 'American Beauty', year: 1999 }, + { title: 'The Dark Knight Rises', year: 2012 }, + { title: 'Princess Mononoke', year: 1997 }, + { title: 'Aliens', year: 1986 }, + { title: 'Oldboy', year: 2003 }, + { title: 'Once Upon a Time in America', year: 1984 }, + { title: 'Witness for the Prosecution', year: 1957 }, + { title: 'Das Boot', year: 1981 }, + { title: 'Citizen Kane', year: 1941 }, + { title: 'North by Northwest', year: 1959 }, + { title: 'Vertigo', year: 1958 }, + { title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 }, + { title: 'Reservoir Dogs', year: 1992 }, + { title: 'Braveheart', year: 1995 }, + { title: 'M', year: 1931 }, + { title: 'Requiem for a Dream', year: 2000 }, + { title: 'Amélie', year: 2001 }, + { title: 'A Clockwork Orange', year: 1971 }, + { title: 'Like Stars on Earth', year: 2007 }, + { title: 'Taxi Driver', year: 1976 }, + { title: 'Lawrence of Arabia', year: 1962 }, + { title: 'Double Indemnity', year: 1944 }, + { title: 'Eternal Sunshine of the Spotless Mind', year: 2004 }, + { title: 'Amadeus', year: 1984 }, + { title: 'To Kill a Mockingbird', year: 1962 }, + { title: 'Toy Story 3', year: 2010 }, + { title: 'Logan', year: 2017 }, + { title: 'Full Metal Jacket', year: 1987 }, + { title: 'Dangal', year: 2016 }, + { title: 'The Sting', year: 1973 }, + { title: '2001: A Space Odyssey', year: 1968 }, + { title: "Singin' in the Rain", year: 1952 }, + { title: 'Toy Story', year: 1995 }, + { title: 'Bicycle Thieves', year: 1948 }, + { title: 'The Kid', year: 1921 }, + { title: 'Inglourious Basterds', year: 2009 }, + { title: 'Snatch', year: 2000 }, + { title: '3 Idiots', year: 2009 }, + { title: 'Monty Python and the Holy Grail', year: 1975 }, +]; + +export default { + title: "Material-ui|autocomplete|FreeSoloCreateOptionDialog.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/GitHubLabel.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/GitHubLabel.stories.tsx new file mode 100644 index 00000000000000..6a62eb1d0fc479 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/GitHubLabel.stories.tsx @@ -0,0 +1,340 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import { useTheme, fade, makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Popper from '@material-ui/core/Popper'; +import SettingsIcon from '@material-ui/icons/Settings'; +import CloseIcon from '@material-ui/icons/Close'; +import DoneIcon from '@material-ui/icons/Done'; +import Autocomplete, { AutocompleteCloseReason } from '@material-ui/lab/Autocomplete'; +import ButtonBase from '@material-ui/core/ButtonBase'; +import InputBase from '@material-ui/core/InputBase'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: 221, + fontSize: 13, + }, + button: { + fontSize: 13, + width: '100%', + textAlign: 'left', + paddingBottom: 8, + color: '#586069', + fontWeight: 600, + '&:hover,&:focus': { + color: '#0366d6', + }, + '& span': { + width: '100%', + }, + '& svg': { + width: 16, + height: 16, + }, + }, + tag: { + marginTop: 3, + height: 20, + padding: '.15em 4px', + fontWeight: 600, + lineHeight: '15px', + borderRadius: 2, + }, + popper: { + border: '1px solid rgba(27,31,35,.15)', + boxShadow: '0 3px 12px rgba(27,31,35,.15)', + borderRadius: 3, + width: 300, + zIndex: 1, + fontSize: 13, + color: '#586069', + backgroundColor: '#f6f8fa', + }, + header: { + borderBottom: '1px solid #e1e4e8', + padding: '8px 10px', + fontWeight: 600, + }, + inputBase: { + padding: 10, + width: '100%', + borderBottom: '1px solid #dfe2e5', + '& input': { + borderRadius: 4, + backgroundColor: theme.palette.common.white, + padding: 8, + transition: theme.transitions.create(['border-color', 'box-shadow']), + border: '1px solid #ced4da', + fontSize: 14, + '&:focus': { + boxShadow: `${fade(theme.palette.primary.main, 0.25)} 0 0 0 0.2rem`, + borderColor: theme.palette.primary.main, + }, + }, + }, + paper: { + boxShadow: 'none', + margin: 0, + color: '#586069', + fontSize: 13, + }, + option: { + minHeight: 'auto', + alignItems: 'flex-start', + padding: 8, + '&[aria-selected="true"]': { + backgroundColor: 'transparent', + }, + '&[data-focus="true"]': { + backgroundColor: theme.palette.action.hover, + }, + }, + popperDisablePortal: { + position: 'relative', + }, + iconSelected: { + width: 17, + height: 17, + marginRight: 5, + marginLeft: -2, + }, + color: { + width: 14, + height: 14, + flexShrink: 0, + borderRadius: 3, + marginRight: 8, + marginTop: 2, + }, + text: { + flexGrow: 1, + }, + close: { + opacity: 0.6, + width: 18, + height: 18, + }, + }), +); + +export function GitHubLabel() { + const classes = useStyles(); + const [anchorEl, setAnchorEl] = React.useState(null); + const [value, setValue] = React.useState([labels[1], labels[11]]); + const [pendingValue, setPendingValue] = React.useState([]); + const theme = useTheme(); + + const handleClick = (event: React.MouseEvent) => { + setPendingValue(value); + setAnchorEl(event.currentTarget); + }; + + const handleClose = (event: React.ChangeEvent<{}>, reason: AutocompleteCloseReason) => { + if (reason === 'toggleInput') { + return; + } + setValue(pendingValue); + if (anchorEl) { + anchorEl.focus(); + } + setAnchorEl(null); + }; + + const open = Boolean(anchorEl); + const id = open ? 'github-label' : undefined; + + return ( + +
    + + Labels + + + {value.map((label) => ( +
    + {label.name} +
    + ))} +
    + +
    Apply labels to this pull request
    + { + setPendingValue(newValue); + }} + disableCloseOnSelect + disablePortal + renderTags={() => null} + noOptionsText="No labels" + renderOption={(option, { selected }) => ( + + + +
    + {option.name} +
    + {option.description} +
    + +
    + )} + options={[...labels].sort((a, b) => { + // Display the selected labels first. + let ai = value.indexOf(a); + ai = ai === -1 ? value.length + labels.indexOf(a) : ai; + let bi = value.indexOf(b); + bi = bi === -1 ? value.length + labels.indexOf(b) : bi; + return ai - bi; + })} + getOptionLabel={(option) => option.name} + renderInput={(params) => ( + + )} + /> +
    +
    + ); +} + +interface LabelType { + name: string; + color: string; + description?: string; +} + +// From https://github.com/abdonrd/github-labels +const labels = [ + { + name: 'good first issue', + color: '#7057ff', + description: 'Good for newcomers', + }, + { + name: 'help wanted', + color: '#008672', + description: 'Extra attention is needed', + }, + { + name: 'priority: critical', + color: '#b60205', + description: '', + }, + { + name: 'priority: high', + color: '#d93f0b', + description: '', + }, + { + name: 'priority: low', + color: '#0e8a16', + description: '', + }, + { + name: 'priority: medium', + color: '#fbca04', + description: '', + }, + { + name: "status: can't reproduce", + color: '#fec1c1', + description: '', + }, + { + name: 'status: confirmed', + color: '#215cea', + description: '', + }, + { + name: 'status: duplicate', + color: '#cfd3d7', + description: 'This issue or pull request already exists', + }, + { + name: 'status: needs information', + color: '#fef2c0', + description: '', + }, + { + name: 'status: wont do/fix', + color: '#eeeeee', + description: 'This will not be worked on', + }, + { + name: 'type: bug', + color: '#d73a4a', + description: "Something isn't working", + }, + { + name: 'type: discussion', + color: '#d4c5f9', + description: '', + }, + { + name: 'type: documentation', + color: '#006b75', + description: '', + }, + { + name: 'type: enhancement', + color: '#84b6eb', + description: '', + }, + { + name: 'type: epic', + color: '#3e4b9e', + description: 'A theme of work that contain sub-tasks', + }, + { + name: 'type: feature request', + color: '#fbca04', + description: 'New feature or request', + }, + { + name: 'type: question', + color: '#d876e3', + description: 'Further information is requested', + }, +]; + +export default { + title: "Material-ui|autocomplete|GitHubLabel.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/GoogleMaps.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/GoogleMaps.stories.tsx new file mode 100644 index 00000000000000..91981274c6d0ba --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/GoogleMaps.stories.tsx @@ -0,0 +1,161 @@ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import LocationOnIcon from '@material-ui/icons/LocationOn'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles } from '@material-ui/core/styles'; +import parse from 'autosuggest-highlight/parse'; +import throttle from 'lodash/throttle'; + +function loadScript(src: string, position: HTMLElement | null, id: string) { + if (!position) { + return; + } + + const script = document.createElement('script'); + script.setAttribute('async', ''); + script.setAttribute('id', id); + script.src = src; + position.appendChild(script); +} + +const autocompleteService = { current: null }; + +const useStyles = makeStyles((theme) => ({ + icon: { + color: theme.palette.text.secondary, + marginRight: theme.spacing(2), + }, +})); + +interface PlaceType { + description: string; + structured_formatting: { + main_text: string; + secondary_text: string; + main_text_matched_substrings: [ + { + offset: number; + length: number; + }, + ]; + }; +} + +export function GoogleMaps() { + const classes = useStyles(); + const [value, setValue] = React.useState(null); + const [inputValue, setInputValue] = React.useState(''); + const [options, setOptions] = React.useState([]); + const loaded = React.useRef(false); + + if (typeof window !== 'undefined' && !loaded.current) { + if (!document.querySelector('#google-maps')) { + loadScript( + 'https://maps.googleapis.com/maps/api/js?key=AIzaSyBwRp1e12ec1vOTtGiA4fcCt2sCUS78UYc&libraries=places', + document.querySelector('head'), + 'google-maps', + ); + } + + loaded.current = true; + } + + const fetch = React.useMemo( + () => + throttle((request: { input: string }, callback: (results?: PlaceType[]) => void) => { + (autocompleteService.current as any).getPlacePredictions(request, callback); + }, 200), + [], + ); + + React.useEffect(() => { + let active = true; + + if (!autocompleteService.current && (window as any).google) { + autocompleteService.current = new (window as any).google.maps.places.AutocompleteService(); + } + if (!autocompleteService.current) { + return undefined; + } + + if (inputValue === '') { + setOptions(value ? [value] : []); + return undefined; + } + + fetch({ input: inputValue }, (results?: PlaceType[]) => { + if (active) { + let newOptions = [] as PlaceType[]; + + if (value) { + newOptions = [value]; + } + + if (results) { + newOptions = [...newOptions, ...results]; + } + + setOptions(newOptions); + } + }); + + return () => { + active = false; + }; + }, [value, inputValue, fetch]); + + return ( + (typeof option === 'string' ? option : option.description)} + filterOptions={(x) => x} + options={options} + autoComplete + includeInputInList + filterSelectedOptions + value={value} + onChange={(event: any, newValue: PlaceType | null) => { + setOptions(newValue ? [newValue, ...options] : options); + setValue(newValue); + }} + onInputChange={(event, newInputValue) => { + setInputValue(newInputValue); + }} + renderInput={(params) => ( + + )} + renderOption={(option) => { + const matches = option.structured_formatting.main_text_matched_substrings; + const parts = parse( + option.structured_formatting.main_text, + matches.map((match: any) => [match.offset, match.offset + match.length]), + ); + + return ( + + + + + + {parts.map((part, index) => ( + + {part.text} + + ))} + + {option.structured_formatting.secondary_text} + + + + ); + }} + /> + ); +} + +export default { + title: "Material-ui|autocomplete|GoogleMaps.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/Grouped.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/Grouped.stories.tsx new file mode 100644 index 00000000000000..246b6dc11edcc3 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/Grouped.stories.tsx @@ -0,0 +1,133 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete from '@material-ui/lab/Autocomplete'; + +export function Grouped() { + const options = top100Films.map((option) => { + const firstLetter = option.title[0].toUpperCase(); + return { + firstLetter: /[0-9]/.test(firstLetter) ? '0-9' : firstLetter, + ...option, + }; + }); + + return ( + -b.firstLetter.localeCompare(a.firstLetter))} + groupBy={(option) => option.firstLetter} + getOptionLabel={(option) => option.title} + style={{ width: 300 }} + renderInput={(params) => } + /> + ); +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { title: 'The Shawshank Redemption', year: 1994 }, + { title: 'The Godfather', year: 1972 }, + { title: 'The Godfather: Part II', year: 1974 }, + { title: 'The Dark Knight', year: 2008 }, + { title: '12 Angry Men', year: 1957 }, + { title: "Schindler's List", year: 1993 }, + { title: 'Pulp Fiction', year: 1994 }, + { title: 'The Lord of the Rings: The Return of the King', year: 2003 }, + { title: 'The Good, the Bad and the Ugly', year: 1966 }, + { title: 'Fight Club', year: 1999 }, + { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 }, + { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 }, + { title: 'Forrest Gump', year: 1994 }, + { title: 'Inception', year: 2010 }, + { title: 'The Lord of the Rings: The Two Towers', year: 2002 }, + { title: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { title: 'Goodfellas', year: 1990 }, + { title: 'The Matrix', year: 1999 }, + { title: 'Seven Samurai', year: 1954 }, + { title: 'Star Wars: Episode IV - A New Hope', year: 1977 }, + { title: 'City of God', year: 2002 }, + { title: 'Se7en', year: 1995 }, + { title: 'The Silence of the Lambs', year: 1991 }, + { title: "It's a Wonderful Life", year: 1946 }, + { title: 'Life Is Beautiful', year: 1997 }, + { title: 'The Usual Suspects', year: 1995 }, + { title: 'Léon: The Professional', year: 1994 }, + { title: 'Spirited Away', year: 2001 }, + { title: 'Saving Private Ryan', year: 1998 }, + { title: 'Once Upon a Time in the West', year: 1968 }, + { title: 'American History X', year: 1998 }, + { title: 'Interstellar', year: 2014 }, + { title: 'Casablanca', year: 1942 }, + { title: 'City Lights', year: 1931 }, + { title: 'Psycho', year: 1960 }, + { title: 'The Green Mile', year: 1999 }, + { title: 'The Intouchables', year: 2011 }, + { title: 'Modern Times', year: 1936 }, + { title: 'Raiders of the Lost Ark', year: 1981 }, + { title: 'Rear Window', year: 1954 }, + { title: 'The Pianist', year: 2002 }, + { title: 'The Departed', year: 2006 }, + { title: 'Terminator 2: Judgment Day', year: 1991 }, + { title: 'Back to the Future', year: 1985 }, + { title: 'Whiplash', year: 2014 }, + { title: 'Gladiator', year: 2000 }, + { title: 'Memento', year: 2000 }, + { title: 'The Prestige', year: 2006 }, + { title: 'The Lion King', year: 1994 }, + { title: 'Apocalypse Now', year: 1979 }, + { title: 'Alien', year: 1979 }, + { title: 'Sunset Boulevard', year: 1950 }, + { title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', year: 1964 }, + { title: 'The Great Dictator', year: 1940 }, + { title: 'Cinema Paradiso', year: 1988 }, + { title: 'The Lives of Others', year: 2006 }, + { title: 'Grave of the Fireflies', year: 1988 }, + { title: 'Paths of Glory', year: 1957 }, + { title: 'Django Unchained', year: 2012 }, + { title: 'The Shining', year: 1980 }, + { title: 'WALL·E', year: 2008 }, + { title: 'American Beauty', year: 1999 }, + { title: 'The Dark Knight Rises', year: 2012 }, + { title: 'Princess Mononoke', year: 1997 }, + { title: 'Aliens', year: 1986 }, + { title: 'Oldboy', year: 2003 }, + { title: 'Once Upon a Time in America', year: 1984 }, + { title: 'Witness for the Prosecution', year: 1957 }, + { title: 'Das Boot', year: 1981 }, + { title: 'Citizen Kane', year: 1941 }, + { title: 'North by Northwest', year: 1959 }, + { title: 'Vertigo', year: 1958 }, + { title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 }, + { title: 'Reservoir Dogs', year: 1992 }, + { title: 'Braveheart', year: 1995 }, + { title: 'M', year: 1931 }, + { title: 'Requiem for a Dream', year: 2000 }, + { title: 'Amélie', year: 2001 }, + { title: 'A Clockwork Orange', year: 1971 }, + { title: 'Like Stars on Earth', year: 2007 }, + { title: 'Taxi Driver', year: 1976 }, + { title: 'Lawrence of Arabia', year: 1962 }, + { title: 'Double Indemnity', year: 1944 }, + { title: 'Eternal Sunshine of the Spotless Mind', year: 2004 }, + { title: 'Amadeus', year: 1984 }, + { title: 'To Kill a Mockingbird', year: 1962 }, + { title: 'Toy Story 3', year: 2010 }, + { title: 'Logan', year: 2017 }, + { title: 'Full Metal Jacket', year: 1987 }, + { title: 'Dangal', year: 2016 }, + { title: 'The Sting', year: 1973 }, + { title: '2001: A Space Odyssey', year: 1968 }, + { title: "Singin' in the Rain", year: 1952 }, + { title: 'Toy Story', year: 1995 }, + { title: 'Bicycle Thieves', year: 1948 }, + { title: 'The Kid', year: 1921 }, + { title: 'Inglourious Basterds', year: 2009 }, + { title: 'Snatch', year: 2000 }, + { title: '3 Idiots', year: 2009 }, + { title: 'Monty Python and the Holy Grail', year: 1975 }, +]; + +export default { + title: "Material-ui|autocomplete|Grouped.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/Highlights.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/Highlights.stories.tsx new file mode 100644 index 00000000000000..73a7de778eb639 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/Highlights.stories.tsx @@ -0,0 +1,142 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import parse from 'autosuggest-highlight/parse'; +import match from 'autosuggest-highlight/match'; + +export function Highlights() { + return ( + option.title} + renderInput={(params) => ( + + )} + renderOption={(option, { inputValue }) => { + const matches = match(option.title, inputValue); + const parts = parse(option.title, matches); + + return ( +
    + {parts.map((part, index) => ( + + {part.text} + + ))} +
    + ); + }} + /> + ); +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { title: 'The Shawshank Redemption', year: 1994 }, + { title: 'The Godfather', year: 1972 }, + { title: 'The Godfather: Part II', year: 1974 }, + { title: 'The Dark Knight', year: 2008 }, + { title: '12 Angry Men', year: 1957 }, + { title: "Schindler's List", year: 1993 }, + { title: 'Pulp Fiction', year: 1994 }, + { title: 'The Lord of the Rings: The Return of the King', year: 2003 }, + { title: 'The Good, the Bad and the Ugly', year: 1966 }, + { title: 'Fight Club', year: 1999 }, + { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 }, + { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 }, + { title: 'Forrest Gump', year: 1994 }, + { title: 'Inception', year: 2010 }, + { title: 'The Lord of the Rings: The Two Towers', year: 2002 }, + { title: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { title: 'Goodfellas', year: 1990 }, + { title: 'The Matrix', year: 1999 }, + { title: 'Seven Samurai', year: 1954 }, + { title: 'Star Wars: Episode IV - A New Hope', year: 1977 }, + { title: 'City of God', year: 2002 }, + { title: 'Se7en', year: 1995 }, + { title: 'The Silence of the Lambs', year: 1991 }, + { title: "It's a Wonderful Life", year: 1946 }, + { title: 'Life Is Beautiful', year: 1997 }, + { title: 'The Usual Suspects', year: 1995 }, + { title: 'Léon: The Professional', year: 1994 }, + { title: 'Spirited Away', year: 2001 }, + { title: 'Saving Private Ryan', year: 1998 }, + { title: 'Once Upon a Time in the West', year: 1968 }, + { title: 'American History X', year: 1998 }, + { title: 'Interstellar', year: 2014 }, + { title: 'Casablanca', year: 1942 }, + { title: 'City Lights', year: 1931 }, + { title: 'Psycho', year: 1960 }, + { title: 'The Green Mile', year: 1999 }, + { title: 'The Intouchables', year: 2011 }, + { title: 'Modern Times', year: 1936 }, + { title: 'Raiders of the Lost Ark', year: 1981 }, + { title: 'Rear Window', year: 1954 }, + { title: 'The Pianist', year: 2002 }, + { title: 'The Departed', year: 2006 }, + { title: 'Terminator 2: Judgment Day', year: 1991 }, + { title: 'Back to the Future', year: 1985 }, + { title: 'Whiplash', year: 2014 }, + { title: 'Gladiator', year: 2000 }, + { title: 'Memento', year: 2000 }, + { title: 'The Prestige', year: 2006 }, + { title: 'The Lion King', year: 1994 }, + { title: 'Apocalypse Now', year: 1979 }, + { title: 'Alien', year: 1979 }, + { title: 'Sunset Boulevard', year: 1950 }, + { title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', year: 1964 }, + { title: 'The Great Dictator', year: 1940 }, + { title: 'Cinema Paradiso', year: 1988 }, + { title: 'The Lives of Others', year: 2006 }, + { title: 'Grave of the Fireflies', year: 1988 }, + { title: 'Paths of Glory', year: 1957 }, + { title: 'Django Unchained', year: 2012 }, + { title: 'The Shining', year: 1980 }, + { title: 'WALL·E', year: 2008 }, + { title: 'American Beauty', year: 1999 }, + { title: 'The Dark Knight Rises', year: 2012 }, + { title: 'Princess Mononoke', year: 1997 }, + { title: 'Aliens', year: 1986 }, + { title: 'Oldboy', year: 2003 }, + { title: 'Once Upon a Time in America', year: 1984 }, + { title: 'Witness for the Prosecution', year: 1957 }, + { title: 'Das Boot', year: 1981 }, + { title: 'Citizen Kane', year: 1941 }, + { title: 'North by Northwest', year: 1959 }, + { title: 'Vertigo', year: 1958 }, + { title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 }, + { title: 'Reservoir Dogs', year: 1992 }, + { title: 'Braveheart', year: 1995 }, + { title: 'M', year: 1931 }, + { title: 'Requiem for a Dream', year: 2000 }, + { title: 'Amélie', year: 2001 }, + { title: 'A Clockwork Orange', year: 1971 }, + { title: 'Like Stars on Earth', year: 2007 }, + { title: 'Taxi Driver', year: 1976 }, + { title: 'Lawrence of Arabia', year: 1962 }, + { title: 'Double Indemnity', year: 1944 }, + { title: 'Eternal Sunshine of the Spotless Mind', year: 2004 }, + { title: 'Amadeus', year: 1984 }, + { title: 'To Kill a Mockingbird', year: 1962 }, + { title: 'Toy Story 3', year: 2010 }, + { title: 'Logan', year: 2017 }, + { title: 'Full Metal Jacket', year: 1987 }, + { title: 'Dangal', year: 2016 }, + { title: 'The Sting', year: 1973 }, + { title: '2001: A Space Odyssey', year: 1968 }, + { title: "Singin' in the Rain", year: 1952 }, + { title: 'Toy Story', year: 1995 }, + { title: 'Bicycle Thieves', year: 1948 }, + { title: 'The Kid', year: 1921 }, + { title: 'Inglourious Basterds', year: 2009 }, + { title: 'Snatch', year: 2000 }, + { title: '3 Idiots', year: 2009 }, + { title: 'Monty Python and the Holy Grail', year: 1975 }, +]; + +export default { + title: "Material-ui|autocomplete|Highlights.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/LimitTags.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/LimitTags.stories.tsx new file mode 100644 index 00000000000000..65ff975fd6a054 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/LimitTags.stories.tsx @@ -0,0 +1,144 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import TextField from '@material-ui/core/TextField'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: 500, + '& > * + *': { + marginTop: theme.spacing(3), + }, + }, + }), +); + +export function LimitTags() { + const classes = useStyles(); + + return ( +
    + option.title} + defaultValue={[top100Films[13], top100Films[12], top100Films[11]]} + renderInput={(params) => ( + + )} + /> +
    + ); +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { title: 'The Shawshank Redemption', year: 1994 }, + { title: 'The Godfather', year: 1972 }, + { title: 'The Godfather: Part II', year: 1974 }, + { title: 'The Dark Knight', year: 2008 }, + { title: '12 Angry Men', year: 1957 }, + { title: "Schindler's List", year: 1993 }, + { title: 'Pulp Fiction', year: 1994 }, + { title: 'The Lord of the Rings: The Return of the King', year: 2003 }, + { title: 'The Good, the Bad and the Ugly', year: 1966 }, + { title: 'Fight Club', year: 1999 }, + { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 }, + { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 }, + { title: 'Forrest Gump', year: 1994 }, + { title: 'Inception', year: 2010 }, + { title: 'The Lord of the Rings: The Two Towers', year: 2002 }, + { title: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { title: 'Goodfellas', year: 1990 }, + { title: 'The Matrix', year: 1999 }, + { title: 'Seven Samurai', year: 1954 }, + { title: 'Star Wars: Episode IV - A New Hope', year: 1977 }, + { title: 'City of God', year: 2002 }, + { title: 'Se7en', year: 1995 }, + { title: 'The Silence of the Lambs', year: 1991 }, + { title: "It's a Wonderful Life", year: 1946 }, + { title: 'Life Is Beautiful', year: 1997 }, + { title: 'The Usual Suspects', year: 1995 }, + { title: 'Léon: The Professional', year: 1994 }, + { title: 'Spirited Away', year: 2001 }, + { title: 'Saving Private Ryan', year: 1998 }, + { title: 'Once Upon a Time in the West', year: 1968 }, + { title: 'American History X', year: 1998 }, + { title: 'Interstellar', year: 2014 }, + { title: 'Casablanca', year: 1942 }, + { title: 'City Lights', year: 1931 }, + { title: 'Psycho', year: 1960 }, + { title: 'The Green Mile', year: 1999 }, + { title: 'The Intouchables', year: 2011 }, + { title: 'Modern Times', year: 1936 }, + { title: 'Raiders of the Lost Ark', year: 1981 }, + { title: 'Rear Window', year: 1954 }, + { title: 'The Pianist', year: 2002 }, + { title: 'The Departed', year: 2006 }, + { title: 'Terminator 2: Judgment Day', year: 1991 }, + { title: 'Back to the Future', year: 1985 }, + { title: 'Whiplash', year: 2014 }, + { title: 'Gladiator', year: 2000 }, + { title: 'Memento', year: 2000 }, + { title: 'The Prestige', year: 2006 }, + { title: 'The Lion King', year: 1994 }, + { title: 'Apocalypse Now', year: 1979 }, + { title: 'Alien', year: 1979 }, + { title: 'Sunset Boulevard', year: 1950 }, + { title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', year: 1964 }, + { title: 'The Great Dictator', year: 1940 }, + { title: 'Cinema Paradiso', year: 1988 }, + { title: 'The Lives of Others', year: 2006 }, + { title: 'Grave of the Fireflies', year: 1988 }, + { title: 'Paths of Glory', year: 1957 }, + { title: 'Django Unchained', year: 2012 }, + { title: 'The Shining', year: 1980 }, + { title: 'WALL·E', year: 2008 }, + { title: 'American Beauty', year: 1999 }, + { title: 'The Dark Knight Rises', year: 2012 }, + { title: 'Princess Mononoke', year: 1997 }, + { title: 'Aliens', year: 1986 }, + { title: 'Oldboy', year: 2003 }, + { title: 'Once Upon a Time in America', year: 1984 }, + { title: 'Witness for the Prosecution', year: 1957 }, + { title: 'Das Boot', year: 1981 }, + { title: 'Citizen Kane', year: 1941 }, + { title: 'North by Northwest', year: 1959 }, + { title: 'Vertigo', year: 1958 }, + { title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 }, + { title: 'Reservoir Dogs', year: 1992 }, + { title: 'Braveheart', year: 1995 }, + { title: 'M', year: 1931 }, + { title: 'Requiem for a Dream', year: 2000 }, + { title: 'Amélie', year: 2001 }, + { title: 'A Clockwork Orange', year: 1971 }, + { title: 'Like Stars on Earth', year: 2007 }, + { title: 'Taxi Driver', year: 1976 }, + { title: 'Lawrence of Arabia', year: 1962 }, + { title: 'Double Indemnity', year: 1944 }, + { title: 'Eternal Sunshine of the Spotless Mind', year: 2004 }, + { title: 'Amadeus', year: 1984 }, + { title: 'To Kill a Mockingbird', year: 1962 }, + { title: 'Toy Story 3', year: 2010 }, + { title: 'Logan', year: 2017 }, + { title: 'Full Metal Jacket', year: 1987 }, + { title: 'Dangal', year: 2016 }, + { title: 'The Sting', year: 1973 }, + { title: '2001: A Space Odyssey', year: 1968 }, + { title: "Singin' in the Rain", year: 1952 }, + { title: 'Toy Story', year: 1995 }, + { title: 'Bicycle Thieves', year: 1948 }, + { title: 'The Kid', year: 1921 }, + { title: 'Inglourious Basterds', year: 2009 }, + { title: 'Snatch', year: 2000 }, + { title: '3 Idiots', year: 2009 }, + { title: 'Monty Python and the Holy Grail', year: 1975 }, +]; + +export default { + title: "Material-ui|autocomplete|LimitTags.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/Playground.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/Playground.stories.tsx new file mode 100644 index 00000000000000..5a7fd9f316cccf --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/Playground.stories.tsx @@ -0,0 +1,242 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete from '@material-ui/lab/Autocomplete'; + +export function Playground() { + const defaultProps = { + options: top100Films, + getOptionLabel: (option: FilmOptionType) => option.title, + }; + const flatProps = { + options: top100Films.map((option) => option.title), + }; + const [value, setValue] = React.useState(null); + + return ( +
    + } + /> + ( + + )} + /> + } + /> + } + /> + ( + + )} + /> + } + /> + { + setValue(newValue); + }} + renderInput={(params) => } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> +
    + ); +} + +interface FilmOptionType { + title: string; + year: number; +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { title: 'The Shawshank Redemption', year: 1994 }, + { title: 'The Godfather', year: 1972 }, + { title: 'The Godfather: Part II', year: 1974 }, + { title: 'The Dark Knight', year: 2008 }, + { title: '12 Angry Men', year: 1957 }, + { title: "Schindler's List", year: 1993 }, + { title: 'Pulp Fiction', year: 1994 }, + { title: 'The Lord of the Rings: The Return of the King', year: 2003 }, + { title: 'The Good, the Bad and the Ugly', year: 1966 }, + { title: 'Fight Club', year: 1999 }, + { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 }, + { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 }, + { title: 'Forrest Gump', year: 1994 }, + { title: 'Inception', year: 2010 }, + { title: 'The Lord of the Rings: The Two Towers', year: 2002 }, + { title: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { title: 'Goodfellas', year: 1990 }, + { title: 'The Matrix', year: 1999 }, + { title: 'Seven Samurai', year: 1954 }, + { title: 'Star Wars: Episode IV - A New Hope', year: 1977 }, + { title: 'City of God', year: 2002 }, + { title: 'Se7en', year: 1995 }, + { title: 'The Silence of the Lambs', year: 1991 }, + { title: "It's a Wonderful Life", year: 1946 }, + { title: 'Life Is Beautiful', year: 1997 }, + { title: 'The Usual Suspects', year: 1995 }, + { title: 'Léon: The Professional', year: 1994 }, + { title: 'Spirited Away', year: 2001 }, + { title: 'Saving Private Ryan', year: 1998 }, + { title: 'Once Upon a Time in the West', year: 1968 }, + { title: 'American History X', year: 1998 }, + { title: 'Interstellar', year: 2014 }, + { title: 'Casablanca', year: 1942 }, + { title: 'City Lights', year: 1931 }, + { title: 'Psycho', year: 1960 }, + { title: 'The Green Mile', year: 1999 }, + { title: 'The Intouchables', year: 2011 }, + { title: 'Modern Times', year: 1936 }, + { title: 'Raiders of the Lost Ark', year: 1981 }, + { title: 'Rear Window', year: 1954 }, + { title: 'The Pianist', year: 2002 }, + { title: 'The Departed', year: 2006 }, + { title: 'Terminator 2: Judgment Day', year: 1991 }, + { title: 'Back to the Future', year: 1985 }, + { title: 'Whiplash', year: 2014 }, + { title: 'Gladiator', year: 2000 }, + { title: 'Memento', year: 2000 }, + { title: 'The Prestige', year: 2006 }, + { title: 'The Lion King', year: 1994 }, + { title: 'Apocalypse Now', year: 1979 }, + { title: 'Alien', year: 1979 }, + { title: 'Sunset Boulevard', year: 1950 }, + { title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', year: 1964 }, + { title: 'The Great Dictator', year: 1940 }, + { title: 'Cinema Paradiso', year: 1988 }, + { title: 'The Lives of Others', year: 2006 }, + { title: 'Grave of the Fireflies', year: 1988 }, + { title: 'Paths of Glory', year: 1957 }, + { title: 'Django Unchained', year: 2012 }, + { title: 'The Shining', year: 1980 }, + { title: 'WALL·E', year: 2008 }, + { title: 'American Beauty', year: 1999 }, + { title: 'The Dark Knight Rises', year: 2012 }, + { title: 'Princess Mononoke', year: 1997 }, + { title: 'Aliens', year: 1986 }, + { title: 'Oldboy', year: 2003 }, + { title: 'Once Upon a Time in America', year: 1984 }, + { title: 'Witness for the Prosecution', year: 1957 }, + { title: 'Das Boot', year: 1981 }, + { title: 'Citizen Kane', year: 1941 }, + { title: 'North by Northwest', year: 1959 }, + { title: 'Vertigo', year: 1958 }, + { title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 }, + { title: 'Reservoir Dogs', year: 1992 }, + { title: 'Braveheart', year: 1995 }, + { title: 'M', year: 1931 }, + { title: 'Requiem for a Dream', year: 2000 }, + { title: 'Amélie', year: 2001 }, + { title: 'A Clockwork Orange', year: 1971 }, + { title: 'Like Stars on Earth', year: 2007 }, + { title: 'Taxi Driver', year: 1976 }, + { title: 'Lawrence of Arabia', year: 1962 }, + { title: 'Double Indemnity', year: 1944 }, + { title: 'Eternal Sunshine of the Spotless Mind', year: 2004 }, + { title: 'Amadeus', year: 1984 }, + { title: 'To Kill a Mockingbird', year: 1962 }, + { title: 'Toy Story 3', year: 2010 }, + { title: 'Logan', year: 2017 }, + { title: 'Full Metal Jacket', year: 1987 }, + { title: 'Dangal', year: 2016 }, + { title: 'The Sting', year: 1973 }, + { title: '2001: A Space Odyssey', year: 1968 }, + { title: "Singin' in the Rain", year: 1952 }, + { title: 'Toy Story', year: 1995 }, + { title: 'Bicycle Thieves', year: 1948 }, + { title: 'The Kid', year: 1921 }, + { title: 'Inglourious Basterds', year: 2009 }, + { title: 'Snatch', year: 2000 }, + { title: '3 Idiots', year: 2009 }, + { title: 'Monty Python and the Holy Grail', year: 1975 }, +]; + +export default { + title: "Material-ui|autocomplete|Playground.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/Sizes.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/Sizes.stories.tsx new file mode 100644 index 00000000000000..83e51401bffc87 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/Sizes.stories.tsx @@ -0,0 +1,217 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import Chip from '@material-ui/core/Chip'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import TextField from '@material-ui/core/TextField'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: 500, + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function Sizes() { + const classes = useStyles(); + + return ( +
    + option.title} + defaultValue={top100Films[13]} + renderInput={(params) => ( + + )} + /> + option.title} + defaultValue={[top100Films[13]]} + renderInput={(params) => ( + + )} + /> + option.title} + defaultValue={top100Films[13]} + renderInput={(params) => ( + + )} + /> + option.title} + defaultValue={[top100Films[13]]} + renderInput={(params) => ( + + )} + /> + option.title} + defaultValue={top100Films[13]} + renderTags={(value, getTagProps) => + value.map((option, index) => ( + + )) + } + renderInput={(params) => ( + + )} + /> + option.title} + defaultValue={[top100Films[13]]} + renderTags={(value, getTagProps) => + value.map((option, index) => ( + + )) + } + renderInput={(params) => ( + + )} + /> +
    + ); +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { title: 'The Shawshank Redemption', year: 1994 }, + { title: 'The Godfather', year: 1972 }, + { title: 'The Godfather: Part II', year: 1974 }, + { title: 'The Dark Knight', year: 2008 }, + { title: '12 Angry Men', year: 1957 }, + { title: "Schindler's List", year: 1993 }, + { title: 'Pulp Fiction', year: 1994 }, + { title: 'The Lord of the Rings: The Return of the King', year: 2003 }, + { title: 'The Good, the Bad and the Ugly', year: 1966 }, + { title: 'Fight Club', year: 1999 }, + { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 }, + { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 }, + { title: 'Forrest Gump', year: 1994 }, + { title: 'Inception', year: 2010 }, + { title: 'The Lord of the Rings: The Two Towers', year: 2002 }, + { title: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { title: 'Goodfellas', year: 1990 }, + { title: 'The Matrix', year: 1999 }, + { title: 'Seven Samurai', year: 1954 }, + { title: 'Star Wars: Episode IV - A New Hope', year: 1977 }, + { title: 'City of God', year: 2002 }, + { title: 'Se7en', year: 1995 }, + { title: 'The Silence of the Lambs', year: 1991 }, + { title: "It's a Wonderful Life", year: 1946 }, + { title: 'Life Is Beautiful', year: 1997 }, + { title: 'The Usual Suspects', year: 1995 }, + { title: 'Léon: The Professional', year: 1994 }, + { title: 'Spirited Away', year: 2001 }, + { title: 'Saving Private Ryan', year: 1998 }, + { title: 'Once Upon a Time in the West', year: 1968 }, + { title: 'American History X', year: 1998 }, + { title: 'Interstellar', year: 2014 }, + { title: 'Casablanca', year: 1942 }, + { title: 'City Lights', year: 1931 }, + { title: 'Psycho', year: 1960 }, + { title: 'The Green Mile', year: 1999 }, + { title: 'The Intouchables', year: 2011 }, + { title: 'Modern Times', year: 1936 }, + { title: 'Raiders of the Lost Ark', year: 1981 }, + { title: 'Rear Window', year: 1954 }, + { title: 'The Pianist', year: 2002 }, + { title: 'The Departed', year: 2006 }, + { title: 'Terminator 2: Judgment Day', year: 1991 }, + { title: 'Back to the Future', year: 1985 }, + { title: 'Whiplash', year: 2014 }, + { title: 'Gladiator', year: 2000 }, + { title: 'Memento', year: 2000 }, + { title: 'The Prestige', year: 2006 }, + { title: 'The Lion King', year: 1994 }, + { title: 'Apocalypse Now', year: 1979 }, + { title: 'Alien', year: 1979 }, + { title: 'Sunset Boulevard', year: 1950 }, + { title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', year: 1964 }, + { title: 'The Great Dictator', year: 1940 }, + { title: 'Cinema Paradiso', year: 1988 }, + { title: 'The Lives of Others', year: 2006 }, + { title: 'Grave of the Fireflies', year: 1988 }, + { title: 'Paths of Glory', year: 1957 }, + { title: 'Django Unchained', year: 2012 }, + { title: 'The Shining', year: 1980 }, + { title: 'WALL·E', year: 2008 }, + { title: 'American Beauty', year: 1999 }, + { title: 'The Dark Knight Rises', year: 2012 }, + { title: 'Princess Mononoke', year: 1997 }, + { title: 'Aliens', year: 1986 }, + { title: 'Oldboy', year: 2003 }, + { title: 'Once Upon a Time in America', year: 1984 }, + { title: 'Witness for the Prosecution', year: 1957 }, + { title: 'Das Boot', year: 1981 }, + { title: 'Citizen Kane', year: 1941 }, + { title: 'North by Northwest', year: 1959 }, + { title: 'Vertigo', year: 1958 }, + { title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 }, + { title: 'Reservoir Dogs', year: 1992 }, + { title: 'Braveheart', year: 1995 }, + { title: 'M', year: 1931 }, + { title: 'Requiem for a Dream', year: 2000 }, + { title: 'Amélie', year: 2001 }, + { title: 'A Clockwork Orange', year: 1971 }, + { title: 'Like Stars on Earth', year: 2007 }, + { title: 'Taxi Driver', year: 1976 }, + { title: 'Lawrence of Arabia', year: 1962 }, + { title: 'Double Indemnity', year: 1944 }, + { title: 'Eternal Sunshine of the Spotless Mind', year: 2004 }, + { title: 'Amadeus', year: 1984 }, + { title: 'To Kill a Mockingbird', year: 1962 }, + { title: 'Toy Story 3', year: 2010 }, + { title: 'Logan', year: 2017 }, + { title: 'Full Metal Jacket', year: 1987 }, + { title: 'Dangal', year: 2016 }, + { title: 'The Sting', year: 1973 }, + { title: '2001: A Space Odyssey', year: 1968 }, + { title: "Singin' in the Rain", year: 1952 }, + { title: 'Toy Story', year: 1995 }, + { title: 'Bicycle Thieves', year: 1948 }, + { title: 'The Kid', year: 1921 }, + { title: 'Inglourious Basterds', year: 2009 }, + { title: 'Snatch', year: 2000 }, + { title: '3 Idiots', year: 2009 }, + { title: 'Monty Python and the Holy Grail', year: 1975 }, +]; + +export default { + title: "Material-ui|autocomplete|Sizes.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/Tags.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/Tags.stories.tsx new file mode 100644 index 00000000000000..8b50466de3abbf --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/Tags.stories.tsx @@ -0,0 +1,180 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import Chip from '@material-ui/core/Chip'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import TextField from '@material-ui/core/TextField'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: 500, + '& > * + *': { + marginTop: theme.spacing(3), + }, + }, + }), +); + +export function Tags() { + const classes = useStyles(); + + return ( +
    + option.title} + defaultValue={[top100Films[13]]} + renderInput={(params) => ( + + )} + /> + option.title} + defaultValue={[top100Films[13]]} + filterSelectedOptions + renderInput={(params) => ( + + )} + /> + option.title)} + defaultValue={[top100Films[13].title]} + freeSolo + renderTags={(value: string[], getTagProps) => + value.map((option: string, index: number) => ( + + )) + } + renderInput={(params) => ( + + )} + /> +
    + ); +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { title: 'The Shawshank Redemption', year: 1994 }, + { title: 'The Godfather', year: 1972 }, + { title: 'The Godfather: Part II', year: 1974 }, + { title: 'The Dark Knight', year: 2008 }, + { title: '12 Angry Men', year: 1957 }, + { title: "Schindler's List", year: 1993 }, + { title: 'Pulp Fiction', year: 1994 }, + { title: 'The Lord of the Rings: The Return of the King', year: 2003 }, + { title: 'The Good, the Bad and the Ugly', year: 1966 }, + { title: 'Fight Club', year: 1999 }, + { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 }, + { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 }, + { title: 'Forrest Gump', year: 1994 }, + { title: 'Inception', year: 2010 }, + { title: 'The Lord of the Rings: The Two Towers', year: 2002 }, + { title: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { title: 'Goodfellas', year: 1990 }, + { title: 'The Matrix', year: 1999 }, + { title: 'Seven Samurai', year: 1954 }, + { title: 'Star Wars: Episode IV - A New Hope', year: 1977 }, + { title: 'City of God', year: 2002 }, + { title: 'Se7en', year: 1995 }, + { title: 'The Silence of the Lambs', year: 1991 }, + { title: "It's a Wonderful Life", year: 1946 }, + { title: 'Life Is Beautiful', year: 1997 }, + { title: 'The Usual Suspects', year: 1995 }, + { title: 'Léon: The Professional', year: 1994 }, + { title: 'Spirited Away', year: 2001 }, + { title: 'Saving Private Ryan', year: 1998 }, + { title: 'Once Upon a Time in the West', year: 1968 }, + { title: 'American History X', year: 1998 }, + { title: 'Interstellar', year: 2014 }, + { title: 'Casablanca', year: 1942 }, + { title: 'City Lights', year: 1931 }, + { title: 'Psycho', year: 1960 }, + { title: 'The Green Mile', year: 1999 }, + { title: 'The Intouchables', year: 2011 }, + { title: 'Modern Times', year: 1936 }, + { title: 'Raiders of the Lost Ark', year: 1981 }, + { title: 'Rear Window', year: 1954 }, + { title: 'The Pianist', year: 2002 }, + { title: 'The Departed', year: 2006 }, + { title: 'Terminator 2: Judgment Day', year: 1991 }, + { title: 'Back to the Future', year: 1985 }, + { title: 'Whiplash', year: 2014 }, + { title: 'Gladiator', year: 2000 }, + { title: 'Memento', year: 2000 }, + { title: 'The Prestige', year: 2006 }, + { title: 'The Lion King', year: 1994 }, + { title: 'Apocalypse Now', year: 1979 }, + { title: 'Alien', year: 1979 }, + { title: 'Sunset Boulevard', year: 1950 }, + { title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', year: 1964 }, + { title: 'The Great Dictator', year: 1940 }, + { title: 'Cinema Paradiso', year: 1988 }, + { title: 'The Lives of Others', year: 2006 }, + { title: 'Grave of the Fireflies', year: 1988 }, + { title: 'Paths of Glory', year: 1957 }, + { title: 'Django Unchained', year: 2012 }, + { title: 'The Shining', year: 1980 }, + { title: 'WALL·E', year: 2008 }, + { title: 'American Beauty', year: 1999 }, + { title: 'The Dark Knight Rises', year: 2012 }, + { title: 'Princess Mononoke', year: 1997 }, + { title: 'Aliens', year: 1986 }, + { title: 'Oldboy', year: 2003 }, + { title: 'Once Upon a Time in America', year: 1984 }, + { title: 'Witness for the Prosecution', year: 1957 }, + { title: 'Das Boot', year: 1981 }, + { title: 'Citizen Kane', year: 1941 }, + { title: 'North by Northwest', year: 1959 }, + { title: 'Vertigo', year: 1958 }, + { title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 }, + { title: 'Reservoir Dogs', year: 1992 }, + { title: 'Braveheart', year: 1995 }, + { title: 'M', year: 1931 }, + { title: 'Requiem for a Dream', year: 2000 }, + { title: 'Amélie', year: 2001 }, + { title: 'A Clockwork Orange', year: 1971 }, + { title: 'Like Stars on Earth', year: 2007 }, + { title: 'Taxi Driver', year: 1976 }, + { title: 'Lawrence of Arabia', year: 1962 }, + { title: 'Double Indemnity', year: 1944 }, + { title: 'Eternal Sunshine of the Spotless Mind', year: 2004 }, + { title: 'Amadeus', year: 1984 }, + { title: 'To Kill a Mockingbird', year: 1962 }, + { title: 'Toy Story 3', year: 2010 }, + { title: 'Logan', year: 2017 }, + { title: 'Full Metal Jacket', year: 1987 }, + { title: 'Dangal', year: 2016 }, + { title: 'The Sting', year: 1973 }, + { title: '2001: A Space Odyssey', year: 1968 }, + { title: "Singin' in the Rain", year: 1952 }, + { title: 'Toy Story', year: 1995 }, + { title: 'Bicycle Thieves', year: 1948 }, + { title: 'The Kid', year: 1921 }, + { title: 'Inglourious Basterds', year: 2009 }, + { title: 'Snatch', year: 2000 }, + { title: '3 Idiots', year: 2009 }, + { title: 'Monty Python and the Holy Grail', year: 1975 }, +]; + +export default { + title: "Material-ui|autocomplete|Tags.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/UseAutocomplete.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/UseAutocomplete.stories.tsx new file mode 100644 index 00000000000000..3a483ee6850c80 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/UseAutocomplete.stories.tsx @@ -0,0 +1,178 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import useAutocomplete from '@material-ui/lab/useAutocomplete'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + label: { + display: 'block', + }, + input: { + width: 200, + }, + listbox: { + width: 200, + margin: 0, + padding: 0, + zIndex: 1, + position: 'absolute', + listStyle: 'none', + backgroundColor: theme.palette.background.paper, + overflow: 'auto', + maxHeight: 200, + border: '1px solid rgba(0,0,0,.25)', + '& li[data-focus="true"]': { + backgroundColor: '#4a8df6', + color: 'white', + cursor: 'pointer', + }, + '& li:active': { + backgroundColor: '#2977f5', + color: 'white', + }, + }, + }), +); + +export function UseAutocomplete() { + const classes = useStyles(); + const { + getRootProps, + getInputLabelProps, + getInputProps, + getListboxProps, + getOptionProps, + groupedOptions, + } = useAutocomplete({ + id: 'use-autocomplete-demo', + options: top100Films, + getOptionLabel: (option) => option.title, + }); + + return ( +
    +
    + + +
    + {groupedOptions.length > 0 ? ( +
      + {groupedOptions.map((option, index) => ( +
    • {option.title}
    • + ))} +
    + ) : null} +
    + ); +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { title: 'The Shawshank Redemption', year: 1994 }, + { title: 'The Godfather', year: 1972 }, + { title: 'The Godfather: Part II', year: 1974 }, + { title: 'The Dark Knight', year: 2008 }, + { title: '12 Angry Men', year: 1957 }, + { title: "Schindler's List", year: 1993 }, + { title: 'Pulp Fiction', year: 1994 }, + { title: 'The Lord of the Rings: The Return of the King', year: 2003 }, + { title: 'The Good, the Bad and the Ugly', year: 1966 }, + { title: 'Fight Club', year: 1999 }, + { title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 }, + { title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 }, + { title: 'Forrest Gump', year: 1994 }, + { title: 'Inception', year: 2010 }, + { title: 'The Lord of the Rings: The Two Towers', year: 2002 }, + { title: "One Flew Over the Cuckoo's Nest", year: 1975 }, + { title: 'Goodfellas', year: 1990 }, + { title: 'The Matrix', year: 1999 }, + { title: 'Seven Samurai', year: 1954 }, + { title: 'Star Wars: Episode IV - A New Hope', year: 1977 }, + { title: 'City of God', year: 2002 }, + { title: 'Se7en', year: 1995 }, + { title: 'The Silence of the Lambs', year: 1991 }, + { title: "It's a Wonderful Life", year: 1946 }, + { title: 'Life Is Beautiful', year: 1997 }, + { title: 'The Usual Suspects', year: 1995 }, + { title: 'Léon: The Professional', year: 1994 }, + { title: 'Spirited Away', year: 2001 }, + { title: 'Saving Private Ryan', year: 1998 }, + { title: 'Once Upon a Time in the West', year: 1968 }, + { title: 'American History X', year: 1998 }, + { title: 'Interstellar', year: 2014 }, + { title: 'Casablanca', year: 1942 }, + { title: 'City Lights', year: 1931 }, + { title: 'Psycho', year: 1960 }, + { title: 'The Green Mile', year: 1999 }, + { title: 'The Intouchables', year: 2011 }, + { title: 'Modern Times', year: 1936 }, + { title: 'Raiders of the Lost Ark', year: 1981 }, + { title: 'Rear Window', year: 1954 }, + { title: 'The Pianist', year: 2002 }, + { title: 'The Departed', year: 2006 }, + { title: 'Terminator 2: Judgment Day', year: 1991 }, + { title: 'Back to the Future', year: 1985 }, + { title: 'Whiplash', year: 2014 }, + { title: 'Gladiator', year: 2000 }, + { title: 'Memento', year: 2000 }, + { title: 'The Prestige', year: 2006 }, + { title: 'The Lion King', year: 1994 }, + { title: 'Apocalypse Now', year: 1979 }, + { title: 'Alien', year: 1979 }, + { title: 'Sunset Boulevard', year: 1950 }, + { title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', year: 1964 }, + { title: 'The Great Dictator', year: 1940 }, + { title: 'Cinema Paradiso', year: 1988 }, + { title: 'The Lives of Others', year: 2006 }, + { title: 'Grave of the Fireflies', year: 1988 }, + { title: 'Paths of Glory', year: 1957 }, + { title: 'Django Unchained', year: 2012 }, + { title: 'The Shining', year: 1980 }, + { title: 'WALL·E', year: 2008 }, + { title: 'American Beauty', year: 1999 }, + { title: 'The Dark Knight Rises', year: 2012 }, + { title: 'Princess Mononoke', year: 1997 }, + { title: 'Aliens', year: 1986 }, + { title: 'Oldboy', year: 2003 }, + { title: 'Once Upon a Time in America', year: 1984 }, + { title: 'Witness for the Prosecution', year: 1957 }, + { title: 'Das Boot', year: 1981 }, + { title: 'Citizen Kane', year: 1941 }, + { title: 'North by Northwest', year: 1959 }, + { title: 'Vertigo', year: 1958 }, + { title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 }, + { title: 'Reservoir Dogs', year: 1992 }, + { title: 'Braveheart', year: 1995 }, + { title: 'M', year: 1931 }, + { title: 'Requiem for a Dream', year: 2000 }, + { title: 'Amélie', year: 2001 }, + { title: 'A Clockwork Orange', year: 1971 }, + { title: 'Like Stars on Earth', year: 2007 }, + { title: 'Taxi Driver', year: 1976 }, + { title: 'Lawrence of Arabia', year: 1962 }, + { title: 'Double Indemnity', year: 1944 }, + { title: 'Eternal Sunshine of the Spotless Mind', year: 2004 }, + { title: 'Amadeus', year: 1984 }, + { title: 'To Kill a Mockingbird', year: 1962 }, + { title: 'Toy Story 3', year: 2010 }, + { title: 'Logan', year: 2017 }, + { title: 'Full Metal Jacket', year: 1987 }, + { title: 'Dangal', year: 2016 }, + { title: 'The Sting', year: 1973 }, + { title: '2001: A Space Odyssey', year: 1968 }, + { title: "Singin' in the Rain", year: 1952 }, + { title: 'Toy Story', year: 1995 }, + { title: 'Bicycle Thieves', year: 1948 }, + { title: 'The Kid', year: 1921 }, + { title: 'Inglourious Basterds', year: 2009 }, + { title: 'Snatch', year: 2000 }, + { title: '3 Idiots', year: 2009 }, + { title: 'Monty Python and the Holy Grail', year: 1975 }, +]; + +export default { + title: "Material-ui|autocomplete|UseAutocomplete.stories" +}; diff --git a/examples/storybook/stories/material-ui/autocomplete/Virtualize.stories.tsx b/examples/storybook/stories/material-ui/autocomplete/Virtualize.stories.tsx new file mode 100644 index 00000000000000..8da972f1af6335 --- /dev/null +++ b/examples/storybook/stories/material-ui/autocomplete/Virtualize.stories.tsx @@ -0,0 +1,139 @@ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete, { AutocompleteRenderGroupParams } from '@material-ui/lab/Autocomplete'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; +import ListSubheader from '@material-ui/core/ListSubheader'; +import { useTheme, makeStyles } from '@material-ui/core/styles'; +import { VariableSizeList, ListChildComponentProps } from 'react-window'; +import { Typography } from '@material-ui/core'; + +const LISTBOX_PADDING = 8; // px + +function renderRow(props: ListChildComponentProps) { + const { data, index, style } = props; + return React.cloneElement(data[index], { + style: { + ...style, + top: (style.top as number) + LISTBOX_PADDING, + }, + }); +} + +const OuterElementContext = React.createContext({}); + +const OuterElementType = React.forwardRef((props, ref) => { + const outerProps = React.useContext(OuterElementContext); + return
    ; +}); + +function useResetCache(data: any) { + const ref = React.useRef(null); + React.useEffect(() => { + if (ref.current != null) { + ref.current.resetAfterIndex(0, true); + } + }, [data]); + return ref; +} + +// Adapter for react-window +const ListboxComponent = React.forwardRef(function ListboxComponent(props, ref) { + const { children, ...other } = props; + const itemData = React.Children.toArray(children); + const theme = useTheme(); + const smUp = useMediaQuery(theme.breakpoints.up('sm'), { noSsr: true }); + const itemCount = itemData.length; + const itemSize = smUp ? 36 : 48; + + const getChildSize = (child: React.ReactNode) => { + if (React.isValidElement(child) && child.type === ListSubheader) { + return 48; + } + + return itemSize; + }; + + const getHeight = () => { + if (itemCount > 8) { + return 8 * itemSize; + } + return itemData.map(getChildSize).reduce((a, b) => a + b, 0); + }; + + const gridRef = useResetCache(itemCount); + + return ( +
    + + getChildSize(itemData[index])} + overscanCount={5} + itemCount={itemCount} + > + {renderRow} + + +
    + ); +}); + +function random(length: number) { + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let result = ''; + + for (let i = 0; i < length; i += 1) { + result += characters.charAt(Math.floor(Math.random() * characters.length)); + } + + return result; +} + +const useStyles = makeStyles({ + listbox: { + boxSizing: 'border-box', + '& ul': { + padding: 0, + margin: 0, + }, + }, +}); + +const OPTIONS = Array.from(new Array(10000)) + .map(() => random(10 + Math.ceil(Math.random() * 20))) + .sort((a: string, b: string) => a.toUpperCase().localeCompare(b.toUpperCase())); + +const renderGroup = (params: AutocompleteRenderGroupParams) => [ + + {params.group} + , + params.children, +]; + +export function Virtualize() { + const classes = useStyles(); + + return ( + >} + renderGroup={renderGroup} + options={OPTIONS} + groupBy={(option) => option[0].toUpperCase()} + renderInput={(params) => } + renderOption={(option) => {option}} + /> + ); +} + +export default { + title: "Material-ui|autocomplete|Virtualize.stories" +}; diff --git a/examples/storybook/stories/material-ui/avatars/BadgeAvatars.stories.tsx b/examples/storybook/stories/material-ui/avatars/BadgeAvatars.stories.tsx new file mode 100644 index 00000000000000..e7c13d5fd22d08 --- /dev/null +++ b/examples/storybook/stories/material-ui/avatars/BadgeAvatars.stories.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import Badge from '@material-ui/core/Badge'; +import Avatar from '@material-ui/core/Avatar'; +import { Theme, makeStyles, withStyles, createStyles } from '@material-ui/core/styles'; + +const StyledBadge = withStyles((theme: Theme) => + createStyles({ + badge: { + backgroundColor: '#44b700', + color: '#44b700', + boxShadow: `0 0 0 2px ${theme.palette.background.paper}`, + '&::after': { + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + borderRadius: '50%', + animation: '$ripple 1.2s infinite ease-in-out', + border: '1px solid currentColor', + content: '""', + }, + }, + '@keyframes ripple': { + '0%': { + transform: 'scale(.8)', + opacity: 1, + }, + '100%': { + transform: 'scale(2.4)', + opacity: 0, + }, + }, + }), +)(Badge); + +const SmallAvatar = withStyles((theme: Theme) => + createStyles({ + root: { + width: 22, + height: 22, + border: `2px solid ${theme.palette.background.paper}`, + }, + }), +)(Avatar); + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +export function BadgeAvatars() { + const classes = useStyles(); + + return ( +
    + + + + } + > + + +
    + ); +} + +export default { + title: "Material-ui|avatars|BadgeAvatars.stories" +}; diff --git a/examples/storybook/stories/material-ui/avatars/FallbackAvatars.stories.tsx b/examples/storybook/stories/material-ui/avatars/FallbackAvatars.stories.tsx new file mode 100644 index 00000000000000..80699f72c11704 --- /dev/null +++ b/examples/storybook/stories/material-ui/avatars/FallbackAvatars.stories.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Avatar from '@material-ui/core/Avatar'; +import { deepOrange } from '@material-ui/core/colors'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + '& > *': { + margin: theme.spacing(1), + }, + }, + orange: { + color: theme.palette.getContrastText(deepOrange[500]), + backgroundColor: deepOrange[500], + }, + }), +); + +export function FallbackAvatars() { + const classes = useStyles(); + + return ( +
    + + B + + + +
    + ); +} + +export default { + title: "Material-ui|avatars|FallbackAvatars.stories" +}; diff --git a/examples/storybook/stories/material-ui/avatars/GroupAvatars.stories.tsx b/examples/storybook/stories/material-ui/avatars/GroupAvatars.stories.tsx new file mode 100644 index 00000000000000..df1085aa03f7c5 --- /dev/null +++ b/examples/storybook/stories/material-ui/avatars/GroupAvatars.stories.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import Avatar from '@material-ui/core/Avatar'; +import AvatarGroup from '@material-ui/lab/AvatarGroup'; + +export function GroupAvatars() { + return ( + + + + + + + + ); +} + +export default { + title: "Material-ui|avatars|GroupAvatars.stories" +}; diff --git a/examples/storybook/stories/material-ui/avatars/IconAvatars.stories.tsx b/examples/storybook/stories/material-ui/avatars/IconAvatars.stories.tsx new file mode 100644 index 00000000000000..d57aff8e0459f9 --- /dev/null +++ b/examples/storybook/stories/material-ui/avatars/IconAvatars.stories.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import { green, pink } from '@material-ui/core/colors'; +import Avatar from '@material-ui/core/Avatar'; +import FolderIcon from '@material-ui/icons/Folder'; +import PageviewIcon from '@material-ui/icons/Pageview'; +import AssignmentIcon from '@material-ui/icons/Assignment'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + '& > *': { + margin: theme.spacing(1), + }, + }, + pink: { + color: theme.palette.getContrastText(pink[500]), + backgroundColor: pink[500], + }, + green: { + color: '#fff', + backgroundColor: green[500], + }, + }), +); + +export function IconAvatars() { + const classes = useStyles(); + + return ( +
    + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|avatars|IconAvatars.stories" +}; diff --git a/examples/storybook/stories/material-ui/avatars/ImageAvatars.stories.tsx b/examples/storybook/stories/material-ui/avatars/ImageAvatars.stories.tsx new file mode 100644 index 00000000000000..9184f39cf5d96a --- /dev/null +++ b/examples/storybook/stories/material-ui/avatars/ImageAvatars.stories.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Avatar from '@material-ui/core/Avatar'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +export function ImageAvatars() { + const classes = useStyles(); + + return ( +
    + + + +
    + ); +} + +export default { + title: "Material-ui|avatars|ImageAvatars.stories" +}; diff --git a/examples/storybook/stories/material-ui/avatars/LetterAvatars.stories.tsx b/examples/storybook/stories/material-ui/avatars/LetterAvatars.stories.tsx new file mode 100644 index 00000000000000..7fb3b9ffc24848 --- /dev/null +++ b/examples/storybook/stories/material-ui/avatars/LetterAvatars.stories.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Avatar from '@material-ui/core/Avatar'; +import { deepOrange, deepPurple } from '@material-ui/core/colors'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + '& > *': { + margin: theme.spacing(1), + }, + }, + orange: { + color: theme.palette.getContrastText(deepOrange[500]), + backgroundColor: deepOrange[500], + }, + purple: { + color: theme.palette.getContrastText(deepPurple[500]), + backgroundColor: deepPurple[500], + }, + }), +); + +export function LetterAvatars() { + const classes = useStyles(); + + return ( +
    + H + N + OP +
    + ); +} + +export default { + title: "Material-ui|avatars|LetterAvatars.stories" +}; diff --git a/examples/storybook/stories/material-ui/avatars/SizeAvatars.stories.tsx b/examples/storybook/stories/material-ui/avatars/SizeAvatars.stories.tsx new file mode 100644 index 00000000000000..e933fbe6037f96 --- /dev/null +++ b/examples/storybook/stories/material-ui/avatars/SizeAvatars.stories.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Avatar from '@material-ui/core/Avatar'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + '& > *': { + margin: theme.spacing(1), + }, + }, + small: { + width: theme.spacing(3), + height: theme.spacing(3), + }, + large: { + width: theme.spacing(7), + height: theme.spacing(7), + }, + }), +); + +export function ImageAvatars() { + const classes = useStyles(); + + return ( +
    + + + +
    + ); +} + +export default { + title: "Material-ui|avatars|SizeAvatars.stories" +}; diff --git a/examples/storybook/stories/material-ui/avatars/VariantAvatars.stories.tsx b/examples/storybook/stories/material-ui/avatars/VariantAvatars.stories.tsx new file mode 100644 index 00000000000000..f3d9c4cfac3d16 --- /dev/null +++ b/examples/storybook/stories/material-ui/avatars/VariantAvatars.stories.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Avatar from '@material-ui/core/Avatar'; +import { deepOrange, green } from '@material-ui/core/colors'; +import AssignmentIcon from '@material-ui/icons/Assignment'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + '& > *': { + margin: theme.spacing(1), + }, + }, + square: { + color: theme.palette.getContrastText(deepOrange[500]), + backgroundColor: deepOrange[500], + }, + rounded: { + color: '#fff', + backgroundColor: green[500], + }, + }), +); + +export function VariantAvatars() { + const classes = useStyles(); + + return ( +
    + + N + + + + +
    + ); +} + +export default { + title: "Material-ui|avatars|VariantAvatars.stories" +}; diff --git a/examples/storybook/stories/material-ui/backdrop/SimpleBackdrop.stories.tsx b/examples/storybook/stories/material-ui/backdrop/SimpleBackdrop.stories.tsx new file mode 100644 index 00000000000000..849e55655fc056 --- /dev/null +++ b/examples/storybook/stories/material-ui/backdrop/SimpleBackdrop.stories.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import Backdrop from '@material-ui/core/Backdrop'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import Button from '@material-ui/core/Button'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + backdrop: { + zIndex: theme.zIndex.drawer + 1, + color: '#fff', + }, + }), +); + +export function SimpleBackdrop() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + const handleClose = () => { + setOpen(false); + }; + const handleToggle = () => { + setOpen(!open); + }; + + return ( +
    + + + + +
    + ); +} + +export default { + title: "Material-ui|backdrop|SimpleBackdrop.stories" +}; diff --git a/examples/storybook/stories/material-ui/badges/BadgeMax.stories.tsx b/examples/storybook/stories/material-ui/badges/BadgeMax.stories.tsx new file mode 100644 index 00000000000000..f5d13310789d49 --- /dev/null +++ b/examples/storybook/stories/material-ui/badges/BadgeMax.stories.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Badge from '@material-ui/core/Badge'; +import MailIcon from '@material-ui/icons/Mail'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(2), + }, + }, + }), +); + +const defaultProps = { + color: 'secondary' as 'secondary', + children: , +}; + +export function BadgeMax() { + const classes = useStyles(); + + return ( +
    + + + +
    + ); +} + +export default { + title: "Material-ui|badges|BadgeMax.stories" +}; diff --git a/examples/storybook/stories/material-ui/badges/BadgeOverlap.stories.tsx b/examples/storybook/stories/material-ui/badges/BadgeOverlap.stories.tsx new file mode 100644 index 00000000000000..f824f26e5ad124 --- /dev/null +++ b/examples/storybook/stories/material-ui/badges/BadgeOverlap.stories.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import clsx from 'clsx'; +import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; +import Badge from '@material-ui/core/Badge'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + }, + }, + shape: { + backgroundColor: theme.palette.primary.main, + width: 40, + height: 40, + }, + shapeCircle: { + borderRadius: '50%', + }, + }), +); + +export function BadgeOverlap() { + const classes = useStyles(); + + const rectangle =
    ; + const circle =
    ; + + return ( +
    + + {rectangle} + + + {rectangle} + + + {circle} + + + {circle} + +
    + ); +} + +export default { + title: "Material-ui|badges|BadgeOverlap.stories" +}; diff --git a/examples/storybook/stories/material-ui/badges/BadgeVisibility.stories.tsx b/examples/storybook/stories/material-ui/badges/BadgeVisibility.stories.tsx new file mode 100644 index 00000000000000..d53bf663b3189c --- /dev/null +++ b/examples/storybook/stories/material-ui/badges/BadgeVisibility.stories.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; +import Badge from '@material-ui/core/Badge'; +import ButtonGroup from '@material-ui/core/ButtonGroup'; +import Button from '@material-ui/core/Button'; +import AddIcon from '@material-ui/icons/Add'; +import RemoveIcon from '@material-ui/icons/Remove'; +import MailIcon from '@material-ui/icons/Mail'; +import Switch from '@material-ui/core/Switch'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + flexDirection: 'column', + '& > *': { + marginBottom: theme.spacing(2), + }, + '& .MuiBadge-root': { + marginRight: theme.spacing(4), + }, + }, + }), +); + +export function BadgeVisibility() { + const classes = useStyles(); + const [count, setCount] = React.useState(1); + const [invisible, setInvisible] = React.useState(false); + + const handleBadgeVisibility = () => { + setInvisible(!invisible); + }; + + return ( +
    +
    + + + + + + + +
    +
    + + + + } + label="Show Badge" + /> +
    +
    + ); +} + +export default { + title: "Material-ui|badges|BadgeVisibility.stories" +}; diff --git a/examples/storybook/stories/material-ui/badges/CustomizedBadges.stories.tsx b/examples/storybook/stories/material-ui/badges/CustomizedBadges.stories.tsx new file mode 100644 index 00000000000000..3da227af128a0e --- /dev/null +++ b/examples/storybook/stories/material-ui/badges/CustomizedBadges.stories.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import Badge from '@material-ui/core/Badge'; +import { Theme, withStyles, createStyles } from '@material-ui/core/styles'; +import IconButton from '@material-ui/core/IconButton'; +import ShoppingCartIcon from '@material-ui/icons/ShoppingCart'; + +const StyledBadge = withStyles((theme: Theme) => + createStyles({ + badge: { + right: -3, + top: 13, + border: `2px solid ${theme.palette.background.paper}`, + padding: '0 4px', + }, + }), +)(Badge); + +export function CustomizedBadges() { + return ( + + + + + + ); +} + +export default { + title: "Material-ui|badges|CustomizedBadges.stories" +}; diff --git a/examples/storybook/stories/material-ui/badges/DotBadge.stories.tsx b/examples/storybook/stories/material-ui/badges/DotBadge.stories.tsx new file mode 100644 index 00000000000000..e823f726a72c44 --- /dev/null +++ b/examples/storybook/stories/material-ui/badges/DotBadge.stories.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Badge from '@material-ui/core/Badge'; +import MailIcon from '@material-ui/icons/Mail'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +export function DotBadge() { + const classes = useStyles(); + + return ( +
    + + + + + Typography + +
    + ); +} + +export default { + title: "Material-ui|badges|DotBadge.stories" +}; diff --git a/examples/storybook/stories/material-ui/badges/ShowZeroBadge.stories.tsx b/examples/storybook/stories/material-ui/badges/ShowZeroBadge.stories.tsx new file mode 100644 index 00000000000000..736f1ddbd19b2c --- /dev/null +++ b/examples/storybook/stories/material-ui/badges/ShowZeroBadge.stories.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; +import Badge from '@material-ui/core/Badge'; +import MailIcon from '@material-ui/icons/Mail'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +export function ShowZeroBadge() { + const classes = useStyles(); + + return ( +
    + + + + + + +
    + ); +} + +export default { + title: "Material-ui|badges|ShowZeroBadge.stories" +}; diff --git a/examples/storybook/stories/material-ui/badges/SimpleBadge.stories.tsx b/examples/storybook/stories/material-ui/badges/SimpleBadge.stories.tsx new file mode 100644 index 00000000000000..22f254969b9aee --- /dev/null +++ b/examples/storybook/stories/material-ui/badges/SimpleBadge.stories.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Badge from '@material-ui/core/Badge'; +import MailIcon from '@material-ui/icons/Mail'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +export function SimpleBadge() { + const classes = useStyles(); + + return ( +
    + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|badges|SimpleBadge.stories" +}; diff --git a/examples/storybook/stories/material-ui/bottom-navigation/LabelBottomNavigation.stories.tsx b/examples/storybook/stories/material-ui/bottom-navigation/LabelBottomNavigation.stories.tsx new file mode 100644 index 00000000000000..8c9986881339b8 --- /dev/null +++ b/examples/storybook/stories/material-ui/bottom-navigation/LabelBottomNavigation.stories.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import BottomNavigation from '@material-ui/core/BottomNavigation'; +import BottomNavigationAction from '@material-ui/core/BottomNavigationAction'; +import FolderIcon from '@material-ui/icons/Folder'; +import RestoreIcon from '@material-ui/icons/Restore'; +import FavoriteIcon from '@material-ui/icons/Favorite'; +import LocationOnIcon from '@material-ui/icons/LocationOn'; + +const useStyles = makeStyles({ + root: { + width: 500, + }, +}); + +export function LabelBottomNavigation() { + const classes = useStyles(); + const [value, setValue] = React.useState('recents'); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: string) => { + setValue(newValue); + }; + + return ( + + } /> + } /> + } /> + } /> + + ); +} + +export default { + title: "Material-ui|bottom-navigation|LabelBottomNavigation.stories" +}; diff --git a/examples/storybook/stories/material-ui/bottom-navigation/SimpleBottomNavigation.stories.tsx b/examples/storybook/stories/material-ui/bottom-navigation/SimpleBottomNavigation.stories.tsx new file mode 100644 index 00000000000000..fef744947cce56 --- /dev/null +++ b/examples/storybook/stories/material-ui/bottom-navigation/SimpleBottomNavigation.stories.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import BottomNavigation from '@material-ui/core/BottomNavigation'; +import BottomNavigationAction from '@material-ui/core/BottomNavigationAction'; +import RestoreIcon from '@material-ui/icons/Restore'; +import FavoriteIcon from '@material-ui/icons/Favorite'; +import LocationOnIcon from '@material-ui/icons/LocationOn'; + +const useStyles = makeStyles({ + root: { + width: 500, + }, +}); + +export function SimpleBottomNavigation() { + const classes = useStyles(); + const [value, setValue] = React.useState(0); + + return ( + { + setValue(newValue); + }} + showLabels + className={classes.root} + > + } /> + } /> + } /> + + ); +} + +export default { + title: "Material-ui|bottom-navigation|SimpleBottomNavigation.stories" +}; diff --git a/examples/storybook/stories/material-ui/breadcrumbs/ActiveLastBreadcrumb.stories.tsx b/examples/storybook/stories/material-ui/breadcrumbs/ActiveLastBreadcrumb.stories.tsx new file mode 100644 index 00000000000000..9aaee6bda20e78 --- /dev/null +++ b/examples/storybook/stories/material-ui/breadcrumbs/ActiveLastBreadcrumb.stories.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import Breadcrumbs from '@material-ui/core/Breadcrumbs'; +import Link from '@material-ui/core/Link'; + +function handleClick(event: React.MouseEvent) { + event.preventDefault(); + console.info('You clicked a breadcrumb.'); +} + +export function ActiveLastBreadcrumb() { + return ( + + + Material-UI + + + Core + + + Breadcrumb + + + ); +} + +export default { + title: "Material-ui|breadcrumbs|ActiveLastBreadcrumb.stories" +}; diff --git a/examples/storybook/stories/material-ui/breadcrumbs/CollapsedBreadcrumbs.stories.tsx b/examples/storybook/stories/material-ui/breadcrumbs/CollapsedBreadcrumbs.stories.tsx new file mode 100644 index 00000000000000..7a5405c386aefb --- /dev/null +++ b/examples/storybook/stories/material-ui/breadcrumbs/CollapsedBreadcrumbs.stories.tsx @@ -0,0 +1,34 @@ +/* eslint-disable jsx-a11y/anchor-is-valid */ +import React from 'react'; +import Breadcrumbs from '@material-ui/core/Breadcrumbs'; +import Typography from '@material-ui/core/Typography'; +import Link from '@material-ui/core/Link'; + +function handleClick(event: React.MouseEvent) { + event.preventDefault(); + console.info('You clicked a breadcrumb.'); +} + +export function CollapsedBreadcrumbs() { + return ( + + + Home + + + Catalog + + + Accessories + + + New Collection + + Belts + + ); +} + +export default { + title: "Material-ui|breadcrumbs|CollapsedBreadcrumbs.stories" +}; diff --git a/examples/storybook/stories/material-ui/breadcrumbs/CustomSeparator.stories.tsx b/examples/storybook/stories/material-ui/breadcrumbs/CustomSeparator.stories.tsx new file mode 100644 index 00000000000000..f7de86aeb0fe03 --- /dev/null +++ b/examples/storybook/stories/material-ui/breadcrumbs/CustomSeparator.stories.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Breadcrumbs from '@material-ui/core/Breadcrumbs'; +import Typography from '@material-ui/core/Typography'; +import Link from '@material-ui/core/Link'; +import NavigateNextIcon from '@material-ui/icons/NavigateNext'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +function handleClick(event: React.MouseEvent) { + event.preventDefault(); + console.info('You clicked a breadcrumb.'); +} + +export function CustomSeparator() { + const classes = useStyles(); + + return ( +
    + + + Material-UI + + + Core + + Breadcrumb + + + + Material-UI + + + Core + + Breadcrumb + + } aria-label="breadcrumb"> + + Material-UI + + + Core + + Breadcrumb + +
    + ); +} + +export default { + title: "Material-ui|breadcrumbs|CustomSeparator.stories" +}; diff --git a/examples/storybook/stories/material-ui/breadcrumbs/CustomizedBreadcrumbs.stories.tsx b/examples/storybook/stories/material-ui/breadcrumbs/CustomizedBreadcrumbs.stories.tsx new file mode 100644 index 00000000000000..1dcc651249cac5 --- /dev/null +++ b/examples/storybook/stories/material-ui/breadcrumbs/CustomizedBreadcrumbs.stories.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { emphasize, withStyles, Theme } from '@material-ui/core/styles'; +import Breadcrumbs from '@material-ui/core/Breadcrumbs'; +import Chip from '@material-ui/core/Chip'; +import HomeIcon from '@material-ui/icons/Home'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; + +const StyledBreadcrumb = withStyles((theme: Theme) => ({ + root: { + backgroundColor: theme.palette.grey[100], + height: theme.spacing(3), + color: theme.palette.grey[800], + fontWeight: theme.typography.fontWeightRegular, + '&:hover, &:focus': { + backgroundColor: theme.palette.grey[300], + }, + '&:active': { + boxShadow: theme.shadows[1], + backgroundColor: emphasize(theme.palette.grey[300], 0.12), + }, + }, +}))(Chip) as typeof Chip; // TypeScript only: need a type cast here because https://github.com/Microsoft/TypeScript/issues/26591 + +function handleClick(event: React.MouseEvent) { + event.preventDefault(); + console.info('You clicked a breadcrumb.'); +} + +export function CustomizedBreadcrumbs() { + return ( + + } + onClick={handleClick} + /> + + } + onClick={handleClick} + onDelete={handleClick} + /> + + ); +} + +export default { + title: "Material-ui|breadcrumbs|CustomizedBreadcrumbs.stories" +}; diff --git a/examples/storybook/stories/material-ui/breadcrumbs/IconBreadcrumbs.stories.tsx b/examples/storybook/stories/material-ui/breadcrumbs/IconBreadcrumbs.stories.tsx new file mode 100644 index 00000000000000..5dc0b14591dd50 --- /dev/null +++ b/examples/storybook/stories/material-ui/breadcrumbs/IconBreadcrumbs.stories.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Breadcrumbs from '@material-ui/core/Breadcrumbs'; +import Link from '@material-ui/core/Link'; +import HomeIcon from '@material-ui/icons/Home'; +import WhatshotIcon from '@material-ui/icons/Whatshot'; +import GrainIcon from '@material-ui/icons/Grain'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + link: { + display: 'flex', + }, + icon: { + marginRight: theme.spacing(0.5), + width: 20, + height: 20, + }, + }), +); + +function handleClick(event: React.MouseEvent) { + event.preventDefault(); + console.info('You clicked a breadcrumb.'); +} + +export function IconBreadcrumbs() { + const classes = useStyles(); + + return ( + + + + Material-UI + + + + Core + + + + Breadcrumb + + + ); +} + +export default { + title: "Material-ui|breadcrumbs|IconBreadcrumbs.stories" +}; diff --git a/examples/storybook/stories/material-ui/breadcrumbs/RouterBreadcrumbs.stories.tsx b/examples/storybook/stories/material-ui/breadcrumbs/RouterBreadcrumbs.stories.tsx new file mode 100644 index 00000000000000..3ab5992e8c71af --- /dev/null +++ b/examples/storybook/stories/material-ui/breadcrumbs/RouterBreadcrumbs.stories.tsx @@ -0,0 +1,125 @@ +/* eslint-disable no-nested-ternary */ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import Link, { LinkProps } from '@material-ui/core/Link'; +import ListItem from '@material-ui/core/ListItem'; +import Collapse from '@material-ui/core/Collapse'; +import ListItemText from '@material-ui/core/ListItemText'; +import Typography from '@material-ui/core/Typography'; +import ExpandLess from '@material-ui/icons/ExpandLess'; +import ExpandMore from '@material-ui/icons/ExpandMore'; +import Breadcrumbs from '@material-ui/core/Breadcrumbs'; +import { Route, MemoryRouter } from 'react-router'; +import { Link as RouterLink } from 'react-router-dom'; +import { Omit } from '@material-ui/types'; + +interface ListItemLinkProps extends LinkProps { + to: string; + open?: boolean; +} + +const breadcrumbNameMap: { [key: string]: string } = { + '/inbox': 'Inbox', + '/inbox/important': 'Important', + '/trash': 'Trash', + '/spam': 'Spam', + '/drafts': 'Drafts', +}; + +function ListItemLink(props: Omit) { + const { to, open, ...other } = props; + const primary = breadcrumbNameMap[to]; + + return ( +
  • + + + {open != null ? open ? : : null} + +
  • + ); +} + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + flexDirection: 'column', + width: 360, + }, + lists: { + backgroundColor: theme.palette.background.paper, + marginTop: theme.spacing(1), + }, + nested: { + paddingLeft: theme.spacing(4), + }, + }), +); + +interface LinkRouterProps extends LinkProps { + to: string; + replace?: boolean; +} + +const LinkRouter = (props: LinkRouterProps) => ; + +export function RouterBreadcrumbs() { + const classes = useStyles(); + const [open, setOpen] = React.useState(true); + + const handleClick = () => { + setOpen((prevOpen) => !prevOpen); + }; + + return ( + +
    + + {({ location }) => { + const pathnames = location.pathname.split('/').filter((x) => x); + + return ( + + + Home + + {pathnames.map((value, index) => { + const last = index === pathnames.length - 1; + const to = `/${pathnames.slice(0, index + 1).join('/')}`; + + return last ? ( + + {breadcrumbNameMap[to]} + + ) : ( + + {breadcrumbNameMap[to]} + + ); + })} + + ); + }} + + +
    +
    + ); +} + +export default { + title: "Material-ui|breadcrumbs|RouterBreadcrumbs.stories" +}; diff --git a/examples/storybook/stories/material-ui/breadcrumbs/SimpleBreadcrumbs.stories.tsx b/examples/storybook/stories/material-ui/breadcrumbs/SimpleBreadcrumbs.stories.tsx new file mode 100644 index 00000000000000..ded95bbfe307ee --- /dev/null +++ b/examples/storybook/stories/material-ui/breadcrumbs/SimpleBreadcrumbs.stories.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import Typography from '@material-ui/core/Typography'; +import Breadcrumbs from '@material-ui/core/Breadcrumbs'; +import Link from '@material-ui/core/Link'; + +function handleClick(event: React.MouseEvent) { + event.preventDefault(); + console.info('You clicked a breadcrumb.'); +} + +export function SimpleBreadcrumbs() { + return ( + + + Material-UI + + + Core + + Breadcrumb + + ); +} + +export default { + title: "Material-ui|breadcrumbs|SimpleBreadcrumbs.stories" +}; diff --git a/examples/storybook/stories/material-ui/button-group/BasicButtonGroup.stories.tsx b/examples/storybook/stories/material-ui/button-group/BasicButtonGroup.stories.tsx new file mode 100644 index 00000000000000..fad02513e47880 --- /dev/null +++ b/examples/storybook/stories/material-ui/button-group/BasicButtonGroup.stories.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import ButtonGroup from '@material-ui/core/ButtonGroup'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +export function BasicButtonGroup() { + const classes = useStyles(); + + return ( +
    + + + + + + + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|button-group|BasicButtonGroup.stories" +}; diff --git a/examples/storybook/stories/material-ui/button-group/DisableElevation.stories.tsx b/examples/storybook/stories/material-ui/button-group/DisableElevation.stories.tsx new file mode 100644 index 00000000000000..7331c719bb8396 --- /dev/null +++ b/examples/storybook/stories/material-ui/button-group/DisableElevation.stories.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import ButtonGroup from '@material-ui/core/ButtonGroup'; +import Button from '@material-ui/core/Button'; + +export function DisableElevation() { + return ( + + + + + ); +} + +export default { + title: "Material-ui|button-group|DisableElevation.stories" +}; diff --git a/examples/storybook/stories/material-ui/button-group/GroupOrientation.stories.tsx b/examples/storybook/stories/material-ui/button-group/GroupOrientation.stories.tsx new file mode 100644 index 00000000000000..8d0dd0eb23cd93 --- /dev/null +++ b/examples/storybook/stories/material-ui/button-group/GroupOrientation.stories.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import ButtonGroup from '@material-ui/core/ButtonGroup'; +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme) => ({ + root: { + display: 'flex', + '& > *': { + margin: theme.spacing(1), + }, + }, +})); + +export function GroupOrientation() { + const classes = useStyles(); + + return ( +
    + + + + + + + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|button-group|GroupOrientation.stories" +}; diff --git a/examples/storybook/stories/material-ui/button-group/GroupSizesColors.stories.tsx b/examples/storybook/stories/material-ui/button-group/GroupSizesColors.stories.tsx new file mode 100644 index 00000000000000..3bb1e17545fead --- /dev/null +++ b/examples/storybook/stories/material-ui/button-group/GroupSizesColors.stories.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import ButtonGroup from '@material-ui/core/ButtonGroup'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +export function GroupSizesColors() { + const classes = useStyles(); + + return ( +
    + + + + + + + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|button-group|GroupSizesColors.stories" +}; diff --git a/examples/storybook/stories/material-ui/button-group/SplitButton.stories.tsx b/examples/storybook/stories/material-ui/button-group/SplitButton.stories.tsx new file mode 100644 index 00000000000000..6acdd65fe98ac3 --- /dev/null +++ b/examples/storybook/stories/material-ui/button-group/SplitButton.stories.tsx @@ -0,0 +1,95 @@ +import React from 'react'; +import Grid from '@material-ui/core/Grid'; +import Button from '@material-ui/core/Button'; +import ButtonGroup from '@material-ui/core/ButtonGroup'; +import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; +import ClickAwayListener from '@material-ui/core/ClickAwayListener'; +import Grow from '@material-ui/core/Grow'; +import Paper from '@material-ui/core/Paper'; +import Popper from '@material-ui/core/Popper'; +import MenuItem from '@material-ui/core/MenuItem'; +import MenuList from '@material-ui/core/MenuList'; + +const options = ['Create a merge commit', 'Squash and merge', 'Rebase and merge']; + +export function SplitButton() { + const [open, setOpen] = React.useState(false); + const anchorRef = React.useRef(null); + const [selectedIndex, setSelectedIndex] = React.useState(1); + + const handleClick = () => { + console.info(`You clicked ${options[selectedIndex]}`); + }; + + const handleMenuItemClick = ( + event: React.MouseEvent, + index: number, + ) => { + setSelectedIndex(index); + setOpen(false); + }; + + const handleToggle = () => { + setOpen((prevOpen) => !prevOpen); + }; + + const handleClose = (event: React.MouseEvent) => { + if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) { + return; + } + + setOpen(false); + }; + + return ( + + + + + + + + {({ TransitionProps, placement }) => ( + + + + + {options.map((option, index) => ( + handleMenuItemClick(event, index)} + > + {option} + + ))} + + + + + )} + + + + ); +} + +export default { + title: "Material-ui|button-group|SplitButton.stories" +}; diff --git a/examples/storybook/stories/material-ui/buttons/ButtonBase.stories.tsx b/examples/storybook/stories/material-ui/buttons/ButtonBase.stories.tsx new file mode 100644 index 00000000000000..75e37700d580dc --- /dev/null +++ b/examples/storybook/stories/material-ui/buttons/ButtonBase.stories.tsx @@ -0,0 +1,140 @@ +import React from 'react'; +import { Theme, makeStyles, createStyles } from '@material-ui/core/styles'; +import ButtonBase from '@material-ui/core/ButtonBase'; +import Typography from '@material-ui/core/Typography'; + +const images = [ + { + url: '/static/images/grid-list/breakfast.jpg', + title: 'Breakfast', + width: '40%', + }, + { + url: '/static/images/grid-list/burgers.jpg', + title: 'Burgers', + width: '30%', + }, + { + url: '/static/images/grid-list/camera.jpg', + title: 'Camera', + width: '30%', + }, +]; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + flexWrap: 'wrap', + minWidth: 300, + width: '100%', + }, + image: { + position: 'relative', + height: 200, + [theme.breakpoints.down('xs')]: { + width: '100% !important', // Overrides inline-style + height: 100, + }, + '&:hover, &$focusVisible': { + zIndex: 1, + '& $imageBackdrop': { + opacity: 0.15, + }, + '& $imageMarked': { + opacity: 0, + }, + '& $imageTitle': { + border: '4px solid currentColor', + }, + }, + }, + focusVisible: {}, + imageButton: { + position: 'absolute', + left: 0, + right: 0, + top: 0, + bottom: 0, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + color: theme.palette.common.white, + }, + imageSrc: { + position: 'absolute', + left: 0, + right: 0, + top: 0, + bottom: 0, + backgroundSize: 'cover', + backgroundPosition: 'center 40%', + }, + imageBackdrop: { + position: 'absolute', + left: 0, + right: 0, + top: 0, + bottom: 0, + backgroundColor: theme.palette.common.black, + opacity: 0.4, + transition: theme.transitions.create('opacity'), + }, + imageTitle: { + position: 'relative', + padding: `${theme.spacing(2)}px ${theme.spacing(4)}px ${theme.spacing(1) + 6}px`, + }, + imageMarked: { + height: 3, + width: 18, + backgroundColor: theme.palette.common.white, + position: 'absolute', + bottom: -2, + left: 'calc(50% - 9px)', + transition: theme.transitions.create('opacity'), + }, + }), +); + +export function ButtonBases() { + const classes = useStyles(); + + return ( +
    + {images.map((image) => ( + + + + + + {image.title} + + + + + ))} +
    + ); +} + +export default { + title: "Material-ui|buttons|ButtonBase.stories" +}; diff --git a/examples/storybook/stories/material-ui/buttons/ButtonSizes.stories.tsx b/examples/storybook/stories/material-ui/buttons/ButtonSizes.stories.tsx new file mode 100644 index 00000000000000..c10ec601adc5d3 --- /dev/null +++ b/examples/storybook/stories/material-ui/buttons/ButtonSizes.stories.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import IconButton from '@material-ui/core/IconButton'; +import DeleteIcon from '@material-ui/icons/Delete'; +import ArrowDownwardIcon from '@material-ui/icons/ArrowDownward'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + margin: { + margin: theme.spacing(1), + }, + extendedIcon: { + marginRight: theme.spacing(1), + }, + }), +); + +export function ButtonSizes() { + const classes = useStyles(); + + return ( +
    +
    + + + +
    +
    + + + +
    +
    + + + +
    +
    + + + + + + + + + + + + +
    +
    + ); +} + +export default { + title: "Material-ui|buttons|ButtonSizes.stories" +}; diff --git a/examples/storybook/stories/material-ui/buttons/ContainedButtons.stories.tsx b/examples/storybook/stories/material-ui/buttons/ContainedButtons.stories.tsx new file mode 100644 index 00000000000000..f06e0b935002b6 --- /dev/null +++ b/examples/storybook/stories/material-ui/buttons/ContainedButtons.stories.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +export function ContainedButtons() { + const classes = useStyles(); + + return ( +
    + + + + + +
    + ); +} + +export default { + title: "Material-ui|buttons|ContainedButtons.stories" +}; diff --git a/examples/storybook/stories/material-ui/buttons/CustomizedButtons.stories.tsx b/examples/storybook/stories/material-ui/buttons/CustomizedButtons.stories.tsx new file mode 100644 index 00000000000000..60e28019238cd2 --- /dev/null +++ b/examples/storybook/stories/material-ui/buttons/CustomizedButtons.stories.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import { + createMuiTheme, + createStyles, + withStyles, + makeStyles, + Theme, + ThemeProvider, +} from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import { green, purple } from '@material-ui/core/colors'; + +const BootstrapButton = withStyles({ + root: { + boxShadow: 'none', + textTransform: 'none', + fontSize: 16, + padding: '6px 12px', + border: '1px solid', + lineHeight: 1.5, + backgroundColor: '#0063cc', + borderColor: '#0063cc', + fontFamily: [ + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', + 'Roboto', + '"Helvetica Neue"', + 'Arial', + 'sans-serif', + '"Apple Color Emoji"', + '"Segoe UI Emoji"', + '"Segoe UI Symbol"', + ].join(','), + '&:hover': { + backgroundColor: '#0069d9', + borderColor: '#0062cc', + boxShadow: 'none', + }, + '&:active': { + boxShadow: 'none', + backgroundColor: '#0062cc', + borderColor: '#005cbf', + }, + '&:focus': { + boxShadow: '0 0 0 0.2rem rgba(0,123,255,.5)', + }, + }, +})(Button); + +const ColorButton = withStyles((theme: Theme) => ({ + root: { + color: theme.palette.getContrastText(purple[500]), + backgroundColor: purple[500], + '&:hover': { + backgroundColor: purple[700], + }, + }, +}))(Button); + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + margin: { + margin: theme.spacing(1), + }, + }), +); + +const theme = createMuiTheme({ + palette: { + primary: green, + }, +}); + +export function CustomizedButtons() { + const classes = useStyles(); + + return ( +
    + + Custom CSS + + + + + + Bootstrap + +
    + ); +} + +export default { + title: "Material-ui|buttons|CustomizedButtons.stories" +}; diff --git a/examples/storybook/stories/material-ui/buttons/DisableElevation.stories.tsx b/examples/storybook/stories/material-ui/buttons/DisableElevation.stories.tsx new file mode 100644 index 00000000000000..bc78b123ea2528 --- /dev/null +++ b/examples/storybook/stories/material-ui/buttons/DisableElevation.stories.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; + +export function DisableElevation() { + return ( + + ); +} + +export default { + title: "Material-ui|buttons|DisableElevation.stories" +}; diff --git a/examples/storybook/stories/material-ui/buttons/IconButtons.stories.tsx b/examples/storybook/stories/material-ui/buttons/IconButtons.stories.tsx new file mode 100644 index 00000000000000..9c655703705989 --- /dev/null +++ b/examples/storybook/stories/material-ui/buttons/IconButtons.stories.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import IconButton from '@material-ui/core/IconButton'; +import DeleteIcon from '@material-ui/icons/Delete'; +import AlarmIcon from '@material-ui/icons/Alarm'; +import AddShoppingCartIcon from '@material-ui/icons/AddShoppingCart'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +export function IconButtons() { + const classes = useStyles(); + + return ( +
    + + + + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|buttons|IconButtons.stories" +}; diff --git a/examples/storybook/stories/material-ui/buttons/IconLabelButtons.stories.tsx b/examples/storybook/stories/material-ui/buttons/IconLabelButtons.stories.tsx new file mode 100644 index 00000000000000..c2f706b8ff3336 --- /dev/null +++ b/examples/storybook/stories/material-ui/buttons/IconLabelButtons.stories.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import DeleteIcon from '@material-ui/icons/Delete'; +import CloudUploadIcon from '@material-ui/icons/CloudUpload'; +import KeyboardVoiceIcon from '@material-ui/icons/KeyboardVoice'; +import Icon from '@material-ui/core/Icon'; +import SaveIcon from '@material-ui/icons/Save'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + button: { + margin: theme.spacing(1), + }, + }), +); + +export function IconLabelButtons() { + const classes = useStyles(); + + return ( +
    + + {/* This Button uses a Font Icon, see the installation instructions in the Icon component docs. */} + + + + + +
    + ); +} + +export default { + title: "Material-ui|buttons|IconLabelButtons.stories" +}; diff --git a/examples/storybook/stories/material-ui/buttons/OutlinedButtons.stories.tsx b/examples/storybook/stories/material-ui/buttons/OutlinedButtons.stories.tsx new file mode 100644 index 00000000000000..ae970540d9df04 --- /dev/null +++ b/examples/storybook/stories/material-ui/buttons/OutlinedButtons.stories.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +export function OutlinedButtons() { + const classes = useStyles(); + + return ( +
    + + + + + +
    + ); +} + +export default { + title: "Material-ui|buttons|OutlinedButtons.stories" +}; diff --git a/examples/storybook/stories/material-ui/buttons/TextButtons.stories.tsx b/examples/storybook/stories/material-ui/buttons/TextButtons.stories.tsx new file mode 100644 index 00000000000000..1c2d54d9acfe54 --- /dev/null +++ b/examples/storybook/stories/material-ui/buttons/TextButtons.stories.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +export function TextButtons() { + const classes = useStyles(); + + return ( +
    + + + + + +
    + ); +} + +export default { + title: "Material-ui|buttons|TextButtons.stories" +}; diff --git a/examples/storybook/stories/material-ui/buttons/UploadButtons.stories.tsx b/examples/storybook/stories/material-ui/buttons/UploadButtons.stories.tsx new file mode 100644 index 00000000000000..a78570958437e2 --- /dev/null +++ b/examples/storybook/stories/material-ui/buttons/UploadButtons.stories.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import IconButton from '@material-ui/core/IconButton'; +import PhotoCamera from '@material-ui/icons/PhotoCamera'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + }, + }, + input: { + display: 'none', + }, + }), +); + +export function UploadButtons() { + const classes = useStyles(); + + return ( +
    + + + + +
    + ); +} + +export default { + title: "Material-ui|buttons|UploadButtons.stories" +}; diff --git a/examples/storybook/stories/material-ui/cards/ImgMediaCard.stories.tsx b/examples/storybook/stories/material-ui/cards/ImgMediaCard.stories.tsx new file mode 100644 index 00000000000000..42928bb6a5dc1e --- /dev/null +++ b/examples/storybook/stories/material-ui/cards/ImgMediaCard.stories.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardActionArea from '@material-ui/core/CardActionArea'; +import CardActions from '@material-ui/core/CardActions'; +import CardContent from '@material-ui/core/CardContent'; +import CardMedia from '@material-ui/core/CardMedia'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles({ + root: { + maxWidth: 345, + }, +}); + +export function ImgMediaCard() { + const classes = useStyles(); + + return ( + + + + + + Lizard + + + Lizards are a widespread group of squamate reptiles, with over 6,000 species, ranging + across all continents except Antarctica + + + + + + + + + ); +} + +export default { + title: "Material-ui|cards|ImgMediaCard.stories" +}; diff --git a/examples/storybook/stories/material-ui/cards/MediaCard.stories.tsx b/examples/storybook/stories/material-ui/cards/MediaCard.stories.tsx new file mode 100644 index 00000000000000..fc1b50962fe35f --- /dev/null +++ b/examples/storybook/stories/material-ui/cards/MediaCard.stories.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardActionArea from '@material-ui/core/CardActionArea'; +import CardActions from '@material-ui/core/CardActions'; +import CardContent from '@material-ui/core/CardContent'; +import CardMedia from '@material-ui/core/CardMedia'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles({ + root: { + maxWidth: 345, + }, + media: { + height: 140, + }, +}); + +export function MediaCard() { + const classes = useStyles(); + + return ( + + + + + + Lizard + + + Lizards are a widespread group of squamate reptiles, with over 6,000 species, ranging + across all continents except Antarctica + + + + + + + + + ); +} + +export default { + title: "Material-ui|cards|MediaCard.stories" +}; diff --git a/examples/storybook/stories/material-ui/cards/MediaControlCard.stories.tsx b/examples/storybook/stories/material-ui/cards/MediaControlCard.stories.tsx new file mode 100644 index 00000000000000..876946a197cbd4 --- /dev/null +++ b/examples/storybook/stories/material-ui/cards/MediaControlCard.stories.tsx @@ -0,0 +1,78 @@ +import React from 'react'; +import { Theme, createStyles, makeStyles, useTheme } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardContent from '@material-ui/core/CardContent'; +import CardMedia from '@material-ui/core/CardMedia'; +import IconButton from '@material-ui/core/IconButton'; +import Typography from '@material-ui/core/Typography'; +import SkipPreviousIcon from '@material-ui/icons/SkipPrevious'; +import PlayArrowIcon from '@material-ui/icons/PlayArrow'; +import SkipNextIcon from '@material-ui/icons/SkipNext'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + }, + details: { + display: 'flex', + flexDirection: 'column', + }, + content: { + flex: '1 0 auto', + }, + cover: { + width: 151, + }, + controls: { + display: 'flex', + alignItems: 'center', + paddingLeft: theme.spacing(1), + paddingBottom: theme.spacing(1), + }, + playIcon: { + height: 38, + width: 38, + }, + }), +); + +export function MediaControlCard() { + const classes = useStyles(); + const theme = useTheme(); + + return ( + +
    + + + Live From Space + + + Mac Miller + + +
    + + {theme.direction === 'rtl' ? : } + + + + + + {theme.direction === 'rtl' ? : } + +
    +
    + +
    + ); +} + +export default { + title: "Material-ui|cards|MediaControlCard.stories" +}; diff --git a/examples/storybook/stories/material-ui/cards/OutlinedCard.stories.tsx b/examples/storybook/stories/material-ui/cards/OutlinedCard.stories.tsx new file mode 100644 index 00000000000000..2d85508da644ce --- /dev/null +++ b/examples/storybook/stories/material-ui/cards/OutlinedCard.stories.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardActions from '@material-ui/core/CardActions'; +import CardContent from '@material-ui/core/CardContent'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles({ + root: { + minWidth: 275, + }, + bullet: { + display: 'inline-block', + margin: '0 2px', + transform: 'scale(0.8)', + }, + title: { + fontSize: 14, + }, + pos: { + marginBottom: 12, + }, +}); + +export function OutlinedCard() { + const classes = useStyles(); + const bull = ; + + return ( + + + + Word of the Day + + + be{bull}nev{bull}o{bull}lent + + + adjective + + + well meaning and kindly. +
    + {'"a benevolent smile"'} +
    +
    + + + +
    + ); +} + +export default { + title: "Material-ui|cards|OutlinedCard.stories" +}; diff --git a/examples/storybook/stories/material-ui/cards/RecipeReviewCard.stories.tsx b/examples/storybook/stories/material-ui/cards/RecipeReviewCard.stories.tsx new file mode 100644 index 00000000000000..137370941ae534 --- /dev/null +++ b/examples/storybook/stories/material-ui/cards/RecipeReviewCard.stories.tsx @@ -0,0 +1,130 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import clsx from 'clsx'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardMedia from '@material-ui/core/CardMedia'; +import CardContent from '@material-ui/core/CardContent'; +import CardActions from '@material-ui/core/CardActions'; +import Collapse from '@material-ui/core/Collapse'; +import Avatar from '@material-ui/core/Avatar'; +import IconButton from '@material-ui/core/IconButton'; +import Typography from '@material-ui/core/Typography'; +import { red } from '@material-ui/core/colors'; +import FavoriteIcon from '@material-ui/icons/Favorite'; +import ShareIcon from '@material-ui/icons/Share'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import MoreVertIcon from '@material-ui/icons/MoreVert'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + maxWidth: 345, + }, + media: { + height: 0, + paddingTop: '56.25%', // 16:9 + }, + expand: { + transform: 'rotate(0deg)', + marginLeft: 'auto', + transition: theme.transitions.create('transform', { + duration: theme.transitions.duration.shortest, + }), + }, + expandOpen: { + transform: 'rotate(180deg)', + }, + avatar: { + backgroundColor: red[500], + }, + }), +); + +export function RecipeReviewCard() { + const classes = useStyles(); + const [expanded, setExpanded] = React.useState(false); + + const handleExpandClick = () => { + setExpanded(!expanded); + }; + + return ( + + + R + + } + action={ + + + + } + title="Shrimp and Chorizo Paella" + subheader="September 14, 2016" + /> + + + + This impressive paella is a perfect party dish and a fun meal to cook together with your + guests. Add 1 cup of frozen peas along with the mussels, if you like. + + + + + + + + + + + + + + + + Method: + + Heat 1/2 cup of the broth in a pot until simmering, add saffron and set aside for 10 + minutes. + + + Heat oil in a (14- to 16-inch) paella pan or a large, deep skillet over medium-high + heat. Add chicken, shrimp and chorizo, and cook, stirring occasionally until lightly + browned, 6 to 8 minutes. Transfer shrimp to a large plate and set aside, leaving chicken + and chorizo in the pan. Add pimentón, bay leaves, garlic, tomatoes, onion, salt and + pepper, and cook, stirring often until thickened and fragrant, about 10 minutes. Add + saffron broth and remaining 4 1/2 cups chicken broth; bring to a boil. + + + Add rice and stir very gently to distribute. Top with artichokes and peppers, and cook + without stirring, until most of the liquid is absorbed, 15 to 18 minutes. Reduce heat to + medium-low, add reserved shrimp and mussels, tucking them down into the rice, and cook + again without stirring, until mussels have opened and rice is just tender, 5 to 7 + minutes more. (Discard any mussels that don’t open.) + + + Set aside off of the heat to let rest for 10 minutes, and then serve. + + + + + ); +} + +export default { + title: "Material-ui|cards|RecipeReviewCard.stories" +}; diff --git a/examples/storybook/stories/material-ui/cards/SimpleCard.stories.tsx b/examples/storybook/stories/material-ui/cards/SimpleCard.stories.tsx new file mode 100644 index 00000000000000..cfe4fa6a038274 --- /dev/null +++ b/examples/storybook/stories/material-ui/cards/SimpleCard.stories.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardActions from '@material-ui/core/CardActions'; +import CardContent from '@material-ui/core/CardContent'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles({ + root: { + minWidth: 275, + }, + bullet: { + display: 'inline-block', + margin: '0 2px', + transform: 'scale(0.8)', + }, + title: { + fontSize: 14, + }, + pos: { + marginBottom: 12, + }, +}); + +export function SimpleCard() { + const classes = useStyles(); + const bull = ; + + return ( + + + + Word of the Day + + + be{bull}nev{bull}o{bull}lent + + + adjective + + + well meaning and kindly. +
    + {'"a benevolent smile"'} +
    +
    + + + +
    + ); +} + +export default { + title: "Material-ui|cards|SimpleCard.stories" +}; diff --git a/examples/storybook/stories/material-ui/checkboxes/CheckboxLabels.stories.tsx b/examples/storybook/stories/material-ui/checkboxes/CheckboxLabels.stories.tsx new file mode 100644 index 00000000000000..bc87afe619e7d1 --- /dev/null +++ b/examples/storybook/stories/material-ui/checkboxes/CheckboxLabels.stories.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import { withStyles } from '@material-ui/core/styles'; +import { green } from '@material-ui/core/colors'; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import Checkbox, { CheckboxProps } from '@material-ui/core/Checkbox'; +import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; +import CheckBoxIcon from '@material-ui/icons/CheckBox'; +import Favorite from '@material-ui/icons/Favorite'; +import FavoriteBorder from '@material-ui/icons/FavoriteBorder'; + +const GreenCheckbox = withStyles({ + root: { + color: green[400], + '&$checked': { + color: green[600], + }, + }, + checked: {}, +})((props: CheckboxProps) => ); + +export function CheckboxLabels() { + const [state, setState] = React.useState({ + checkedA: true, + checkedB: true, + checkedF: true, + checkedG: true, + }); + + const handleChange = (event: React.ChangeEvent) => { + setState({ ...state, [event.target.name]: event.target.checked }); + }; + + return ( + + } + label="Secondary" + /> + + } + label="Primary" + /> + } label="Uncontrolled" /> + } label="Disabled" /> + } label="Disabled" /> + + } + label="Indeterminate" + /> + } + label="Custom color" + /> + } checkedIcon={} name="checkedH" />} + label="Custom icon" + /> + } + checkedIcon={} + name="checkedI" + /> + } + label="Custom size" + /> + + ); +} + +export default { + title: "Material-ui|checkboxes|CheckboxLabels.stories" +}; diff --git a/examples/storybook/stories/material-ui/checkboxes/Checkboxes.stories.tsx b/examples/storybook/stories/material-ui/checkboxes/Checkboxes.stories.tsx new file mode 100644 index 00000000000000..6a1c2c151d096c --- /dev/null +++ b/examples/storybook/stories/material-ui/checkboxes/Checkboxes.stories.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import Checkbox from '@material-ui/core/Checkbox'; + +export function Checkboxes() { + const [checked, setChecked] = React.useState(true); + + const handleChange = (event: React.ChangeEvent) => { + setChecked(event.target.checked); + }; + + return ( +
    + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|checkboxes|Checkboxes.stories" +}; diff --git a/examples/storybook/stories/material-ui/checkboxes/CheckboxesGroup.stories.tsx b/examples/storybook/stories/material-ui/checkboxes/CheckboxesGroup.stories.tsx new file mode 100644 index 00000000000000..f5f09aabd4976d --- /dev/null +++ b/examples/storybook/stories/material-ui/checkboxes/CheckboxesGroup.stories.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import FormLabel from '@material-ui/core/FormLabel'; +import FormControl from '@material-ui/core/FormControl'; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import Checkbox from '@material-ui/core/Checkbox'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + }, + formControl: { + margin: theme.spacing(3), + }, + }), +); + +export function CheckboxesGroup() { + const classes = useStyles(); + const [state, setState] = React.useState({ + gilad: true, + jason: false, + antoine: false, + }); + + const handleChange = (event: React.ChangeEvent) => { + setState({ ...state, [event.target.name]: event.target.checked }); + }; + + const { gilad, jason, antoine } = state; + const error = [gilad, jason, antoine].filter((v) => v).length !== 2; + + return ( +
    + + Assign responsibility + + } + label="Gilad Gray" + /> + } + label="Jason Killian" + /> + } + label="Antoine Llorca" + /> + + Be careful + + + Pick two + + } + label="Gilad Gray" + /> + } + label="Jason Killian" + /> + } + label="Antoine Llorca" + /> + + You can display an error + +
    + ); +} + +export default { + title: "Material-ui|checkboxes|CheckboxesGroup.stories" +}; diff --git a/examples/storybook/stories/material-ui/checkboxes/CustomizedCheckbox.stories.tsx b/examples/storybook/stories/material-ui/checkboxes/CustomizedCheckbox.stories.tsx new file mode 100644 index 00000000000000..0c17fa0488c4f5 --- /dev/null +++ b/examples/storybook/stories/material-ui/checkboxes/CustomizedCheckbox.stories.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import clsx from 'clsx'; +import { makeStyles } from '@material-ui/core/styles'; +import Checkbox, { CheckboxProps } from '@material-ui/core/Checkbox'; + +const useStyles = makeStyles({ + root: { + '&:hover': { + backgroundColor: 'transparent', + }, + }, + icon: { + borderRadius: 3, + width: 16, + height: 16, + boxShadow: 'inset 0 0 0 1px rgba(16,22,26,.2), inset 0 -1px 0 rgba(16,22,26,.1)', + backgroundColor: '#f5f8fa', + backgroundImage: 'linear-gradient(180deg,hsla(0,0%,100%,.8),hsla(0,0%,100%,0))', + '$root.Mui-focusVisible &': { + outline: '2px auto rgba(19,124,189,.6)', + outlineOffset: 2, + }, + 'input:hover ~ &': { + backgroundColor: '#ebf1f5', + }, + 'input:disabled ~ &': { + boxShadow: 'none', + background: 'rgba(206,217,224,.5)', + }, + }, + checkedIcon: { + backgroundColor: '#137cbd', + backgroundImage: 'linear-gradient(180deg,hsla(0,0%,100%,.1),hsla(0,0%,100%,0))', + '&:before': { + display: 'block', + width: 16, + height: 16, + backgroundImage: + "url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath" + + " fill-rule='evenodd' clip-rule='evenodd' d='M12 5c-.28 0-.53.11-.71.29L7 9.59l-2.29-2.3a1.003 " + + "1.003 0 00-1.42 1.42l3 3c.18.18.43.29.71.29s.53-.11.71-.29l5-5A1.003 1.003 0 0012 5z' fill='%23fff'/%3E%3C/svg%3E\")", + content: '""', + }, + 'input:hover ~ &': { + backgroundColor: '#106ba3', + }, + }, +}); + +// Inspired by blueprintjs +function StyledCheckbox(props: CheckboxProps) { + const classes = useStyles(); + + return ( + } + icon={} + inputProps={{ 'aria-label': 'decorative checkbox' }} + {...props} + /> + ); +} + +export function CustomizedCheckbox() { + return ( +
    + + + +
    + ); +} + +export default { + title: "Material-ui|checkboxes|CustomizedCheckbox.stories" +}; diff --git a/examples/storybook/stories/material-ui/checkboxes/FormControlLabelPosition.stories.tsx b/examples/storybook/stories/material-ui/checkboxes/FormControlLabelPosition.stories.tsx new file mode 100644 index 00000000000000..cdf97320e83f51 --- /dev/null +++ b/examples/storybook/stories/material-ui/checkboxes/FormControlLabelPosition.stories.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import Checkbox from '@material-ui/core/Checkbox'; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormControl from '@material-ui/core/FormControl'; +import FormLabel from '@material-ui/core/FormLabel'; + +export function FormControlLabelPosition() { + return ( + + Label Placement + + } + label="Top" + labelPlacement="top" + /> + } + label="Start" + labelPlacement="start" + /> + } + label="Bottom" + labelPlacement="bottom" + /> + } + label="End" + labelPlacement="end" + /> + + + ); +} + +export default { + title: "Material-ui|checkboxes|FormControlLabelPosition.stories" +}; diff --git a/examples/storybook/stories/material-ui/chips/Chips.stories.tsx b/examples/storybook/stories/material-ui/chips/Chips.stories.tsx new file mode 100644 index 00000000000000..00e34ec601886b --- /dev/null +++ b/examples/storybook/stories/material-ui/chips/Chips.stories.tsx @@ -0,0 +1,84 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import Avatar from '@material-ui/core/Avatar'; +import Chip from '@material-ui/core/Chip'; +import FaceIcon from '@material-ui/icons/Face'; +import DoneIcon from '@material-ui/icons/Done'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + justifyContent: 'center', + flexWrap: 'wrap', + '& > *': { + margin: theme.spacing(0.5), + }, + }, + }), +); + +export function Chips() { + const classes = useStyles(); + + const handleDelete = () => { + console.info('You clicked the delete icon.'); + }; + + const handleClick = () => { + console.info('You clicked the Chip.'); + }; + + return ( +
    + + + M} label="Clickable" onClick={handleClick} /> + } + label="Deletable" + onDelete={handleDelete} + /> + } + label="Clickable deletable" + onClick={handleClick} + onDelete={handleDelete} + /> + } + /> + + M} + label="Primary clickable" + clickable + color="primary" + onDelete={handleDelete} + deleteIcon={} + /> + } + label="Primary clickable" + clickable + color="primary" + onDelete={handleDelete} + deleteIcon={} + /> + + } + label="Deletable secondary" + onDelete={handleDelete} + color="secondary" + /> +
    + ); +} + +export default { + title: "Material-ui|chips|Chips.stories" +}; diff --git a/examples/storybook/stories/material-ui/chips/ChipsArray.stories.tsx b/examples/storybook/stories/material-ui/chips/ChipsArray.stories.tsx new file mode 100644 index 00000000000000..011e642cc2424a --- /dev/null +++ b/examples/storybook/stories/material-ui/chips/ChipsArray.stories.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import Chip from '@material-ui/core/Chip'; +import Paper from '@material-ui/core/Paper'; +import TagFacesIcon from '@material-ui/icons/TagFaces'; + +interface ChipData { + key: number; + label: string; +} + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + justifyContent: 'center', + flexWrap: 'wrap', + listStyle: 'none', + padding: theme.spacing(0.5), + margin: 0, + }, + chip: { + margin: theme.spacing(0.5), + }, + }), +); + +export function ChipsArray() { + const classes = useStyles(); + const [chipData, setChipData] = React.useState([ + { key: 0, label: 'Angular' }, + { key: 1, label: 'jQuery' }, + { key: 2, label: 'Polymer' }, + { key: 3, label: 'React' }, + { key: 4, label: 'Vue.js' }, + ]); + + const handleDelete = (chipToDelete: ChipData) => () => { + setChipData((chips) => chips.filter((chip) => chip.key !== chipToDelete.key)); + }; + + return ( + + {chipData.map((data) => { + let icon; + + if (data.label === 'React') { + icon = ; + } + + return ( +
  • + +
  • + ); + })} +
    + ); +} + +export default { + title: "Material-ui|chips|ChipsArray.stories" +}; diff --git a/examples/storybook/stories/material-ui/chips/OutlinedChips.stories.tsx b/examples/storybook/stories/material-ui/chips/OutlinedChips.stories.tsx new file mode 100644 index 00000000000000..e42599b9203e34 --- /dev/null +++ b/examples/storybook/stories/material-ui/chips/OutlinedChips.stories.tsx @@ -0,0 +1,95 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import Avatar from '@material-ui/core/Avatar'; +import Chip from '@material-ui/core/Chip'; +import FaceIcon from '@material-ui/icons/Face'; +import DoneIcon from '@material-ui/icons/Done'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + justifyContent: 'center', + flexWrap: 'wrap', + '& > *': { + margin: theme.spacing(0.5), + }, + }, + }), +); + +export function OutlinedChips() { + const classes = useStyles(); + + const handleDelete = () => { + console.info('You clicked the delete icon.'); + }; + + const handleClick = () => { + console.info('You clicked the Chip.'); + }; + + return ( +
    + + + M} + label="Clickable" + onClick={handleClick} + variant="outlined" + /> + } + label="Deletable" + onDelete={handleDelete} + variant="outlined" + /> + } + label="Clickable deletable" + onClick={handleClick} + onDelete={handleDelete} + variant="outlined" + /> + } + variant="outlined" + /> + + M} + label="Primary clickable" + clickable + color="primary" + onDelete={handleDelete} + deleteIcon={} + variant="outlined" + /> + } + label="Primary clickable" + clickable + color="primary" + onDelete={handleDelete} + deleteIcon={} + variant="outlined" + /> + + } + label="Deletable secondary" + onDelete={handleDelete} + color="secondary" + variant="outlined" + /> +
    + ); +} + +export default { + title: "Material-ui|chips|OutlinedChips.stories" +}; diff --git a/examples/storybook/stories/material-ui/chips/SmallChips.stories.tsx b/examples/storybook/stories/material-ui/chips/SmallChips.stories.tsx new file mode 100644 index 00000000000000..639eefc90a34bf --- /dev/null +++ b/examples/storybook/stories/material-ui/chips/SmallChips.stories.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import Avatar from '@material-ui/core/Avatar'; +import Chip from '@material-ui/core/Chip'; +import FaceIcon from '@material-ui/icons/Face'; +import DoneIcon from '@material-ui/icons/Done'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + justifyContent: 'center', + flexWrap: 'wrap', + '& > *': { + margin: theme.spacing(0.5), + }, + }, + }), +); + +export function SmallChips() { + const classes = useStyles(); + + const handleDelete = () => { + console.info('You clicked the delete icon.'); + }; + + const handleClick = () => { + console.info('You clicked the Chip.'); + }; + + return ( +
    + + M} label="Clickable" onClick={handleClick} /> + } + label="Deletable" + onDelete={handleDelete} + /> + } + label="Clickable Deletable" + onClick={handleClick} + onDelete={handleDelete} + /> + } + /> + + M} + label="Primary Clickable" + clickable + color="primary" + onDelete={handleDelete} + deleteIcon={} + /> + } + label="Primary Clickable" + clickable + color="primary" + onDelete={handleDelete} + deleteIcon={} + /> + + } + label="Deletable Secondary" + onDelete={handleDelete} + color="secondary" + /> +
    + ); +} + +export default { + title: "Material-ui|chips|SmallChips.stories" +}; diff --git a/examples/storybook/stories/material-ui/chips/SmallOutlinedChips.stories.tsx b/examples/storybook/stories/material-ui/chips/SmallOutlinedChips.stories.tsx new file mode 100644 index 00000000000000..adf8e8e4a8fe5b --- /dev/null +++ b/examples/storybook/stories/material-ui/chips/SmallOutlinedChips.stories.tsx @@ -0,0 +1,114 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import Avatar from '@material-ui/core/Avatar'; +import Chip from '@material-ui/core/Chip'; +import FaceIcon from '@material-ui/icons/Face'; +import DoneIcon from '@material-ui/icons/Done'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + justifyContent: 'center', + flexWrap: 'wrap', + '& > *': { + margin: theme.spacing(0.5), + }, + }, + }), +); + +export function SmallOutlinedChips() { + const classes = useStyles(); + + const handleDelete = () => { + console.info('You clicked the delete icon.'); + }; + + const handleClick = () => { + console.info('You clicked the Chip.'); + }; + + return ( +
    + + M} + label="Clickable" + onClick={handleClick} + /> + } + label="Deletable" + onDelete={handleDelete} + /> + } + label="Clickable deletable" + onClick={handleClick} + onDelete={handleDelete} + /> + } + /> + + M} + label="Primary clickable" + clickable + color="primary" + onDelete={handleDelete} + deleteIcon={} + /> + } + label="Primary clickable" + clickable + color="primary" + onDelete={handleDelete} + deleteIcon={} + /> + + } + label="Deletable secondary" + onDelete={handleDelete} + color="secondary" + /> +
    + ); +} + +export default { + title: "Material-ui|chips|SmallOutlinedChips.stories" +}; diff --git a/examples/storybook/stories/material-ui/click-away-listener/ClickAway.stories.tsx b/examples/storybook/stories/material-ui/click-away-listener/ClickAway.stories.tsx new file mode 100644 index 00000000000000..f22099a5c60e89 --- /dev/null +++ b/examples/storybook/stories/material-ui/click-away-listener/ClickAway.stories.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import ClickAwayListener from '@material-ui/core/ClickAwayListener'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + position: 'relative', + }, + dropdown: { + position: 'absolute', + top: 28, + right: 0, + left: 0, + zIndex: 1, + border: '1px solid', + padding: theme.spacing(1), + backgroundColor: theme.palette.background.paper, + }, + }), +); + +export function ClickAway() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + + const handleClick = () => { + setOpen((prev) => !prev); + }; + + const handleClickAway = () => { + setOpen(false); + }; + + return ( + +
    + + {open ? ( +
    + Click me, I will stay visible until you click outside. +
    + ) : null} +
    +
    + ); +} + +export default { + title: "Material-ui|click-away-listener|ClickAway.stories" +}; diff --git a/examples/storybook/stories/material-ui/click-away-listener/LeadingClickAway.stories.tsx b/examples/storybook/stories/material-ui/click-away-listener/LeadingClickAway.stories.tsx new file mode 100644 index 00000000000000..3735c672c4fe72 --- /dev/null +++ b/examples/storybook/stories/material-ui/click-away-listener/LeadingClickAway.stories.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import ClickAwayListener from '@material-ui/core/ClickAwayListener'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + position: 'relative', + }, + dropdown: { + position: 'absolute', + top: 28, + right: 0, + left: 0, + zIndex: 1, + border: '1px solid', + padding: theme.spacing(1), + backgroundColor: theme.palette.background.paper, + }, + }), +); + +export function LeadingClickAway() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + + const handleClick = () => { + setOpen((prev) => !prev); + }; + + const handleClickAway = () => { + setOpen(false); + }; + + return ( + +
    + + {open ? ( +
    + Click me, I will stay visible until you click outside. +
    + ) : null} +
    +
    + ); +} + +export default { + title: "Material-ui|click-away-listener|LeadingClickAway.stories" +}; diff --git a/examples/storybook/stories/material-ui/click-away-listener/PortalClickAway.stories.tsx b/examples/storybook/stories/material-ui/click-away-listener/PortalClickAway.stories.tsx new file mode 100644 index 00000000000000..afd6598ec9d66f --- /dev/null +++ b/examples/storybook/stories/material-ui/click-away-listener/PortalClickAway.stories.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import ClickAwayListener from '@material-ui/core/ClickAwayListener'; +import Portal from '@material-ui/core/Portal'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + dropdown: { + position: 'fixed', + width: 200, + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + border: '1px solid', + padding: theme.spacing(1), + backgroundColor: theme.palette.background.paper, + }, + }), +); + +export function PortalClickAway() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + + const handleClick = () => { + setOpen((prev) => !prev); + }; + + const handleClickAway = () => { + setOpen(false); + }; + + return ( + +
    + + {open ? ( + +
    + Click me, I will stay visible until you click outside. +
    +
    + ) : null} +
    +
    + ); +} + +export default { + title: "Material-ui|click-away-listener|PortalClickAway.stories" +}; diff --git a/examples/storybook/stories/material-ui/container/FixedContainer.stories.tsx b/examples/storybook/stories/material-ui/container/FixedContainer.stories.tsx new file mode 100644 index 00000000000000..8191ada29e8097 --- /dev/null +++ b/examples/storybook/stories/material-ui/container/FixedContainer.stories.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import Typography from '@material-ui/core/Typography'; +import Container from '@material-ui/core/Container'; + +export function FixedContainer() { + return ( + + + + + + + ); +} + +export default { + title: "Material-ui|container|FixedContainer.stories" +}; diff --git a/examples/storybook/stories/material-ui/container/SimpleContainer.stories.tsx b/examples/storybook/stories/material-ui/container/SimpleContainer.stories.tsx new file mode 100644 index 00000000000000..829f621c6050cd --- /dev/null +++ b/examples/storybook/stories/material-ui/container/SimpleContainer.stories.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import Typography from '@material-ui/core/Typography'; +import Container from '@material-ui/core/Container'; + +export function SimpleContainer() { + return ( + + + + + + + ); +} + +export default { + title: "Material-ui|container|SimpleContainer.stories" +}; diff --git a/examples/storybook/stories/material-ui/dialogs/AlertDialog.stories.tsx b/examples/storybook/stories/material-ui/dialogs/AlertDialog.stories.tsx new file mode 100644 index 00000000000000..59655fa26514c1 --- /dev/null +++ b/examples/storybook/stories/material-ui/dialogs/AlertDialog.stories.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; + +export function AlertDialog() { + const [open, setOpen] = React.useState(false); + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + return ( +
    + + + {"Use Google's location service?"} + + + Let Google help apps determine location. This means sending anonymous location data to + Google, even when no apps are running. + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|dialogs|AlertDialog.stories" +}; diff --git a/examples/storybook/stories/material-ui/dialogs/AlertDialogSlide.stories.tsx b/examples/storybook/stories/material-ui/dialogs/AlertDialogSlide.stories.tsx new file mode 100644 index 00000000000000..2a339c175a01ac --- /dev/null +++ b/examples/storybook/stories/material-ui/dialogs/AlertDialogSlide.stories.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import Slide from '@material-ui/core/Slide'; +import { TransitionProps } from '@material-ui/core/transitions'; + +const Transition = React.forwardRef(function Transition( + props: TransitionProps & { children?: React.ReactElement }, + ref: React.Ref, +) { + return ; +}); + +export function AlertDialogSlide() { + const [open, setOpen] = React.useState(false); + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + return ( +
    + + + {"Use Google's location service?"} + + + Let Google help apps determine location. This means sending anonymous location data to + Google, even when no apps are running. + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|dialogs|AlertDialogSlide.stories" +}; diff --git a/examples/storybook/stories/material-ui/dialogs/ConfirmationDialog.stories.tsx b/examples/storybook/stories/material-ui/dialogs/ConfirmationDialog.stories.tsx new file mode 100644 index 00000000000000..b38ba0e6da6e93 --- /dev/null +++ b/examples/storybook/stories/material-ui/dialogs/ConfirmationDialog.stories.tsx @@ -0,0 +1,174 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogActions from '@material-ui/core/DialogActions'; +import Dialog from '@material-ui/core/Dialog'; +import RadioGroup from '@material-ui/core/RadioGroup'; +import Radio from '@material-ui/core/Radio'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; + +const options = [ + 'None', + 'Atria', + 'Callisto', + 'Dione', + 'Ganymede', + 'Hangouts Call', + 'Luna', + 'Oberon', + 'Phobos', + 'Pyxis', + 'Sedna', + 'Titania', + 'Triton', + 'Umbriel', +]; + +export interface ConfirmationDialogRawProps { + classes: Record<'paper', string>; + id: string; + keepMounted: boolean; + value: string; + open: boolean; + onClose: (value?: string) => void; +} + +function ConfirmationDialogRaw(props: ConfirmationDialogRawProps) { + const { onClose, value: valueProp, open, ...other } = props; + const [value, setValue] = React.useState(valueProp); + const radioGroupRef = React.useRef(null); + + React.useEffect(() => { + if (!open) { + setValue(valueProp); + } + }, [valueProp, open]); + + const handleEntering = () => { + if (radioGroupRef.current != null) { + radioGroupRef.current.focus(); + } + }; + + const handleCancel = () => { + onClose(); + }; + + const handleOk = () => { + onClose(value); + }; + + const handleChange = (event: React.ChangeEvent) => { + setValue((event.target as HTMLInputElement).value); + }; + + return ( + + Phone Ringtone + + + {options.map((option) => ( + } label={option} /> + ))} + + + + + + + + ); +} + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + maxWidth: 360, + backgroundColor: theme.palette.background.paper, + }, + paper: { + width: '80%', + maxHeight: 435, + }, + }), +); + +export function ConfirmationDialog() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + const [value, setValue] = React.useState('Dione'); + + const handleClickListItem = () => { + setOpen(true); + }; + + const handleClose = (newValue?: string) => { + setOpen(false); + + if (newValue) { + setValue(newValue); + } + }; + + return ( +
    + + + + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|dialogs|ConfirmationDialog.stories" +}; diff --git a/examples/storybook/stories/material-ui/dialogs/CustomizedDialogs.stories.tsx b/examples/storybook/stories/material-ui/dialogs/CustomizedDialogs.stories.tsx new file mode 100644 index 00000000000000..c1cf0a60d84bb7 --- /dev/null +++ b/examples/storybook/stories/material-ui/dialogs/CustomizedDialogs.stories.tsx @@ -0,0 +1,105 @@ +import React from 'react'; +import { createStyles, Theme, withStyles, WithStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import MuiDialogTitle from '@material-ui/core/DialogTitle'; +import MuiDialogContent from '@material-ui/core/DialogContent'; +import MuiDialogActions from '@material-ui/core/DialogActions'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from '@material-ui/icons/Close'; +import Typography from '@material-ui/core/Typography'; + +const styles = (theme: Theme) => + createStyles({ + root: { + margin: 0, + padding: theme.spacing(2), + }, + closeButton: { + position: 'absolute', + right: theme.spacing(1), + top: theme.spacing(1), + color: theme.palette.grey[500], + }, + }); + +export interface DialogTitleProps extends WithStyles { + id: string; + children: React.ReactNode; + onClose: () => void; +} + +const DialogTitle = withStyles(styles)((props: DialogTitleProps) => { + const { children, classes, onClose, ...other } = props; + return ( + + {children} + {onClose ? ( + + + + ) : null} + + ); +}); + +const DialogContent = withStyles((theme: Theme) => ({ + root: { + padding: theme.spacing(2), + }, +}))(MuiDialogContent); + +const DialogActions = withStyles((theme: Theme) => ({ + root: { + margin: 0, + padding: theme.spacing(1), + }, +}))(MuiDialogActions); + +export function CustomizedDialogs() { + const [open, setOpen] = React.useState(false); + + const handleClickOpen = () => { + setOpen(true); + }; + const handleClose = () => { + setOpen(false); + }; + + return ( +
    + + + + Modal title + + + + Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis + in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. + + + Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis + lacus vel augue laoreet rutrum faucibus dolor auctor. + + + Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel + scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus + auctor fringilla. + + + + + + +
    + ); +} + +export default { + title: "Material-ui|dialogs|CustomizedDialogs.stories" +}; diff --git a/examples/storybook/stories/material-ui/dialogs/DraggableDialog.stories.tsx b/examples/storybook/stories/material-ui/dialogs/DraggableDialog.stories.tsx new file mode 100644 index 00000000000000..178cca1001ff10 --- /dev/null +++ b/examples/storybook/stories/material-ui/dialogs/DraggableDialog.stories.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import Paper, { PaperProps } from '@material-ui/core/Paper'; +import Draggable from 'react-draggable'; + +function PaperComponent(props: PaperProps) { + return ( + + + + ); +} + +export function DraggableDialog() { + const [open, setOpen] = React.useState(false); + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + return ( +
    + + + + Subscribe + + + + To subscribe to this website, please enter your email address here. We will send updates + occasionally. + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|dialogs|DraggableDialog.stories" +}; diff --git a/examples/storybook/stories/material-ui/dialogs/FormDialog.stories.tsx b/examples/storybook/stories/material-ui/dialogs/FormDialog.stories.tsx new file mode 100644 index 00000000000000..70db77392a734f --- /dev/null +++ b/examples/storybook/stories/material-ui/dialogs/FormDialog.stories.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; + +export function FormDialog() { + const [open, setOpen] = React.useState(false); + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + return ( +
    + + + Subscribe + + + To subscribe to this website, please enter your email address here. We will send updates + occasionally. + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|dialogs|FormDialog.stories" +}; diff --git a/examples/storybook/stories/material-ui/dialogs/FullScreenDialog.stories.tsx b/examples/storybook/stories/material-ui/dialogs/FullScreenDialog.stories.tsx new file mode 100644 index 00000000000000..b995eab6ba2f8b --- /dev/null +++ b/examples/storybook/stories/material-ui/dialogs/FullScreenDialog.stories.tsx @@ -0,0 +1,83 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import ListItemText from '@material-ui/core/ListItemText'; +import ListItem from '@material-ui/core/ListItem'; +import List from '@material-ui/core/List'; +import Divider from '@material-ui/core/Divider'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import IconButton from '@material-ui/core/IconButton'; +import Typography from '@material-ui/core/Typography'; +import CloseIcon from '@material-ui/icons/Close'; +import Slide from '@material-ui/core/Slide'; +import { TransitionProps } from '@material-ui/core/transitions'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + appBar: { + position: 'relative', + }, + title: { + marginLeft: theme.spacing(2), + flex: 1, + }, + }), +); + +const Transition = React.forwardRef(function Transition( + props: TransitionProps & { children?: React.ReactElement }, + ref: React.Ref, +) { + return ; +}); + +export function FullScreenDialog() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + return ( +
    + + + + + + + + + Sound + + + + + + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|dialogs|FullScreenDialog.stories" +}; diff --git a/examples/storybook/stories/material-ui/dialogs/MaxWidthDialog.stories.tsx b/examples/storybook/stories/material-ui/dialogs/MaxWidthDialog.stories.tsx new file mode 100644 index 00000000000000..614db215d8284c --- /dev/null +++ b/examples/storybook/stories/material-ui/dialogs/MaxWidthDialog.stories.tsx @@ -0,0 +1,112 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Dialog, { DialogProps } from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import FormControl from '@material-ui/core/FormControl'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import InputLabel from '@material-ui/core/InputLabel'; +import MenuItem from '@material-ui/core/MenuItem'; +import Select from '@material-ui/core/Select'; +import Switch from '@material-ui/core/Switch'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + form: { + display: 'flex', + flexDirection: 'column', + margin: 'auto', + width: 'fit-content', + }, + formControl: { + marginTop: theme.spacing(2), + minWidth: 120, + }, + formControlLabel: { + marginTop: theme.spacing(1), + }, + }), +); + +export function MaxWidthDialog() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + const [fullWidth, setFullWidth] = React.useState(true); + const [maxWidth, setMaxWidth] = React.useState('sm'); + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + const handleMaxWidthChange = (event: React.ChangeEvent<{ value: unknown }>) => { + setMaxWidth(event.target.value as DialogProps['maxWidth']); + }; + + const handleFullWidthChange = (event: React.ChangeEvent) => { + setFullWidth(event.target.checked); + }; + + return ( + + + + Optional sizes + + + You can set my maximum width and whether to adapt or not. + +
    + + maxWidth + + + } + label="Full width" + /> + +
    + + + +
    +
    + ); +} + +export default { + title: "Material-ui|dialogs|MaxWidthDialog.stories" +}; diff --git a/examples/storybook/stories/material-ui/dialogs/ResponsiveDialog.stories.tsx b/examples/storybook/stories/material-ui/dialogs/ResponsiveDialog.stories.tsx new file mode 100644 index 00000000000000..ee735e7ce0daf6 --- /dev/null +++ b/examples/storybook/stories/material-ui/dialogs/ResponsiveDialog.stories.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; +import { useTheme } from '@material-ui/core/styles'; + +export function ResponsiveDialog() { + const [open, setOpen] = React.useState(false); + const theme = useTheme(); + const fullScreen = useMediaQuery(theme.breakpoints.down('sm')); + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + return ( +
    + + + {"Use Google's location service?"} + + + Let Google help apps determine location. This means sending anonymous location data to + Google, even when no apps are running. + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|dialogs|ResponsiveDialog.stories" +}; diff --git a/examples/storybook/stories/material-ui/dialogs/ScrollDialog.stories.tsx b/examples/storybook/stories/material-ui/dialogs/ScrollDialog.stories.tsx new file mode 100644 index 00000000000000..dca9af145069cc --- /dev/null +++ b/examples/storybook/stories/material-ui/dialogs/ScrollDialog.stories.tsx @@ -0,0 +1,75 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Dialog, { DialogProps } from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; + +export function ScrollDialog() { + const [open, setOpen] = React.useState(false); + const [scroll, setScroll] = React.useState('paper'); + + const handleClickOpen = (scrollType: DialogProps['scroll']) => () => { + setOpen(true); + setScroll(scrollType); + }; + + const handleClose = () => { + setOpen(false); + }; + + const descriptionElementRef = React.useRef(null); + React.useEffect(() => { + if (open) { + const { current: descriptionElement } = descriptionElementRef; + if (descriptionElement !== null) { + descriptionElement.focus(); + } + } + }, [open]); + + return ( +
    + + + + Subscribe + + + {[...new Array(50)] + .map( + () => `Cras mattis consectetur purus sit amet fermentum. +Cras justo odio, dapibus ac facilisis in, egestas eget quam. +Morbi leo risus, porta ac consectetur ac, vestibulum at eros. +Praesent commodo cursus magna, vel scelerisque nisl consectetur et.`, + ) + .join('\n')} + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|dialogs|ScrollDialog.stories" +}; diff --git a/examples/storybook/stories/material-ui/dialogs/SimpleDialog.stories.tsx b/examples/storybook/stories/material-ui/dialogs/SimpleDialog.stories.tsx new file mode 100644 index 00000000000000..70aa66c31189da --- /dev/null +++ b/examples/storybook/stories/material-ui/dialogs/SimpleDialog.stories.tsx @@ -0,0 +1,96 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Avatar from '@material-ui/core/Avatar'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemAvatar from '@material-ui/core/ListItemAvatar'; +import ListItemText from '@material-ui/core/ListItemText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import Dialog from '@material-ui/core/Dialog'; +import PersonIcon from '@material-ui/icons/Person'; +import AddIcon from '@material-ui/icons/Add'; +import Typography from '@material-ui/core/Typography'; +import { blue } from '@material-ui/core/colors'; + +const emails = ['username@gmail.com', 'user02@gmail.com']; +const useStyles = makeStyles({ + avatar: { + backgroundColor: blue[100], + color: blue[600], + }, +}); + +export interface SimpleDialogProps { + open: boolean; + selectedValue: string; + onClose: (value: string) => void; +} + +function SimpleDialog(props: SimpleDialogProps) { + const classes = useStyles(); + const { onClose, selectedValue, open } = props; + + const handleClose = () => { + onClose(selectedValue); + }; + + const handleListItemClick = (value: string) => { + onClose(value); + }; + + return ( + + Set backup account + + {emails.map((email) => ( + handleListItemClick(email)} key={email}> + + + + + + + + ))} + handleListItemClick('addAccount')}> + + + + + + + + + + ); +} + +export function SimpleDialogDemo() { + const [open, setOpen] = React.useState(false); + const [selectedValue, setSelectedValue] = React.useState(emails[1]); + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = (value: string) => { + setOpen(false); + setSelectedValue(value); + }; + + return ( +
    + Selected: {selectedValue} +
    + + +
    + ); +} + +export default { + title: "Material-ui|dialogs|SimpleDialog.stories" +}; diff --git a/examples/storybook/stories/material-ui/dividers/InsetDividers.stories.tsx b/examples/storybook/stories/material-ui/dividers/InsetDividers.stories.tsx new file mode 100644 index 00000000000000..2f78f50e57364d --- /dev/null +++ b/examples/storybook/stories/material-ui/dividers/InsetDividers.stories.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; +import ListItemAvatar from '@material-ui/core/ListItemAvatar'; +import Avatar from '@material-ui/core/Avatar'; +import ImageIcon from '@material-ui/icons/Image'; +import WorkIcon from '@material-ui/icons/Work'; +import BeachAccessIcon from '@material-ui/icons/BeachAccess'; +import Divider from '@material-ui/core/Divider'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + maxWidth: 360, + backgroundColor: theme.palette.background.paper, + }, + }), +); + +export function InsetDividers() { + const classes = useStyles(); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|dividers|InsetDividers.stories" +}; diff --git a/examples/storybook/stories/material-ui/dividers/ListDividers.stories.tsx b/examples/storybook/stories/material-ui/dividers/ListDividers.stories.tsx new file mode 100644 index 00000000000000..a09d8ceec9eaca --- /dev/null +++ b/examples/storybook/stories/material-ui/dividers/ListDividers.stories.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; +import Divider from '@material-ui/core/Divider'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + maxWidth: 360, + backgroundColor: theme.palette.background.paper, + }, + }), +); + +export function ListDividers() { + const classes = useStyles(); + + return ( + + + + + + + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|dividers|ListDividers.stories" +}; diff --git a/examples/storybook/stories/material-ui/dividers/MiddleDividers.stories.tsx b/examples/storybook/stories/material-ui/dividers/MiddleDividers.stories.tsx new file mode 100644 index 00000000000000..5c1a177fe9f50d --- /dev/null +++ b/examples/storybook/stories/material-ui/dividers/MiddleDividers.stories.tsx @@ -0,0 +1,75 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import Chip from '@material-ui/core/Chip'; +import Button from '@material-ui/core/Button'; +import Grid from '@material-ui/core/Grid'; +import Divider from '@material-ui/core/Divider'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + maxWidth: 360, + backgroundColor: theme.palette.background.paper, + }, + chip: { + margin: theme.spacing(0.5), + }, + section1: { + margin: theme.spacing(3, 2), + }, + section2: { + margin: theme.spacing(2), + }, + section3: { + margin: theme.spacing(3, 1, 1), + }, + }), +); + +export function MiddleDividers() { + const classes = useStyles(); + + return ( +
    +
    + + + + Toothbrush + + + + + $4.50 + + + + + Pinstriped cornflower blue cotton blouse takes you on a walk to the park or just down the + hall. + +
    + +
    + + Select type + +
    + + + + +
    +
    +
    + +
    +
    + ); +} + +export default { + title: "Material-ui|dividers|MiddleDividers.stories" +}; diff --git a/examples/storybook/stories/material-ui/dividers/SubheaderDividers.stories.tsx b/examples/storybook/stories/material-ui/dividers/SubheaderDividers.stories.tsx new file mode 100644 index 00000000000000..63d6ab30d63652 --- /dev/null +++ b/examples/storybook/stories/material-ui/dividers/SubheaderDividers.stories.tsx @@ -0,0 +1,75 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemAvatar from '@material-ui/core/ListItemAvatar'; +import ListItemText from '@material-ui/core/ListItemText'; +import Avatar from '@material-ui/core/Avatar'; +import BeachAccessIcon from '@material-ui/icons/BeachAccess'; +import Divider from '@material-ui/core/Divider'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + maxWidth: 360, + backgroundColor: theme.palette.background.paper, + }, + dividerFullWidth: { + margin: `5px 0 0 ${theme.spacing(2)}px`, + }, + dividerInset: { + margin: `5px 0 0 ${theme.spacing(9)}px`, + }, + }), +); + +export function SubheaderDividers() { + const classes = useStyles(); + + return ( + + + + + +
  • + + Divider + +
  • + + + + +
  • + + Leisure + +
  • + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|dividers|SubheaderDividers.stories" +}; diff --git a/examples/storybook/stories/material-ui/dividers/VerticalDividers.stories.tsx b/examples/storybook/stories/material-ui/dividers/VerticalDividers.stories.tsx new file mode 100644 index 00000000000000..bb020f3d96c518 --- /dev/null +++ b/examples/storybook/stories/material-ui/dividers/VerticalDividers.stories.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import FormatAlignLeftIcon from '@material-ui/icons/FormatAlignLeft'; +import FormatAlignCenterIcon from '@material-ui/icons/FormatAlignCenter'; +import FormatAlignRightIcon from '@material-ui/icons/FormatAlignRight'; +import FormatBoldIcon from '@material-ui/icons/FormatBold'; +import FormatItalicIcon from '@material-ui/icons/FormatItalic'; +import FormatUnderlinedIcon from '@material-ui/icons/FormatUnderlined'; +import Grid from '@material-ui/core/Grid'; +import Divider from '@material-ui/core/Divider'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: 'fit-content', + border: `1px solid ${theme.palette.divider}`, + borderRadius: theme.shape.borderRadius, + backgroundColor: theme.palette.background.paper, + color: theme.palette.text.secondary, + '& svg': { + margin: theme.spacing(1.5), + }, + '& hr': { + margin: theme.spacing(0, 0.5), + }, + }, + }), +); + +export function VerticalDividers() { + const classes = useStyles(); + + return ( +
    + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|dividers|VerticalDividers.stories" +}; diff --git a/examples/storybook/stories/material-ui/drawers/ClippedDrawer.stories.tsx b/examples/storybook/stories/material-ui/drawers/ClippedDrawer.stories.tsx new file mode 100644 index 00000000000000..6e919dc43fda28 --- /dev/null +++ b/examples/storybook/stories/material-ui/drawers/ClippedDrawer.stories.tsx @@ -0,0 +1,116 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import Drawer from '@material-ui/core/Drawer'; +import AppBar from '@material-ui/core/AppBar'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import Toolbar from '@material-ui/core/Toolbar'; +import List from '@material-ui/core/List'; +import Typography from '@material-ui/core/Typography'; +import Divider from '@material-ui/core/Divider'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import InboxIcon from '@material-ui/icons/MoveToInbox'; +import MailIcon from '@material-ui/icons/Mail'; + +const drawerWidth = 240; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + }, + appBar: { + zIndex: theme.zIndex.drawer + 1, + }, + drawer: { + width: drawerWidth, + flexShrink: 0, + }, + drawerPaper: { + width: drawerWidth, + }, + drawerContainer: { + overflow: 'auto', + }, + content: { + flexGrow: 1, + padding: theme.spacing(3), + }, + }), +); + +export function ClippedDrawer() { + const classes = useStyles(); + + return ( +
    + + + + + Clipped drawer + + + + + +
    + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + +
    +
    +
    + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Rhoncus dolor purus non enim praesent elementum + facilisis leo vel. Risus at ultrices mi tempus imperdiet. Semper risus in hendrerit + gravida rutrum quisque non tellus. Convallis convallis tellus id interdum velit laoreet id + donec ultrices. Odio morbi quis commodo odio aenean sed adipiscing. Amet nisl suscipit + adipiscing bibendum est ultricies integer quis. Cursus euismod quis viverra nibh cras. + Metus vulputate eu scelerisque felis imperdiet proin fermentum leo. Mauris commodo quis + imperdiet massa tincidunt. Cras tincidunt lobortis feugiat vivamus at augue. At augue eget + arcu dictum varius duis at consectetur lorem. Velit sed ullamcorper morbi tincidunt. Lorem + donec massa sapien faucibus et molestie ac. + + + Consequat mauris nunc congue nisi vitae suscipit. Fringilla est ullamcorper eget nulla + facilisi etiam dignissim diam. Pulvinar elementum integer enim neque volutpat ac + tincidunt. Ornare suspendisse sed nisi lacus sed viverra tellus. Purus sit amet volutpat + consequat mauris. Elementum eu facilisis sed odio morbi. Euismod lacinia at quis risus sed + vulputate odio. Morbi tincidunt ornare massa eget egestas purus viverra accumsan in. In + hendrerit gravida rutrum quisque non tellus orci ac. Pellentesque nec nam aliquam sem et + tortor. Habitant morbi tristique senectus et. Adipiscing elit duis tristique sollicitudin + nibh sit. Ornare aenean euismod elementum nisi quis eleifend. Commodo viverra maecenas + accumsan lacus vel facilisis. Nulla posuere sollicitudin aliquam ultrices sagittis orci a. + +
    +
    + ); +} + +export default { + title: "Material-ui|drawers|ClippedDrawer.stories" +}; diff --git a/examples/storybook/stories/material-ui/drawers/MiniDrawer.stories.tsx b/examples/storybook/stories/material-ui/drawers/MiniDrawer.stories.tsx new file mode 100644 index 00000000000000..8cc8a861305570 --- /dev/null +++ b/examples/storybook/stories/material-ui/drawers/MiniDrawer.stories.tsx @@ -0,0 +1,195 @@ +import React from 'react'; +import clsx from 'clsx'; +import { createStyles, makeStyles, useTheme, Theme } from '@material-ui/core/styles'; +import Drawer from '@material-ui/core/Drawer'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import List from '@material-ui/core/List'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import Typography from '@material-ui/core/Typography'; +import Divider from '@material-ui/core/Divider'; +import IconButton from '@material-ui/core/IconButton'; +import MenuIcon from '@material-ui/icons/Menu'; +import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'; +import ChevronRightIcon from '@material-ui/icons/ChevronRight'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import InboxIcon from '@material-ui/icons/MoveToInbox'; +import MailIcon from '@material-ui/icons/Mail'; + +const drawerWidth = 240; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + }, + appBar: { + zIndex: theme.zIndex.drawer + 1, + transition: theme.transitions.create(['width', 'margin'], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + appBarShift: { + marginLeft: drawerWidth, + width: `calc(100% - ${drawerWidth}px)`, + transition: theme.transitions.create(['width', 'margin'], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + }, + menuButton: { + marginRight: 36, + }, + hide: { + display: 'none', + }, + drawer: { + width: drawerWidth, + flexShrink: 0, + whiteSpace: 'nowrap', + }, + drawerOpen: { + width: drawerWidth, + transition: theme.transitions.create('width', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + }, + drawerClose: { + transition: theme.transitions.create('width', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + overflowX: 'hidden', + width: theme.spacing(7) + 1, + [theme.breakpoints.up('sm')]: { + width: theme.spacing(9) + 1, + }, + }, + toolbar: { + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-end', + padding: theme.spacing(0, 1), + // necessary for content to be below app bar + ...theme.mixins.toolbar, + }, + content: { + flexGrow: 1, + padding: theme.spacing(3), + }, + }), +); + +export function MiniDrawer() { + const classes = useStyles(); + const theme = useTheme(); + const [open, setOpen] = React.useState(false); + + const handleDrawerOpen = () => { + setOpen(true); + }; + + const handleDrawerClose = () => { + setOpen(false); + }; + + return ( +
    + + + + + + + + Mini variant drawer + + + + +
    + + {theme.direction === 'rtl' ? : } + +
    + + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + +
    +
    +
    + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Rhoncus dolor purus non enim praesent elementum + facilisis leo vel. Risus at ultrices mi tempus imperdiet. Semper risus in hendrerit + gravida rutrum quisque non tellus. Convallis convallis tellus id interdum velit laoreet id + donec ultrices. Odio morbi quis commodo odio aenean sed adipiscing. Amet nisl suscipit + adipiscing bibendum est ultricies integer quis. Cursus euismod quis viverra nibh cras. + Metus vulputate eu scelerisque felis imperdiet proin fermentum leo. Mauris commodo quis + imperdiet massa tincidunt. Cras tincidunt lobortis feugiat vivamus at augue. At augue eget + arcu dictum varius duis at consectetur lorem. Velit sed ullamcorper morbi tincidunt. Lorem + donec massa sapien faucibus et molestie ac. + + + Consequat mauris nunc congue nisi vitae suscipit. Fringilla est ullamcorper eget nulla + facilisi etiam dignissim diam. Pulvinar elementum integer enim neque volutpat ac + tincidunt. Ornare suspendisse sed nisi lacus sed viverra tellus. Purus sit amet volutpat + consequat mauris. Elementum eu facilisis sed odio morbi. Euismod lacinia at quis risus sed + vulputate odio. Morbi tincidunt ornare massa eget egestas purus viverra accumsan in. In + hendrerit gravida rutrum quisque non tellus orci ac. Pellentesque nec nam aliquam sem et + tortor. Habitant morbi tristique senectus et. Adipiscing elit duis tristique sollicitudin + nibh sit. Ornare aenean euismod elementum nisi quis eleifend. Commodo viverra maecenas + accumsan lacus vel facilisis. Nulla posuere sollicitudin aliquam ultrices sagittis orci a. + +
    +
    + ); +} + +export default { + title: "Material-ui|drawers|MiniDrawer.stories" +}; diff --git a/examples/storybook/stories/material-ui/drawers/PermanentDrawerLeft.stories.tsx b/examples/storybook/stories/material-ui/drawers/PermanentDrawerLeft.stories.tsx new file mode 100644 index 00000000000000..6d9f0e446931b4 --- /dev/null +++ b/examples/storybook/stories/material-ui/drawers/PermanentDrawerLeft.stories.tsx @@ -0,0 +1,117 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import Drawer from '@material-ui/core/Drawer'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import List from '@material-ui/core/List'; +import Typography from '@material-ui/core/Typography'; +import Divider from '@material-ui/core/Divider'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import InboxIcon from '@material-ui/icons/MoveToInbox'; +import MailIcon from '@material-ui/icons/Mail'; + +const drawerWidth = 240; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + }, + appBar: { + width: `calc(100% - ${drawerWidth}px)`, + marginLeft: drawerWidth, + }, + drawer: { + width: drawerWidth, + flexShrink: 0, + }, + drawerPaper: { + width: drawerWidth, + }, + // necessary for content to be below app bar + toolbar: theme.mixins.toolbar, + content: { + flexGrow: 1, + backgroundColor: theme.palette.background.default, + padding: theme.spacing(3), + }, + }), +); + +export function PermanentDrawerLeft() { + const classes = useStyles(); + + return ( +
    + + + + + Permanent drawer + + + + +
    + + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + + +
    +
    + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Rhoncus dolor purus non enim praesent elementum + facilisis leo vel. Risus at ultrices mi tempus imperdiet. Semper risus in hendrerit + gravida rutrum quisque non tellus. Convallis convallis tellus id interdum velit laoreet id + donec ultrices. Odio morbi quis commodo odio aenean sed adipiscing. Amet nisl suscipit + adipiscing bibendum est ultricies integer quis. Cursus euismod quis viverra nibh cras. + Metus vulputate eu scelerisque felis imperdiet proin fermentum leo. Mauris commodo quis + imperdiet massa tincidunt. Cras tincidunt lobortis feugiat vivamus at augue. At augue eget + arcu dictum varius duis at consectetur lorem. Velit sed ullamcorper morbi tincidunt. Lorem + donec massa sapien faucibus et molestie ac. + + + Consequat mauris nunc congue nisi vitae suscipit. Fringilla est ullamcorper eget nulla + facilisi etiam dignissim diam. Pulvinar elementum integer enim neque volutpat ac + tincidunt. Ornare suspendisse sed nisi lacus sed viverra tellus. Purus sit amet volutpat + consequat mauris. Elementum eu facilisis sed odio morbi. Euismod lacinia at quis risus sed + vulputate odio. Morbi tincidunt ornare massa eget egestas purus viverra accumsan in. In + hendrerit gravida rutrum quisque non tellus orci ac. Pellentesque nec nam aliquam sem et + tortor. Habitant morbi tristique senectus et. Adipiscing elit duis tristique sollicitudin + nibh sit. Ornare aenean euismod elementum nisi quis eleifend. Commodo viverra maecenas + accumsan lacus vel facilisis. Nulla posuere sollicitudin aliquam ultrices sagittis orci a. + +
    +
    + ); +} + +export default { + title: "Material-ui|drawers|PermanentDrawerLeft.stories" +}; diff --git a/examples/storybook/stories/material-ui/drawers/PermanentDrawerRight.stories.tsx b/examples/storybook/stories/material-ui/drawers/PermanentDrawerRight.stories.tsx new file mode 100644 index 00000000000000..1cf232ed1b4dd5 --- /dev/null +++ b/examples/storybook/stories/material-ui/drawers/PermanentDrawerRight.stories.tsx @@ -0,0 +1,117 @@ +import React from 'react'; +import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; +import Drawer from '@material-ui/core/Drawer'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import List from '@material-ui/core/List'; +import Typography from '@material-ui/core/Typography'; +import Divider from '@material-ui/core/Divider'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import InboxIcon from '@material-ui/icons/MoveToInbox'; +import MailIcon from '@material-ui/icons/Mail'; + +const drawerWidth = 240; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + }, + appBar: { + width: `calc(100% - ${drawerWidth}px)`, + marginRight: drawerWidth, + }, + drawer: { + width: drawerWidth, + flexShrink: 0, + }, + drawerPaper: { + width: drawerWidth, + }, + // necessary for content to be below app bar + toolbar: theme.mixins.toolbar, + content: { + flexGrow: 1, + backgroundColor: theme.palette.background.default, + padding: theme.spacing(3), + }, + }), +); + +export function PermanentDrawerRight() { + const classes = useStyles(); + + return ( +
    + + + + + Permanent drawer + + + +
    +
    + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Rhoncus dolor purus non enim praesent elementum + facilisis leo vel. Risus at ultrices mi tempus imperdiet. Semper risus in hendrerit + gravida rutrum quisque non tellus. Convallis convallis tellus id interdum velit laoreet id + donec ultrices. Odio morbi quis commodo odio aenean sed adipiscing. Amet nisl suscipit + adipiscing bibendum est ultricies integer quis. Cursus euismod quis viverra nibh cras. + Metus vulputate eu scelerisque felis imperdiet proin fermentum leo. Mauris commodo quis + imperdiet massa tincidunt. Cras tincidunt lobortis feugiat vivamus at augue. At augue eget + arcu dictum varius duis at consectetur lorem. Velit sed ullamcorper morbi tincidunt. Lorem + donec massa sapien faucibus et molestie ac. + + + Consequat mauris nunc congue nisi vitae suscipit. Fringilla est ullamcorper eget nulla + facilisi etiam dignissim diam. Pulvinar elementum integer enim neque volutpat ac + tincidunt. Ornare suspendisse sed nisi lacus sed viverra tellus. Purus sit amet volutpat + consequat mauris. Elementum eu facilisis sed odio morbi. Euismod lacinia at quis risus sed + vulputate odio. Morbi tincidunt ornare massa eget egestas purus viverra accumsan in. In + hendrerit gravida rutrum quisque non tellus orci ac. Pellentesque nec nam aliquam sem et + tortor. Habitant morbi tristique senectus et. Adipiscing elit duis tristique sollicitudin + nibh sit. Ornare aenean euismod elementum nisi quis eleifend. Commodo viverra maecenas + accumsan lacus vel facilisis. Nulla posuere sollicitudin aliquam ultrices sagittis orci a. + +
    + +
    + + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + + +
    + ); +} + +export default { + title: "Material-ui|drawers|PermanentDrawerRight.stories" +}; diff --git a/examples/storybook/stories/material-ui/drawers/PersistentDrawerLeft.stories.tsx b/examples/storybook/stories/material-ui/drawers/PersistentDrawerLeft.stories.tsx new file mode 100644 index 00000000000000..e49f8a3d074004 --- /dev/null +++ b/examples/storybook/stories/material-ui/drawers/PersistentDrawerLeft.stories.tsx @@ -0,0 +1,188 @@ +import React from 'react'; +import clsx from 'clsx'; +import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles'; +import Drawer from '@material-ui/core/Drawer'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import List from '@material-ui/core/List'; +import Typography from '@material-ui/core/Typography'; +import Divider from '@material-ui/core/Divider'; +import IconButton from '@material-ui/core/IconButton'; +import MenuIcon from '@material-ui/icons/Menu'; +import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'; +import ChevronRightIcon from '@material-ui/icons/ChevronRight'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import InboxIcon from '@material-ui/icons/MoveToInbox'; +import MailIcon from '@material-ui/icons/Mail'; + +const drawerWidth = 240; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + }, + appBar: { + transition: theme.transitions.create(['margin', 'width'], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + appBarShift: { + width: `calc(100% - ${drawerWidth}px)`, + marginLeft: drawerWidth, + transition: theme.transitions.create(['margin', 'width'], { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + }, + menuButton: { + marginRight: theme.spacing(2), + }, + hide: { + display: 'none', + }, + drawer: { + width: drawerWidth, + flexShrink: 0, + }, + drawerPaper: { + width: drawerWidth, + }, + drawerHeader: { + display: 'flex', + alignItems: 'center', + padding: theme.spacing(0, 1), + // necessary for content to be below app bar + ...theme.mixins.toolbar, + justifyContent: 'flex-end', + }, + content: { + flexGrow: 1, + padding: theme.spacing(3), + transition: theme.transitions.create('margin', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + marginLeft: -drawerWidth, + }, + contentShift: { + transition: theme.transitions.create('margin', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + marginLeft: 0, + }, + }), +); + +export function PersistentDrawerLeft() { + const classes = useStyles(); + const theme = useTheme(); + const [open, setOpen] = React.useState(false); + + const handleDrawerOpen = () => { + setOpen(true); + }; + + const handleDrawerClose = () => { + setOpen(false); + }; + + return ( +
    + + + + + + + + Persistent drawer + + + + +
    + + {theme.direction === 'ltr' ? : } + +
    + + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + +
    +
    +
    + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Rhoncus dolor purus non enim praesent elementum + facilisis leo vel. Risus at ultrices mi tempus imperdiet. Semper risus in hendrerit + gravida rutrum quisque non tellus. Convallis convallis tellus id interdum velit laoreet id + donec ultrices. Odio morbi quis commodo odio aenean sed adipiscing. Amet nisl suscipit + adipiscing bibendum est ultricies integer quis. Cursus euismod quis viverra nibh cras. + Metus vulputate eu scelerisque felis imperdiet proin fermentum leo. Mauris commodo quis + imperdiet massa tincidunt. Cras tincidunt lobortis feugiat vivamus at augue. At augue eget + arcu dictum varius duis at consectetur lorem. Velit sed ullamcorper morbi tincidunt. Lorem + donec massa sapien faucibus et molestie ac. + + + Consequat mauris nunc congue nisi vitae suscipit. Fringilla est ullamcorper eget nulla + facilisi etiam dignissim diam. Pulvinar elementum integer enim neque volutpat ac + tincidunt. Ornare suspendisse sed nisi lacus sed viverra tellus. Purus sit amet volutpat + consequat mauris. Elementum eu facilisis sed odio morbi. Euismod lacinia at quis risus sed + vulputate odio. Morbi tincidunt ornare massa eget egestas purus viverra accumsan in. In + hendrerit gravida rutrum quisque non tellus orci ac. Pellentesque nec nam aliquam sem et + tortor. Habitant morbi tristique senectus et. Adipiscing elit duis tristique sollicitudin + nibh sit. Ornare aenean euismod elementum nisi quis eleifend. Commodo viverra maecenas + accumsan lacus vel facilisis. Nulla posuere sollicitudin aliquam ultrices sagittis orci a. + +
    +
    + ); +} + +export default { + title: "Material-ui|drawers|PersistentDrawerLeft.stories" +}; diff --git a/examples/storybook/stories/material-ui/drawers/PersistentDrawerRight.stories.tsx b/examples/storybook/stories/material-ui/drawers/PersistentDrawerRight.stories.tsx new file mode 100644 index 00000000000000..1de46a33ac3533 --- /dev/null +++ b/examples/storybook/stories/material-ui/drawers/PersistentDrawerRight.stories.tsx @@ -0,0 +1,188 @@ +import React from 'react'; +import clsx from 'clsx'; +import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles'; +import Drawer from '@material-ui/core/Drawer'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import List from '@material-ui/core/List'; +import Typography from '@material-ui/core/Typography'; +import Divider from '@material-ui/core/Divider'; +import IconButton from '@material-ui/core/IconButton'; +import MenuIcon from '@material-ui/icons/Menu'; +import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'; +import ChevronRightIcon from '@material-ui/icons/ChevronRight'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import InboxIcon from '@material-ui/icons/MoveToInbox'; +import MailIcon from '@material-ui/icons/Mail'; + +const drawerWidth = 240; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + }, + appBar: { + transition: theme.transitions.create(['margin', 'width'], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + appBarShift: { + width: `calc(100% - ${drawerWidth}px)`, + transition: theme.transitions.create(['margin', 'width'], { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + marginRight: drawerWidth, + }, + title: { + flexGrow: 1, + }, + hide: { + display: 'none', + }, + drawer: { + width: drawerWidth, + flexShrink: 0, + }, + drawerPaper: { + width: drawerWidth, + }, + drawerHeader: { + display: 'flex', + alignItems: 'center', + padding: theme.spacing(0, 1), + // necessary for content to be below app bar + ...theme.mixins.toolbar, + justifyContent: 'flex-start', + }, + content: { + flexGrow: 1, + padding: theme.spacing(3), + transition: theme.transitions.create('margin', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + marginRight: -drawerWidth, + }, + contentShift: { + transition: theme.transitions.create('margin', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + marginRight: 0, + }, + }), +); + +export function PersistentDrawerRight() { + const classes = useStyles(); + const theme = useTheme(); + const [open, setOpen] = React.useState(false); + + const handleDrawerOpen = () => { + setOpen(true); + }; + + const handleDrawerClose = () => { + setOpen(false); + }; + + return ( +
    + + + + + Persistent drawer + + + + + + +
    +
    + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Rhoncus dolor purus non enim praesent elementum + facilisis leo vel. Risus at ultrices mi tempus imperdiet. Semper risus in hendrerit + gravida rutrum quisque non tellus. Convallis convallis tellus id interdum velit laoreet id + donec ultrices. Odio morbi quis commodo odio aenean sed adipiscing. Amet nisl suscipit + adipiscing bibendum est ultricies integer quis. Cursus euismod quis viverra nibh cras. + Metus vulputate eu scelerisque felis imperdiet proin fermentum leo. Mauris commodo quis + imperdiet massa tincidunt. Cras tincidunt lobortis feugiat vivamus at augue. At augue eget + arcu dictum varius duis at consectetur lorem. Velit sed ullamcorper morbi tincidunt. Lorem + donec massa sapien faucibus et molestie ac. + + + Consequat mauris nunc congue nisi vitae suscipit. Fringilla est ullamcorper eget nulla + facilisi etiam dignissim diam. Pulvinar elementum integer enim neque volutpat ac + tincidunt. Ornare suspendisse sed nisi lacus sed viverra tellus. Purus sit amet volutpat + consequat mauris. Elementum eu facilisis sed odio morbi. Euismod lacinia at quis risus sed + vulputate odio. Morbi tincidunt ornare massa eget egestas purus viverra accumsan in. In + hendrerit gravida rutrum quisque non tellus orci ac. Pellentesque nec nam aliquam sem et + tortor. Habitant morbi tristique senectus et. Adipiscing elit duis tristique sollicitudin + nibh sit. Ornare aenean euismod elementum nisi quis eleifend. Commodo viverra maecenas + accumsan lacus vel facilisis. Nulla posuere sollicitudin aliquam ultrices sagittis orci a. + +
    + +
    + + {theme.direction === 'rtl' ? : } + +
    + + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + +
    +
    + ); +} + +export default { + title: "Material-ui|drawers|PersistentDrawerRight.stories" +}; diff --git a/examples/storybook/stories/material-ui/drawers/ResponsiveDrawer.stories.tsx b/examples/storybook/stories/material-ui/drawers/ResponsiveDrawer.stories.tsx new file mode 100644 index 00000000000000..605607024ac420 --- /dev/null +++ b/examples/storybook/stories/material-ui/drawers/ResponsiveDrawer.stories.tsx @@ -0,0 +1,182 @@ +import React from 'react'; +import AppBar from '@material-ui/core/AppBar'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import Divider from '@material-ui/core/Divider'; +import Drawer from '@material-ui/core/Drawer'; +import Hidden from '@material-ui/core/Hidden'; +import IconButton from '@material-ui/core/IconButton'; +import InboxIcon from '@material-ui/icons/MoveToInbox'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import MailIcon from '@material-ui/icons/Mail'; +import MenuIcon from '@material-ui/icons/Menu'; +import Toolbar from '@material-ui/core/Toolbar'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles'; + +const drawerWidth = 240; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + }, + drawer: { + [theme.breakpoints.up('sm')]: { + width: drawerWidth, + flexShrink: 0, + }, + }, + appBar: { + [theme.breakpoints.up('sm')]: { + width: `calc(100% - ${drawerWidth}px)`, + marginLeft: drawerWidth, + }, + }, + menuButton: { + marginRight: theme.spacing(2), + [theme.breakpoints.up('sm')]: { + display: 'none', + }, + }, + // necessary for content to be below app bar + toolbar: theme.mixins.toolbar, + drawerPaper: { + width: drawerWidth, + }, + content: { + flexGrow: 1, + padding: theme.spacing(3), + }, + }), +); + +interface Props { + /** + * Injected by the documentation to work in an iframe. + * You won't need it on your project. + */ + window?: () => Window; +} + +export function ResponsiveDrawer(props: Props) { + const { window } = props; + const classes = useStyles(); + const theme = useTheme(); + const [mobileOpen, setMobileOpen] = React.useState(false); + + const handleDrawerToggle = () => { + setMobileOpen(!mobileOpen); + }; + + const drawer = ( +
    +
    + + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + +
    + ); + + const container = window !== undefined ? () => window().document.body : undefined; + + return ( +
    + + + + + + + + Responsive drawer + + + + +
    +
    + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Rhoncus dolor purus non enim praesent elementum + facilisis leo vel. Risus at ultrices mi tempus imperdiet. Semper risus in hendrerit + gravida rutrum quisque non tellus. Convallis convallis tellus id interdum velit laoreet id + donec ultrices. Odio morbi quis commodo odio aenean sed adipiscing. Amet nisl suscipit + adipiscing bibendum est ultricies integer quis. Cursus euismod quis viverra nibh cras. + Metus vulputate eu scelerisque felis imperdiet proin fermentum leo. Mauris commodo quis + imperdiet massa tincidunt. Cras tincidunt lobortis feugiat vivamus at augue. At augue eget + arcu dictum varius duis at consectetur lorem. Velit sed ullamcorper morbi tincidunt. Lorem + donec massa sapien faucibus et molestie ac. + + + Consequat mauris nunc congue nisi vitae suscipit. Fringilla est ullamcorper eget nulla + facilisi etiam dignissim diam. Pulvinar elementum integer enim neque volutpat ac + tincidunt. Ornare suspendisse sed nisi lacus sed viverra tellus. Purus sit amet volutpat + consequat mauris. Elementum eu facilisis sed odio morbi. Euismod lacinia at quis risus sed + vulputate odio. Morbi tincidunt ornare massa eget egestas purus viverra accumsan in. In + hendrerit gravida rutrum quisque non tellus orci ac. Pellentesque nec nam aliquam sem et + tortor. Habitant morbi tristique senectus et. Adipiscing elit duis tristique sollicitudin + nibh sit. Ornare aenean euismod elementum nisi quis eleifend. Commodo viverra maecenas + accumsan lacus vel facilisis. Nulla posuere sollicitudin aliquam ultrices sagittis orci a. + +
    +
    + ); +} + +export default { + title: "Material-ui|drawers|ResponsiveDrawer.stories" +}; diff --git a/examples/storybook/stories/material-ui/drawers/SwipeableTemporaryDrawer.stories.tsx b/examples/storybook/stories/material-ui/drawers/SwipeableTemporaryDrawer.stories.tsx new file mode 100644 index 00000000000000..9faa9b344ed7fe --- /dev/null +++ b/examples/storybook/stories/material-ui/drawers/SwipeableTemporaryDrawer.stories.tsx @@ -0,0 +1,99 @@ +import React from 'react'; +import clsx from 'clsx'; +import { makeStyles } from '@material-ui/core/styles'; +import SwipeableDrawer from '@material-ui/core/SwipeableDrawer'; +import Button from '@material-ui/core/Button'; +import List from '@material-ui/core/List'; +import Divider from '@material-ui/core/Divider'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import InboxIcon from '@material-ui/icons/MoveToInbox'; +import MailIcon from '@material-ui/icons/Mail'; + +const useStyles = makeStyles({ + list: { + width: 250, + }, + fullList: { + width: 'auto', + }, +}); + +type Anchor = 'top' | 'left' | 'bottom' | 'right'; + +export function SwipeableTemporaryDrawer() { + const classes = useStyles(); + const [state, setState] = React.useState({ + top: false, + left: false, + bottom: false, + right: false, + }); + + const toggleDrawer = (anchor: Anchor, open: boolean) => ( + event: React.KeyboardEvent | React.MouseEvent, + ) => { + if ( + event && + event.type === 'keydown' && + ((event as React.KeyboardEvent).key === 'Tab' || + (event as React.KeyboardEvent).key === 'Shift') + ) { + return; + } + + setState({ ...state, [anchor]: open }); + }; + + const list = (anchor: Anchor) => ( +
    + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + +
    + ); + + return ( +
    + {(['left', 'right', 'top', 'bottom'] as Anchor[]).map((anchor) => ( + + + + {list(anchor)} + + + ))} +
    + ); +} + +export default { + title: "Material-ui|drawers|SwipeableTemporaryDrawer.stories" +}; diff --git a/examples/storybook/stories/material-ui/drawers/TemporaryDrawer.stories.tsx b/examples/storybook/stories/material-ui/drawers/TemporaryDrawer.stories.tsx new file mode 100644 index 00000000000000..9e24bad14ccc6d --- /dev/null +++ b/examples/storybook/stories/material-ui/drawers/TemporaryDrawer.stories.tsx @@ -0,0 +1,93 @@ +import React from 'react'; +import clsx from 'clsx'; +import { makeStyles } from '@material-ui/core/styles'; +import Drawer from '@material-ui/core/Drawer'; +import Button from '@material-ui/core/Button'; +import List from '@material-ui/core/List'; +import Divider from '@material-ui/core/Divider'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import InboxIcon from '@material-ui/icons/MoveToInbox'; +import MailIcon from '@material-ui/icons/Mail'; + +const useStyles = makeStyles({ + list: { + width: 250, + }, + fullList: { + width: 'auto', + }, +}); + +type Anchor = 'top' | 'left' | 'bottom' | 'right'; + +export function TemporaryDrawer() { + const classes = useStyles(); + const [state, setState] = React.useState({ + top: false, + left: false, + bottom: false, + right: false, + }); + + const toggleDrawer = (anchor: Anchor, open: boolean) => ( + event: React.KeyboardEvent | React.MouseEvent, + ) => { + if ( + event.type === 'keydown' && + ((event as React.KeyboardEvent).key === 'Tab' || + (event as React.KeyboardEvent).key === 'Shift') + ) { + return; + } + + setState({ ...state, [anchor]: open }); + }; + + const list = (anchor: Anchor) => ( +
    + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text, index) => ( + + {index % 2 === 0 ? : } + + + ))} + +
    + ); + + return ( +
    + {(['left', 'right', 'top', 'bottom'] as Anchor[]).map((anchor) => ( + + + + {list(anchor)} + + + ))} +
    + ); +} + +export default { + title: "Material-ui|drawers|TemporaryDrawer.stories" +}; diff --git a/examples/storybook/stories/material-ui/expansion-panels/ActionsInExpansionPanelSummary.stories.tsx b/examples/storybook/stories/material-ui/expansion-panels/ActionsInExpansionPanelSummary.stories.tsx new file mode 100644 index 00000000000000..e255172ba6d5cf --- /dev/null +++ b/examples/storybook/stories/material-ui/expansion-panels/ActionsInExpansionPanelSummary.stories.tsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import ExpansionPanel from '@material-ui/core/ExpansionPanel'; +import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary'; +import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails'; +import Checkbox from '@material-ui/core/Checkbox'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import Typography from '@material-ui/core/Typography'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; + +const useStyles = makeStyles({ + root: { + width: '100%', + }, +}); + +export function ActionsInExpansionPanelSummary() { + const classes = useStyles(); + + return ( +
    + + } + aria-label="Expand" + aria-controls="additional-actions1-content" + id="additional-actions1-header" + > + event.stopPropagation()} + onFocus={(event) => event.stopPropagation()} + control={} + label="I acknowledge that I should stop the click event propagation" + /> + + + + The click event of the nested action will propagate up and expand the panel unless you + explicitly stop it. + + + + + } + aria-label="Expand" + aria-controls="additional-actions2-content" + id="additional-actions2-header" + > + event.stopPropagation()} + onFocus={(event) => event.stopPropagation()} + control={} + label="I acknowledge that I should stop the focus event propagation" + /> + + + + The focus event of the nested action will propagate up and also focus the expansion + panel unless you explicitly stop it. + + + + + } + aria-label="Expand" + aria-controls="additional-actions3-content" + id="additional-actions3-header" + > + event.stopPropagation()} + onFocus={(event) => event.stopPropagation()} + control={} + label="I acknowledge that I should provide an aria-label on each action that I add" + /> + + + + If you forget to put an aria-label on the nested action, the label of the action will + also be included in the label of the parent button that controls the panel expansion. + + + +
    + ); +} + +export default { + title: "Material-ui|expansion-panels|ActionsInExpansionPanelSummary.stories" +}; diff --git a/examples/storybook/stories/material-ui/expansion-panels/ControlledExpansionPanels.stories.tsx b/examples/storybook/stories/material-ui/expansion-panels/ControlledExpansionPanels.stories.tsx new file mode 100644 index 00000000000000..cac3bd4d092223 --- /dev/null +++ b/examples/storybook/stories/material-ui/expansion-panels/ControlledExpansionPanels.stories.tsx @@ -0,0 +1,109 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import ExpansionPanel from '@material-ui/core/ExpansionPanel'; +import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails'; +import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary'; +import Typography from '@material-ui/core/Typography'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + }, + heading: { + fontSize: theme.typography.pxToRem(15), + flexBasis: '33.33%', + flexShrink: 0, + }, + secondaryHeading: { + fontSize: theme.typography.pxToRem(15), + color: theme.palette.text.secondary, + }, + }), +); + +export function ControlledExpansionPanels() { + const classes = useStyles(); + const [expanded, setExpanded] = React.useState(false); + + const handleChange = (panel: string) => (event: React.ChangeEvent<{}>, isExpanded: boolean) => { + setExpanded(isExpanded ? panel : false); + }; + + return ( +
    + + } + aria-controls="panel1bh-content" + id="panel1bh-header" + > + General settings + I am an expansion panel + + + + Nulla facilisi. Phasellus sollicitudin nulla et quam mattis feugiat. Aliquam eget + maximus est, id dignissim quam. + + + + + } + aria-controls="panel2bh-content" + id="panel2bh-header" + > + Users + + You are currently not an owner + + + + + Donec placerat, lectus sed mattis semper, neque lectus feugiat lectus, varius pulvinar + diam eros in elit. Pellentesque convallis laoreet laoreet. + + + + + } + aria-controls="panel3bh-content" + id="panel3bh-header" + > + Advanced settings + + Filtering has been entirely disabled for whole web server + + + + + Nunc vitae orci ultricies, auctor nunc in, volutpat nisl. Integer sit amet egestas eros, + vitae egestas augue. Duis vel est augue. + + + + + } + aria-controls="panel4bh-content" + id="panel4bh-header" + > + Personal data + + + + Nunc vitae orci ultricies, auctor nunc in, volutpat nisl. Integer sit amet egestas eros, + vitae egestas augue. Duis vel est augue. + + + +
    + ); +} + +export default { + title: "Material-ui|expansion-panels|ControlledExpansionPanels.stories" +}; diff --git a/examples/storybook/stories/material-ui/expansion-panels/CustomizedExpansionPanels.stories.tsx b/examples/storybook/stories/material-ui/expansion-panels/CustomizedExpansionPanels.stories.tsx new file mode 100644 index 00000000000000..478674d58855e0 --- /dev/null +++ b/examples/storybook/stories/material-ui/expansion-panels/CustomizedExpansionPanels.stories.tsx @@ -0,0 +1,100 @@ +import React from 'react'; +import { withStyles } from '@material-ui/core/styles'; +import MuiExpansionPanel from '@material-ui/core/ExpansionPanel'; +import MuiExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary'; +import MuiExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails'; +import Typography from '@material-ui/core/Typography'; + +const ExpansionPanel = withStyles({ + root: { + border: '1px solid rgba(0, 0, 0, .125)', + boxShadow: 'none', + '&:not(:last-child)': { + borderBottom: 0, + }, + '&:before': { + display: 'none', + }, + '&$expanded': { + margin: 'auto', + }, + }, + expanded: {}, +})(MuiExpansionPanel); + +const ExpansionPanelSummary = withStyles({ + root: { + backgroundColor: 'rgba(0, 0, 0, .03)', + borderBottom: '1px solid rgba(0, 0, 0, .125)', + marginBottom: -1, + minHeight: 56, + '&$expanded': { + minHeight: 56, + }, + }, + content: { + '&$expanded': { + margin: '12px 0', + }, + }, + expanded: {}, +})(MuiExpansionPanelSummary); + +const ExpansionPanelDetails = withStyles((theme) => ({ + root: { + padding: theme.spacing(2), + }, +}))(MuiExpansionPanelDetails); + +export function CustomizedExpansionPanels() { + const [expanded, setExpanded] = React.useState('panel1'); + + const handleChange = (panel: string) => (event: React.ChangeEvent<{}>, newExpanded: boolean) => { + setExpanded(newExpanded ? panel : false); + }; + + return ( +
    + + + Collapsible Group Item #1 + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, + sit amet blandit leo lobortis eget. Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget. + + + + + + Collapsible Group Item #2 + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, + sit amet blandit leo lobortis eget. Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget. + + + + + + Collapsible Group Item #3 + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, + sit amet blandit leo lobortis eget. Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget. + + + +
    + ); +} + +export default { + title: "Material-ui|expansion-panels|CustomizedExpansionPanels.stories" +}; diff --git a/examples/storybook/stories/material-ui/expansion-panels/DetailedExpansionPanel.stories.tsx b/examples/storybook/stories/material-ui/expansion-panels/DetailedExpansionPanel.stories.tsx new file mode 100644 index 00000000000000..516c11556bf8c5 --- /dev/null +++ b/examples/storybook/stories/material-ui/expansion-panels/DetailedExpansionPanel.stories.tsx @@ -0,0 +1,98 @@ +import React from 'react'; +import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; +import clsx from 'clsx'; +import ExpansionPanel from '@material-ui/core/ExpansionPanel'; +import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails'; +import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary'; +import ExpansionPanelActions from '@material-ui/core/ExpansionPanelActions'; +import Typography from '@material-ui/core/Typography'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import Chip from '@material-ui/core/Chip'; +import Button from '@material-ui/core/Button'; +import Divider from '@material-ui/core/Divider'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + }, + heading: { + fontSize: theme.typography.pxToRem(15), + }, + secondaryHeading: { + fontSize: theme.typography.pxToRem(15), + color: theme.palette.text.secondary, + }, + icon: { + verticalAlign: 'bottom', + height: 20, + width: 20, + }, + details: { + alignItems: 'center', + }, + column: { + flexBasis: '33.33%', + }, + helper: { + borderLeft: `2px solid ${theme.palette.divider}`, + padding: theme.spacing(1, 2), + }, + link: { + color: theme.palette.primary.main, + textDecoration: 'none', + '&:hover': { + textDecoration: 'underline', + }, + }, + }), +); + +export function DetailedExpansionPanel() { + const classes = useStyles(); + + return ( +
    + + } + aria-controls="panel1c-content" + id="panel1c-header" + > +
    + Location +
    +
    + Select trip destination +
    +
    + +
    +
    + {}} /> +
    +
    + + Select your destination of choice +
    + + Learn more + +
    +
    + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|expansion-panels|DetailedExpansionPanel.stories" +}; diff --git a/examples/storybook/stories/material-ui/expansion-panels/SimpleExpansionPanel.stories.tsx b/examples/storybook/stories/material-ui/expansion-panels/SimpleExpansionPanel.stories.tsx new file mode 100644 index 00000000000000..f565e87943a146 --- /dev/null +++ b/examples/storybook/stories/material-ui/expansion-panels/SimpleExpansionPanel.stories.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; +import ExpansionPanel from '@material-ui/core/ExpansionPanel'; +import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary'; +import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails'; +import Typography from '@material-ui/core/Typography'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + }, + heading: { + fontSize: theme.typography.pxToRem(15), + fontWeight: theme.typography.fontWeightRegular, + }, + }), +); + +export function SimpleExpansionPanel() { + const classes = useStyles(); + + return ( +
    + + } + aria-controls="panel1a-content" + id="panel1a-header" + > + Expansion Panel 1 + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, + sit amet blandit leo lobortis eget. + + + + + } + aria-controls="panel2a-content" + id="panel2a-header" + > + Expansion Panel 2 + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, + sit amet blandit leo lobortis eget. + + + + + } + aria-controls="panel3a-content" + id="panel3a-header" + > + Disabled Expansion Panel + + +
    + ); +} + +export default { + title: "Material-ui|expansion-panels|SimpleExpansionPanel.stories" +}; diff --git a/examples/storybook/stories/material-ui/floating-action-button/FloatingActionButtonSize.stories.tsx b/examples/storybook/stories/material-ui/floating-action-button/FloatingActionButtonSize.stories.tsx new file mode 100644 index 00000000000000..719972f1937bde --- /dev/null +++ b/examples/storybook/stories/material-ui/floating-action-button/FloatingActionButtonSize.stories.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import Fab from '@material-ui/core/Fab'; +import AddIcon from '@material-ui/icons/Add'; +import NavigationIcon from '@material-ui/icons/Navigation'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + margin: { + margin: theme.spacing(1), + }, + extendedIcon: { + marginRight: theme.spacing(1), + }, + }), +); + +export function FloatingActionButtonSize() { + const classes = useStyles(); + + return ( +
    +
    + + + + + + + + + +
    +
    + + + Extended + + + + Extended + + + + Extended + +
    +
    + ); +} + +export default { + title: "Material-ui|floating-action-button|FloatingActionButtonSize.stories" +}; diff --git a/examples/storybook/stories/material-ui/floating-action-button/FloatingActionButtonZoom.stories.tsx b/examples/storybook/stories/material-ui/floating-action-button/FloatingActionButtonZoom.stories.tsx new file mode 100644 index 00000000000000..916e4877017af9 --- /dev/null +++ b/examples/storybook/stories/material-ui/floating-action-button/FloatingActionButtonZoom.stories.tsx @@ -0,0 +1,162 @@ +import React from 'react'; +import clsx from 'clsx'; +import SwipeableViews from 'react-swipeable-views'; +import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import Typography from '@material-ui/core/Typography'; +import Zoom from '@material-ui/core/Zoom'; +import Fab from '@material-ui/core/Fab'; +import AddIcon from '@material-ui/icons/Add'; +import EditIcon from '@material-ui/icons/Edit'; +import UpIcon from '@material-ui/icons/KeyboardArrowUp'; +import { green } from '@material-ui/core/colors'; +import Box from '@material-ui/core/Box'; + +interface TabPanelProps { + children?: React.ReactNode; + dir?: string; + index: any; + value: any; +} + +function TabPanel(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +function a11yProps(index: any) { + return { + id: `action-tab-${index}`, + 'aria-controls': `action-tabpanel-${index}`, + }; +} + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + backgroundColor: theme.palette.background.paper, + width: 500, + position: 'relative', + minHeight: 200, + }, + fab: { + position: 'absolute', + bottom: theme.spacing(2), + right: theme.spacing(2), + }, + fabGreen: { + color: theme.palette.common.white, + backgroundColor: green[500], + '&:hover': { + backgroundColor: green[600], + }, + }, + }), +); + +export function FloatingActionButtonZoom() { + const classes = useStyles(); + const theme = useTheme(); + const [value, setValue] = React.useState(0); + + const handleChange = (event: unknown, newValue: number) => { + setValue(newValue); + }; + + const handleChangeIndex = (index: number) => { + setValue(index); + }; + + const transitionDuration = { + enter: theme.transitions.duration.enteringScreen, + exit: theme.transitions.duration.leavingScreen, + }; + + const fabs = [ + { + color: 'primary' as 'primary', + className: classes.fab, + icon: , + label: 'Add', + }, + { + color: 'secondary' as 'secondary', + className: classes.fab, + icon: , + label: 'Edit', + }, + { + color: 'inherit' as 'inherit', + className: clsx(classes.fab, classes.fabGreen), + icon: , + label: 'Expand', + }, + ]; + + return ( +
    + + + + + + + + + + Item One + + + Item Two + + + Item Three + + + {fabs.map((fab, index) => ( + + + {fab.icon} + + + ))} +
    + ); +} + +export default { + title: "Material-ui|floating-action-button|FloatingActionButtonZoom.stories" +}; diff --git a/examples/storybook/stories/material-ui/floating-action-button/FloatingActionButtons.stories.tsx b/examples/storybook/stories/material-ui/floating-action-button/FloatingActionButtons.stories.tsx new file mode 100644 index 00000000000000..b1d562d30b2006 --- /dev/null +++ b/examples/storybook/stories/material-ui/floating-action-button/FloatingActionButtons.stories.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import Fab from '@material-ui/core/Fab'; +import AddIcon from '@material-ui/icons/Add'; +import EditIcon from '@material-ui/icons/Edit'; +import FavoriteIcon from '@material-ui/icons/Favorite'; +import NavigationIcon from '@material-ui/icons/Navigation'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + }, + }, + extendedIcon: { + marginRight: theme.spacing(1), + }, + }), +); + +export function FloatingActionButtons() { + const classes = useStyles(); + + return ( +
    + + + + + + + + + Navigate + + + + +
    + ); +} + +export default { + title: "Material-ui|floating-action-button|FloatingActionButtons.stories" +}; diff --git a/examples/storybook/stories/material-ui/grid/AutoGrid.stories.tsx b/examples/storybook/stories/material-ui/grid/AutoGrid.stories.tsx new file mode 100644 index 00000000000000..473f3420194e9a --- /dev/null +++ b/examples/storybook/stories/material-ui/grid/AutoGrid.stories.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Paper from '@material-ui/core/Paper'; +import Grid from '@material-ui/core/Grid'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + }, + paper: { + padding: theme.spacing(2), + textAlign: 'center', + color: theme.palette.text.secondary, + }, + }), +); + +export function AutoGrid() { + const classes = useStyles(); + + return ( +
    + + + xs + + + xs + + + xs + + + + + xs + + + xs=6 + + + xs + + +
    + ); +} + +export default { + title: "Material-ui|grid|AutoGrid.stories" +}; diff --git a/examples/storybook/stories/material-ui/grid/AutoGridNoWrap.stories.tsx b/examples/storybook/stories/material-ui/grid/AutoGridNoWrap.stories.tsx new file mode 100644 index 00000000000000..01524492e8a4a4 --- /dev/null +++ b/examples/storybook/stories/material-ui/grid/AutoGridNoWrap.stories.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import Paper from '@material-ui/core/Paper'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Grid from '@material-ui/core/Grid'; +import Avatar from '@material-ui/core/Avatar'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + overflow: 'hidden', + padding: theme.spacing(0, 3), + }, + paper: { + maxWidth: 400, + margin: `${theme.spacing(1)}px auto`, + padding: theme.spacing(2), + }, + }), +); + +const message = `Truncation should be conditionally applicable on this long line of text + as this is a much longer line than what the container can support. `; + +export function AutoGridNoWrap() { + const classes = useStyles(); + + return ( +
    + + + + W + + + {message} + + + + + + + W + + + {message} + + + + + + + W + + + {message} + + + +
    + ); +} + +export default { + title: "Material-ui|grid|AutoGridNoWrap.stories" +}; diff --git a/examples/storybook/stories/material-ui/grid/CSSGrid.stories.tsx b/examples/storybook/stories/material-ui/grid/CSSGrid.stories.tsx new file mode 100644 index 00000000000000..8d63737a7365b3 --- /dev/null +++ b/examples/storybook/stories/material-ui/grid/CSSGrid.stories.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Paper from '@material-ui/core/Paper'; +import Divider from '@material-ui/core/Divider'; +import Grid from '@material-ui/core/Grid'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + container: { + display: 'grid', + gridTemplateColumns: 'repeat(12, 1fr)', + gridGap: theme.spacing(3), + }, + paper: { + padding: theme.spacing(1), + textAlign: 'center', + color: theme.palette.text.secondary, + whiteSpace: 'nowrap', + marginBottom: theme.spacing(1), + }, + divider: { + margin: theme.spacing(2, 0), + }, + }), +); + +export function CSSGrid() { + const classes = useStyles(); + + return ( +
    + + Material-UI Grid: + + + + xs=3 + + + xs=3 + + + xs=3 + + + xs=3 + + + xs=8 + + + xs=4 + + + + + CSS Grid Layout: + +
    +
    + xs=3 +
    +
    + xs=3 +
    +
    + xs=3 +
    +
    + xs=3 +
    +
    + xs=8 +
    +
    + xs=4 +
    +
    +
    + ); +} + +export default { + title: "Material-ui|grid|CSSGrid.stories" +}; diff --git a/examples/storybook/stories/material-ui/grid/CenteredGrid.stories.tsx b/examples/storybook/stories/material-ui/grid/CenteredGrid.stories.tsx new file mode 100644 index 00000000000000..727addfa16fde2 --- /dev/null +++ b/examples/storybook/stories/material-ui/grid/CenteredGrid.stories.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Paper from '@material-ui/core/Paper'; +import Grid from '@material-ui/core/Grid'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + }, + paper: { + padding: theme.spacing(2), + textAlign: 'center', + color: theme.palette.text.secondary, + }, + }), +); + +export function CenteredGrid() { + const classes = useStyles(); + + return ( +
    + + + xs=12 + + + xs=6 + + + xs=6 + + + xs=3 + + + xs=3 + + + xs=3 + + + xs=3 + + +
    + ); +} + +export default { + title: "Material-ui|grid|CenteredGrid.stories" +}; diff --git a/examples/storybook/stories/material-ui/grid/ComplexGrid.stories.tsx b/examples/storybook/stories/material-ui/grid/ComplexGrid.stories.tsx new file mode 100644 index 00000000000000..448f5db6e2ff9c --- /dev/null +++ b/examples/storybook/stories/material-ui/grid/ComplexGrid.stories.tsx @@ -0,0 +1,74 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Grid from '@material-ui/core/Grid'; +import Paper from '@material-ui/core/Paper'; +import Typography from '@material-ui/core/Typography'; +import ButtonBase from '@material-ui/core/ButtonBase'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + }, + paper: { + padding: theme.spacing(2), + margin: 'auto', + maxWidth: 500, + }, + image: { + width: 128, + height: 128, + }, + img: { + margin: 'auto', + display: 'block', + maxWidth: '100%', + maxHeight: '100%', + }, + }), +); + +export function ComplexGrid() { + const classes = useStyles(); + + return ( +
    + + + + + complex + + + + + + + Standard license + + + Full resolution 1920x1080 • JPEG + + + ID: 1030114 + + + + + Remove + + + + + $19.00 + + + + +
    + ); +} + +export default { + title: "Material-ui|grid|ComplexGrid.stories" +}; diff --git a/examples/storybook/stories/material-ui/grid/FullWidthGrid.stories.tsx b/examples/storybook/stories/material-ui/grid/FullWidthGrid.stories.tsx new file mode 100644 index 00000000000000..28851bc8b3a7af --- /dev/null +++ b/examples/storybook/stories/material-ui/grid/FullWidthGrid.stories.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Paper from '@material-ui/core/Paper'; +import Grid from '@material-ui/core/Grid'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + }, + paper: { + padding: theme.spacing(2), + textAlign: 'center', + color: theme.palette.text.secondary, + }, + }), +); + +export function FullWidthGrid() { + const classes = useStyles(); + + return ( +
    + + + xs=12 + + + xs=12 sm=6 + + + xs=12 sm=6 + + + xs=6 sm=3 + + + xs=6 sm=3 + + + xs=6 sm=3 + + + xs=6 sm=3 + + +
    + ); +} + +export default { + title: "Material-ui|grid|FullWidthGrid.stories" +}; diff --git a/examples/storybook/stories/material-ui/grid/NestedGrid.stories.tsx b/examples/storybook/stories/material-ui/grid/NestedGrid.stories.tsx new file mode 100644 index 00000000000000..d30a03bca1b9e1 --- /dev/null +++ b/examples/storybook/stories/material-ui/grid/NestedGrid.stories.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Paper from '@material-ui/core/Paper'; +import Grid from '@material-ui/core/Grid'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + }, + paper: { + padding: theme.spacing(1), + textAlign: 'center', + color: theme.palette.text.secondary, + }, + }), +); + +export function NestedGrid() { + const classes = useStyles(); + + function FormRow() { + return ( + + + item + + + item + + + item + + + ); + } + + return ( +
    + + + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|grid|NestedGrid.stories" +}; diff --git a/examples/storybook/stories/material-ui/grid/SpacingGrid.stories.tsx b/examples/storybook/stories/material-ui/grid/SpacingGrid.stories.tsx new file mode 100644 index 00000000000000..d3a4bbaffa76c4 --- /dev/null +++ b/examples/storybook/stories/material-ui/grid/SpacingGrid.stories.tsx @@ -0,0 +1,75 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Grid, { GridSpacing } from '@material-ui/core/Grid'; +import FormLabel from '@material-ui/core/FormLabel'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import RadioGroup from '@material-ui/core/RadioGroup'; +import Radio from '@material-ui/core/Radio'; +import Paper from '@material-ui/core/Paper'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + }, + paper: { + height: 140, + width: 100, + }, + control: { + padding: theme.spacing(2), + }, + }), +); + +export function SpacingGrid() { + const [spacing, setSpacing] = React.useState(2); + const classes = useStyles(); + + const handleChange = (event: React.ChangeEvent) => { + setSpacing(Number((event.target as HTMLInputElement).value) as GridSpacing); + }; + + return ( + + + + {[0, 1, 2].map((value) => ( + + + + ))} + + + + + + + spacing + + {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((value) => ( + } + label={value.toString()} + /> + ))} + + + + + + + ); +} + +export default { + title: "Material-ui|grid|SpacingGrid.stories" +}; diff --git a/examples/storybook/stories/material-ui/icons/FontAwesome.stories.tsx b/examples/storybook/stories/material-ui/icons/FontAwesome.stories.tsx new file mode 100644 index 00000000000000..8f80aa7679e64d --- /dev/null +++ b/examples/storybook/stories/material-ui/icons/FontAwesome.stories.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { loadCSS } from 'fg-loadcss'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import { green } from '@material-ui/core/colors'; +import Icon from '@material-ui/core/Icon'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > .fa': { + margin: theme.spacing(2), + }, + }, + }), +); + +export function FontAwesome() { + const classes = useStyles(); + + React.useEffect(() => { + const node = loadCSS( + 'https://use.fontawesome.com/releases/v5.12.0/css/all.css', + document.querySelector('#font-awesome-css'), + ); + + return () => { + node.parentNode!.removeChild(node); + }; + }, []); + + return ( +
    + + + + + + +
    + ); +} + +export default { + title: "Material-ui|icons|FontAwesome.stories" +}; diff --git a/examples/storybook/stories/material-ui/icons/Icons.stories.tsx b/examples/storybook/stories/material-ui/icons/Icons.stories.tsx new file mode 100644 index 00000000000000..d8da931cb70578 --- /dev/null +++ b/examples/storybook/stories/material-ui/icons/Icons.stories.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import { green } from '@material-ui/core/colors'; +import Icon from '@material-ui/core/Icon'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > span': { + margin: theme.spacing(2), + }, + }, + }), +); + +export function Icons() { + const classes = useStyles(); + + return ( +
    + add_circle + add_circle + add_circle + add_circle + add_circle + add_circle +
    + ); +} + +export default { + title: "Material-ui|icons|Icons.stories" +}; diff --git a/examples/storybook/stories/material-ui/icons/SvgIconsColor.stories.tsx b/examples/storybook/stories/material-ui/icons/SvgIconsColor.stories.tsx new file mode 100644 index 00000000000000..a7d5ffc620e5c0 --- /dev/null +++ b/examples/storybook/stories/material-ui/icons/SvgIconsColor.stories.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import { green } from '@material-ui/core/colors'; +import SvgIcon, { SvgIconProps } from '@material-ui/core/SvgIcon'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > svg': { + margin: theme.spacing(2), + }, + }, + }), +); + +function HomeIcon(props: SvgIconProps) { + return ( + + + + ); +} + +export function SvgIconsColor() { + const classes = useStyles(); + + return ( +
    + + + + + + +
    + ); +} + +export default { + title: "Material-ui|icons|SvgIconsColor.stories" +}; diff --git a/examples/storybook/stories/material-ui/icons/SvgIconsSize.stories.tsx b/examples/storybook/stories/material-ui/icons/SvgIconsSize.stories.tsx new file mode 100644 index 00000000000000..f52c7e9bf1e28d --- /dev/null +++ b/examples/storybook/stories/material-ui/icons/SvgIconsSize.stories.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import SvgIcon, { SvgIconProps } from '@material-ui/core/SvgIcon'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > svg': { + margin: theme.spacing(2), + }, + }, + }), +); + +function HomeIcon(props: SvgIconProps) { + return ( + + + + ); +} + +export function SvgIconsSize() { + const classes = useStyles(); + + return ( +
    + + + + +
    + ); +} + +export default { + title: "Material-ui|icons|SvgIconsSize.stories" +}; diff --git a/examples/storybook/stories/material-ui/icons/SvgMaterialIcons.stories.tsx b/examples/storybook/stories/material-ui/icons/SvgMaterialIcons.stories.tsx new file mode 100644 index 00000000000000..f36734b05615a6 --- /dev/null +++ b/examples/storybook/stories/material-ui/icons/SvgMaterialIcons.stories.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +import DeleteIcon from '@material-ui/icons/Delete'; +import DeleteOutlinedIcon from '@material-ui/icons/DeleteOutlined'; +import DeleteRoundedIcon from '@material-ui/icons/DeleteRounded'; +import DeleteTwoToneIcon from '@material-ui/icons/DeleteTwoTone'; +import DeleteSharpIcon from '@material-ui/icons/DeleteSharp'; +import DeleteForeverIcon from '@material-ui/icons/DeleteForever'; +import DeleteForeverOutlinedIcon from '@material-ui/icons/DeleteForeverOutlined'; +import DeleteForeverRoundedIcon from '@material-ui/icons/DeleteForeverRounded'; +import DeleteForeverTwoToneIcon from '@material-ui/icons/DeleteForeverTwoTone'; +import DeleteForeverSharpIcon from '@material-ui/icons/DeleteForeverSharp'; +import ThreeDRotationIcon from '@material-ui/icons/ThreeDRotation'; +import FourKIcon from '@material-ui/icons/FourK'; +import ThreeSixtyIcon from '@material-ui/icons/ThreeSixty'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + color: theme.palette.text.primary, + }, + }), +); + +export function SvgMaterialIcons() { + const classes = useStyles(); + + return ( + + + Filled + + + + + + + Outlined + + + + + + + Rounded + + + + + + + Two Tone + + + + + + + Sharp + + + + + + + Edge-cases + + + + + + + + ); +} + +export default { + title: "Material-ui|icons|SvgMaterialIcons.stories" +}; diff --git a/examples/storybook/stories/material-ui/links/ButtonLink.stories.tsx b/examples/storybook/stories/material-ui/links/ButtonLink.stories.tsx new file mode 100644 index 00000000000000..e1fa3bd9b49bde --- /dev/null +++ b/examples/storybook/stories/material-ui/links/ButtonLink.stories.tsx @@ -0,0 +1,21 @@ +/* eslint-disable jsx-a11y/anchor-is-valid */ +import React from 'react'; +import Link from '@material-ui/core/Link'; + +export function ButtonLink() { + return ( + { + console.info("I'm a button."); + }} + > + Button Link + + ); +} + +export default { + title: "Material-ui|links|ButtonLink.stories" +}; diff --git a/examples/storybook/stories/material-ui/links/Links.stories.tsx b/examples/storybook/stories/material-ui/links/Links.stories.tsx new file mode 100644 index 00000000000000..3b9145803fd23d --- /dev/null +++ b/examples/storybook/stories/material-ui/links/Links.stories.tsx @@ -0,0 +1,38 @@ +/* eslint-disable jsx-a11y/anchor-is-valid */ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Link from '@material-ui/core/Link'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > * + *': { + marginLeft: theme.spacing(2), + }, + }, + }), +); + +export function Links() { + const classes = useStyles(); + const preventDefault = (event: React.SyntheticEvent) => event.preventDefault(); + + return ( + + + Link + + + {'color="inherit"'} + + + {'variant="body2"'} + + + ); +} + +export default { + title: "Material-ui|links|Links.stories" +}; diff --git a/examples/storybook/stories/material-ui/lists/AlignItemsList.stories.tsx b/examples/storybook/stories/material-ui/lists/AlignItemsList.stories.tsx new file mode 100644 index 00000000000000..e3a7ae8fa16dd9 --- /dev/null +++ b/examples/storybook/stories/material-ui/lists/AlignItemsList.stories.tsx @@ -0,0 +1,100 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import Divider from '@material-ui/core/Divider'; +import ListItemText from '@material-ui/core/ListItemText'; +import ListItemAvatar from '@material-ui/core/ListItemAvatar'; +import Avatar from '@material-ui/core/Avatar'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + maxWidth: '36ch', + backgroundColor: theme.palette.background.paper, + }, + inline: { + display: 'inline', + }, + }), +); + +export function AlignItemsList() { + const classes = useStyles(); + + return ( + + + + + + + + Ali Connors + + {" — I'll be in your neighborhood doing errands this…"} + + } + /> + + + + + + + + + to Scott, Alex, Jennifer + + {" — Wish I could come, but I'm out of town this…"} + + } + /> + + + + + + + + + Sandra Adams + + {' — Do you have Paris recommendations? Have you ever…'} + + } + /> + + + ); +} + +export default { + title: "Material-ui|lists|AlignItemsList.stories" +}; diff --git a/examples/storybook/stories/material-ui/lists/CheckboxList.stories.tsx b/examples/storybook/stories/material-ui/lists/CheckboxList.stories.tsx new file mode 100644 index 00000000000000..b2215da71d82a6 --- /dev/null +++ b/examples/storybook/stories/material-ui/lists/CheckboxList.stories.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; +import ListItemText from '@material-ui/core/ListItemText'; +import Checkbox from '@material-ui/core/Checkbox'; +import IconButton from '@material-ui/core/IconButton'; +import CommentIcon from '@material-ui/icons/Comment'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + maxWidth: 360, + backgroundColor: theme.palette.background.paper, + }, + }), +); + +export function CheckboxList() { + const classes = useStyles(); + const [checked, setChecked] = React.useState([0]); + + const handleToggle = (value: number) => () => { + const currentIndex = checked.indexOf(value); + const newChecked = [...checked]; + + if (currentIndex === -1) { + newChecked.push(value); + } else { + newChecked.splice(currentIndex, 1); + } + + setChecked(newChecked); + }; + + return ( + + {[0, 1, 2, 3].map((value) => { + const labelId = `checkbox-list-label-${value}`; + + return ( + + + + + + + + + + + + ); + })} + + ); +} + +export default { + title: "Material-ui|lists|CheckboxList.stories" +}; diff --git a/examples/storybook/stories/material-ui/lists/CheckboxListSecondary.stories.tsx b/examples/storybook/stories/material-ui/lists/CheckboxListSecondary.stories.tsx new file mode 100644 index 00000000000000..2cd7f9940c97f2 --- /dev/null +++ b/examples/storybook/stories/material-ui/lists/CheckboxListSecondary.stories.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; +import ListItemText from '@material-ui/core/ListItemText'; +import ListItemAvatar from '@material-ui/core/ListItemAvatar'; +import Checkbox from '@material-ui/core/Checkbox'; +import Avatar from '@material-ui/core/Avatar'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + maxWidth: 360, + backgroundColor: theme.palette.background.paper, + }, + }), +); + +export function CheckboxListSecondary() { + const classes = useStyles(); + const [checked, setChecked] = React.useState([1]); + + const handleToggle = (value: number) => () => { + const currentIndex = checked.indexOf(value); + const newChecked = [...checked]; + + if (currentIndex === -1) { + newChecked.push(value); + } else { + newChecked.splice(currentIndex, 1); + } + + setChecked(newChecked); + }; + + return ( + + {[0, 1, 2, 3].map((value) => { + const labelId = `checkbox-list-secondary-label-${value}`; + return ( + + + + + + + + + + ); + })} + + ); +} + +export default { + title: "Material-ui|lists|CheckboxListSecondary.stories" +}; diff --git a/examples/storybook/stories/material-ui/lists/FolderList.stories.tsx b/examples/storybook/stories/material-ui/lists/FolderList.stories.tsx new file mode 100644 index 00000000000000..ab021150a68465 --- /dev/null +++ b/examples/storybook/stories/material-ui/lists/FolderList.stories.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; +import ListItemAvatar from '@material-ui/core/ListItemAvatar'; +import Avatar from '@material-ui/core/Avatar'; +import ImageIcon from '@material-ui/icons/Image'; +import WorkIcon from '@material-ui/icons/Work'; +import BeachAccessIcon from '@material-ui/icons/BeachAccess'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + maxWidth: 360, + backgroundColor: theme.palette.background.paper, + }, + }), +); + +export function FolderList() { + const classes = useStyles(); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|lists|FolderList.stories" +}; diff --git a/examples/storybook/stories/material-ui/lists/InsetList.stories.tsx b/examples/storybook/stories/material-ui/lists/InsetList.stories.tsx new file mode 100644 index 00000000000000..23f40b26ef1d86 --- /dev/null +++ b/examples/storybook/stories/material-ui/lists/InsetList.stories.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import StarIcon from '@material-ui/icons/Star'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + maxWidth: 360, + backgroundColor: theme.palette.background.paper, + }, + }), +); + +export function InsetList() { + const classes = useStyles(); + + return ( + + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|lists|InsetList.stories" +}; diff --git a/examples/storybook/stories/material-ui/lists/InteractiveList.stories.tsx b/examples/storybook/stories/material-ui/lists/InteractiveList.stories.tsx new file mode 100644 index 00000000000000..62f35dba70e7b5 --- /dev/null +++ b/examples/storybook/stories/material-ui/lists/InteractiveList.stories.tsx @@ -0,0 +1,162 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemAvatar from '@material-ui/core/ListItemAvatar'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; +import ListItemText from '@material-ui/core/ListItemText'; +import Avatar from '@material-ui/core/Avatar'; +import IconButton from '@material-ui/core/IconButton'; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import Checkbox from '@material-ui/core/Checkbox'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +import FolderIcon from '@material-ui/icons/Folder'; +import DeleteIcon from '@material-ui/icons/Delete'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + maxWidth: 752, + }, + demo: { + backgroundColor: theme.palette.background.paper, + }, + title: { + margin: theme.spacing(4, 0, 2), + }, + }), +); + +function generate(element: React.ReactElement) { + return [0, 1, 2].map((value) => + React.cloneElement(element, { + key: value, + }), + ); +} + +export function InteractiveList() { + const classes = useStyles(); + const [dense, setDense] = React.useState(false); + const [secondary, setSecondary] = React.useState(false); + + return ( +
    + + setDense(event.target.checked)} /> + } + label="Enable dense" + /> + setSecondary(event.target.checked)} + /> + } + label="Enable secondary text" + /> + + + + + Text only + +
    + + {generate( + + + , + )} + +
    +
    + + + Icon with text + +
    + + {generate( + + + + + + , + )} + +
    +
    +
    + + + + Avatar with text + +
    + + {generate( + + + + + + + + , + )} + +
    +
    + + + Avatar with text and icon + +
    + + {generate( + + + + + + + + + + + + + , + )} + +
    +
    +
    +
    + ); +} + +export default { + title: "Material-ui|lists|InteractiveList.stories" +}; diff --git a/examples/storybook/stories/material-ui/lists/NestedList.stories.tsx b/examples/storybook/stories/material-ui/lists/NestedList.stories.tsx new file mode 100644 index 00000000000000..4cd0a67829338c --- /dev/null +++ b/examples/storybook/stories/material-ui/lists/NestedList.stories.tsx @@ -0,0 +1,83 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import ListSubheader from '@material-ui/core/ListSubheader'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import Collapse from '@material-ui/core/Collapse'; +import InboxIcon from '@material-ui/icons/MoveToInbox'; +import DraftsIcon from '@material-ui/icons/Drafts'; +import SendIcon from '@material-ui/icons/Send'; +import ExpandLess from '@material-ui/icons/ExpandLess'; +import ExpandMore from '@material-ui/icons/ExpandMore'; +import StarBorder from '@material-ui/icons/StarBorder'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + maxWidth: 360, + backgroundColor: theme.palette.background.paper, + }, + nested: { + paddingLeft: theme.spacing(4), + }, + }), +); + +export function NestedList() { + const classes = useStyles(); + const [open, setOpen] = React.useState(true); + + const handleClick = () => { + setOpen(!open); + }; + + return ( + + Nested List Items + + } + className={classes.root} + > + + + + + + + + + + + + + + + + + + {open ? : } + + + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|lists|NestedList.stories" +}; diff --git a/examples/storybook/stories/material-ui/lists/PinnedSubheaderList.stories.tsx b/examples/storybook/stories/material-ui/lists/PinnedSubheaderList.stories.tsx new file mode 100644 index 00000000000000..115c230ace4156 --- /dev/null +++ b/examples/storybook/stories/material-ui/lists/PinnedSubheaderList.stories.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; +import ListSubheader from '@material-ui/core/ListSubheader'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + maxWidth: 360, + backgroundColor: theme.palette.background.paper, + position: 'relative', + overflow: 'auto', + maxHeight: 300, + }, + listSection: { + backgroundColor: 'inherit', + }, + ul: { + backgroundColor: 'inherit', + padding: 0, + }, + }), +); + +export function PinnedSubheaderList() { + const classes = useStyles(); + + return ( + }> + {[0, 1, 2, 3, 4].map((sectionId) => ( +
  • +
      + {`I'm sticky ${sectionId}`} + {[0, 1, 2].map((item) => ( + + + + ))} +
    +
  • + ))} +
    + ); +} + +export default { + title: "Material-ui|lists|PinnedSubheaderList.stories" +}; diff --git a/examples/storybook/stories/material-ui/lists/SelectedListItem.stories.tsx b/examples/storybook/stories/material-ui/lists/SelectedListItem.stories.tsx new file mode 100644 index 00000000000000..1d165a6c20c869 --- /dev/null +++ b/examples/storybook/stories/material-ui/lists/SelectedListItem.stories.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import Divider from '@material-ui/core/Divider'; +import InboxIcon from '@material-ui/icons/Inbox'; +import DraftsIcon from '@material-ui/icons/Drafts'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + maxWidth: 360, + backgroundColor: theme.palette.background.paper, + }, + }), +); + +export function SelectedListItem() { + const classes = useStyles(); + const [selectedIndex, setSelectedIndex] = React.useState(1); + + const handleListItemClick = ( + event: React.MouseEvent, + index: number, + ) => { + setSelectedIndex(index); + }; + + return ( +
    + + handleListItemClick(event, 0)} + > + + + + + + handleListItemClick(event, 1)} + > + + + + + + + + + handleListItemClick(event, 2)} + > + + + handleListItemClick(event, 3)} + > + + + +
    + ); +} + +export default { + title: "Material-ui|lists|SelectedListItem.stories" +}; diff --git a/examples/storybook/stories/material-ui/lists/SimpleList.stories.tsx b/examples/storybook/stories/material-ui/lists/SimpleList.stories.tsx new file mode 100644 index 00000000000000..7c7527c877d10e --- /dev/null +++ b/examples/storybook/stories/material-ui/lists/SimpleList.stories.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem, { ListItemProps } from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import Divider from '@material-ui/core/Divider'; +import InboxIcon from '@material-ui/icons/Inbox'; +import DraftsIcon from '@material-ui/icons/Drafts'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + maxWidth: 360, + backgroundColor: theme.palette.background.paper, + }, + }), +); + +function ListItemLink(props: ListItemProps<'a', { button?: true }>) { + return ; +} + +export function SimpleList() { + const classes = useStyles(); + + return ( +
    + + + + + + + + + + + + + + + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|lists|SimpleList.stories" +}; diff --git a/examples/storybook/stories/material-ui/lists/SwitchListSecondary.stories.tsx b/examples/storybook/stories/material-ui/lists/SwitchListSecondary.stories.tsx new file mode 100644 index 00000000000000..efa41772e68315 --- /dev/null +++ b/examples/storybook/stories/material-ui/lists/SwitchListSecondary.stories.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; +import ListItemText from '@material-ui/core/ListItemText'; +import ListSubheader from '@material-ui/core/ListSubheader'; +import Switch from '@material-ui/core/Switch'; +import WifiIcon from '@material-ui/icons/Wifi'; +import BluetoothIcon from '@material-ui/icons/Bluetooth'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + maxWidth: 360, + backgroundColor: theme.palette.background.paper, + }, + }), +); + +export function SwitchListSecondary() { + const classes = useStyles(); + const [checked, setChecked] = React.useState(['wifi']); + + const handleToggle = (value: string) => () => { + const currentIndex = checked.indexOf(value); + const newChecked = [...checked]; + + if (currentIndex === -1) { + newChecked.push(value); + } else { + newChecked.splice(currentIndex, 1); + } + + setChecked(newChecked); + }; + + return ( + Settings} className={classes.root}> + + + + + + + + + + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|lists|SwitchListSecondary.stories" +}; diff --git a/examples/storybook/stories/material-ui/lists/VirtualizedList.stories.tsx b/examples/storybook/stories/material-ui/lists/VirtualizedList.stories.tsx new file mode 100644 index 00000000000000..e3664c317002ea --- /dev/null +++ b/examples/storybook/stories/material-ui/lists/VirtualizedList.stories.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; +import { FixedSizeList, ListChildComponentProps } from 'react-window'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + height: 400, + maxWidth: 300, + backgroundColor: theme.palette.background.paper, + }, + }), +); + +function renderRow(props: ListChildComponentProps) { + const { index, style } = props; + + return ( + + + + ); +} + +export function VirtualizedList() { + const classes = useStyles(); + + return ( +
    + + {renderRow} + +
    + ); +} + +export default { + title: "Material-ui|lists|VirtualizedList.stories" +}; diff --git a/examples/storybook/stories/material-ui/menus/ContextMenu.stories.tsx b/examples/storybook/stories/material-ui/menus/ContextMenu.stories.tsx new file mode 100644 index 00000000000000..9ebd4c392fa12b --- /dev/null +++ b/examples/storybook/stories/material-ui/menus/ContextMenu.stories.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import Menu from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; +import Typography from '@material-ui/core/Typography'; + +const initialState = { + mouseX: null, + mouseY: null, +}; + +export function ContextMenu() { + const [state, setState] = React.useState<{ + mouseX: null | number; + mouseY: null | number; + }>(initialState); + + const handleClick = (event: React.MouseEvent) => { + event.preventDefault(); + setState({ + mouseX: event.clientX - 2, + mouseY: event.clientY - 4, + }); + }; + + const handleClose = () => { + setState(initialState); + }; + + return ( +
    + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ipsum purus, bibendum sit + amet vulputate eget, porta semper ligula. Donec bibendum vulputate erat, ac fringilla mi + finibus nec. Donec ac dolor sed dolor porttitor blandit vel vel purus. Fusce vel malesuada + ligula. Nam quis vehicula ante, eu finibus est. Proin ullamcorper fermentum orci, quis + finibus massa. Nunc lobortis, massa ut rutrum ultrices, metus metus finibus ex, sit amet + facilisis neque enim sed neque. Quisque accumsan metus vel maximus consequat. Suspendisse + lacinia tellus a libero volutpat maximus. + + + Copy + Print + Highlight + Email + +
    + ); +} + +export default { + title: "Material-ui|menus|ContextMenu.stories" +}; diff --git a/examples/storybook/stories/material-ui/menus/CustomizedMenus.stories.tsx b/examples/storybook/stories/material-ui/menus/CustomizedMenus.stories.tsx new file mode 100644 index 00000000000000..344a0ada5b656c --- /dev/null +++ b/examples/storybook/stories/material-ui/menus/CustomizedMenus.stories.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import { withStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Menu, { MenuProps } from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import InboxIcon from '@material-ui/icons/MoveToInbox'; +import DraftsIcon from '@material-ui/icons/Drafts'; +import SendIcon from '@material-ui/icons/Send'; + +const StyledMenu = withStyles({ + paper: { + border: '1px solid #d3d4d5', + }, +})((props: MenuProps) => ( + +)); + +const StyledMenuItem = withStyles((theme) => ({ + root: { + '&:focus': { + backgroundColor: theme.palette.primary.main, + '& .MuiListItemIcon-root, & .MuiListItemText-primary': { + color: theme.palette.common.white, + }, + }, + }, +}))(MenuItem); + +export function CustomizedMenus() { + const [anchorEl, setAnchorEl] = React.useState(null); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + return ( +
    + + + + + + + + + + + + + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|menus|CustomizedMenus.stories" +}; diff --git a/examples/storybook/stories/material-ui/menus/FadeMenu.stories.tsx b/examples/storybook/stories/material-ui/menus/FadeMenu.stories.tsx new file mode 100644 index 00000000000000..71ab4cb3347728 --- /dev/null +++ b/examples/storybook/stories/material-ui/menus/FadeMenu.stories.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Menu from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; +import Fade from '@material-ui/core/Fade'; + +export function FadeMenu() { + const [anchorEl, setAnchorEl] = React.useState(null); + const open = Boolean(anchorEl); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + return ( +
    + + + Profile + My account + Logout + +
    + ); +} + +export default { + title: "Material-ui|menus|FadeMenu.stories" +}; diff --git a/examples/storybook/stories/material-ui/menus/LongMenu.stories.tsx b/examples/storybook/stories/material-ui/menus/LongMenu.stories.tsx new file mode 100644 index 00000000000000..0514cb9b9b6f59 --- /dev/null +++ b/examples/storybook/stories/material-ui/menus/LongMenu.stories.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import IconButton from '@material-ui/core/IconButton'; +import Menu from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; +import MoreVertIcon from '@material-ui/icons/MoreVert'; + +const options = [ + 'None', + 'Atria', + 'Callisto', + 'Dione', + 'Ganymede', + 'Hangouts Call', + 'Luna', + 'Oberon', + 'Phobos', + 'Pyxis', + 'Sedna', + 'Titania', + 'Triton', + 'Umbriel', +]; + +const ITEM_HEIGHT = 48; + +export function LongMenu() { + const [anchorEl, setAnchorEl] = React.useState(null); + const open = Boolean(anchorEl); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + return ( +
    + + + + + {options.map((option) => ( + + {option} + + ))} + +
    + ); +} + +export default { + title: "Material-ui|menus|LongMenu.stories" +}; diff --git a/examples/storybook/stories/material-ui/menus/MenuListComposition.stories.tsx b/examples/storybook/stories/material-ui/menus/MenuListComposition.stories.tsx new file mode 100644 index 00000000000000..bc9993e932e38f --- /dev/null +++ b/examples/storybook/stories/material-ui/menus/MenuListComposition.stories.tsx @@ -0,0 +1,99 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import ClickAwayListener from '@material-ui/core/ClickAwayListener'; +import Grow from '@material-ui/core/Grow'; +import Paper from '@material-ui/core/Paper'; +import Popper from '@material-ui/core/Popper'; +import MenuItem from '@material-ui/core/MenuItem'; +import MenuList from '@material-ui/core/MenuList'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + }, + paper: { + marginRight: theme.spacing(2), + }, + }), +); + +export function MenuListComposition() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + const anchorRef = React.useRef(null); + + const handleToggle = () => { + setOpen((prevOpen) => !prevOpen); + }; + + const handleClose = (event: React.MouseEvent) => { + if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) { + return; + } + + setOpen(false); + }; + + function handleListKeyDown(event: React.KeyboardEvent) { + if (event.key === 'Tab') { + event.preventDefault(); + setOpen(false); + } + } + + // return focus to the button when we transitioned from !open -> open + const prevOpen = React.useRef(open); + React.useEffect(() => { + if (prevOpen.current === true && open === false) { + anchorRef.current!.focus(); + } + + prevOpen.current = open; + }, [open]); + + return ( +
    + + + Profile + My account + Logout + + +
    + + + {({ TransitionProps, placement }) => ( + + + + + Profile + My account + Logout + + + + + )} + +
    +
    + ); +} + +export default { + title: "Material-ui|menus|MenuListComposition.stories" +}; diff --git a/examples/storybook/stories/material-ui/menus/MenuPopupState.stories.tsx b/examples/storybook/stories/material-ui/menus/MenuPopupState.stories.tsx new file mode 100644 index 00000000000000..a3691304d22fd9 --- /dev/null +++ b/examples/storybook/stories/material-ui/menus/MenuPopupState.stories.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Menu from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; +import PopupState, { bindTrigger, bindMenu } from 'material-ui-popup-state'; + +export function MenuPopupState() { + return ( + + {(popupState) => ( + + + + Cake + Death + + + )} + + ); +} + +export default { + title: "Material-ui|menus|MenuPopupState.stories" +}; diff --git a/examples/storybook/stories/material-ui/menus/SimpleListMenu.stories.tsx b/examples/storybook/stories/material-ui/menus/SimpleListMenu.stories.tsx new file mode 100644 index 00000000000000..b44dfa5abbdfef --- /dev/null +++ b/examples/storybook/stories/material-ui/menus/SimpleListMenu.stories.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; +import MenuItem from '@material-ui/core/MenuItem'; +import Menu from '@material-ui/core/Menu'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + backgroundColor: theme.palette.background.paper, + }, + }), +); + +const options = [ + 'Show some love to Material-UI', + 'Show all notification content', + 'Hide sensitive notification content', + 'Hide all notification content', +]; + +export function SimpleListMenu() { + const classes = useStyles(); + const [anchorEl, setAnchorEl] = React.useState(null); + const [selectedIndex, setSelectedIndex] = React.useState(1); + + const handleClickListItem = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleMenuItemClick = (event: React.MouseEvent, index: number) => { + setSelectedIndex(index); + setAnchorEl(null); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + return ( +
    + + + + + + + {options.map((option, index) => ( + handleMenuItemClick(event, index)} + > + {option} + + ))} + +
    + ); +} + +export default { + title: "Material-ui|menus|SimpleListMenu.stories" +}; diff --git a/examples/storybook/stories/material-ui/menus/SimpleMenu.stories.tsx b/examples/storybook/stories/material-ui/menus/SimpleMenu.stories.tsx new file mode 100644 index 00000000000000..7a340f041d0cb0 --- /dev/null +++ b/examples/storybook/stories/material-ui/menus/SimpleMenu.stories.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Menu from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; + +export function SimpleMenu() { + const [anchorEl, setAnchorEl] = React.useState(null); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + return ( +
    + + + Profile + My account + Logout + +
    + ); +} + +export default { + title: "Material-ui|menus|SimpleMenu.stories" +}; diff --git a/examples/storybook/stories/material-ui/menus/TypographyMenu.stories.tsx b/examples/storybook/stories/material-ui/menus/TypographyMenu.stories.tsx new file mode 100644 index 00000000000000..8b2422c80a1a7a --- /dev/null +++ b/examples/storybook/stories/material-ui/menus/TypographyMenu.stories.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import MenuList from '@material-ui/core/MenuList'; +import MenuItem from '@material-ui/core/MenuItem'; +import Paper from '@material-ui/core/Paper'; +import { makeStyles } from '@material-ui/core/styles'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import Typography from '@material-ui/core/Typography'; +import DraftsIcon from '@material-ui/icons/Drafts'; +import SendIcon from '@material-ui/icons/Send'; +import PriorityHighIcon from '@material-ui/icons/PriorityHigh'; + +const useStyles = makeStyles({ + root: { + width: 230, + }, +}); + +export function TypographyMenu() { + const classes = useStyles(); + + return ( + + + + + + + A short message + + + + + + A very long text that overflows + + + + + + + A very long text that overflows + + + + + ); +} + +export default { + title: "Material-ui|menus|TypographyMenu.stories" +}; diff --git a/examples/storybook/stories/material-ui/modal/ServerModal.stories.tsx b/examples/storybook/stories/material-ui/modal/ServerModal.stories.tsx new file mode 100644 index 00000000000000..150b1fa70f028a --- /dev/null +++ b/examples/storybook/stories/material-ui/modal/ServerModal.stories.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Modal from '@material-ui/core/Modal'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + height: 300, + flexGrow: 1, + minWidth: 300, + transform: 'translateZ(0)', + // The position fixed scoping doesn't work in IE 11. + // Disable this demo to preserve the others. + '@media all and (-ms-high-contrast: none)': { + display: 'none', + }, + }, + modal: { + display: 'flex', + padding: theme.spacing(1), + alignItems: 'center', + justifyContent: 'center', + }, + paper: { + width: 400, + backgroundColor: theme.palette.background.paper, + border: '2px solid #000', + boxShadow: theme.shadows[5], + padding: theme.spacing(2, 4, 3), + }, + }), +); + +export function ServerModal() { + const classes = useStyles(); + const rootRef = React.useRef(null); + + return ( +
    + rootRef.current} + > +
    +

    Server-side modal

    +

    If you disable JavaScript, you will still see me.

    +
    +
    +
    + ); +} + +export default { + title: "Material-ui|modal|ServerModal.stories" +}; diff --git a/examples/storybook/stories/material-ui/modal/SimpleModal.stories.tsx b/examples/storybook/stories/material-ui/modal/SimpleModal.stories.tsx new file mode 100644 index 00000000000000..1a4645cf0d81f1 --- /dev/null +++ b/examples/storybook/stories/material-ui/modal/SimpleModal.stories.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Modal from '@material-ui/core/Modal'; + +function rand() { + return Math.round(Math.random() * 20) - 10; +} + +function getModalStyle() { + const top = 50 + rand(); + const left = 50 + rand(); + + return { + top: `${top}%`, + left: `${left}%`, + transform: `translate(-${top}%, -${left}%)`, + }; +} + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + paper: { + position: 'absolute', + width: 400, + backgroundColor: theme.palette.background.paper, + border: '2px solid #000', + boxShadow: theme.shadows[5], + padding: theme.spacing(2, 4, 3), + }, + }), +); + +export function SimpleModal() { + const classes = useStyles(); + // getModalStyle is not a pure function, we roll the style only on the first render + const [modalStyle] = React.useState(getModalStyle); + const [open, setOpen] = React.useState(false); + + const handleOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + const body = ( +
    +

    Text in a modal

    +

    + Duis mollis, est non commodo luctus, nisi erat porttitor ligula. +

    + +
    + ); + + return ( +
    + + + {body} + +
    + ); +} + +export default { + title: "Material-ui|modal|SimpleModal.stories" +}; diff --git a/examples/storybook/stories/material-ui/modal/SpringModal.stories.tsx b/examples/storybook/stories/material-ui/modal/SpringModal.stories.tsx new file mode 100644 index 00000000000000..66e9fa9cfbea41 --- /dev/null +++ b/examples/storybook/stories/material-ui/modal/SpringModal.stories.tsx @@ -0,0 +1,96 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Modal from '@material-ui/core/Modal'; +import Backdrop from '@material-ui/core/Backdrop'; +import { useSpring, animated } from 'react-spring/web.cjs'; // web.cjs is required for IE 11 support + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + modal: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + paper: { + backgroundColor: theme.palette.background.paper, + border: '2px solid #000', + boxShadow: theme.shadows[5], + padding: theme.spacing(2, 4, 3), + }, + }), +); + +interface FadeProps { + children?: React.ReactElement; + in: boolean; + onEnter?: () => {}; + onExited?: () => {}; +} + +const Fade = React.forwardRef(function Fade(props, ref) { + const { in: open, children, onEnter, onExited, ...other } = props; + const style = useSpring({ + from: { opacity: 0 }, + to: { opacity: open ? 1 : 0 }, + onStart: () => { + if (open && onEnter) { + onEnter(); + } + }, + onRest: () => { + if (!open && onExited) { + onExited(); + } + }, + }); + + return ( + + {children} + + ); +}); + +export function SpringModal() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + + const handleOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + return ( +
    + + + +
    +

    Spring modal

    +

    react-spring animates me.

    +
    +
    +
    +
    + ); +} + +export default { + title: "Material-ui|modal|SpringModal.stories" +}; diff --git a/examples/storybook/stories/material-ui/modal/TransitionsModal.stories.tsx b/examples/storybook/stories/material-ui/modal/TransitionsModal.stories.tsx new file mode 100644 index 00000000000000..3798375ac53cb3 --- /dev/null +++ b/examples/storybook/stories/material-ui/modal/TransitionsModal.stories.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Modal from '@material-ui/core/Modal'; +import Backdrop from '@material-ui/core/Backdrop'; +import Fade from '@material-ui/core/Fade'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + modal: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + paper: { + backgroundColor: theme.palette.background.paper, + border: '2px solid #000', + boxShadow: theme.shadows[5], + padding: theme.spacing(2, 4, 3), + }, + }), +); + +export function TransitionsModal() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + + const handleOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + return ( +
    + + + +
    +

    Transition modal

    +

    react-transition-group animates me.

    +
    +
    +
    +
    + ); +} + +export default { + title: "Material-ui|modal|TransitionsModal.stories" +}; diff --git a/examples/storybook/stories/material-ui/no-ssr/FrameDeferring.stories.tsx b/examples/storybook/stories/material-ui/no-ssr/FrameDeferring.stories.tsx new file mode 100644 index 00000000000000..3bdd9449549b74 --- /dev/null +++ b/examples/storybook/stories/material-ui/no-ssr/FrameDeferring.stories.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import NoSsr from '@material-ui/core/NoSsr'; + +const useStyles = makeStyles({ + container: { + width: 300, + display: 'flex', + flexWrap: 'wrap', + }, +}); + +function LargeTree(): any { + return Array.from(new Array(5000)).map((_, index) => .); +} + +export function FrameDeferring() { + const classes = useStyles(); + const [state, setState] = React.useState({ open: false, defer: false }); + + return ( +
    + +
    + +
    +
    +
    + {state.open ? ( + +
    Outside NoSsr
    + + .....Inside NoSsr + + +
    + ) : null} +
    +
    + ); +} + +export default { + title: "Material-ui|no-ssr|FrameDeferring.stories" +}; diff --git a/examples/storybook/stories/material-ui/no-ssr/SimpleNoSsr.stories.tsx b/examples/storybook/stories/material-ui/no-ssr/SimpleNoSsr.stories.tsx new file mode 100644 index 00000000000000..c94993db13d1b2 --- /dev/null +++ b/examples/storybook/stories/material-ui/no-ssr/SimpleNoSsr.stories.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import NoSsr from '@material-ui/core/NoSsr'; +import Box from '@material-ui/core/Box'; + +export function SimpleNoSsr() { + return ( +
    + + Server and Client + + + + Client only + + +
    + ); +} + +export default { + title: "Material-ui|no-ssr|SimpleNoSsr.stories" +}; diff --git a/examples/storybook/stories/material-ui/pagination/BasicPagination.stories.tsx b/examples/storybook/stories/material-ui/pagination/BasicPagination.stories.tsx new file mode 100644 index 00000000000000..66af4694755ff5 --- /dev/null +++ b/examples/storybook/stories/material-ui/pagination/BasicPagination.stories.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { makeStyles, createStyles } from '@material-ui/core/styles'; +import Pagination from '@material-ui/lab/Pagination'; + +const useStyles = makeStyles((theme) => + createStyles({ + root: { + '& > *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function BasicPagination() { + const classes = useStyles(); + return ( +
    + + + + +
    + ); +} + +export default { + title: "Material-ui|pagination|BasicPagination.stories" +}; diff --git a/examples/storybook/stories/material-ui/pagination/PaginationButtons.stories.tsx b/examples/storybook/stories/material-ui/pagination/PaginationButtons.stories.tsx new file mode 100644 index 00000000000000..0048ff54acec95 --- /dev/null +++ b/examples/storybook/stories/material-ui/pagination/PaginationButtons.stories.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { makeStyles, createStyles } from '@material-ui/core/styles'; +import Pagination from '@material-ui/lab/Pagination'; + +const useStyles = makeStyles((theme) => + createStyles({ + root: { + '& > *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function PaginationButtons() { + const classes = useStyles(); + + return ( +
    + + +
    + ); +} + +export default { + title: "Material-ui|pagination|PaginationButtons.stories" +}; diff --git a/examples/storybook/stories/material-ui/pagination/PaginationControlled.stories.tsx b/examples/storybook/stories/material-ui/pagination/PaginationControlled.stories.tsx new file mode 100644 index 00000000000000..a0f439e0d99220 --- /dev/null +++ b/examples/storybook/stories/material-ui/pagination/PaginationControlled.stories.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { makeStyles, createStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Pagination from '@material-ui/lab/Pagination'; + +const useStyles = makeStyles((theme) => + createStyles({ + root: { + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function PaginationControlled() { + const classes = useStyles(); + const [page, setPage] = React.useState(1); + const handleChange = (event: React.ChangeEvent, value: number) => { + setPage(value); + }; + + return ( +
    + Page: {page} + +
    + ); +} + +export default { + title: "Material-ui|pagination|PaginationControlled.stories" +}; diff --git a/examples/storybook/stories/material-ui/pagination/PaginationLink.stories.tsx b/examples/storybook/stories/material-ui/pagination/PaginationLink.stories.tsx new file mode 100644 index 00000000000000..821b3e54752738 --- /dev/null +++ b/examples/storybook/stories/material-ui/pagination/PaginationLink.stories.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { MemoryRouter, Route } from 'react-router'; +import { Link } from 'react-router-dom'; +import Pagination from '@material-ui/lab/Pagination'; +import PaginationItem from '@material-ui/lab/PaginationItem'; + +export function PaginationLink() { + return ( + + + {({ location }) => { + const query = new URLSearchParams(location.search); + const page = parseInt(query.get('page') || '1', 10); + return ( + ( + + )} + /> + ); + }} + + + ); +} + +export default { + title: "Material-ui|pagination|PaginationLink.stories" +}; diff --git a/examples/storybook/stories/material-ui/pagination/PaginationOutlined.stories.tsx b/examples/storybook/stories/material-ui/pagination/PaginationOutlined.stories.tsx new file mode 100644 index 00000000000000..cb9210e010b5d9 --- /dev/null +++ b/examples/storybook/stories/material-ui/pagination/PaginationOutlined.stories.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { makeStyles, createStyles } from '@material-ui/core/styles'; +import Pagination from '@material-ui/lab/Pagination'; + +const useStyles = makeStyles((theme) => + createStyles({ + root: { + '& > *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function PaginationOutlined() { + const classes = useStyles(); + + return ( +
    + + + + +
    + ); +} + +export default { + title: "Material-ui|pagination|PaginationOutlined.stories" +}; diff --git a/examples/storybook/stories/material-ui/pagination/PaginationRanges.stories.tsx b/examples/storybook/stories/material-ui/pagination/PaginationRanges.stories.tsx new file mode 100644 index 00000000000000..c0a5ebc2321347 --- /dev/null +++ b/examples/storybook/stories/material-ui/pagination/PaginationRanges.stories.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { makeStyles, createStyles } from '@material-ui/core/styles'; +import Pagination from '@material-ui/lab/Pagination'; + +const useStyles = makeStyles((theme) => + createStyles({ + root: { + '& > *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function PaginationRanges() { + const classes = useStyles(); + + return ( +
    + + {/* Default ranges */} + + +
    + ); +} + +export default { + title: "Material-ui|pagination|PaginationRanges.stories" +}; diff --git a/examples/storybook/stories/material-ui/pagination/PaginationRounded.stories.tsx b/examples/storybook/stories/material-ui/pagination/PaginationRounded.stories.tsx new file mode 100644 index 00000000000000..3538dbae4de9c8 --- /dev/null +++ b/examples/storybook/stories/material-ui/pagination/PaginationRounded.stories.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { makeStyles, createStyles } from '@material-ui/core/styles'; +import Pagination from '@material-ui/lab/Pagination'; + +const useStyles = makeStyles((theme) => + createStyles({ + root: { + '& > *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function PaginationRounded() { + const classes = useStyles(); + + return ( +
    + + +
    + ); +} + +export default { + title: "Material-ui|pagination|PaginationRounded.stories" +}; diff --git a/examples/storybook/stories/material-ui/pagination/PaginationSize.stories.tsx b/examples/storybook/stories/material-ui/pagination/PaginationSize.stories.tsx new file mode 100644 index 00000000000000..e62b7276e01aba --- /dev/null +++ b/examples/storybook/stories/material-ui/pagination/PaginationSize.stories.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { makeStyles, createStyles } from '@material-ui/core/styles'; +import Pagination from '@material-ui/lab/Pagination'; + +const useStyles = makeStyles((theme) => + createStyles({ + root: { + '& > *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function PaginationSize() { + const classes = useStyles(); + + return ( +
    + + + +
    + ); +} + +export default { + title: "Material-ui|pagination|PaginationSize.stories" +}; diff --git a/examples/storybook/stories/material-ui/pagination/TablePagination.stories.tsx b/examples/storybook/stories/material-ui/pagination/TablePagination.stories.tsx new file mode 100644 index 00000000000000..098f8cfe6b0764 --- /dev/null +++ b/examples/storybook/stories/material-ui/pagination/TablePagination.stories.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import TablePagination from '@material-ui/core/TablePagination'; + +export function TablePaginationDemo() { + const [page, setPage] = React.useState(2); + const [rowsPerPage, setRowsPerPage] = React.useState(10); + + const handleChangePage = (event: React.MouseEvent | null, newPage: number) => { + setPage(newPage); + }; + + const handleChangeRowsPerPage = ( + event: React.ChangeEvent, + ) => { + setRowsPerPage(parseInt(event.target.value, 10)); + setPage(0); + }; + + return ( + + ); +} + +export default { + title: "Material-ui|pagination|TablePagination.stories" +}; diff --git a/examples/storybook/stories/material-ui/pagination/UsePagination.stories.tsx b/examples/storybook/stories/material-ui/pagination/UsePagination.stories.tsx new file mode 100644 index 00000000000000..d774ff35c4b0b6 --- /dev/null +++ b/examples/storybook/stories/material-ui/pagination/UsePagination.stories.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { usePagination } from '@material-ui/lab/Pagination'; +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles({ + ul: { + listStyle: 'none', + padding: 0, + margin: 0, + display: 'flex', + }, +}); + +export function UsePagination() { + const classes = useStyles(); + const { items } = usePagination({ + count: 10, + }); + + return ( + + ); +} + +export default { + title: "Material-ui|pagination|UsePagination.stories" +}; diff --git a/examples/storybook/stories/material-ui/paper/SimplePaper.stories.tsx b/examples/storybook/stories/material-ui/paper/SimplePaper.stories.tsx new file mode 100644 index 00000000000000..42a910de9439d9 --- /dev/null +++ b/examples/storybook/stories/material-ui/paper/SimplePaper.stories.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; +import Paper from '@material-ui/core/Paper'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + flexWrap: 'wrap', + '& > *': { + margin: theme.spacing(1), + width: theme.spacing(16), + height: theme.spacing(16), + }, + }, + }), +); + +export function SimplePaper() { + const classes = useStyles(); + + return ( +
    + + + +
    + ); +} + +export default { + title: "Material-ui|paper|SimplePaper.stories" +}; diff --git a/examples/storybook/stories/material-ui/paper/Variants.stories.tsx b/examples/storybook/stories/material-ui/paper/Variants.stories.tsx new file mode 100644 index 00000000000000..0c31fc4a320127 --- /dev/null +++ b/examples/storybook/stories/material-ui/paper/Variants.stories.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; +import Paper from '@material-ui/core/Paper'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + '& > *': { + margin: theme.spacing(1), + width: theme.spacing(16), + height: theme.spacing(16), + }, + }, + }), +); + +export function Variants() { + const classes = useStyles(); + + return ( +
    + + +
    + ); +} + +export default { + title: "Material-ui|paper|Variants.stories" +}; diff --git a/examples/storybook/stories/material-ui/pickers/DateAndTimePickers.stories.tsx b/examples/storybook/stories/material-ui/pickers/DateAndTimePickers.stories.tsx new file mode 100644 index 00000000000000..2811346df8351b --- /dev/null +++ b/examples/storybook/stories/material-ui/pickers/DateAndTimePickers.stories.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import TextField from '@material-ui/core/TextField'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + container: { + display: 'flex', + flexWrap: 'wrap', + }, + textField: { + marginLeft: theme.spacing(1), + marginRight: theme.spacing(1), + width: 200, + }, + }), +); + +export function DateAndTimePickers() { + const classes = useStyles(); + + return ( +
    + + + ); +} + +export default { + title: "Material-ui|pickers|DateAndTimePickers.stories" +}; diff --git a/examples/storybook/stories/material-ui/pickers/DatePickers.stories.tsx b/examples/storybook/stories/material-ui/pickers/DatePickers.stories.tsx new file mode 100644 index 00000000000000..5af875ac77fa5d --- /dev/null +++ b/examples/storybook/stories/material-ui/pickers/DatePickers.stories.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import TextField from '@material-ui/core/TextField'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + container: { + display: 'flex', + flexWrap: 'wrap', + }, + textField: { + marginLeft: theme.spacing(1), + marginRight: theme.spacing(1), + width: 200, + }, + }), +); + +export function DatePickers() { + const classes = useStyles(); + + return ( +
    + + + ); +} + +export default { + title: "Material-ui|pickers|DatePickers.stories" +}; diff --git a/examples/storybook/stories/material-ui/pickers/MaterialUIPickers.stories.tsx b/examples/storybook/stories/material-ui/pickers/MaterialUIPickers.stories.tsx new file mode 100644 index 00000000000000..0620807d55d228 --- /dev/null +++ b/examples/storybook/stories/material-ui/pickers/MaterialUIPickers.stories.tsx @@ -0,0 +1,65 @@ +import 'date-fns'; +import React from 'react'; +import Grid from '@material-ui/core/Grid'; +import DateFnsUtils from '@date-io/date-fns'; +import { + MuiPickersUtilsProvider, + KeyboardTimePicker, + KeyboardDatePicker, +} from '@material-ui/pickers'; + +export function MaterialUIPickers() { + // The first commit of Material-UI + const [selectedDate, setSelectedDate] = React.useState( + new Date('2014-08-18T21:11:54'), + ); + + const handleDateChange = (date: Date | null) => { + setSelectedDate(date); + }; + + return ( + + + + + + + + ); +} + +export default { + title: "Material-ui|pickers|MaterialUIPickers.stories" +}; diff --git a/examples/storybook/stories/material-ui/pickers/TimePickers.stories.tsx b/examples/storybook/stories/material-ui/pickers/TimePickers.stories.tsx new file mode 100644 index 00000000000000..7ca2c29a7a85e2 --- /dev/null +++ b/examples/storybook/stories/material-ui/pickers/TimePickers.stories.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import TextField from '@material-ui/core/TextField'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + container: { + display: 'flex', + flexWrap: 'wrap', + }, + textField: { + marginLeft: theme.spacing(1), + marginRight: theme.spacing(1), + width: 200, + }, + }), +); + +export function TimePickers() { + const classes = useStyles(); + + return ( +
    + + + ); +} + +export default { + title: "Material-ui|pickers|TimePickers.stories" +}; diff --git a/examples/storybook/stories/material-ui/popover/MouseOverPopover.stories.tsx b/examples/storybook/stories/material-ui/popover/MouseOverPopover.stories.tsx new file mode 100644 index 00000000000000..ff00aea59da35f --- /dev/null +++ b/examples/storybook/stories/material-ui/popover/MouseOverPopover.stories.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import Popover from '@material-ui/core/Popover'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + popover: { + pointerEvents: 'none', + }, + paper: { + padding: theme.spacing(1), + }, + }), +); + +export function MouseOverPopover() { + const classes = useStyles(); + const [anchorEl, setAnchorEl] = React.useState(null); + + const handlePopoverOpen = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handlePopoverClose = () => { + setAnchorEl(null); + }; + + const open = Boolean(anchorEl); + + return ( +
    + + Hover with a Popover. + + + I use Popover. + +
    + ); +} + +export default { + title: "Material-ui|popover|MouseOverPopover.stories" +}; diff --git a/examples/storybook/stories/material-ui/popover/PopoverPopupState.stories.tsx b/examples/storybook/stories/material-ui/popover/PopoverPopupState.stories.tsx new file mode 100644 index 00000000000000..41887e3ba3a1c0 --- /dev/null +++ b/examples/storybook/stories/material-ui/popover/PopoverPopupState.stories.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import Typography from '@material-ui/core/Typography'; +import Box from '@material-ui/core/Box'; +import Button from '@material-ui/core/Button'; +import Popover from '@material-ui/core/Popover'; +import PopupState, { bindTrigger, bindPopover } from 'material-ui-popup-state'; + +export function PopoverPopupState() { + return ( + + {(popupState) => ( +
    + + + + The content of the Popover. + + +
    + )} +
    + ); +} + +export default { + title: "Material-ui|popover|PopoverPopupState.stories" +}; diff --git a/examples/storybook/stories/material-ui/popover/SimplePopover.stories.tsx b/examples/storybook/stories/material-ui/popover/SimplePopover.stories.tsx new file mode 100644 index 00000000000000..db443809f5dd3a --- /dev/null +++ b/examples/storybook/stories/material-ui/popover/SimplePopover.stories.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Popover from '@material-ui/core/Popover'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + typography: { + padding: theme.spacing(2), + }, + }), +); + +export function SimplePopover() { + const classes = useStyles(); + const [anchorEl, setAnchorEl] = React.useState(null); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + const open = Boolean(anchorEl); + const id = open ? 'simple-popover' : undefined; + + return ( +
    + + + The content of the Popover. + +
    + ); +} + +export default { + title: "Material-ui|popover|SimplePopover.stories" +}; diff --git a/examples/storybook/stories/material-ui/popper/FakedReferencePopper.stories.tsx b/examples/storybook/stories/material-ui/popper/FakedReferencePopper.stories.tsx new file mode 100644 index 00000000000000..1c411fef23c5ae --- /dev/null +++ b/examples/storybook/stories/material-ui/popper/FakedReferencePopper.stories.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Popper, { PopperProps } from '@material-ui/core/Popper'; +import Typography from '@material-ui/core/Typography'; +import Fade from '@material-ui/core/Fade'; +import Paper from '@material-ui/core/Paper'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + typography: { + padding: theme.spacing(2), + }, + }), +); + +export function FakedReferencePopper() { + const [open, setOpen] = React.useState(false); + const [anchorEl, setAnchorEl] = React.useState(null); + const classes = useStyles(); + + const handleClose = () => { + setOpen(false); + }; + + const handleMouseUp = () => { + const selection = window.getSelection(); + + // Resets when the selection has a length of 0 + if (!selection || selection.anchorOffset === selection.focusOffset) { + handleClose(); + return; + } + + const getBoundingClientRect = () => selection.getRangeAt(0).getBoundingClientRect(); + + setOpen(true); + setAnchorEl({ + clientWidth: getBoundingClientRect().width, + clientHeight: getBoundingClientRect().height, + getBoundingClientRect, + }); + }; + + const id = open ? 'faked-reference-popper' : undefined; + + return ( +
    + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ipsum purus, bibendum sit + amet vulputate eget, porta semper ligula. Donec bibendum vulputate erat, ac fringilla mi + finibus nec. Donec ac dolor sed dolor porttitor blandit vel vel purus. Fusce vel malesuada + ligula. Nam quis vehicula ante, eu finibus est. Proin ullamcorper fermentum orci, quis + finibus massa. Nunc lobortis, massa ut rutrum ultrices, metus metus finibus ex, sit amet + facilisis neque enim sed neque. Quisque accumsan metus vel maximus consequat. Suspendisse + lacinia tellus a libero volutpat maximus. + + + {({ TransitionProps }) => ( + + + The content of the Popper. + + + )} + +
    + ); +} + +export default { + title: "Material-ui|popper|FakedReferencePopper.stories" +}; diff --git a/examples/storybook/stories/material-ui/popper/PopperPopupState.stories.tsx b/examples/storybook/stories/material-ui/popper/PopperPopupState.stories.tsx new file mode 100644 index 00000000000000..e71b2b29d3ab08 --- /dev/null +++ b/examples/storybook/stories/material-ui/popper/PopperPopupState.stories.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { makeStyles, createStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; +import Popper from '@material-ui/core/Popper'; +import PopupState, { bindToggle, bindPopper } from 'material-ui-popup-state'; +import Fade from '@material-ui/core/Fade'; +import Paper from '@material-ui/core/Paper'; + +const useStyles = makeStyles((theme) => + createStyles({ + typography: { + padding: theme.spacing(2), + }, + }), +); + +export function PopperPopupState() { + const classes = useStyles(); + + return ( + + {(popupState) => ( +
    + + + {({ TransitionProps }) => ( + + + The content of the Popper. + + + )} + +
    + )} +
    + ); +} + +export default { + title: "Material-ui|popper|PopperPopupState.stories" +}; diff --git a/examples/storybook/stories/material-ui/popper/PositionedPopper.stories.tsx b/examples/storybook/stories/material-ui/popper/PositionedPopper.stories.tsx new file mode 100644 index 00000000000000..325561a690a2ae --- /dev/null +++ b/examples/storybook/stories/material-ui/popper/PositionedPopper.stories.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Popper, { PopperPlacementType } from '@material-ui/core/Popper'; +import Typography from '@material-ui/core/Typography'; +import Grid from '@material-ui/core/Grid'; +import Button from '@material-ui/core/Button'; +import Fade from '@material-ui/core/Fade'; +import Paper from '@material-ui/core/Paper'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: 500, + }, + typography: { + padding: theme.spacing(2), + }, + }), +); + +export function PositionedPopper() { + const [anchorEl, setAnchorEl] = React.useState(null); + const [open, setOpen] = React.useState(false); + const [placement, setPlacement] = React.useState(); + const classes = useStyles(); + + const handleClick = (newPlacement: PopperPlacementType) => ( + event: React.MouseEvent, + ) => { + setAnchorEl(event.currentTarget); + setOpen((prev) => placement !== newPlacement || !prev); + setPlacement(newPlacement); + }; + + return ( +
    + + {({ TransitionProps }) => ( + + + The content of the Popper. + + + )} + + + + + + + + + + + +
    + +
    + +
    + + + + + + + + + + + +
    + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|popper|PositionedPopper.stories" +}; diff --git a/examples/storybook/stories/material-ui/popper/SimplePopper.stories.tsx b/examples/storybook/stories/material-ui/popper/SimplePopper.stories.tsx new file mode 100644 index 00000000000000..84777e0dd87341 --- /dev/null +++ b/examples/storybook/stories/material-ui/popper/SimplePopper.stories.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Popper from '@material-ui/core/Popper'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + paper: { + border: '1px solid', + padding: theme.spacing(1), + backgroundColor: theme.palette.background.paper, + }, + }), +); + +export function SimplePopper() { + const classes = useStyles(); + const [anchorEl, setAnchorEl] = React.useState(null); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(anchorEl ? null : event.currentTarget); + }; + + const open = Boolean(anchorEl); + const id = open ? 'simple-popper' : undefined; + + return ( +
    + + +
    The content of the Popper.
    +
    +
    + ); +} + +export default { + title: "Material-ui|popper|SimplePopper.stories" +}; diff --git a/examples/storybook/stories/material-ui/popper/SpringPopper.stories.tsx b/examples/storybook/stories/material-ui/popper/SpringPopper.stories.tsx new file mode 100644 index 00000000000000..4cac5b8bf87137 --- /dev/null +++ b/examples/storybook/stories/material-ui/popper/SpringPopper.stories.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Popper from '@material-ui/core/Popper'; +import { useSpring, animated } from 'react-spring/web.cjs'; // web.cjs is required for IE 11 support + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + paper: { + border: '1px solid', + padding: theme.spacing(1), + backgroundColor: theme.palette.background.paper, + }, + }), +); + +interface FadeProps { + children?: React.ReactElement; + in?: boolean; + onEnter?: () => {}; + onExited?: () => {}; +} + +const Fade = React.forwardRef(function Fade(props, ref) { + const { in: open, children, onEnter, onExited, ...other } = props; + const style = useSpring({ + from: { opacity: 0 }, + to: { opacity: open ? 1 : 0 }, + onStart: () => { + if (open && onEnter) { + onEnter(); + } + }, + onRest: () => { + if (!open && onExited) { + onExited(); + } + }, + }); + + return ( + + {children} + + ); +}); + +export function SpringPopper() { + const classes = useStyles(); + const [anchorEl, setAnchorEl] = React.useState(null); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(anchorEl ? null : event.currentTarget); + }; + + const open = Boolean(anchorEl); + const id = open ? 'spring-popper' : undefined; + + return ( +
    + + + {({ TransitionProps }) => ( + +
    The content of the Popper.
    +
    + )} +
    +
    + ); +} + +export default { + title: "Material-ui|popper|SpringPopper.stories" +}; diff --git a/examples/storybook/stories/material-ui/popper/TransitionsPopper.stories.tsx b/examples/storybook/stories/material-ui/popper/TransitionsPopper.stories.tsx new file mode 100644 index 00000000000000..1c6ba809e74cde --- /dev/null +++ b/examples/storybook/stories/material-ui/popper/TransitionsPopper.stories.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Popper from '@material-ui/core/Popper'; +import Fade from '@material-ui/core/Fade'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + paper: { + border: '1px solid', + padding: theme.spacing(1), + backgroundColor: theme.palette.background.paper, + }, + }), +); + +export function TransitionsPopper() { + const classes = useStyles(); + const [anchorEl, setAnchorEl] = React.useState(null); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(anchorEl ? null : event.currentTarget); + }; + + const open = Boolean(anchorEl); + const id = open ? 'transitions-popper' : undefined; + + return ( +
    + + + {({ TransitionProps }) => ( + +
    The content of the Popper.
    +
    + )} +
    +
    + ); +} + +export default { + title: "Material-ui|popper|TransitionsPopper.stories" +}; diff --git a/examples/storybook/stories/material-ui/portal/SimplePortal.stories.tsx b/examples/storybook/stories/material-ui/portal/SimplePortal.stories.tsx new file mode 100644 index 00000000000000..392c45235c40a5 --- /dev/null +++ b/examples/storybook/stories/material-ui/portal/SimplePortal.stories.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import Portal from '@material-ui/core/Portal'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + alert: { + padding: theme.spacing(1), + margin: theme.spacing(1, 0), + border: '1px solid', + }, + }), +); + +export function SimplePortal() { + const classes = useStyles(); + const [show, setShow] = React.useState(false); + const container = React.useRef(null); + + const handleClick = () => { + setShow(!show); + }; + + return ( +
    + +
    + It looks like I will render here. + {show ? ( + + But I actually render here! + + ) : null} +
    +
    +
    + ); +} + +export default { + title: "Material-ui|portal|SimplePortal.stories" +}; diff --git a/examples/storybook/stories/material-ui/progress/CircularDeterminate.stories.tsx b/examples/storybook/stories/material-ui/progress/CircularDeterminate.stories.tsx new file mode 100644 index 00000000000000..e290cbca142c02 --- /dev/null +++ b/examples/storybook/stories/material-ui/progress/CircularDeterminate.stories.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import CircularProgress from '@material-ui/core/CircularProgress'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + '& > * + *': { + marginLeft: theme.spacing(2), + }, + }, + }), +); + +export function CircularDeterminate() { + const classes = useStyles(); + const [progress, setProgress] = React.useState(0); + + React.useEffect(() => { + function tick() { + // reset when reaching 100% + setProgress((oldProgress) => (oldProgress >= 100 ? 0 : oldProgress + 1)); + } + + const timer = setInterval(tick, 20); + return () => { + clearInterval(timer); + }; + }, []); + + return ( +
    + + +
    + ); +} + +export default { + title: "Material-ui|progress|CircularDeterminate.stories" +}; diff --git a/examples/storybook/stories/material-ui/progress/CircularIndeterminate.stories.tsx b/examples/storybook/stories/material-ui/progress/CircularIndeterminate.stories.tsx new file mode 100644 index 00000000000000..d5a326f4abb7df --- /dev/null +++ b/examples/storybook/stories/material-ui/progress/CircularIndeterminate.stories.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import CircularProgress from '@material-ui/core/CircularProgress'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + '& > * + *': { + marginLeft: theme.spacing(2), + }, + }, + }), +); + +export function CircularIndeterminate() { + const classes = useStyles(); + + return ( +
    + + +
    + ); +} + +export default { + title: "Material-ui|progress|CircularIndeterminate.stories" +}; diff --git a/examples/storybook/stories/material-ui/progress/CircularIntegration.stories.tsx b/examples/storybook/stories/material-ui/progress/CircularIntegration.stories.tsx new file mode 100644 index 00000000000000..c3f85a333b4078 --- /dev/null +++ b/examples/storybook/stories/material-ui/progress/CircularIntegration.stories.tsx @@ -0,0 +1,103 @@ +import React from 'react'; +import clsx from 'clsx'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import { green } from '@material-ui/core/colors'; +import Button from '@material-ui/core/Button'; +import Fab from '@material-ui/core/Fab'; +import CheckIcon from '@material-ui/icons/Check'; +import SaveIcon from '@material-ui/icons/Save'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + alignItems: 'center', + }, + wrapper: { + margin: theme.spacing(1), + position: 'relative', + }, + buttonSuccess: { + backgroundColor: green[500], + '&:hover': { + backgroundColor: green[700], + }, + }, + fabProgress: { + color: green[500], + position: 'absolute', + top: -6, + left: -6, + zIndex: 1, + }, + buttonProgress: { + color: green[500], + position: 'absolute', + top: '50%', + left: '50%', + marginTop: -12, + marginLeft: -12, + }, + }), +); + +export function CircularIntegration() { + const classes = useStyles(); + const [loading, setLoading] = React.useState(false); + const [success, setSuccess] = React.useState(false); + const timer = React.useRef(); + + const buttonClassname = clsx({ + [classes.buttonSuccess]: success, + }); + + React.useEffect(() => { + return () => { + clearTimeout(timer.current); + }; + }, []); + + const handleButtonClick = () => { + if (!loading) { + setSuccess(false); + setLoading(true); + timer.current = setTimeout(() => { + setSuccess(true); + setLoading(false); + }, 2000); + } + }; + + return ( +
    +
    + + {success ? : } + + {loading && } +
    +
    + + {loading && } +
    +
    + ); +} + +export default { + title: "Material-ui|progress|CircularIntegration.stories" +}; diff --git a/examples/storybook/stories/material-ui/progress/CircularStatic.stories.tsx b/examples/storybook/stories/material-ui/progress/CircularStatic.stories.tsx new file mode 100644 index 00000000000000..27922e9997e652 --- /dev/null +++ b/examples/storybook/stories/material-ui/progress/CircularStatic.stories.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import CircularProgress from '@material-ui/core/CircularProgress'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + '& > * + *': { + marginLeft: theme.spacing(2), + }, + }, + }), +); + +export function CircularStatic() { + const classes = useStyles(); + const [completed, setCompleted] = React.useState(0); + + React.useEffect(() => { + function progress() { + setCompleted((prevCompleted) => (prevCompleted >= 100 ? 0 : prevCompleted + 10)); + } + + const timer = setInterval(progress, 1000); + return () => { + clearInterval(timer); + }; + }, []); + + return ( +
    + + + + + + +
    + ); +} + +export default { + title: "Material-ui|progress|CircularStatic.stories" +}; diff --git a/examples/storybook/stories/material-ui/progress/CircularUnderLoad.stories.tsx b/examples/storybook/stories/material-ui/progress/CircularUnderLoad.stories.tsx new file mode 100644 index 00000000000000..7e3d07d440463b --- /dev/null +++ b/examples/storybook/stories/material-ui/progress/CircularUnderLoad.stories.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import CircularProgress from '@material-ui/core/CircularProgress'; + +export function CircularUnderLoad() { + return ; +} + +export default { + title: "Material-ui|progress|CircularUnderLoad.stories" +}; diff --git a/examples/storybook/stories/material-ui/progress/CustomizedProgressBars.stories.tsx b/examples/storybook/stories/material-ui/progress/CustomizedProgressBars.stories.tsx new file mode 100644 index 00000000000000..d609d34f7b5b80 --- /dev/null +++ b/examples/storybook/stories/material-ui/progress/CustomizedProgressBars.stories.tsx @@ -0,0 +1,104 @@ +import React from 'react'; +import { lighten, makeStyles, createStyles, withStyles, Theme } from '@material-ui/core/styles'; +import CircularProgress, { CircularProgressProps } from '@material-ui/core/CircularProgress'; +import LinearProgress from '@material-ui/core/LinearProgress'; + +const ColorCircularProgress = withStyles({ + root: { + color: '#00695c', + }, +})(CircularProgress); + +const ColorLinearProgress = withStyles({ + colorPrimary: { + backgroundColor: '#b2dfdb', + }, + barColorPrimary: { + backgroundColor: '#00695c', + }, +})(LinearProgress); + +const BorderLinearProgress = withStyles({ + root: { + height: 10, + backgroundColor: lighten('#ff6c5c', 0.5), + }, + bar: { + borderRadius: 20, + backgroundColor: '#ff6c5c', + }, +})(LinearProgress); + +// Inspired by the Facebook spinners. +const useStylesFacebook = makeStyles({ + root: { + position: 'relative', + }, + top: { + color: '#eef3fd', + }, + bottom: { + color: '#6798e5', + animationDuration: '550ms', + position: 'absolute', + left: 0, + }, +}); + +function FacebookProgress(props: CircularProgressProps) { + const classes = useStylesFacebook(); + + return ( +
    + + +
    + ); +} + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + }, + margin: { + margin: theme.spacing(1), + }, + }), +); + +export function CustomizedProgressBars() { + const classes = useStyles(); + + return ( +
    + + + + +
    + ); +} + +export default { + title: "Material-ui|progress|CustomizedProgressBars.stories" +}; diff --git a/examples/storybook/stories/material-ui/progress/DelayingAppearance.stories.tsx b/examples/storybook/stories/material-ui/progress/DelayingAppearance.stories.tsx new file mode 100644 index 00000000000000..700f5ebe289b48 --- /dev/null +++ b/examples/storybook/stories/material-ui/progress/DelayingAppearance.stories.tsx @@ -0,0 +1,95 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Fade from '@material-ui/core/Fade'; +import Button from '@material-ui/core/Button'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + }, + button: { + margin: theme.spacing(2), + }, + placeholder: { + height: 40, + }, + }), +); + +export function DelayingAppearance() { + const classes = useStyles(); + const [loading, setLoading] = React.useState(false); + const [query, setQuery] = React.useState('idle'); + const timerRef = React.useRef(); + + React.useEffect( + () => () => { + clearTimeout(timerRef.current); + }, + [], + ); + + const handleClickLoading = () => { + setLoading((prevLoading) => !prevLoading); + }; + + const handleClickQuery = () => { + clearTimeout(timerRef.current); + + if (query !== 'idle') { + setQuery('idle'); + return; + } + + setQuery('progress'); + timerRef.current = setTimeout(() => { + setQuery('success'); + }, 2000); + }; + + return ( +
    +
    + + + +
    + +
    + {query === 'success' ? ( + Success! + ) : ( + + + + )} +
    + +
    + ); +} + +export default { + title: "Material-ui|progress|DelayingAppearance.stories" +}; diff --git a/examples/storybook/stories/material-ui/progress/LinearBuffer.stories.tsx b/examples/storybook/stories/material-ui/progress/LinearBuffer.stories.tsx new file mode 100644 index 00000000000000..492df968bec7f5 --- /dev/null +++ b/examples/storybook/stories/material-ui/progress/LinearBuffer.stories.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import LinearProgress from '@material-ui/core/LinearProgress'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function LinearBuffer() { + const classes = useStyles(); + const [completed, setCompleted] = React.useState(0); + const [buffer, setBuffer] = React.useState(10); + + const progress = React.useRef(() => {}); + React.useEffect(() => { + progress.current = () => { + if (completed > 100) { + setCompleted(0); + setBuffer(10); + } else { + const diff = Math.random() * 10; + const diff2 = Math.random() * 10; + setCompleted(completed + diff); + setBuffer(completed + diff + diff2); + } + }; + }); + + React.useEffect(() => { + function tick() { + progress.current(); + } + const timer = setInterval(tick, 500); + + return () => { + clearInterval(timer); + }; + }, []); + + return ( +
    + + +
    + ); +} + +export default { + title: "Material-ui|progress|LinearBuffer.stories" +}; diff --git a/examples/storybook/stories/material-ui/progress/LinearDeterminate.stories.tsx b/examples/storybook/stories/material-ui/progress/LinearDeterminate.stories.tsx new file mode 100644 index 00000000000000..485d3ef9b1b1f4 --- /dev/null +++ b/examples/storybook/stories/material-ui/progress/LinearDeterminate.stories.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import LinearProgress from '@material-ui/core/LinearProgress'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function LinearDeterminate() { + const classes = useStyles(); + const [completed, setCompleted] = React.useState(0); + + React.useEffect(() => { + function progress() { + setCompleted((oldCompleted) => { + if (oldCompleted === 100) { + return 0; + } + const diff = Math.random() * 10; + return Math.min(oldCompleted + diff, 100); + }); + } + + const timer = setInterval(progress, 500); + return () => { + clearInterval(timer); + }; + }, []); + + return ( +
    + + +
    + ); +} + +export default { + title: "Material-ui|progress|LinearDeterminate.stories" +}; diff --git a/examples/storybook/stories/material-ui/progress/LinearIndeterminate.stories.tsx b/examples/storybook/stories/material-ui/progress/LinearIndeterminate.stories.tsx new file mode 100644 index 00000000000000..6be9e350655906 --- /dev/null +++ b/examples/storybook/stories/material-ui/progress/LinearIndeterminate.stories.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import LinearProgress from '@material-ui/core/LinearProgress'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function LinearIndeterminate() { + const classes = useStyles(); + + return ( +
    + + +
    + ); +} + +export default { + title: "Material-ui|progress|LinearIndeterminate.stories" +}; diff --git a/examples/storybook/stories/material-ui/progress/LinearQuery.stories.tsx b/examples/storybook/stories/material-ui/progress/LinearQuery.stories.tsx new file mode 100644 index 00000000000000..0db4a9d963c205 --- /dev/null +++ b/examples/storybook/stories/material-ui/progress/LinearQuery.stories.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import LinearProgress from '@material-ui/core/LinearProgress'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function LinearQuery() { + const classes = useStyles(); + + return ( +
    + + +
    + ); +} + +export default { + title: "Material-ui|progress|LinearQuery.stories" +}; diff --git a/examples/storybook/stories/material-ui/radio-buttons/CustomizedRadios.stories.tsx b/examples/storybook/stories/material-ui/radio-buttons/CustomizedRadios.stories.tsx new file mode 100644 index 00000000000000..76f08ff910bcf8 --- /dev/null +++ b/examples/storybook/stories/material-ui/radio-buttons/CustomizedRadios.stories.tsx @@ -0,0 +1,88 @@ +import React from 'react'; +import clsx from 'clsx'; +import { makeStyles } from '@material-ui/core/styles'; +import Radio, { RadioProps } from '@material-ui/core/Radio'; +import RadioGroup from '@material-ui/core/RadioGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormControl from '@material-ui/core/FormControl'; +import FormLabel from '@material-ui/core/FormLabel'; + +const useStyles = makeStyles({ + root: { + '&:hover': { + backgroundColor: 'transparent', + }, + }, + icon: { + borderRadius: '50%', + width: 16, + height: 16, + boxShadow: 'inset 0 0 0 1px rgba(16,22,26,.2), inset 0 -1px 0 rgba(16,22,26,.1)', + backgroundColor: '#f5f8fa', + backgroundImage: 'linear-gradient(180deg,hsla(0,0%,100%,.8),hsla(0,0%,100%,0))', + '$root.Mui-focusVisible &': { + outline: '2px auto rgba(19,124,189,.6)', + outlineOffset: 2, + }, + 'input:hover ~ &': { + backgroundColor: '#ebf1f5', + }, + 'input:disabled ~ &': { + boxShadow: 'none', + background: 'rgba(206,217,224,.5)', + }, + }, + checkedIcon: { + backgroundColor: '#137cbd', + backgroundImage: 'linear-gradient(180deg,hsla(0,0%,100%,.1),hsla(0,0%,100%,0))', + '&:before': { + display: 'block', + width: 16, + height: 16, + backgroundImage: 'radial-gradient(#fff,#fff 28%,transparent 32%)', + content: '""', + }, + 'input:hover ~ &': { + backgroundColor: '#106ba3', + }, + }, +}); + +// Inspired by blueprintjs +function StyledRadio(props: RadioProps) { + const classes = useStyles(); + + return ( + } + icon={} + {...props} + /> + ); +} + +export function CustomizedRadios() { + return ( + + Gender + + } label="Female" /> + } label="Male" /> + } label="Other" /> + } + label="(Disabled option)" + /> + + + ); +} + +export default { + title: "Material-ui|radio-buttons|CustomizedRadios.stories" +}; diff --git a/examples/storybook/stories/material-ui/radio-buttons/ErrorRadios.stories.tsx b/examples/storybook/stories/material-ui/radio-buttons/ErrorRadios.stories.tsx new file mode 100644 index 00000000000000..cb56623859ca91 --- /dev/null +++ b/examples/storybook/stories/material-ui/radio-buttons/ErrorRadios.stories.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Radio from '@material-ui/core/Radio'; +import RadioGroup from '@material-ui/core/RadioGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormControl from '@material-ui/core/FormControl'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import FormLabel from '@material-ui/core/FormLabel'; +import Button from '@material-ui/core/Button'; + +const useStyles = makeStyles((theme) => ({ + formControl: { + margin: theme.spacing(3), + }, + button: { + margin: theme.spacing(1, 1, 0, 0), + }, +})); + +export function ErrorRadios() { + const classes = useStyles(); + const [value, setValue] = React.useState(''); + const [error, setError] = React.useState(false); + const [helperText, setHelperText] = React.useState('Choose wisely'); + + const handleRadioChange = (event: React.ChangeEvent) => { + setValue((event.target as HTMLInputElement).value); + setHelperText(' '); + setError(false); + }; + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + + if (value === 'best') { + setHelperText('You got it!'); + setError(false); + } else if (value === 'worst') { + setHelperText('Sorry, wrong answer!'); + setError(true); + } else { + setHelperText('Please select an option.'); + setError(true); + } + }; + + return ( +
    + + Pop quiz: Material-UI is... + + } label="The best!" /> + } label="The worst." /> + + {helperText} + + +
    + ); +} + +export default { + title: "Material-ui|radio-buttons|ErrorRadios.stories" +}; diff --git a/examples/storybook/stories/material-ui/radio-buttons/FormControlLabelPlacement.stories.tsx b/examples/storybook/stories/material-ui/radio-buttons/FormControlLabelPlacement.stories.tsx new file mode 100644 index 00000000000000..94ae2d52cf013a --- /dev/null +++ b/examples/storybook/stories/material-ui/radio-buttons/FormControlLabelPlacement.stories.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import Radio from '@material-ui/core/Radio'; +import RadioGroup from '@material-ui/core/RadioGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormControl from '@material-ui/core/FormControl'; +import FormLabel from '@material-ui/core/FormLabel'; + +export function FormControlLabelPlacement() { + return ( + + labelPlacement + + } + label="Top" + labelPlacement="top" + /> + } + label="Start" + labelPlacement="start" + /> + } + label="Bottom" + labelPlacement="bottom" + /> + } label="End" /> + + + ); +} + +export default { + title: "Material-ui|radio-buttons|FormControlLabelPlacement.stories" +}; diff --git a/examples/storybook/stories/material-ui/radio-buttons/RadioButtons.stories.tsx b/examples/storybook/stories/material-ui/radio-buttons/RadioButtons.stories.tsx new file mode 100644 index 00000000000000..8d2bc9687564d6 --- /dev/null +++ b/examples/storybook/stories/material-ui/radio-buttons/RadioButtons.stories.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { withStyles } from '@material-ui/core/styles'; +import { green } from '@material-ui/core/colors'; +import Radio, { RadioProps } from '@material-ui/core/Radio'; + +const GreenRadio = withStyles({ + root: { + color: green[400], + '&$checked': { + color: green[600], + }, + }, + checked: {}, +})((props: RadioProps) => ); + +export function RadioButtons() { + const [selectedValue, setSelectedValue] = React.useState('a'); + + const handleChange = (event: React.ChangeEvent) => { + setSelectedValue(event.target.value); + }; + + return ( +
    + + + + + +
    + ); +} + +export default { + title: "Material-ui|radio-buttons|RadioButtons.stories" +}; diff --git a/examples/storybook/stories/material-ui/radio-buttons/RadioButtonsGroup.stories.tsx b/examples/storybook/stories/material-ui/radio-buttons/RadioButtonsGroup.stories.tsx new file mode 100644 index 00000000000000..b5429d32c66f67 --- /dev/null +++ b/examples/storybook/stories/material-ui/radio-buttons/RadioButtonsGroup.stories.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import Radio from '@material-ui/core/Radio'; +import RadioGroup from '@material-ui/core/RadioGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormControl from '@material-ui/core/FormControl'; +import FormLabel from '@material-ui/core/FormLabel'; + +export function RadioButtonsGroup() { + const [value, setValue] = React.useState('female'); + + const handleChange = (event: React.ChangeEvent) => { + setValue((event.target as HTMLInputElement).value); + }; + + return ( + + Gender + + } label="Female" /> + } label="Male" /> + } label="Other" /> + } label="(Disabled option)" /> + + + ); +} + +export default { + title: "Material-ui|radio-buttons|RadioButtonsGroup.stories" +}; diff --git a/examples/storybook/stories/material-ui/rating/CustomizedRatings.stories.tsx b/examples/storybook/stories/material-ui/rating/CustomizedRatings.stories.tsx new file mode 100644 index 00000000000000..b79344fab81f6c --- /dev/null +++ b/examples/storybook/stories/material-ui/rating/CustomizedRatings.stories.tsx @@ -0,0 +1,92 @@ +import React from 'react'; +import { withStyles } from '@material-ui/core/styles'; +import Rating, { IconContainerProps } from '@material-ui/lab/Rating'; +import StarBorderIcon from '@material-ui/icons/StarBorder'; +import FavoriteIcon from '@material-ui/icons/Favorite'; +import SentimentVeryDissatisfiedIcon from '@material-ui/icons/SentimentVeryDissatisfied'; +import SentimentDissatisfiedIcon from '@material-ui/icons/SentimentDissatisfied'; +import SentimentSatisfiedIcon from '@material-ui/icons/SentimentSatisfied'; +import SentimentSatisfiedAltIcon from '@material-ui/icons/SentimentSatisfiedAltOutlined'; +import SentimentVerySatisfiedIcon from '@material-ui/icons/SentimentVerySatisfied'; +import Typography from '@material-ui/core/Typography'; +import Box from '@material-ui/core/Box'; + +const StyledRating = withStyles({ + iconFilled: { + color: '#ff6d75', + }, + iconHover: { + color: '#ff3d47', + }, +})(Rating); + +const customIcons: { [index: string]: { icon: React.ReactElement; label: string } } = { + 1: { + icon: , + label: 'Very Dissatisfied', + }, + 2: { + icon: , + label: 'Dissatisfied', + }, + 3: { + icon: , + label: 'Neutral', + }, + 4: { + icon: , + label: 'Satisfied', + }, + 5: { + icon: , + label: 'Very Satisfied', + }, +}; + +function IconContainer(props: IconContainerProps) { + const { value, ...other } = props; + return {customIcons[value].icon}; +} + +export function CustomizedRatings() { + return ( +
    + + Custom empty icon + } + /> + + + Custom icon and color + `${value} Heart${value !== 1 ? 's' : ''}`} + precision={0.5} + icon={} + /> + + + 10 stars + + + + Custom icon set + customIcons[value].label} + IconContainerComponent={IconContainer} + /> + +
    + ); +} + +export default { + title: "Material-ui|rating|CustomizedRatings.stories" +}; diff --git a/examples/storybook/stories/material-ui/rating/HalfRating.stories.tsx b/examples/storybook/stories/material-ui/rating/HalfRating.stories.tsx new file mode 100644 index 00000000000000..b632e53fd9479e --- /dev/null +++ b/examples/storybook/stories/material-ui/rating/HalfRating.stories.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import Rating from '@material-ui/lab/Rating'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + flexDirection: 'column', + '& > * + *': { + marginTop: theme.spacing(1), + }, + }, + }), +); + +export function HalfRating() { + const classes = useStyles(); + + return ( +
    + + +
    + ); +} + +export default { + title: "Material-ui|rating|HalfRating.stories" +}; diff --git a/examples/storybook/stories/material-ui/rating/HoverRating.stories.tsx b/examples/storybook/stories/material-ui/rating/HoverRating.stories.tsx new file mode 100644 index 00000000000000..42f977088a3122 --- /dev/null +++ b/examples/storybook/stories/material-ui/rating/HoverRating.stories.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Rating from '@material-ui/lab/Rating'; +import Box from '@material-ui/core/Box'; + +const labels: { [index: string]: string } = { + 0.5: 'Useless', + 1: 'Useless+', + 1.5: 'Poor', + 2: 'Poor+', + 2.5: 'Ok', + 3: 'Ok+', + 3.5: 'Good', + 4: 'Good+', + 4.5: 'Excellent', + 5: 'Excellent+', +}; + +const useStyles = makeStyles({ + root: { + width: 200, + display: 'flex', + alignItems: 'center', + }, +}); + +export function HoverRating() { + const [value, setValue] = React.useState(2); + const [hover, setHover] = React.useState(-1); + const classes = useStyles(); + + return ( +
    + { + setValue(newValue); + }} + onChangeActive={(event, newHover) => { + setHover(newHover); + }} + /> + {value !== null && {labels[hover !== -1 ? hover : value]}} +
    + ); +} + +export default { + title: "Material-ui|rating|HoverRating.stories" +}; diff --git a/examples/storybook/stories/material-ui/rating/RatingSize.stories.tsx b/examples/storybook/stories/material-ui/rating/RatingSize.stories.tsx new file mode 100644 index 00000000000000..b7d1f2e0ea96fb --- /dev/null +++ b/examples/storybook/stories/material-ui/rating/RatingSize.stories.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import Rating from '@material-ui/lab/Rating'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + flexDirection: 'column', + '& > * + *': { + marginTop: theme.spacing(1), + }, + }, + }), +); + +export function HalfRating() { + const classes = useStyles(); + + return ( +
    + + + +
    + ); +} + +export default { + title: "Material-ui|rating|RatingSize.stories" +}; diff --git a/examples/storybook/stories/material-ui/rating/SimpleRating.stories.tsx b/examples/storybook/stories/material-ui/rating/SimpleRating.stories.tsx new file mode 100644 index 00000000000000..439529c47aa548 --- /dev/null +++ b/examples/storybook/stories/material-ui/rating/SimpleRating.stories.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import Rating from '@material-ui/lab/Rating'; +import Typography from '@material-ui/core/Typography'; +import Box from '@material-ui/core/Box'; + +export function SimpleRating() { + const [value, setValue] = React.useState(2); + + return ( +
    + + Controlled + { + setValue(newValue); + }} + /> + + + Read only + + + + Disabled + + + + Pristine + + +
    + ); +} + +export default { + title: "Material-ui|rating|SimpleRating.stories" +}; diff --git a/examples/storybook/stories/material-ui/selects/ControlledOpenSelect.stories.tsx b/examples/storybook/stories/material-ui/selects/ControlledOpenSelect.stories.tsx new file mode 100644 index 00000000000000..e7dd6d70ebee66 --- /dev/null +++ b/examples/storybook/stories/material-ui/selects/ControlledOpenSelect.stories.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import InputLabel from '@material-ui/core/InputLabel'; +import MenuItem from '@material-ui/core/MenuItem'; +import FormControl from '@material-ui/core/FormControl'; +import Select from '@material-ui/core/Select'; +import Button from '@material-ui/core/Button'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + button: { + display: 'block', + marginTop: theme.spacing(2), + }, + formControl: { + margin: theme.spacing(1), + minWidth: 120, + }, + }), +); + +export function ControlledOpenSelect() { + const classes = useStyles(); + const [age, setAge] = React.useState(''); + const [open, setOpen] = React.useState(false); + + const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => { + setAge(event.target.value as number); + }; + + const handleClose = () => { + setOpen(false); + }; + + const handleOpen = () => { + setOpen(true); + }; + + return ( +
    + + + Age + + +
    + ); +} + +export default { + title: "Material-ui|selects|ControlledOpenSelect.stories" +}; diff --git a/examples/storybook/stories/material-ui/selects/CustomizedSelects.stories.tsx b/examples/storybook/stories/material-ui/selects/CustomizedSelects.stories.tsx new file mode 100644 index 00000000000000..652802ae4358f0 --- /dev/null +++ b/examples/storybook/stories/material-ui/selects/CustomizedSelects.stories.tsx @@ -0,0 +1,104 @@ +import React from 'react'; +import { createStyles, makeStyles, withStyles, Theme } from '@material-ui/core/styles'; +import InputLabel from '@material-ui/core/InputLabel'; +import MenuItem from '@material-ui/core/MenuItem'; +import FormControl from '@material-ui/core/FormControl'; +import Select from '@material-ui/core/Select'; +import NativeSelect from '@material-ui/core/NativeSelect'; +import InputBase from '@material-ui/core/InputBase'; + +const BootstrapInput = withStyles((theme: Theme) => + createStyles({ + root: { + 'label + &': { + marginTop: theme.spacing(3), + }, + }, + input: { + borderRadius: 4, + position: 'relative', + backgroundColor: theme.palette.background.paper, + border: '1px solid #ced4da', + fontSize: 16, + padding: '10px 26px 10px 12px', + transition: theme.transitions.create(['border-color', 'box-shadow']), + // Use the system font instead of the default Roboto font. + fontFamily: [ + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', + 'Roboto', + '"Helvetica Neue"', + 'Arial', + 'sans-serif', + '"Apple Color Emoji"', + '"Segoe UI Emoji"', + '"Segoe UI Symbol"', + ].join(','), + '&:focus': { + borderRadius: 4, + borderColor: '#80bdff', + boxShadow: '0 0 0 0.2rem rgba(0,123,255,.25)', + }, + }, + }), +)(InputBase); + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + margin: { + margin: theme.spacing(1), + }, + }), +); + +export function CustomizedSelects() { + const classes = useStyles(); + const [age, setAge] = React.useState(''); + const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => { + setAge(event.target.value as string); + }; + return ( +
    + + Age + + + + Age + + + + Age + } + > + + + + + +
    + ); +} + +export default { + title: "Material-ui|selects|CustomizedSelects.stories" +}; diff --git a/examples/storybook/stories/material-ui/selects/DialogSelect.stories.tsx b/examples/storybook/stories/material-ui/selects/DialogSelect.stories.tsx new file mode 100644 index 00000000000000..77fdd67f637e89 --- /dev/null +++ b/examples/storybook/stories/material-ui/selects/DialogSelect.stories.tsx @@ -0,0 +1,99 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import InputLabel from '@material-ui/core/InputLabel'; +import Input from '@material-ui/core/Input'; +import MenuItem from '@material-ui/core/MenuItem'; +import FormControl from '@material-ui/core/FormControl'; +import Select from '@material-ui/core/Select'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + container: { + display: 'flex', + flexWrap: 'wrap', + }, + formControl: { + margin: theme.spacing(1), + minWidth: 120, + }, + }), +); + +export function DialogSelect() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + const [age, setAge] = React.useState(''); + + const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => { + setAge(Number(event.target.value) || ''); + }; + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + return ( +
    + + + Fill the form + +
    + + Age + + + + Age + + +
    +
    + + + + +
    +
    + ); +} + +export default { + title: "Material-ui|selects|DialogSelect.stories" +}; diff --git a/examples/storybook/stories/material-ui/selects/GroupedSelect.stories.tsx b/examples/storybook/stories/material-ui/selects/GroupedSelect.stories.tsx new file mode 100644 index 00000000000000..01ae8d01bb6715 --- /dev/null +++ b/examples/storybook/stories/material-ui/selects/GroupedSelect.stories.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import InputLabel from '@material-ui/core/InputLabel'; +import MenuItem from '@material-ui/core/MenuItem'; +import ListSubheader from '@material-ui/core/ListSubheader'; +import FormControl from '@material-ui/core/FormControl'; +import Select from '@material-ui/core/Select'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + formControl: { + margin: theme.spacing(1), + minWidth: 120, + }, + }), +); + +export function GroupedSelect() { + const classes = useStyles(); + + return ( +
    + + Grouping + + + + Grouping + + +
    + ); +} + +export default { + title: "Material-ui|selects|GroupedSelect.stories" +}; diff --git a/examples/storybook/stories/material-ui/selects/MultipleSelect.stories.tsx b/examples/storybook/stories/material-ui/selects/MultipleSelect.stories.tsx new file mode 100644 index 00000000000000..8e8ace5179287d --- /dev/null +++ b/examples/storybook/stories/material-ui/selects/MultipleSelect.stories.tsx @@ -0,0 +1,204 @@ +import React from 'react'; +import clsx from 'clsx'; +import { createStyles, makeStyles, useTheme, Theme } from '@material-ui/core/styles'; +import Input from '@material-ui/core/Input'; +import InputLabel from '@material-ui/core/InputLabel'; +import MenuItem from '@material-ui/core/MenuItem'; +import FormControl from '@material-ui/core/FormControl'; +import ListItemText from '@material-ui/core/ListItemText'; +import Select from '@material-ui/core/Select'; +import Checkbox from '@material-ui/core/Checkbox'; +import Chip from '@material-ui/core/Chip'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + formControl: { + margin: theme.spacing(1), + minWidth: 120, + maxWidth: 300, + }, + chips: { + display: 'flex', + flexWrap: 'wrap', + }, + chip: { + margin: 2, + }, + noLabel: { + marginTop: theme.spacing(3), + }, + }), +); + +const ITEM_HEIGHT = 48; +const ITEM_PADDING_TOP = 8; +const MenuProps = { + PaperProps: { + style: { + maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, + width: 250, + }, + }, +}; + +const names = [ + 'Oliver Hansen', + 'Van Henry', + 'April Tucker', + 'Ralph Hubbard', + 'Omar Alexander', + 'Carlos Abbott', + 'Miriam Wagner', + 'Bradley Wilkerson', + 'Virginia Andrews', + 'Kelly Snyder', +]; + +function getStyles(name: string, personName: string[], theme: Theme) { + return { + fontWeight: + personName.indexOf(name) === -1 + ? theme.typography.fontWeightRegular + : theme.typography.fontWeightMedium, + }; +} + +export function MultipleSelect() { + const classes = useStyles(); + const theme = useTheme(); + const [personName, setPersonName] = React.useState([]); + + const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => { + setPersonName(event.target.value as string[]); + }; + + const handleChangeMultiple = (event: React.ChangeEvent<{ value: unknown }>) => { + const { options } = event.target as HTMLSelectElement; + const value: string[] = []; + for (let i = 0, l = options.length; i < l; i += 1) { + if (options[i].selected) { + value.push(options[i].value); + } + } + setPersonName(value); + }; + + return ( +
    + + Name + + + + Tag + + + + Chip + + + + + + + + Native + + + +
    + ); +} + +export default { + title: "Material-ui|selects|MultipleSelect.stories" +}; diff --git a/examples/storybook/stories/material-ui/selects/NativeSelects.stories.tsx b/examples/storybook/stories/material-ui/selects/NativeSelects.stories.tsx new file mode 100644 index 00000000000000..0334dff51dfb0e --- /dev/null +++ b/examples/storybook/stories/material-ui/selects/NativeSelects.stories.tsx @@ -0,0 +1,238 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import InputLabel from '@material-ui/core/InputLabel'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import FormControl from '@material-ui/core/FormControl'; +import Select from '@material-ui/core/Select'; +import NativeSelect from '@material-ui/core/NativeSelect'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + formControl: { + margin: theme.spacing(1), + minWidth: 120, + }, + selectEmpty: { + marginTop: theme.spacing(2), + }, + }), +); + +export function NativeSelects() { + const classes = useStyles(); + const [state, setState] = React.useState<{ age: string | number; name: string }>({ + age: '', + name: 'hai', + }); + + const handleChange = (event: React.ChangeEvent<{ name?: string; value: unknown }>) => { + const name = event.target.name as keyof typeof state; + setState({ + ...state, + [name]: event.target.value, + }); + }; + + return ( +
    + + Age + + + + Age + + + + + + Some important helper text + + + + + + + + + With visually hidden label + + + + Age + + + + + + + + Label + placeholder + + + Name + + + + + + + + + + + Disabled + + + Name + + + + + + + + + + Error + + + Name + + + + + + Uncontrolled + + + + + + + + + Placeholder + + + Age + + Required + + + Age + + + + Age + + +
    + ); +} + +export default { + title: "Material-ui|selects|NativeSelects.stories" +}; diff --git a/examples/storybook/stories/material-ui/selects/SimpleSelect.stories.tsx b/examples/storybook/stories/material-ui/selects/SimpleSelect.stories.tsx new file mode 100644 index 00000000000000..fd38d0e0a87d1f --- /dev/null +++ b/examples/storybook/stories/material-ui/selects/SimpleSelect.stories.tsx @@ -0,0 +1,244 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import InputLabel from '@material-ui/core/InputLabel'; +import MenuItem from '@material-ui/core/MenuItem'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import FormControl from '@material-ui/core/FormControl'; +import Select from '@material-ui/core/Select'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + formControl: { + margin: theme.spacing(1), + minWidth: 120, + }, + selectEmpty: { + marginTop: theme.spacing(2), + }, + }), +); + +export function SimpleSelect() { + const classes = useStyles(); + const [age, setAge] = React.useState(''); + + const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => { + setAge(event.target.value as string); + }; + + return ( +
    + + Age + + + + Age + + Some important helper text + + + + Without label + + + + Age + + + Label + placeholder + + + Name + + Disabled + + + Name + + Error + + + Name + + Read only + + + Age + + Auto width + + + + Placeholder + + + Age + + Required + + + Age + + + + Age + + +
    + ); +} + +export default { + title: "Material-ui|selects|SimpleSelect.stories" +}; diff --git a/examples/storybook/stories/material-ui/skeleton/Animations.stories.tsx b/examples/storybook/stories/material-ui/skeleton/Animations.stories.tsx new file mode 100644 index 00000000000000..fab6f904101dcd --- /dev/null +++ b/examples/storybook/stories/material-ui/skeleton/Animations.stories.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import Skeleton from '@material-ui/lab/Skeleton'; +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles({ + root: { + width: 300, + }, +}); + +export function Animations() { + const classes = useStyles(); + return ( +
    + + + +
    + ); +} + +export default { + title: "Material-ui|skeleton|Animations.stories" +}; diff --git a/examples/storybook/stories/material-ui/skeleton/Facebook.stories.tsx b/examples/storybook/stories/material-ui/skeleton/Facebook.stories.tsx new file mode 100644 index 00000000000000..da8c271bc43898 --- /dev/null +++ b/examples/storybook/stories/material-ui/skeleton/Facebook.stories.tsx @@ -0,0 +1,100 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardContent from '@material-ui/core/CardContent'; +import CardMedia from '@material-ui/core/CardMedia'; +import Avatar from '@material-ui/core/Avatar'; +import Typography from '@material-ui/core/Typography'; +import IconButton from '@material-ui/core/IconButton'; +import MoreVertIcon from '@material-ui/icons/MoreVert'; +import Skeleton from '@material-ui/lab/Skeleton'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + card: { + maxWidth: 345, + margin: theme.spacing(2), + }, + media: { + height: 190, + }, + }), +); + +interface MediaProps { + loading?: boolean; +} + +function Media(props: MediaProps) { + const { loading = false } = props; + const classes = useStyles(); + + return ( + + + ) : ( + + ) + } + action={ + loading ? null : ( + + + + ) + } + title={ + loading ? ( + + ) : ( + 'Ted' + ) + } + subheader={loading ? : '5 hours ago'} + /> + {loading ? ( + + ) : ( + + )} + + {loading ? ( + + + + + ) : ( + + { + "Why First Minister of Scotland Nicola Sturgeon thinks GDP is the wrong measure of a country's success:" + } + + )} + + + ); +} + +export function Facebook() { + return ( +
    + + +
    + ); +} + +export default { + title: "Material-ui|skeleton|Facebook.stories" +}; diff --git a/examples/storybook/stories/material-ui/skeleton/SkeletonChildren.stories.tsx b/examples/storybook/stories/material-ui/skeleton/SkeletonChildren.stories.tsx new file mode 100644 index 00000000000000..16319ca86f698b --- /dev/null +++ b/examples/storybook/stories/material-ui/skeleton/SkeletonChildren.stories.tsx @@ -0,0 +1,75 @@ +import React from 'react'; +import Box from '@material-ui/core/Box'; +import Typography from '@material-ui/core/Typography'; +import Avatar from '@material-ui/core/Avatar'; +import Grid from '@material-ui/core/Grid'; +import { makeStyles } from '@material-ui/core/styles'; +import Skeleton from '@material-ui/lab/Skeleton'; + +const useStyles = makeStyles(() => ({ + image: { + width: '100%', + }, +})); + +function SkeletonChildrenDemo(props: { loading?: boolean }) { + const { loading = false } = props; + const classes = useStyles(); + + return ( +
    + + + {loading ? ( + + + + ) : ( + + )} + + + {loading ? ( + + . + + ) : ( + Ted + )} + + + {loading ? ( + + + + ) : ( + + )} +
    + ); +} + +export function SkeletonChildren() { + return ( + + + + + + + + + ); +} + +export default { + title: "Material-ui|skeleton|SkeletonChildren.stories" +}; diff --git a/examples/storybook/stories/material-ui/skeleton/SkeletonTypography.stories.tsx b/examples/storybook/stories/material-ui/skeleton/SkeletonTypography.stories.tsx new file mode 100644 index 00000000000000..5a35adfd5f0798 --- /dev/null +++ b/examples/storybook/stories/material-ui/skeleton/SkeletonTypography.stories.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import Typography, { TypographyProps } from '@material-ui/core/Typography'; +import Skeleton from '@material-ui/lab/Skeleton'; +import Grid from '@material-ui/core/Grid'; + +const variants = ['h1', 'h3', 'body1', 'caption'] as TypographyProps['variant'][]; + +function TypographyDemo(props: { loading?: boolean }) { + const { loading = false } = props; + + return ( +
    + {variants.map((variant) => ( + + {loading ? : variant} + + ))} +
    + ); +} + +export function SkeletonTypography() { + return ( + + + + + + + + + ); +} + +export default { + title: "Material-ui|skeleton|SkeletonTypography.stories" +}; diff --git a/examples/storybook/stories/material-ui/skeleton/Variants.stories.tsx b/examples/storybook/stories/material-ui/skeleton/Variants.stories.tsx new file mode 100644 index 00000000000000..7b94e163779664 --- /dev/null +++ b/examples/storybook/stories/material-ui/skeleton/Variants.stories.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import Skeleton from '@material-ui/lab/Skeleton'; + +export function Variants() { + return ( +
    + + + +
    + ); +} + +export default { + title: "Material-ui|skeleton|Variants.stories" +}; diff --git a/examples/storybook/stories/material-ui/skeleton/YouTube.stories.tsx b/examples/storybook/stories/material-ui/skeleton/YouTube.stories.tsx new file mode 100644 index 00000000000000..cca64254abe73e --- /dev/null +++ b/examples/storybook/stories/material-ui/skeleton/YouTube.stories.tsx @@ -0,0 +1,85 @@ +import React from 'react'; +import Grid from '@material-ui/core/Grid'; +import Box from '@material-ui/core/Box'; +import Typography from '@material-ui/core/Typography'; +import Skeleton from '@material-ui/lab/Skeleton'; + +const data = [ + { + src: + 'https://i.ytimg.com/vi/pLqipJNItIo/hqdefault.jpg?sqp=-oaymwEYCNIBEHZIVfKriqkDCwgBFQAAiEIYAXAB&rs=AOn4CLBkklsyaw9FxDmMKapyBYCn9tbPNQ', + title: 'Don Diablo @ Tomorrowland Main Stage 2019 | Official…', + channel: 'Don Diablo', + views: '396 k views', + createdAt: 'a week ago', + }, + { + src: + 'https://i.ytimg.com/vi/_Uu12zY01ts/hqdefault.jpg?sqp=-oaymwEZCPYBEIoBSFXyq4qpAwsIARUAAIhCGAFwAQ==&rs=AOn4CLCpX6Jan2rxrCAZxJYDXppTP4MoQA', + title: 'Queen - Greatest Hits', + channel: 'Queen Official', + views: '40 M views', + createdAt: '3 years ago', + }, + { + src: + 'https://i.ytimg.com/vi/kkLk2XWMBf8/hqdefault.jpg?sqp=-oaymwEYCNIBEHZIVfKriqkDCwgBFQAAiEIYAXAB&rs=AOn4CLB4GZTFu1Ju2EPPPXnhMZtFVvYBaw', + title: 'Calvin Harris, Sam Smith - Promises (Official Video)', + channel: 'Calvin Harris', + views: '130 M views', + createdAt: '10 months ago', + }, +]; + +interface MediaProps { + loading?: boolean; +} + +function Media(props: MediaProps) { + const { loading = false } = props; + + return ( + + {(loading ? Array.from(new Array(3)) : data).map((item, index) => ( + + {item ? ( + {item.title} + ) : ( + + )} + {item ? ( + + + {item.title} + + + {item.channel} + + + {`${item.views} • ${item.createdAt}`} + + + ) : ( + + + + + )} + + ))} + + ); +} + +export function YouTube() { + return ( + + + + + ); +} + +export default { + title: "Material-ui|skeleton|YouTube.stories" +}; diff --git a/examples/storybook/stories/material-ui/slider/ContinuousSlider.stories.tsx b/examples/storybook/stories/material-ui/slider/ContinuousSlider.stories.tsx new file mode 100644 index 00000000000000..7b81ec8f7c9864 --- /dev/null +++ b/examples/storybook/stories/material-ui/slider/ContinuousSlider.stories.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +import Slider from '@material-ui/core/Slider'; +import VolumeDown from '@material-ui/icons/VolumeDown'; +import VolumeUp from '@material-ui/icons/VolumeUp'; + +const useStyles = makeStyles({ + root: { + width: 200, + }, +}); + +export function ContinuousSlider() { + const classes = useStyles(); + const [value, setValue] = React.useState(30); + + const handleChange = (event: any, newValue: number | number[]) => { + setValue(newValue as number); + }; + + return ( +
    + + Volume + + + + + + + + + + + + + + Disabled slider + + +
    + ); +} + +export default { + title: "Material-ui|slider|ContinuousSlider.stories" +}; diff --git a/examples/storybook/stories/material-ui/slider/CustomizedSlider.stories.tsx b/examples/storybook/stories/material-ui/slider/CustomizedSlider.stories.tsx new file mode 100644 index 00000000000000..d88b0405434bca --- /dev/null +++ b/examples/storybook/stories/material-ui/slider/CustomizedSlider.stories.tsx @@ -0,0 +1,209 @@ +import React from 'react'; +import { withStyles, makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Slider from '@material-ui/core/Slider'; +import Typography from '@material-ui/core/Typography'; +import Tooltip from '@material-ui/core/Tooltip'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: 300 + theme.spacing(3) * 2, + }, + margin: { + height: theme.spacing(3), + }, + }), +); + +interface Props { + children: React.ReactElement; + open: boolean; + value: number; +} + +function ValueLabelComponent(props: Props) { + const { children, open, value } = props; + + return ( + + {children} + + ); +} + +const iOSBoxShadow = + '0 3px 1px rgba(0,0,0,0.1),0 4px 8px rgba(0,0,0,0.13),0 0 0 1px rgba(0,0,0,0.02)'; + +const marks = [ + { + value: 0, + }, + { + value: 20, + }, + { + value: 37, + }, + { + value: 100, + }, +]; + +const IOSSlider = withStyles({ + root: { + color: '#3880ff', + height: 2, + padding: '15px 0', + }, + thumb: { + height: 28, + width: 28, + backgroundColor: '#fff', + boxShadow: iOSBoxShadow, + marginTop: -14, + marginLeft: -14, + '&:focus, &:hover, &$active': { + boxShadow: '0 3px 1px rgba(0,0,0,0.1),0 4px 8px rgba(0,0,0,0.3),0 0 0 1px rgba(0,0,0,0.02)', + // Reset on touch devices, it doesn't add specificity + '@media (hover: none)': { + boxShadow: iOSBoxShadow, + }, + }, + }, + active: {}, + valueLabel: { + left: 'calc(-50% + 12px)', + top: -22, + '& *': { + background: 'transparent', + color: '#000', + }, + }, + track: { + height: 2, + }, + rail: { + height: 2, + opacity: 0.5, + backgroundColor: '#bfbfbf', + }, + mark: { + backgroundColor: '#bfbfbf', + height: 8, + width: 1, + marginTop: -3, + }, + markActive: { + opacity: 1, + backgroundColor: 'currentColor', + }, +})(Slider); + +const PrettoSlider = withStyles({ + root: { + color: '#52af77', + height: 8, + }, + thumb: { + height: 24, + width: 24, + backgroundColor: '#fff', + border: '2px solid currentColor', + marginTop: -8, + marginLeft: -12, + '&:focus, &:hover, &$active': { + boxShadow: 'inherit', + }, + }, + active: {}, + valueLabel: { + left: 'calc(-50% + 4px)', + }, + track: { + height: 8, + borderRadius: 4, + }, + rail: { + height: 8, + borderRadius: 4, + }, +})(Slider); + +const AirbnbSlider = withStyles({ + root: { + color: '#3a8589', + height: 3, + padding: '13px 0', + }, + thumb: { + height: 27, + width: 27, + backgroundColor: '#fff', + border: '1px solid currentColor', + marginTop: -12, + marginLeft: -13, + boxShadow: '#ebebeb 0 2px 2px', + '&:focus, &:hover, &$active': { + boxShadow: '#ccc 0 2px 3px 1px', + }, + '& .bar': { + // display: inline-block !important; + height: 9, + width: 1, + backgroundColor: 'currentColor', + marginLeft: 1, + marginRight: 1, + }, + }, + active: {}, + track: { + height: 3, + }, + rail: { + color: '#d8d8d8', + opacity: 1, + height: 3, + }, +})(Slider); + +function AirbnbThumbComponent(props: any) { + return ( + + + + + + ); +} + +export function CustomizedSlider() { + const classes = useStyles(); + + return ( +
    + iOS + +
    + pretto.fr + +
    + Tooltip value label + +
    + Airbnb + (index === 0 ? 'Minimum price' : 'Maximum price')} + defaultValue={[20, 40]} + /> +
    + ); +} + +export default { + title: "Material-ui|slider|CustomizedSlider.stories" +}; diff --git a/examples/storybook/stories/material-ui/slider/DiscreteSlider.stories.tsx b/examples/storybook/stories/material-ui/slider/DiscreteSlider.stories.tsx new file mode 100644 index 00000000000000..afbb0d2284333c --- /dev/null +++ b/examples/storybook/stories/material-ui/slider/DiscreteSlider.stories.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Slider from '@material-ui/core/Slider'; + +const useStyles = makeStyles({ + root: { + width: 300, + }, +}); + +function valuetext(value: number) { + return `${value}°C`; +} + +export function DiscreteSlider() { + const classes = useStyles(); + + return ( +
    + + Temperature + + + + Disabled + + +
    + ); +} + +export default { + title: "Material-ui|slider|DiscreteSlider.stories" +}; diff --git a/examples/storybook/stories/material-ui/slider/DiscreteSliderLabel.stories.tsx b/examples/storybook/stories/material-ui/slider/DiscreteSliderLabel.stories.tsx new file mode 100644 index 00000000000000..75160c6ee4f625 --- /dev/null +++ b/examples/storybook/stories/material-ui/slider/DiscreteSliderLabel.stories.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Slider from '@material-ui/core/Slider'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: 300, + }, + margin: { + height: theme.spacing(3), + }, + }), +); + +const marks = [ + { + value: 0, + label: '0°C', + }, + { + value: 20, + label: '20°C', + }, + { + value: 37, + label: '37°C', + }, + { + value: 100, + label: '100°C', + }, +]; + +function valuetext(value: number) { + return `${value}°C`; +} + +export function DiscreteSlider() { + const classes = useStyles(); + + return ( +
    + + Always visible + + +
    + ); +} + +export default { + title: "Material-ui|slider|DiscreteSliderLabel.stories" +}; diff --git a/examples/storybook/stories/material-ui/slider/DiscreteSliderMarks.stories.tsx b/examples/storybook/stories/material-ui/slider/DiscreteSliderMarks.stories.tsx new file mode 100644 index 00000000000000..881c592bab293f --- /dev/null +++ b/examples/storybook/stories/material-ui/slider/DiscreteSliderMarks.stories.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Slider from '@material-ui/core/Slider'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: 300, + }, + margin: { + height: theme.spacing(3), + }, + }), +); + +const marks = [ + { + value: 0, + label: '0°C', + }, + { + value: 20, + label: '20°C', + }, + { + value: 37, + label: '37°C', + }, + { + value: 100, + label: '100°C', + }, +]; + +function valuetext(value: number) { + return `${value}°C`; +} + +export function DiscreteSlider() { + const classes = useStyles(); + + return ( +
    + + Custom marks + + +
    + ); +} + +export default { + title: "Material-ui|slider|DiscreteSliderMarks.stories" +}; diff --git a/examples/storybook/stories/material-ui/slider/DiscreteSliderSteps.stories.tsx b/examples/storybook/stories/material-ui/slider/DiscreteSliderSteps.stories.tsx new file mode 100644 index 00000000000000..09ede983c55575 --- /dev/null +++ b/examples/storybook/stories/material-ui/slider/DiscreteSliderSteps.stories.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Slider from '@material-ui/core/Slider'; + +const useStyles = makeStyles({ + root: { + width: 300, + }, +}); + +function valuetext(value: number) { + return `${value}°C`; +} + +export function DiscreteSlider() { + const classes = useStyles(); + + return ( +
    + + Small steps + + +
    + ); +} + +export default { + title: "Material-ui|slider|DiscreteSliderSteps.stories" +}; diff --git a/examples/storybook/stories/material-ui/slider/DiscreteSliderValues.stories.tsx b/examples/storybook/stories/material-ui/slider/DiscreteSliderValues.stories.tsx new file mode 100644 index 00000000000000..9853281ec20a53 --- /dev/null +++ b/examples/storybook/stories/material-ui/slider/DiscreteSliderValues.stories.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Slider from '@material-ui/core/Slider'; + +const useStyles = makeStyles({ + root: { + width: 300, + }, +}); + +const marks = [ + { + value: 0, + label: '0°C', + }, + { + value: 20, + label: '20°C', + }, + { + value: 37, + label: '37°C', + }, + { + value: 100, + label: '100°C', + }, +]; + +function valuetext(value: number) { + return `${value}°C`; +} + +function valueLabelFormat(value: number) { + return marks.findIndex((mark) => mark.value === value) + 1; +} + +export function DiscreteSlider() { + const classes = useStyles(); + + return ( +
    + + Restricted values + + +
    + ); +} + +export default { + title: "Material-ui|slider|DiscreteSliderValues.stories" +}; diff --git a/examples/storybook/stories/material-ui/slider/InputSlider.stories.tsx b/examples/storybook/stories/material-ui/slider/InputSlider.stories.tsx new file mode 100644 index 00000000000000..77739cd6953a5f --- /dev/null +++ b/examples/storybook/stories/material-ui/slider/InputSlider.stories.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +import Slider from '@material-ui/core/Slider'; +import Input from '@material-ui/core/Input'; +import VolumeUp from '@material-ui/icons/VolumeUp'; + +const useStyles = makeStyles({ + root: { + width: 250, + }, + input: { + width: 42, + }, +}); + +export function InputSlider() { + const classes = useStyles(); + const [value, setValue] = React.useState>(30); + + const handleSliderChange = (event: any, newValue: number | number[]) => { + setValue(newValue); + }; + + const handleInputChange = (event: React.ChangeEvent) => { + setValue(event.target.value === '' ? '' : Number(event.target.value)); + }; + + const handleBlur = () => { + if (value < 0) { + setValue(0); + } else if (value > 100) { + setValue(100); + } + }; + + return ( +
    + + Volume + + + + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|slider|InputSlider.stories" +}; diff --git a/examples/storybook/stories/material-ui/slider/NonLinearSlider.stories.tsx b/examples/storybook/stories/material-ui/slider/NonLinearSlider.stories.tsx new file mode 100644 index 00000000000000..9778675d87d9bc --- /dev/null +++ b/examples/storybook/stories/material-ui/slider/NonLinearSlider.stories.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import Typography from '@material-ui/core/Typography'; +import Slider from '@material-ui/core/Slider'; + +function valueLabelFormat(value: number) { + const [coefficient, exponent] = value + .toExponential() + .split('e') + .map((item) => Number(item)); + return `${Math.round(coefficient)}e^${exponent}`; +} + +export function NonLinearSlider() { + const [value, setValue] = React.useState(1); + + const handleChange = (event: any, newValue: number | number[]) => { + setValue(newValue); + }; + + return ( +
    + + Temperature range + + x ** 10} + getAriaValueText={valueLabelFormat} + valueLabelFormat={valueLabelFormat} + onChange={handleChange} + valueLabelDisplay="auto" + aria-labelledby="non-linear-slider" + /> +
    + ); +} + +export default { + title: "Material-ui|slider|NonLinearSlider.stories" +}; diff --git a/examples/storybook/stories/material-ui/slider/RangeSlider.stories.tsx b/examples/storybook/stories/material-ui/slider/RangeSlider.stories.tsx new file mode 100644 index 00000000000000..c350cc68895009 --- /dev/null +++ b/examples/storybook/stories/material-ui/slider/RangeSlider.stories.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Slider from '@material-ui/core/Slider'; + +const useStyles = makeStyles({ + root: { + width: 300, + }, +}); + +function valuetext(value: number) { + return `${value}°C`; +} + +export function RangeSlider() { + const classes = useStyles(); + const [value, setValue] = React.useState([20, 37]); + + const handleChange = (event: any, newValue: number | number[]) => { + setValue(newValue as number[]); + }; + + return ( +
    + + Temperature range + + +
    + ); +} + +export default { + title: "Material-ui|slider|RangeSlider.stories" +}; diff --git a/examples/storybook/stories/material-ui/slider/TrackFalseSlider.stories.tsx b/examples/storybook/stories/material-ui/slider/TrackFalseSlider.stories.tsx new file mode 100644 index 00000000000000..49a87a56218764 --- /dev/null +++ b/examples/storybook/stories/material-ui/slider/TrackFalseSlider.stories.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Slider from '@material-ui/core/Slider'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: 250, + }, + margin: { + height: theme.spacing(3), + }, + }), +); + +const marks = [ + { + value: 0, + label: '0°C', + }, + { + value: 20, + label: '20°C', + }, + { + value: 37, + label: '37°C', + }, + { + value: 100, + label: '100°C', + }, +]; + +function valuetext(value: number) { + return `${value}°C`; +} + +export function TrackFalseSlider() { + const classes = useStyles(); + + return ( +
    + + Removed track + + +
    + + Removed track range slider + + +
    + ); +} + +export default { + title: "Material-ui|slider|TrackFalseSlider.stories" +}; diff --git a/examples/storybook/stories/material-ui/slider/TrackInvertedSlider.stories.tsx b/examples/storybook/stories/material-ui/slider/TrackInvertedSlider.stories.tsx new file mode 100644 index 00000000000000..bb9b6bbf54ee61 --- /dev/null +++ b/examples/storybook/stories/material-ui/slider/TrackInvertedSlider.stories.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Slider from '@material-ui/core/Slider'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: 250, + }, + margin: { + height: theme.spacing(3), + }, + }), +); + +const marks = [ + { + value: 0, + label: '0°C', + }, + { + value: 20, + label: '20°C', + }, + { + value: 37, + label: '37°C', + }, + { + value: 100, + label: '100°C', + }, +]; + +function valuetext(value: number) { + return `${value}°C`; +} + +export function TrackInvertedSlider() { + const classes = useStyles(); + + return ( +
    + + Inverted track + + +
    + + Inverted track range + + +
    + ); +} + +export default { + title: "Material-ui|slider|TrackInvertedSlider.stories" +}; diff --git a/examples/storybook/stories/material-ui/slider/VerticalSlider.stories.tsx b/examples/storybook/stories/material-ui/slider/VerticalSlider.stories.tsx new file mode 100644 index 00000000000000..589baadf84146b --- /dev/null +++ b/examples/storybook/stories/material-ui/slider/VerticalSlider.stories.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Slider from '@material-ui/core/Slider'; + +const useStyles = makeStyles({ + root: { + height: 300, + }, +}); + +function valuetext(value: number) { + return `${value}°C`; +} + +const marks = [ + { + value: 0, + label: '0°C', + }, + { + value: 20, + label: '20°C', + }, + { + value: 37, + label: '37°C', + }, + { + value: 100, + label: '100°C', + }, +]; + +export function VerticalSlider() { + const classes = useStyles(); + + return ( + + + Temperature + +
    + + + +
    +
    + ); +} + +export default { + title: "Material-ui|slider|VerticalSlider.stories" +}; diff --git a/examples/storybook/stories/material-ui/snackbars/ConsecutiveSnackbars.stories.tsx b/examples/storybook/stories/material-ui/snackbars/ConsecutiveSnackbars.stories.tsx new file mode 100644 index 00000000000000..fbf660fdc9f86d --- /dev/null +++ b/examples/storybook/stories/material-ui/snackbars/ConsecutiveSnackbars.stories.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Snackbar from '@material-ui/core/Snackbar'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from '@material-ui/icons/Close'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + close: { + padding: theme.spacing(0.5), + }, + }), +); + +export interface SnackbarMessage { + message: string; + key: number; +} + +export interface State { + open: boolean; + snackPack: SnackbarMessage[]; + messageInfo?: SnackbarMessage; +} + +export function ConsecutiveSnackbars() { + const [snackPack, setSnackPack] = React.useState([]); + const [open, setOpen] = React.useState(false); + const [messageInfo, setMessageInfo] = React.useState(undefined); + + React.useEffect(() => { + if (snackPack.length && !messageInfo) { + // Set a new snack when we don't have an active one + setMessageInfo({ ...snackPack[0] }); + setSnackPack((prev) => prev.slice(1)); + setOpen(true); + } else if (snackPack.length && messageInfo && open) { + // Close an active snack when a new one is added + setOpen(false); + } + }, [snackPack, messageInfo, open]); + + const handleClick = (message: string) => () => { + setSnackPack((prev) => [...prev, { message, key: new Date().getTime() }]); + }; + + const handleClose = (event: React.SyntheticEvent | MouseEvent, reason?: string) => { + if (reason === 'clickaway') { + return; + } + setOpen(false); + }; + + const handleExited = () => { + setMessageInfo(undefined); + }; + + const classes = useStyles(); + return ( +
    + + + + + + + + + } + /> +
    + ); +} + +export default { + title: "Material-ui|snackbars|ConsecutiveSnackbars.stories" +}; diff --git a/examples/storybook/stories/material-ui/snackbars/CustomizedSnackbars.stories.tsx b/examples/storybook/stories/material-ui/snackbars/CustomizedSnackbars.stories.tsx new file mode 100644 index 00000000000000..49bd3cf494cf7b --- /dev/null +++ b/examples/storybook/stories/material-ui/snackbars/CustomizedSnackbars.stories.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Snackbar from '@material-ui/core/Snackbar'; +import MuiAlert, { AlertProps } from '@material-ui/lab/Alert'; +import { makeStyles, Theme } from '@material-ui/core/styles'; + +function Alert(props: AlertProps) { + return ; +} + +const useStyles = makeStyles((theme: Theme) => ({ + root: { + width: '100%', + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, +})); + +export function CustomizedSnackbars() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + + const handleClick = () => { + setOpen(true); + }; + + const handleClose = (event?: React.SyntheticEvent, reason?: string) => { + if (reason === 'clickaway') { + return; + } + + setOpen(false); + }; + + return ( +
    + + + + This is a success message! + + + This is an error message! + This is a warning message! + This is an information message! + This is a success message! +
    + ); +} + +export default { + title: "Material-ui|snackbars|CustomizedSnackbars.stories" +}; diff --git a/examples/storybook/stories/material-ui/snackbars/DirectionSnackbar.stories.tsx b/examples/storybook/stories/material-ui/snackbars/DirectionSnackbar.stories.tsx new file mode 100644 index 00000000000000..0d6ab8cdb93724 --- /dev/null +++ b/examples/storybook/stories/material-ui/snackbars/DirectionSnackbar.stories.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Snackbar from '@material-ui/core/Snackbar'; +import Slide, { SlideProps } from '@material-ui/core/Slide'; + +type TransitionProps = Omit; + +function TransitionLeft(props: TransitionProps) { + return ; +} + +function TransitionUp(props: TransitionProps) { + return ; +} + +function TransitionRight(props: TransitionProps) { + return ; +} + +function TransitionDown(props: TransitionProps) { + return ; +} + +export function DirectionSnackbar() { + const [open, setOpen] = React.useState(false); + const [transition, setTransition] = React.useState< + React.ComponentType | undefined + >(undefined); + + const handleClick = (Transition: React.ComponentType) => () => { + setTransition(() => Transition); + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + return ( +
    + + + + + +
    + ); +} + +export default { + title: "Material-ui|snackbars|DirectionSnackbar.stories" +}; diff --git a/examples/storybook/stories/material-ui/snackbars/FabIntegrationSnackbar.stories.tsx b/examples/storybook/stories/material-ui/snackbars/FabIntegrationSnackbar.stories.tsx new file mode 100644 index 00000000000000..84132f1dbcc900 --- /dev/null +++ b/examples/storybook/stories/material-ui/snackbars/FabIntegrationSnackbar.stories.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import Toolbar from '@material-ui/core/Toolbar'; +import IconButton from '@material-ui/core/IconButton'; +import MenuIcon from '@material-ui/icons/Menu'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; +import Fab from '@material-ui/core/Fab'; +import AddIcon from '@material-ui/icons/Add'; +import Snackbar from '@material-ui/core/Snackbar'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + '@global': { + body: { + backgroundColor: theme.palette.background.paper, + }, + }, + menuButton: { + marginRight: theme.spacing(2), + }, + fab: { + position: 'absolute', + bottom: theme.spacing(2), + right: theme.spacing(2), + }, + snackbar: { + [theme.breakpoints.down('xs')]: { + bottom: 90, + }, + }, + }), +); + +export function FabIntegrationSnackbar() { + const classes = useStyles(); + + return ( + + +
    + + + + + + + App Bar + + + + + + + + Undo + + } + className={classes.snackbar} + /> +
    +
    + ); +} + +export default { + title: "Material-ui|snackbars|FabIntegrationSnackbar.stories" +}; diff --git a/examples/storybook/stories/material-ui/snackbars/IntegrationNotistack.stories.tsx b/examples/storybook/stories/material-ui/snackbars/IntegrationNotistack.stories.tsx new file mode 100644 index 00000000000000..0af632c6427b1a --- /dev/null +++ b/examples/storybook/stories/material-ui/snackbars/IntegrationNotistack.stories.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import { SnackbarProvider, VariantType, useSnackbar } from 'notistack'; + +function MyApp() { + const { enqueueSnackbar } = useSnackbar(); + + const handleClick = () => { + enqueueSnackbar('I love snacks.'); + }; + + const handleClickVariant = (variant: VariantType) => () => { + // variant could be success, error, warning, info, or default + enqueueSnackbar('This is a success message!', { variant }); + }; + + return ( + + + + + ); +} + +export function IntegrationNotistack() { + return ( + + + + ); +} + +export default { + title: "Material-ui|snackbars|IntegrationNotistack.stories" +}; diff --git a/examples/storybook/stories/material-ui/snackbars/LongTextSnackbar.stories.tsx b/examples/storybook/stories/material-ui/snackbars/LongTextSnackbar.stories.tsx new file mode 100644 index 00000000000000..b478eed8bd004c --- /dev/null +++ b/examples/storybook/stories/material-ui/snackbars/LongTextSnackbar.stories.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import SnackbarContent from '@material-ui/core/SnackbarContent'; + +const action = ( + +); + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + maxWidth: 600, + '& > * + *': { + marginTop: theme.spacing(2), + }, + }, + }), +); + +export function LongTextSnackbar() { + const classes = useStyles(); + + return ( +
    + + + + +
    + ); +} + +export default { + title: "Material-ui|snackbars|LongTextSnackbar.stories" +}; diff --git a/examples/storybook/stories/material-ui/snackbars/PositionedSnackbar.stories.tsx b/examples/storybook/stories/material-ui/snackbars/PositionedSnackbar.stories.tsx new file mode 100644 index 00000000000000..fef1c2a9306b97 --- /dev/null +++ b/examples/storybook/stories/material-ui/snackbars/PositionedSnackbar.stories.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Snackbar, { SnackbarOrigin } from '@material-ui/core/Snackbar'; + +export interface State extends SnackbarOrigin { + open: boolean; +} + +export function PositionedSnackbar() { + const [state, setState] = React.useState({ + open: false, + vertical: 'top', + horizontal: 'center', + }); + const { vertical, horizontal, open } = state; + + const handleClick = (newState: SnackbarOrigin) => () => { + setState({ open: true, ...newState }); + }; + + const handleClose = () => { + setState({ ...state, open: false }); + }; + + const buttons = ( + + + + + + + + + ); + + return ( +
    + {buttons} + +
    + ); +} + +export default { + title: "Material-ui|snackbars|PositionedSnackbar.stories" +}; diff --git a/examples/storybook/stories/material-ui/snackbars/SimpleSnackbar.stories.tsx b/examples/storybook/stories/material-ui/snackbars/SimpleSnackbar.stories.tsx new file mode 100644 index 00000000000000..387ac0fcd82f3f --- /dev/null +++ b/examples/storybook/stories/material-ui/snackbars/SimpleSnackbar.stories.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Snackbar from '@material-ui/core/Snackbar'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from '@material-ui/icons/Close'; + +export function SimpleSnackbar() { + const [open, setOpen] = React.useState(false); + + const handleClick = () => { + setOpen(true); + }; + + const handleClose = (event: React.SyntheticEvent | React.MouseEvent, reason?: string) => { + if (reason === 'clickaway') { + return; + } + + setOpen(false); + }; + + return ( +
    + + + + + + + + } + /> +
    + ); +} + +export default { + title: "Material-ui|snackbars|SimpleSnackbar.stories" +}; diff --git a/examples/storybook/stories/material-ui/snackbars/TransitionsSnackbar.stories.tsx b/examples/storybook/stories/material-ui/snackbars/TransitionsSnackbar.stories.tsx new file mode 100644 index 00000000000000..a322afc30f04cb --- /dev/null +++ b/examples/storybook/stories/material-ui/snackbars/TransitionsSnackbar.stories.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Snackbar from '@material-ui/core/Snackbar'; +import Fade from '@material-ui/core/Fade'; +import Slide from '@material-ui/core/Slide'; +import Grow from '@material-ui/core/Grow'; +import { TransitionProps } from '@material-ui/core/transitions'; + +function SlideTransition(props: TransitionProps) { + return ; +} + +function GrowTransition(props: TransitionProps) { + return ; +} + +export function TransitionsSnackbar() { + const [state, setState] = React.useState<{ + open: boolean; + Transition: React.ComponentType }>; + }>({ + open: false, + Transition: Fade, + }); + + const handleClick = ( + Transition: React.ComponentType }>, + ) => () => { + setState({ + open: true, + Transition, + }); + }; + + const handleClose = () => { + setState({ + ...state, + open: false, + }); + }; + + return ( +
    + + + + +
    + ); +} + +export default { + title: "Material-ui|snackbars|TransitionsSnackbar.stories" +}; diff --git a/examples/storybook/stories/material-ui/speed-dial/OpenIconSpeedDial.stories.tsx b/examples/storybook/stories/material-ui/speed-dial/OpenIconSpeedDial.stories.tsx new file mode 100644 index 00000000000000..72859db5fe7a9e --- /dev/null +++ b/examples/storybook/stories/material-ui/speed-dial/OpenIconSpeedDial.stories.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import SpeedDial from '@material-ui/lab/SpeedDial'; +import SpeedDialIcon from '@material-ui/lab/SpeedDialIcon'; +import SpeedDialAction from '@material-ui/lab/SpeedDialAction'; +import FileCopyIcon from '@material-ui/icons/FileCopyOutlined'; +import SaveIcon from '@material-ui/icons/Save'; +import PrintIcon from '@material-ui/icons/Print'; +import ShareIcon from '@material-ui/icons/Share'; +import FavoriteIcon from '@material-ui/icons/Favorite'; +import EditIcon from '@material-ui/icons/Edit'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + height: 380, + transform: 'translateZ(0px)', + flexGrow: 1, + }, + speedDial: { + position: 'absolute', + bottom: theme.spacing(2), + right: theme.spacing(2), + }, + }), +); + +const actions = [ + { icon: , name: 'Copy' }, + { icon: , name: 'Save' }, + { icon: , name: 'Print' }, + { icon: , name: 'Share' }, + { icon: , name: 'Like' }, +]; + +export function OpenIconSpeedDial() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + const [hidden, setHidden] = React.useState(false); + + const handleVisibility = () => { + setHidden((prevHidden) => !prevHidden); + }; + + const handleOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + return ( +
    + + +
    + ); +} + +export default { + title: "Material-ui|speed-dial|OpenIconSpeedDial.stories" +}; diff --git a/examples/storybook/stories/material-ui/speed-dial/SpeedDialTooltipOpen.stories.tsx b/examples/storybook/stories/material-ui/speed-dial/SpeedDialTooltipOpen.stories.tsx new file mode 100644 index 00000000000000..bc4f964ca58c56 --- /dev/null +++ b/examples/storybook/stories/material-ui/speed-dial/SpeedDialTooltipOpen.stories.tsx @@ -0,0 +1,83 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Backdrop from '@material-ui/core/Backdrop'; +import SpeedDial from '@material-ui/lab/SpeedDial'; +import SpeedDialIcon from '@material-ui/lab/SpeedDialIcon'; +import SpeedDialAction from '@material-ui/lab/SpeedDialAction'; +import FileCopyIcon from '@material-ui/icons/FileCopyOutlined'; +import SaveIcon from '@material-ui/icons/Save'; +import PrintIcon from '@material-ui/icons/Print'; +import ShareIcon from '@material-ui/icons/Share'; +import FavoriteIcon from '@material-ui/icons/Favorite'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + height: 380, + transform: 'translateZ(0px)', + flexGrow: 1, + }, + speedDial: { + position: 'absolute', + bottom: theme.spacing(2), + right: theme.spacing(2), + }, + }), +); + +const actions = [ + { icon: , name: 'Copy' }, + { icon: , name: 'Save' }, + { icon: , name: 'Print' }, + { icon: , name: 'Share' }, + { icon: , name: 'Like' }, +]; + +export function SpeedDialTooltipOpen() { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + const [hidden, setHidden] = React.useState(false); + + const handleVisibility = () => { + setHidden((prevHidden) => !prevHidden); + }; + + const handleOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + return ( +
    + + + +
    + ); +} + +export default { + title: "Material-ui|speed-dial|SpeedDialTooltipOpen.stories" +}; diff --git a/examples/storybook/stories/material-ui/speed-dial/SpeedDials.stories.tsx b/examples/storybook/stories/material-ui/speed-dial/SpeedDials.stories.tsx new file mode 100644 index 00000000000000..fcd1fffb6b3ff2 --- /dev/null +++ b/examples/storybook/stories/material-ui/speed-dial/SpeedDials.stories.tsx @@ -0,0 +1,123 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormLabel from '@material-ui/core/FormLabel'; +import Radio from '@material-ui/core/Radio'; +import RadioGroup from '@material-ui/core/RadioGroup'; +import Switch from '@material-ui/core/Switch'; +import SpeedDial, { SpeedDialProps } from '@material-ui/lab/SpeedDial'; +import SpeedDialIcon from '@material-ui/lab/SpeedDialIcon'; +import SpeedDialAction from '@material-ui/lab/SpeedDialAction'; +import FileCopyIcon from '@material-ui/icons/FileCopyOutlined'; +import SaveIcon from '@material-ui/icons/Save'; +import PrintIcon from '@material-ui/icons/Print'; +import ShareIcon from '@material-ui/icons/Share'; +import FavoriteIcon from '@material-ui/icons/Favorite'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + transform: 'translateZ(0px)', + flexGrow: 1, + }, + exampleWrapper: { + position: 'relative', + marginTop: theme.spacing(3), + height: 380, + }, + radioGroup: { + margin: theme.spacing(1, 0), + }, + speedDial: { + position: 'absolute', + '&.MuiSpeedDial-directionUp, &.MuiSpeedDial-directionLeft': { + bottom: theme.spacing(2), + right: theme.spacing(2), + }, + '&.MuiSpeedDial-directionDown, &.MuiSpeedDial-directionRight': { + top: theme.spacing(2), + left: theme.spacing(2), + }, + }, + }), +); + +const actions = [ + { icon: , name: 'Copy' }, + { icon: , name: 'Save' }, + { icon: , name: 'Print' }, + { icon: , name: 'Share' }, + { icon: , name: 'Like' }, +]; + +export function SpeedDials() { + const classes = useStyles(); + const [direction, setDirection] = React.useState('up'); + const [open, setOpen] = React.useState(false); + const [hidden, setHidden] = React.useState(false); + + const handleDirectionChange = (event: React.ChangeEvent) => { + setDirection((event.target as HTMLInputElement).value as SpeedDialProps['direction']); + }; + + const handleHiddenChange = (event: React.ChangeEvent) => { + setHidden(event.target.checked); + }; + + const handleClose = () => { + setOpen(false); + }; + + const handleOpen = () => { + setOpen(true); + }; + + return ( +
    + } + label="Hidden" + /> + + Direction + + + } label="Up" /> + } label="Right" /> + } label="Down" /> + } label="Left" /> + +
    + +
    +
    + ); +} + +export default { + title: "Material-ui|speed-dial|SpeedDials.stories" +}; diff --git a/examples/storybook/stories/material-ui/steppers/CustomizedSteppers.stories.tsx b/examples/storybook/stories/material-ui/steppers/CustomizedSteppers.stories.tsx new file mode 100644 index 00000000000000..19d7090086d9fb --- /dev/null +++ b/examples/storybook/stories/material-ui/steppers/CustomizedSteppers.stories.tsx @@ -0,0 +1,253 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles, withStyles } from '@material-ui/core/styles'; +import clsx from 'clsx'; +import Stepper from '@material-ui/core/Stepper'; +import Step from '@material-ui/core/Step'; +import StepLabel from '@material-ui/core/StepLabel'; +import Check from '@material-ui/icons/Check'; +import SettingsIcon from '@material-ui/icons/Settings'; +import GroupAddIcon from '@material-ui/icons/GroupAdd'; +import VideoLabelIcon from '@material-ui/icons/VideoLabel'; +import StepConnector from '@material-ui/core/StepConnector'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; +import { StepIconProps } from '@material-ui/core/StepIcon'; + +const QontoConnector = withStyles({ + alternativeLabel: { + top: 10, + left: 'calc(-50% + 16px)', + right: 'calc(50% + 16px)', + }, + active: { + '& $line': { + borderColor: '#784af4', + }, + }, + completed: { + '& $line': { + borderColor: '#784af4', + }, + }, + line: { + borderColor: '#eaeaf0', + borderTopWidth: 3, + borderRadius: 1, + }, +})(StepConnector); + +const useQontoStepIconStyles = makeStyles({ + root: { + color: '#eaeaf0', + display: 'flex', + height: 22, + alignItems: 'center', + }, + active: { + color: '#784af4', + }, + circle: { + width: 8, + height: 8, + borderRadius: '50%', + backgroundColor: 'currentColor', + }, + completed: { + color: '#784af4', + zIndex: 1, + fontSize: 18, + }, +}); + +function QontoStepIcon(props: StepIconProps) { + const classes = useQontoStepIconStyles(); + const { active, completed } = props; + + return ( +
    + {completed ? :
    } +
    + ); +} + +const ColorlibConnector = withStyles({ + alternativeLabel: { + top: 22, + }, + active: { + '& $line': { + backgroundImage: + 'linear-gradient( 95deg,rgb(242,113,33) 0%,rgb(233,64,87) 50%,rgb(138,35,135) 100%)', + }, + }, + completed: { + '& $line': { + backgroundImage: + 'linear-gradient( 95deg,rgb(242,113,33) 0%,rgb(233,64,87) 50%,rgb(138,35,135) 100%)', + }, + }, + line: { + height: 3, + border: 0, + backgroundColor: '#eaeaf0', + borderRadius: 1, + }, +})(StepConnector); + +const useColorlibStepIconStyles = makeStyles({ + root: { + backgroundColor: '#ccc', + zIndex: 1, + color: '#fff', + width: 50, + height: 50, + display: 'flex', + borderRadius: '50%', + justifyContent: 'center', + alignItems: 'center', + }, + active: { + backgroundImage: + 'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)', + boxShadow: '0 4px 10px 0 rgba(0,0,0,.25)', + }, + completed: { + backgroundImage: + 'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)', + }, +}); + +function ColorlibStepIcon(props: StepIconProps) { + const classes = useColorlibStepIconStyles(); + const { active, completed } = props; + + const icons: { [index: string]: React.ReactElement } = { + 1: , + 2: , + 3: , + }; + + return ( +
    + {icons[String(props.icon)]} +
    + ); +} + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + }, + button: { + marginRight: theme.spacing(1), + }, + instructions: { + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1), + }, + }), +); + +function getSteps() { + return ['Select campaign settings', 'Create an ad group', 'Create an ad']; +} + +function getStepContent(step: number) { + switch (step) { + case 0: + return 'Select campaign settings...'; + case 1: + return 'What is an ad group anyways?'; + case 2: + return 'This is the bit I really care about!'; + default: + return 'Unknown step'; + } +} + +export function CustomizedSteppers() { + const classes = useStyles(); + const [activeStep, setActiveStep] = React.useState(1); + const steps = getSteps(); + + const handleNext = () => { + setActiveStep((prevActiveStep) => prevActiveStep + 1); + }; + + const handleBack = () => { + setActiveStep((prevActiveStep) => prevActiveStep - 1); + }; + + const handleReset = () => { + setActiveStep(0); + }; + + return ( +
    + + {steps.map((label) => ( + + {label} + + ))} + + }> + {steps.map((label) => ( + + {label} + + ))} + + }> + {steps.map((label) => ( + + {label} + + ))} + +
    + {activeStep === steps.length ? ( +
    + + All steps completed - you're finished + + +
    + ) : ( +
    + {getStepContent(activeStep)} +
    + + +
    +
    + )} +
    +
    + ); +} + +export default { + title: "Material-ui|steppers|CustomizedSteppers.stories" +}; diff --git a/examples/storybook/stories/material-ui/steppers/DotsMobileStepper.stories.tsx b/examples/storybook/stories/material-ui/steppers/DotsMobileStepper.stories.tsx new file mode 100644 index 00000000000000..a57c02df8a6c64 --- /dev/null +++ b/examples/storybook/stories/material-ui/steppers/DotsMobileStepper.stories.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { makeStyles, useTheme } from '@material-ui/core/styles'; +import MobileStepper from '@material-ui/core/MobileStepper'; +import Button from '@material-ui/core/Button'; +import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft'; +import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight'; + +const useStyles = makeStyles({ + root: { + maxWidth: 400, + flexGrow: 1, + }, +}); + +export function DotsMobileStepper() { + const classes = useStyles(); + const theme = useTheme(); + const [activeStep, setActiveStep] = React.useState(0); + + const handleNext = () => { + setActiveStep((prevActiveStep) => prevActiveStep + 1); + }; + + const handleBack = () => { + setActiveStep((prevActiveStep) => prevActiveStep - 1); + }; + + return ( + + Next + {theme.direction === 'rtl' ? : } + + } + backButton={ + + } + /> + ); +} + +export default { + title: "Material-ui|steppers|DotsMobileStepper.stories" +}; diff --git a/examples/storybook/stories/material-ui/steppers/HorizontalLinearAlternativeLabelStepper.stories.tsx b/examples/storybook/stories/material-ui/steppers/HorizontalLinearAlternativeLabelStepper.stories.tsx new file mode 100644 index 00000000000000..e8633884e9ace6 --- /dev/null +++ b/examples/storybook/stories/material-ui/steppers/HorizontalLinearAlternativeLabelStepper.stories.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Stepper from '@material-ui/core/Stepper'; +import Step from '@material-ui/core/Step'; +import StepLabel from '@material-ui/core/StepLabel'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + }, + backButton: { + marginRight: theme.spacing(1), + }, + instructions: { + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1), + }, + }), +); + +function getSteps() { + return ['Select master blaster campaign settings', 'Create an ad group', 'Create an ad']; +} + +function getStepContent(stepIndex: number) { + switch (stepIndex) { + case 0: + return 'Select campaign settings...'; + case 1: + return 'What is an ad group anyways?'; + case 2: + return 'This is the bit I really care about!'; + default: + return 'Unknown stepIndex'; + } +} + +export function HorizontalLabelPositionBelowStepper() { + const classes = useStyles(); + const [activeStep, setActiveStep] = React.useState(0); + const steps = getSteps(); + + const handleNext = () => { + setActiveStep((prevActiveStep) => prevActiveStep + 1); + }; + + const handleBack = () => { + setActiveStep((prevActiveStep) => prevActiveStep - 1); + }; + + const handleReset = () => { + setActiveStep(0); + }; + + return ( +
    + + {steps.map((label) => ( + + {label} + + ))} + +
    + {activeStep === steps.length ? ( +
    + All steps completed + +
    + ) : ( +
    + {getStepContent(activeStep)} +
    + + +
    +
    + )} +
    +
    + ); +} + +export default { + title: "Material-ui|steppers|HorizontalLinearAlternativeLabelStepper.stories" +}; diff --git a/examples/storybook/stories/material-ui/steppers/HorizontalLinearStepper.stories.tsx b/examples/storybook/stories/material-ui/steppers/HorizontalLinearStepper.stories.tsx new file mode 100644 index 00000000000000..753bae73d0d32d --- /dev/null +++ b/examples/storybook/stories/material-ui/steppers/HorizontalLinearStepper.stories.tsx @@ -0,0 +1,153 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Stepper from '@material-ui/core/Stepper'; +import Step from '@material-ui/core/Step'; +import StepLabel from '@material-ui/core/StepLabel'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + }, + button: { + marginRight: theme.spacing(1), + }, + instructions: { + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1), + }, + }), +); + +function getSteps() { + return ['Select campaign settings', 'Create an ad group', 'Create an ad']; +} + +function getStepContent(step: number) { + switch (step) { + case 0: + return 'Select campaign settings...'; + case 1: + return 'What is an ad group anyways?'; + case 2: + return 'This is the bit I really care about!'; + default: + return 'Unknown step'; + } +} + +export function HorizontalLinearStepper() { + const classes = useStyles(); + const [activeStep, setActiveStep] = React.useState(0); + const [skipped, setSkipped] = React.useState(new Set()); + const steps = getSteps(); + + const isStepOptional = (step: number) => { + return step === 1; + }; + + const isStepSkipped = (step: number) => { + return skipped.has(step); + }; + + const handleNext = () => { + let newSkipped = skipped; + if (isStepSkipped(activeStep)) { + newSkipped = new Set(newSkipped.values()); + newSkipped.delete(activeStep); + } + + setActiveStep((prevActiveStep) => prevActiveStep + 1); + setSkipped(newSkipped); + }; + + const handleBack = () => { + setActiveStep((prevActiveStep) => prevActiveStep - 1); + }; + + const handleSkip = () => { + if (!isStepOptional(activeStep)) { + // You probably want to guard against something like this, + // it should never occur unless someone's actively trying to break something. + throw new Error("You can't skip a step that isn't optional."); + } + + setActiveStep((prevActiveStep) => prevActiveStep + 1); + setSkipped((prevSkipped) => { + const newSkipped = new Set(prevSkipped.values()); + newSkipped.add(activeStep); + return newSkipped; + }); + }; + + const handleReset = () => { + setActiveStep(0); + }; + + return ( +
    + + {steps.map((label, index) => { + const stepProps: { completed?: boolean } = {}; + const labelProps: { optional?: React.ReactNode } = {}; + if (isStepOptional(index)) { + labelProps.optional = Optional; + } + if (isStepSkipped(index)) { + stepProps.completed = false; + } + return ( + + {label} + + ); + })} + +
    + {activeStep === steps.length ? ( +
    + + All steps completed - you're finished + + +
    + ) : ( +
    + {getStepContent(activeStep)} +
    + + {isStepOptional(activeStep) && ( + + )} + +
    +
    + )} +
    +
    + ); +} + +export default { + title: "Material-ui|steppers|HorizontalLinearStepper.stories" +}; diff --git a/examples/storybook/stories/material-ui/steppers/HorizontalNonLinearAlternativeLabelStepper.stories.tsx b/examples/storybook/stories/material-ui/steppers/HorizontalNonLinearAlternativeLabelStepper.stories.tsx new file mode 100644 index 00000000000000..077a0bfb2cfea9 --- /dev/null +++ b/examples/storybook/stories/material-ui/steppers/HorizontalNonLinearAlternativeLabelStepper.stories.tsx @@ -0,0 +1,219 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Stepper from '@material-ui/core/Stepper'; +import Step from '@material-ui/core/Step'; +import StepButton from '@material-ui/core/StepButton'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + }, + button: { + marginRight: theme.spacing(1), + }, + backButton: { + marginRight: theme.spacing(1), + }, + completed: { + display: 'inline-block', + }, + instructions: { + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1), + }, + }), +); + +function getSteps() { + return ['Select campaign settings', 'Create an ad group', 'Create an ad']; +} + +function getStepContent(step: number) { + switch (step) { + case 0: + return 'Step 1: Select campaign settings...'; + case 1: + return 'Step 2: What is an ad group anyways?'; + case 2: + return 'Step 3: This is the bit I really care about!'; + default: + return 'Unknown step'; + } +} + +export function HorizontalNonLinearAlternativeLabelStepper() { + const classes = useStyles(); + const [activeStep, setActiveStep] = React.useState(0); + const [completed, setCompleted] = React.useState(new Set()); + const [skipped, setSkipped] = React.useState(new Set()); + const steps = getSteps(); + + const totalSteps = () => { + return getSteps().length; + }; + + const isStepOptional = (step: number) => { + return step === 1; + }; + + const handleSkip = () => { + if (!isStepOptional(activeStep)) { + // You probably want to guard against something like this + // it should never occur unless someone's actively trying to break something. + throw new Error("You can't skip a step that isn't optional."); + } + + setActiveStep((prevActiveStep) => prevActiveStep + 1); + setSkipped((prevSkipped) => { + const newSkipped = new Set(prevSkipped.values()); + newSkipped.add(activeStep); + return newSkipped; + }); + }; + + const skippedSteps = () => { + return skipped.size; + }; + + const completedSteps = () => { + return completed.size; + }; + + const allStepsCompleted = () => { + return completedSteps() === totalSteps() - skippedSteps(); + }; + + const isLastStep = () => { + return activeStep === totalSteps() - 1; + }; + + const handleNext = () => { + const newActiveStep = + isLastStep() && !allStepsCompleted() + ? // It's the last step, but not all steps have been completed + // find the first step that has been completed + steps.findIndex((step, i) => !completed.has(i)) + : activeStep + 1; + + setActiveStep(newActiveStep); + }; + + const handleBack = () => { + setActiveStep((prevActiveStep) => prevActiveStep - 1); + }; + + const handleStep = (step: number) => () => { + setActiveStep(step); + }; + + const handleComplete = () => { + const newCompleted = new Set(completed); + newCompleted.add(activeStep); + setCompleted(newCompleted); + + /** + * Sigh... it would be much nicer to replace the following if conditional with + * `if (!this.allStepsComplete())` however state is not set when we do this, + * thus we have to resort to not being very DRY. + */ + if (completed.size !== totalSteps() - skippedSteps()) { + handleNext(); + } + }; + + const handleReset = () => { + setActiveStep(0); + setCompleted(new Set()); + setSkipped(new Set()); + }; + + const isStepSkipped = (step: number) => { + return skipped.has(step); + }; + + function isStepComplete(step: number) { + return completed.has(step); + } + + return ( +
    + + {steps.map((label, index) => { + const stepProps: { completed?: boolean } = {}; + const buttonProps: { optional?: React.ReactNode } = {}; + if (isStepOptional(index)) { + buttonProps.optional = Optional; + } + if (isStepSkipped(index)) { + stepProps.completed = false; + } + return ( + + + {label} + + + ); + })} + +
    + {allStepsCompleted() ? ( +
    + + All steps completed - you're finished + + +
    + ) : ( +
    + {getStepContent(activeStep)} +
    + + + {isStepOptional(activeStep) && !completed.has(activeStep) && ( + + )} + {activeStep !== steps.length && + (completed.has(activeStep) ? ( + + Step {activeStep + 1} already completed + + ) : ( + + ))} +
    +
    + )} +
    +
    + ); +} + +export default { + title: "Material-ui|steppers|HorizontalNonLinearAlternativeLabelStepper.stories" +}; diff --git a/examples/storybook/stories/material-ui/steppers/HorizontalNonLinearStepper.stories.tsx b/examples/storybook/stories/material-ui/steppers/HorizontalNonLinearStepper.stories.tsx new file mode 100644 index 00000000000000..dbafee45b885a6 --- /dev/null +++ b/examples/storybook/stories/material-ui/steppers/HorizontalNonLinearStepper.stories.tsx @@ -0,0 +1,150 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Stepper from '@material-ui/core/Stepper'; +import Step from '@material-ui/core/Step'; +import StepButton from '@material-ui/core/StepButton'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + }, + button: { + marginRight: theme.spacing(1), + }, + completed: { + display: 'inline-block', + }, + instructions: { + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1), + }, + }), +); + +function getSteps() { + return ['Select campaign settings', 'Create an ad group', 'Create an ad']; +} + +function getStepContent(step: number) { + switch (step) { + case 0: + return 'Step 1: Select campaign settings...'; + case 1: + return 'Step 2: What is an ad group anyways?'; + case 2: + return 'Step 3: This is the bit I really care about!'; + default: + return 'Unknown step'; + } +} + +export function HorizontalNonLinearStepper() { + const classes = useStyles(); + const [activeStep, setActiveStep] = React.useState(0); + const [completed, setCompleted] = React.useState<{ [k: number]: boolean }>({}); + const steps = getSteps(); + + const totalSteps = () => { + return steps.length; + }; + + const completedSteps = () => { + return Object.keys(completed).length; + }; + + const isLastStep = () => { + return activeStep === totalSteps() - 1; + }; + + const allStepsCompleted = () => { + return completedSteps() === totalSteps(); + }; + + const handleNext = () => { + const newActiveStep = + isLastStep() && !allStepsCompleted() + ? // It's the last step, but not all steps have been completed, + // find the first step that has been completed + steps.findIndex((step, i) => !(i in completed)) + : activeStep + 1; + setActiveStep(newActiveStep); + }; + + const handleBack = () => { + setActiveStep((prevActiveStep) => prevActiveStep - 1); + }; + + const handleStep = (step: number) => () => { + setActiveStep(step); + }; + + const handleComplete = () => { + const newCompleted = completed; + newCompleted[activeStep] = true; + setCompleted(newCompleted); + handleNext(); + }; + + const handleReset = () => { + setActiveStep(0); + setCompleted({}); + }; + + return ( +
    + + {steps.map((label, index) => ( + + + {label} + + + ))} + +
    + {allStepsCompleted() ? ( +
    + + All steps completed - you're finished + + +
    + ) : ( +
    + {getStepContent(activeStep)} +
    + + + {activeStep !== steps.length && + (completed[activeStep] ? ( + + Step {activeStep + 1} already completed + + ) : ( + + ))} +
    +
    + )} +
    +
    + ); +} + +export default { + title: "Material-ui|steppers|HorizontalNonLinearStepper.stories" +}; diff --git a/examples/storybook/stories/material-ui/steppers/HorizontalNonLinearStepperWithError.stories.tsx b/examples/storybook/stories/material-ui/steppers/HorizontalNonLinearStepperWithError.stories.tsx new file mode 100644 index 00000000000000..d8a28416203664 --- /dev/null +++ b/examples/storybook/stories/material-ui/steppers/HorizontalNonLinearStepperWithError.stories.tsx @@ -0,0 +1,164 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Stepper from '@material-ui/core/Stepper'; +import Step from '@material-ui/core/Step'; +import StepLabel from '@material-ui/core/StepLabel'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + }, + button: { + marginRight: theme.spacing(1), + }, + instructions: { + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1), + }, + }), +); + +function getSteps() { + return ['Select campaign settings', 'Create an ad group', 'Create an ad']; +} + +function getStepContent(step: number) { + switch (step) { + case 0: + return 'Select campaign settings...'; + case 1: + return 'What is an ad group anyways?'; + case 2: + return 'This is the bit I really care about!'; + default: + return 'Unknown step'; + } +} + +export function HorizontalNonLinearStepperWithError() { + const classes = useStyles(); + const [activeStep, setActiveStep] = React.useState(0); + const [skipped, setSkipped] = React.useState(new Set()); + const steps = getSteps(); + + const isStepOptional = (step: number) => { + return step === 1; + }; + + const isStepFailed = (step: number) => { + return step === 1; + }; + + const isStepSkipped = (step: number) => { + return skipped.has(step); + }; + + const handleNext = () => { + let newSkipped = skipped; + if (isStepSkipped(activeStep)) { + newSkipped = new Set(skipped.values()); + newSkipped.delete(activeStep); + } + + setActiveStep((prevActiveStep) => prevActiveStep + 1); + setSkipped(newSkipped); + }; + + const handleBack = () => { + setActiveStep((prevActiveStep) => prevActiveStep - 1); + }; + + const handleSkip = () => { + if (!isStepOptional(activeStep)) { + // You probably want to guard against something like this, + // it should never occur unless someone's actively trying to break something. + throw new Error("You can't skip a step that isn't optional."); + } + + setSkipped((prevSkipped) => { + const newSkipped = new Set(prevSkipped.values()); + newSkipped.add(activeStep); + return newSkipped; + }); + setActiveStep((prevActiveStep) => prevActiveStep + 1); + }; + + const handleReset = () => { + setActiveStep(0); + }; + + return ( +
    + + {steps.map((label, index) => { + const stepProps: { completed?: boolean } = {}; + const labelProps: { optional?: React.ReactNode; error?: boolean } = {}; + if (isStepOptional(index)) { + labelProps.optional = ( + + Alert message + + ); + } + if (isStepFailed(index)) { + labelProps.error = true; + } + if (isStepSkipped(index)) { + stepProps.completed = false; + } + return ( + + {label} + + ); + })} + +
    + {activeStep === steps.length ? ( +
    + + All steps completed - you're finished + + +
    + ) : ( +
    + {getStepContent(activeStep)} +
    + + {isStepOptional(activeStep) && ( + + )} + +
    +
    + )} +
    +
    + ); +} + +export default { + title: "Material-ui|steppers|HorizontalNonLinearStepperWithError.stories" +}; diff --git a/examples/storybook/stories/material-ui/steppers/ProgressMobileStepper.stories.tsx b/examples/storybook/stories/material-ui/steppers/ProgressMobileStepper.stories.tsx new file mode 100644 index 00000000000000..fd8dcb2c8492c2 --- /dev/null +++ b/examples/storybook/stories/material-ui/steppers/ProgressMobileStepper.stories.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { makeStyles, useTheme } from '@material-ui/core/styles'; +import MobileStepper from '@material-ui/core/MobileStepper'; +import Button from '@material-ui/core/Button'; +import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft'; +import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight'; + +const useStyles = makeStyles({ + root: { + maxWidth: 400, + flexGrow: 1, + }, +}); + +export function ProgressMobileStepper() { + const classes = useStyles(); + const theme = useTheme(); + const [activeStep, setActiveStep] = React.useState(0); + + const handleNext = () => { + setActiveStep((prevActiveStep) => prevActiveStep + 1); + }; + + const handleBack = () => { + setActiveStep((prevActiveStep) => prevActiveStep - 1); + }; + + return ( + + Next + {theme.direction === 'rtl' ? : } + + } + backButton={ + + } + /> + ); +} + +export default { + title: "Material-ui|steppers|ProgressMobileStepper.stories" +}; diff --git a/examples/storybook/stories/material-ui/steppers/TextMobileStepper.stories.tsx b/examples/storybook/stories/material-ui/steppers/TextMobileStepper.stories.tsx new file mode 100644 index 00000000000000..5c7f761b3c124e --- /dev/null +++ b/examples/storybook/stories/material-ui/steppers/TextMobileStepper.stories.tsx @@ -0,0 +1,109 @@ +import React from 'react'; +import { makeStyles, Theme, useTheme, createStyles } from '@material-ui/core/styles'; +import MobileStepper from '@material-ui/core/MobileStepper'; +import Paper from '@material-ui/core/Paper'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; +import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft'; +import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight'; + +const tutorialSteps = [ + { + label: 'San Francisco – Oakland Bay Bridge, United States', + imgPath: + 'https://images.unsplash.com/photo-1537944434965-cf4679d1a598?auto=format&fit=crop&w=400&h=250&q=60', + }, + { + label: 'Bird', + imgPath: + 'https://images.unsplash.com/photo-1538032746644-0212e812a9e7?auto=format&fit=crop&w=400&h=250&q=60', + }, + { + label: 'Bali, Indonesia', + imgPath: + 'https://images.unsplash.com/photo-1537996194471-e657df975ab4?auto=format&fit=crop&w=400&h=250&q=80', + }, + { + label: 'NeONBRAND Digital Marketing, Las Vegas, United States', + imgPath: + 'https://images.unsplash.com/photo-1518732714860-b62714ce0c59?auto=format&fit=crop&w=400&h=250&q=60', + }, + { + label: 'Goč, Serbia', + imgPath: + 'https://images.unsplash.com/photo-1512341689857-198e7e2f3ca8?auto=format&fit=crop&w=400&h=250&q=60', + }, +]; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + maxWidth: 400, + flexGrow: 1, + }, + header: { + display: 'flex', + alignItems: 'center', + height: 50, + paddingLeft: theme.spacing(4), + backgroundColor: theme.palette.background.default, + }, + img: { + height: 255, + maxWidth: 400, + overflow: 'hidden', + display: 'block', + width: '100%', + }, + }), +); + +export function TextMobileStepper() { + const classes = useStyles(); + const theme = useTheme(); + const [activeStep, setActiveStep] = React.useState(0); + const maxSteps = tutorialSteps.length; + + const handleNext = () => { + setActiveStep((prevActiveStep) => prevActiveStep + 1); + }; + + const handleBack = () => { + setActiveStep((prevActiveStep) => prevActiveStep - 1); + }; + + return ( +
    + + {tutorialSteps[activeStep].label} + + {tutorialSteps[activeStep].label} + + Next + {theme.direction === 'rtl' ? : } + + } + backButton={ + + } + /> +
    + ); +} + +export default { + title: "Material-ui|steppers|TextMobileStepper.stories" +}; diff --git a/examples/storybook/stories/material-ui/steppers/VerticalLinearStepper.stories.tsx b/examples/storybook/stories/material-ui/steppers/VerticalLinearStepper.stories.tsx new file mode 100644 index 00000000000000..d06b901bf89b17 --- /dev/null +++ b/examples/storybook/stories/material-ui/steppers/VerticalLinearStepper.stories.tsx @@ -0,0 +1,113 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Stepper from '@material-ui/core/Stepper'; +import Step from '@material-ui/core/Step'; +import StepLabel from '@material-ui/core/StepLabel'; +import StepContent from '@material-ui/core/StepContent'; +import Button from '@material-ui/core/Button'; +import Paper from '@material-ui/core/Paper'; +import Typography from '@material-ui/core/Typography'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + }, + button: { + marginTop: theme.spacing(1), + marginRight: theme.spacing(1), + }, + actionsContainer: { + marginBottom: theme.spacing(2), + }, + resetContainer: { + padding: theme.spacing(3), + }, + }), +); + +function getSteps() { + return ['Select campaign settings', 'Create an ad group', 'Create an ad']; +} + +function getStepContent(step: number) { + switch (step) { + case 0: + return `For each ad campaign that you create, you can control how much + you're willing to spend on clicks and conversions, which networks + and geographical locations you want your ads to show on, and more.`; + case 1: + return 'An ad group contains one or more ads which target a shared set of keywords.'; + case 2: + return `Try out different ad text to see what brings in the most customers, + and learn how to enhance your ads using features like ad extensions. + If you run into any problems with your ads, find out how to tell if + they're running and how to resolve approval issues.`; + default: + return 'Unknown step'; + } +} + +export function VerticalLinearStepper() { + const classes = useStyles(); + const [activeStep, setActiveStep] = React.useState(0); + const steps = getSteps(); + + const handleNext = () => { + setActiveStep((prevActiveStep) => prevActiveStep + 1); + }; + + const handleBack = () => { + setActiveStep((prevActiveStep) => prevActiveStep - 1); + }; + + const handleReset = () => { + setActiveStep(0); + }; + + return ( +
    + + {steps.map((label, index) => ( + + {label} + + {getStepContent(index)} +
    +
    + + +
    +
    +
    +
    + ))} +
    + {activeStep === steps.length && ( + + All steps completed - you're finished + + + )} +
    + ); +} + +export default { + title: "Material-ui|steppers|VerticalLinearStepper.stories" +}; diff --git a/examples/storybook/stories/material-ui/switches/CustomizedSwitches.stories.tsx b/examples/storybook/stories/material-ui/switches/CustomizedSwitches.stories.tsx new file mode 100644 index 00000000000000..850a4c35de03f7 --- /dev/null +++ b/examples/storybook/stories/material-ui/switches/CustomizedSwitches.stories.tsx @@ -0,0 +1,159 @@ +import React from 'react'; +import { withStyles, Theme, createStyles } from '@material-ui/core/styles'; +import { purple } from '@material-ui/core/colors'; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import Switch, { SwitchClassKey, SwitchProps } from '@material-ui/core/Switch'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; + +interface Styles extends Partial> { + focusVisible?: string; +} + +interface Props extends SwitchProps { + classes: Styles; +} + +const PurpleSwitch = withStyles({ + switchBase: { + color: purple[300], + '&$checked': { + color: purple[500], + }, + '&$checked + $track': { + backgroundColor: purple[500], + }, + }, + checked: {}, + track: {}, +})(Switch); + +const IOSSwitch = withStyles((theme: Theme) => + createStyles({ + root: { + width: 42, + height: 26, + padding: 0, + margin: theme.spacing(1), + }, + switchBase: { + padding: 1, + '&$checked': { + transform: 'translateX(16px)', + color: theme.palette.common.white, + '& + $track': { + backgroundColor: '#52d869', + opacity: 1, + border: 'none', + }, + }, + '&$focusVisible $thumb': { + color: '#52d869', + border: '6px solid #fff', + }, + }, + thumb: { + width: 24, + height: 24, + }, + track: { + borderRadius: 26 / 2, + border: `1px solid ${theme.palette.grey[400]}`, + backgroundColor: theme.palette.grey[50], + opacity: 1, + transition: theme.transitions.create(['background-color', 'border']), + }, + checked: {}, + focusVisible: {}, + }), +)(({ classes, ...props }: Props) => { + return ( + + ); +}); + +const AntSwitch = withStyles((theme: Theme) => + createStyles({ + root: { + width: 28, + height: 16, + padding: 0, + display: 'flex', + }, + switchBase: { + padding: 2, + color: theme.palette.grey[500], + '&$checked': { + transform: 'translateX(12px)', + color: theme.palette.common.white, + '& + $track': { + opacity: 1, + backgroundColor: theme.palette.primary.main, + borderColor: theme.palette.primary.main, + }, + }, + }, + thumb: { + width: 12, + height: 12, + boxShadow: 'none', + }, + track: { + border: `1px solid ${theme.palette.grey[500]}`, + borderRadius: 16 / 2, + opacity: 1, + backgroundColor: theme.palette.common.white, + }, + checked: {}, + }), +)(Switch); + +export function CustomizedSwitches() { + const [state, setState] = React.useState({ + checkedA: true, + checkedB: true, + checkedC: true, + }); + + const handleChange = (event: React.ChangeEvent) => { + setState({ ...state, [event.target.name]: event.target.checked }); + }; + + return ( + + } + label="Custom color" + /> + } + label="iOS style" + /> + + + Off + + + + On + + + + ); +} + +export default { + title: "Material-ui|switches|CustomizedSwitches.stories" +}; diff --git a/examples/storybook/stories/material-ui/switches/FormControlLabelPosition.stories.tsx b/examples/storybook/stories/material-ui/switches/FormControlLabelPosition.stories.tsx new file mode 100644 index 00000000000000..2ec4ea860d748d --- /dev/null +++ b/examples/storybook/stories/material-ui/switches/FormControlLabelPosition.stories.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import Switch from '@material-ui/core/Switch'; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormControl from '@material-ui/core/FormControl'; + +export function FormControlLabelPosition() { + return ( + + + } + label="Top" + labelPlacement="top" + /> + } + label="Start" + labelPlacement="start" + /> + } + label="Bottom" + labelPlacement="bottom" + /> + } + label="End" + labelPlacement="end" + /> + + + ); +} + +export default { + title: "Material-ui|switches|FormControlLabelPosition.stories" +}; diff --git a/examples/storybook/stories/material-ui/switches/SwitchLabels.stories.tsx b/examples/storybook/stories/material-ui/switches/SwitchLabels.stories.tsx new file mode 100644 index 00000000000000..8e63b57648e90b --- /dev/null +++ b/examples/storybook/stories/material-ui/switches/SwitchLabels.stories.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import Switch from '@material-ui/core/Switch'; + +export function SwitchLabels() { + const [state, setState] = React.useState({ + checkedA: true, + checkedB: true, + }); + + const handleChange = (event: React.ChangeEvent) => { + setState({ ...state, [event.target.name]: event.target.checked }); + }; + + return ( + + } + label="Secondary" + /> + + } + label="Primary" + /> + } label="Uncontrolled" /> + } label="Disabled" /> + } label="Disabled" /> + + ); +} + +export default { + title: "Material-ui|switches|SwitchLabels.stories" +}; diff --git a/examples/storybook/stories/material-ui/switches/Switches.stories.tsx b/examples/storybook/stories/material-ui/switches/Switches.stories.tsx new file mode 100644 index 00000000000000..2ddcb0ea2a7e5f --- /dev/null +++ b/examples/storybook/stories/material-ui/switches/Switches.stories.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import Switch from '@material-ui/core/Switch'; + +export function Switches() { + const [state, setState] = React.useState({ + checkedA: true, + checkedB: true, + }); + + const handleChange = (event: React.ChangeEvent) => { + setState({ ...state, [event.target.name]: event.target.checked }); + }; + + return ( +
    + + + + + + +
    + ); +} + +export default { + title: "Material-ui|switches|Switches.stories" +}; diff --git a/examples/storybook/stories/material-ui/switches/SwitchesGroup.stories.tsx b/examples/storybook/stories/material-ui/switches/SwitchesGroup.stories.tsx new file mode 100644 index 00000000000000..7a813340bfd1a5 --- /dev/null +++ b/examples/storybook/stories/material-ui/switches/SwitchesGroup.stories.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import FormLabel from '@material-ui/core/FormLabel'; +import FormControl from '@material-ui/core/FormControl'; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import Switch from '@material-ui/core/Switch'; + +export function SwitchesGroup() { + const [state, setState] = React.useState({ + gilad: true, + jason: false, + antoine: true, + }); + + const handleChange = (event: React.ChangeEvent) => { + setState({ ...state, [event.target.name]: event.target.checked }); + }; + + return ( + + Assign responsibility + + } + label="Gilad Gray" + /> + } + label="Jason Killian" + /> + } + label="Antoine Llorca" + /> + + Be careful + + ); +} + +export default { + title: "Material-ui|switches|SwitchesGroup.stories" +}; diff --git a/examples/storybook/stories/material-ui/switches/SwitchesSize.stories.tsx b/examples/storybook/stories/material-ui/switches/SwitchesSize.stories.tsx new file mode 100644 index 00000000000000..b5cd2f65372e5b --- /dev/null +++ b/examples/storybook/stories/material-ui/switches/SwitchesSize.stories.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import Switch from '@material-ui/core/Switch'; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; + +export function SwitchesSize() { + const [checked, setChecked] = React.useState(false); + + const toggleChecked = () => { + setChecked((prev) => !prev); + }; + + return ( + + } + label="Small" + /> + } + label="Normal" + /> + + ); +} + +export default { + title: "Material-ui|switches|SwitchesSize.stories" +}; diff --git a/examples/storybook/stories/material-ui/tables/AcccessibleTable.stories.tsx b/examples/storybook/stories/material-ui/tables/AcccessibleTable.stories.tsx new file mode 100644 index 00000000000000..9eb4eae1977b72 --- /dev/null +++ b/examples/storybook/stories/material-ui/tables/AcccessibleTable.stories.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableContainer from '@material-ui/core/TableContainer'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import Paper from '@material-ui/core/Paper'; + +const useStyles = makeStyles({ + table: { + minWidth: 650, + }, +}); + +function createData(name: string, calories: number, fat: number, carbs: number, protein: number) { + return { name, calories, fat, carbs, protein }; +} + +const rows = [ + createData('Frozen yoghurt', 159, 6.0, 24, 4.0), + createData('Ice cream sandwich', 237, 9.0, 37, 4.3), + createData('Eclair', 262, 16.0, 24, 6.0), +]; + +export function AcccessibleTable() { + const classes = useStyles(); + + return ( + + + + + + Dessert (100g serving) + Calories + Fat (g) + Carbs (g) + Protein (g) + + + + {rows.map((row) => ( + + + {row.name} + + {row.calories} + {row.fat} + {row.carbs} + {row.protein} + + ))} + +
    A basic table example with a caption
    +
    + ); +} + +export default { + title: "Material-ui|tables|AcccessibleTable.stories" +}; diff --git a/examples/storybook/stories/material-ui/tables/CollapsibleTable.stories.tsx b/examples/storybook/stories/material-ui/tables/CollapsibleTable.stories.tsx new file mode 100644 index 00000000000000..9e417219465c8b --- /dev/null +++ b/examples/storybook/stories/material-ui/tables/CollapsibleTable.stories.tsx @@ -0,0 +1,141 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Box from '@material-ui/core/Box'; +import Collapse from '@material-ui/core/Collapse'; +import IconButton from '@material-ui/core/IconButton'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableContainer from '@material-ui/core/TableContainer'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import Typography from '@material-ui/core/Typography'; +import Paper from '@material-ui/core/Paper'; +import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown'; +import KeyboardArrowUpIcon from '@material-ui/icons/KeyboardArrowUp'; + +const useRowStyles = makeStyles({ + root: { + '& > *': { + borderBottom: 'unset', + }, + }, +}); + +function createData( + name: string, + calories: number, + fat: number, + carbs: number, + protein: number, + price: number, +) { + return { + name, + calories, + fat, + carbs, + protein, + price, + history: [ + { date: '2020-01-05', customerId: '11091700', amount: 3 }, + { date: '2020-01-02', customerId: 'Anonymous', amount: 1 }, + ], + }; +} + +function Row(props: { row: ReturnType }) { + const { row } = props; + const [open, setOpen] = React.useState(false); + const classes = useRowStyles(); + + return ( + + + + setOpen(!open)}> + {open ? : } + + + + {row.name} + + {row.calories} + {row.fat} + {row.carbs} + {row.protein} + + + + + + + History + + + + + Date + Customer + Amount + Total price ($) + + + + {row.history.map((historyRow) => ( + + + {historyRow.date} + + {historyRow.customerId} + {historyRow.amount} + + {Math.round(historyRow.amount * row.price * 100) / 100} + + + ))} + +
    +
    +
    +
    +
    +
    + ); +} + +const rows = [ + createData('Frozen yoghurt', 159, 6.0, 24, 4.0, 3.99), + createData('Ice cream sandwich', 237, 9.0, 37, 4.3, 4.99), + createData('Eclair', 262, 16.0, 24, 6.0, 3.79), + createData('Cupcake', 305, 3.7, 67, 4.3, 2.5), + createData('Gingerbread', 356, 16.0, 49, 3.9, 1.5), +]; + +export function CollapsibleTable() { + return ( + + + + + + Dessert (100g serving) + Calories + Fat (g) + Carbs (g) + Protein (g) + + + + {rows.map((row) => ( + + ))} + +
    +
    + ); +} + +export default { + title: "Material-ui|tables|CollapsibleTable.stories" +}; diff --git a/examples/storybook/stories/material-ui/tables/CustomPaginationActionsTable.stories.tsx b/examples/storybook/stories/material-ui/tables/CustomPaginationActionsTable.stories.tsx new file mode 100644 index 00000000000000..9887a41fdc0d93 --- /dev/null +++ b/examples/storybook/stories/material-ui/tables/CustomPaginationActionsTable.stories.tsx @@ -0,0 +1,179 @@ +import React from 'react'; +import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableContainer from '@material-ui/core/TableContainer'; +import TableFooter from '@material-ui/core/TableFooter'; +import TablePagination from '@material-ui/core/TablePagination'; +import TableRow from '@material-ui/core/TableRow'; +import Paper from '@material-ui/core/Paper'; +import IconButton from '@material-ui/core/IconButton'; +import FirstPageIcon from '@material-ui/icons/FirstPage'; +import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft'; +import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight'; +import LastPageIcon from '@material-ui/icons/LastPage'; + +const useStyles1 = makeStyles((theme: Theme) => + createStyles({ + root: { + flexShrink: 0, + marginLeft: theme.spacing(2.5), + }, + }), +); + +interface TablePaginationActionsProps { + count: number; + page: number; + rowsPerPage: number; + onChangePage: (event: React.MouseEvent, newPage: number) => void; +} + +function TablePaginationActions(props: TablePaginationActionsProps) { + const classes = useStyles1(); + const theme = useTheme(); + const { count, page, rowsPerPage, onChangePage } = props; + + const handleFirstPageButtonClick = (event: React.MouseEvent) => { + onChangePage(event, 0); + }; + + const handleBackButtonClick = (event: React.MouseEvent) => { + onChangePage(event, page - 1); + }; + + const handleNextButtonClick = (event: React.MouseEvent) => { + onChangePage(event, page + 1); + }; + + const handleLastPageButtonClick = (event: React.MouseEvent) => { + onChangePage(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1)); + }; + + return ( +
    + + {theme.direction === 'rtl' ? : } + + + {theme.direction === 'rtl' ? : } + + = Math.ceil(count / rowsPerPage) - 1} + aria-label="next page" + > + {theme.direction === 'rtl' ? : } + + = Math.ceil(count / rowsPerPage) - 1} + aria-label="last page" + > + {theme.direction === 'rtl' ? : } + +
    + ); +} + +function createData(name: string, calories: number, fat: number) { + return { name, calories, fat }; +} + +const rows = [ + createData('Cupcake', 305, 3.7), + createData('Donut', 452, 25.0), + createData('Eclair', 262, 16.0), + createData('Frozen yoghurt', 159, 6.0), + createData('Gingerbread', 356, 16.0), + createData('Honeycomb', 408, 3.2), + createData('Ice cream sandwich', 237, 9.0), + createData('Jelly Bean', 375, 0.0), + createData('KitKat', 518, 26.0), + createData('Lollipop', 392, 0.2), + createData('Marshmallow', 318, 0), + createData('Nougat', 360, 19.0), + createData('Oreo', 437, 18.0), +].sort((a, b) => (a.calories < b.calories ? -1 : 1)); + +const useStyles2 = makeStyles({ + table: { + minWidth: 500, + }, +}); + +export function CustomPaginationActionsTable() { + const classes = useStyles2(); + const [page, setPage] = React.useState(0); + const [rowsPerPage, setRowsPerPage] = React.useState(5); + + const emptyRows = rowsPerPage - Math.min(rowsPerPage, rows.length - page * rowsPerPage); + + const handleChangePage = (event: React.MouseEvent | null, newPage: number) => { + setPage(newPage); + }; + + const handleChangeRowsPerPage = ( + event: React.ChangeEvent, + ) => { + setRowsPerPage(parseInt(event.target.value, 10)); + setPage(0); + }; + + return ( + + + + {(rowsPerPage > 0 + ? rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + : rows + ).map((row) => ( + + + {row.name} + + + {row.calories} + + + {row.fat} + + + ))} + {emptyRows > 0 && ( + + + + )} + + + + + + +
    +
    + ); +} + +export default { + title: "Material-ui|tables|CustomPaginationActionsTable.stories" +}; diff --git a/examples/storybook/stories/material-ui/tables/CustomizedTables.stories.tsx b/examples/storybook/stories/material-ui/tables/CustomizedTables.stories.tsx new file mode 100644 index 00000000000000..a3684e067fd280 --- /dev/null +++ b/examples/storybook/stories/material-ui/tables/CustomizedTables.stories.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { withStyles, Theme, createStyles, makeStyles } from '@material-ui/core/styles'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableContainer from '@material-ui/core/TableContainer'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import Paper from '@material-ui/core/Paper'; + +const StyledTableCell = withStyles((theme: Theme) => + createStyles({ + head: { + backgroundColor: theme.palette.common.black, + color: theme.palette.common.white, + }, + body: { + fontSize: 14, + }, + }), +)(TableCell); + +const StyledTableRow = withStyles((theme: Theme) => + createStyles({ + root: { + '&:nth-of-type(odd)': { + backgroundColor: theme.palette.action.hover, + }, + }, + }), +)(TableRow); + +function createData(name: string, calories: number, fat: number, carbs: number, protein: number) { + return { name, calories, fat, carbs, protein }; +} + +const rows = [ + createData('Frozen yoghurt', 159, 6.0, 24, 4.0), + createData('Ice cream sandwich', 237, 9.0, 37, 4.3), + createData('Eclair', 262, 16.0, 24, 6.0), + createData('Cupcake', 305, 3.7, 67, 4.3), + createData('Gingerbread', 356, 16.0, 49, 3.9), +]; + +const useStyles = makeStyles({ + table: { + minWidth: 700, + }, +}); + +export function CustomizedTables() { + const classes = useStyles(); + + return ( + + + + + Dessert (100g serving) + Calories + Fat (g) + Carbs (g) + Protein (g) + + + + {rows.map((row) => ( + + + {row.name} + + {row.calories} + {row.fat} + {row.carbs} + {row.protein} + + ))} + +
    +
    + ); +} + +export default { + title: "Material-ui|tables|CustomizedTables.stories" +}; diff --git a/examples/storybook/stories/material-ui/tables/DenseTable.stories.tsx b/examples/storybook/stories/material-ui/tables/DenseTable.stories.tsx new file mode 100644 index 00000000000000..882f89097e841c --- /dev/null +++ b/examples/storybook/stories/material-ui/tables/DenseTable.stories.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableContainer from '@material-ui/core/TableContainer'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import Paper from '@material-ui/core/Paper'; + +const useStyles = makeStyles({ + table: { + minWidth: 650, + }, +}); + +function createData(name: string, calories: number, fat: number, carbs: number, protein: number) { + return { name, calories, fat, carbs, protein }; +} + +const rows = [ + createData('Frozen yoghurt', 159, 6.0, 24, 4.0), + createData('Ice cream sandwich', 237, 9.0, 37, 4.3), + createData('Eclair', 262, 16.0, 24, 6.0), + createData('Cupcake', 305, 3.7, 67, 4.3), + createData('Gingerbread', 356, 16.0, 49, 3.9), +]; + +export function DenseTable() { + const classes = useStyles(); + + return ( + + + + + Dessert (100g serving) + Calories + Fat (g) + Carbs (g) + Protein (g) + + + + {rows.map((row) => ( + + + {row.name} + + {row.calories} + {row.fat} + {row.carbs} + {row.protein} + + ))} + +
    +
    + ); +} + +export default { + title: "Material-ui|tables|DenseTable.stories" +}; diff --git a/examples/storybook/stories/material-ui/tables/EnhancedTable.stories.tsx b/examples/storybook/stories/material-ui/tables/EnhancedTable.stories.tsx new file mode 100644 index 00000000000000..d286594f057d01 --- /dev/null +++ b/examples/storybook/stories/material-ui/tables/EnhancedTable.stories.tsx @@ -0,0 +1,386 @@ +import React from 'react'; +import clsx from 'clsx'; +import { createStyles, lighten, makeStyles, Theme } from '@material-ui/core/styles'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableContainer from '@material-ui/core/TableContainer'; +import TableHead from '@material-ui/core/TableHead'; +import TablePagination from '@material-ui/core/TablePagination'; +import TableRow from '@material-ui/core/TableRow'; +import TableSortLabel from '@material-ui/core/TableSortLabel'; +import Toolbar from '@material-ui/core/Toolbar'; +import Typography from '@material-ui/core/Typography'; +import Paper from '@material-ui/core/Paper'; +import Checkbox from '@material-ui/core/Checkbox'; +import IconButton from '@material-ui/core/IconButton'; +import Tooltip from '@material-ui/core/Tooltip'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import Switch from '@material-ui/core/Switch'; +import DeleteIcon from '@material-ui/icons/Delete'; +import FilterListIcon from '@material-ui/icons/FilterList'; + +interface Data { + calories: number; + carbs: number; + fat: number; + name: string; + protein: number; +} + +function createData( + name: string, + calories: number, + fat: number, + carbs: number, + protein: number, +): Data { + return { name, calories, fat, carbs, protein }; +} + +const rows = [ + createData('Cupcake', 305, 3.7, 67, 4.3), + createData('Donut', 452, 25.0, 51, 4.9), + createData('Eclair', 262, 16.0, 24, 6.0), + createData('Frozen yoghurt', 159, 6.0, 24, 4.0), + createData('Gingerbread', 356, 16.0, 49, 3.9), + createData('Honeycomb', 408, 3.2, 87, 6.5), + createData('Ice cream sandwich', 237, 9.0, 37, 4.3), + createData('Jelly Bean', 375, 0.0, 94, 0.0), + createData('KitKat', 518, 26.0, 65, 7.0), + createData('Lollipop', 392, 0.2, 98, 0.0), + createData('Marshmallow', 318, 0, 81, 2.0), + createData('Nougat', 360, 19.0, 9, 37.0), + createData('Oreo', 437, 18.0, 63, 4.0), +]; + +function descendingComparator(a: T, b: T, orderBy: keyof T) { + if (b[orderBy] < a[orderBy]) { + return -1; + } + if (b[orderBy] > a[orderBy]) { + return 1; + } + return 0; +} + +type Order = 'asc' | 'desc'; + +function getComparator( + order: Order, + orderBy: Key, +): (a: { [key in Key]: number | string }, b: { [key in Key]: number | string }) => number { + return order === 'desc' + ? (a, b) => descendingComparator(a, b, orderBy) + : (a, b) => -descendingComparator(a, b, orderBy); +} + +function stableSort(array: T[], comparator: (a: T, b: T) => number) { + const stabilizedThis = array.map((el, index) => [el, index] as [T, number]); + stabilizedThis.sort((a, b) => { + const order = comparator(a[0], b[0]); + if (order !== 0) return order; + return a[1] - b[1]; + }); + return stabilizedThis.map((el) => el[0]); +} + +interface HeadCell { + disablePadding: boolean; + id: keyof Data; + label: string; + numeric: boolean; +} + +const headCells: HeadCell[] = [ + { id: 'name', numeric: false, disablePadding: true, label: 'Dessert (100g serving)' }, + { id: 'calories', numeric: true, disablePadding: false, label: 'Calories' }, + { id: 'fat', numeric: true, disablePadding: false, label: 'Fat (g)' }, + { id: 'carbs', numeric: true, disablePadding: false, label: 'Carbs (g)' }, + { id: 'protein', numeric: true, disablePadding: false, label: 'Protein (g)' }, +]; + +interface EnhancedTableProps { + classes: ReturnType; + numSelected: number; + onRequestSort: (event: React.MouseEvent, property: keyof Data) => void; + onSelectAllClick: (event: React.ChangeEvent) => void; + order: Order; + orderBy: string; + rowCount: number; +} + +function EnhancedTableHead(props: EnhancedTableProps) { + const { classes, onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort } = props; + const createSortHandler = (property: keyof Data) => (event: React.MouseEvent) => { + onRequestSort(event, property); + }; + + return ( + + + + 0 && numSelected < rowCount} + checked={rowCount > 0 && numSelected === rowCount} + onChange={onSelectAllClick} + inputProps={{ 'aria-label': 'select all desserts' }} + /> + + {headCells.map((headCell) => ( + + + {headCell.label} + {orderBy === headCell.id ? ( + + {order === 'desc' ? 'sorted descending' : 'sorted ascending'} + + ) : null} + + + ))} + + + ); +} + +const useToolbarStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(1), + }, + highlight: + theme.palette.type === 'light' + ? { + color: theme.palette.secondary.main, + backgroundColor: lighten(theme.palette.secondary.light, 0.85), + } + : { + color: theme.palette.text.primary, + backgroundColor: theme.palette.secondary.dark, + }, + title: { + flex: '1 1 100%', + }, + }), +); + +interface EnhancedTableToolbarProps { + numSelected: number; +} + +const EnhancedTableToolbar = (props: EnhancedTableToolbarProps) => { + const classes = useToolbarStyles(); + const { numSelected } = props; + + return ( + 0, + })} + > + {numSelected > 0 ? ( + + {numSelected} selected + + ) : ( + + Nutrition + + )} + {numSelected > 0 ? ( + + + + + + ) : ( + + + + + + )} + + ); +}; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + }, + paper: { + width: '100%', + marginBottom: theme.spacing(2), + }, + table: { + minWidth: 750, + }, + visuallyHidden: { + border: 0, + clip: 'rect(0 0 0 0)', + height: 1, + margin: -1, + overflow: 'hidden', + padding: 0, + position: 'absolute', + top: 20, + width: 1, + }, + }), +); + +export function EnhancedTable() { + const classes = useStyles(); + const [order, setOrder] = React.useState('asc'); + const [orderBy, setOrderBy] = React.useState('calories'); + const [selected, setSelected] = React.useState([]); + const [page, setPage] = React.useState(0); + const [dense, setDense] = React.useState(false); + const [rowsPerPage, setRowsPerPage] = React.useState(5); + + const handleRequestSort = (event: React.MouseEvent, property: keyof Data) => { + const isAsc = orderBy === property && order === 'asc'; + setOrder(isAsc ? 'desc' : 'asc'); + setOrderBy(property); + }; + + const handleSelectAllClick = (event: React.ChangeEvent) => { + if (event.target.checked) { + const newSelecteds = rows.map((n) => n.name); + setSelected(newSelecteds); + return; + } + setSelected([]); + }; + + const handleClick = (event: React.MouseEvent, name: string) => { + const selectedIndex = selected.indexOf(name); + let newSelected: string[] = []; + + if (selectedIndex === -1) { + newSelected = newSelected.concat(selected, name); + } else if (selectedIndex === 0) { + newSelected = newSelected.concat(selected.slice(1)); + } else if (selectedIndex === selected.length - 1) { + newSelected = newSelected.concat(selected.slice(0, -1)); + } else if (selectedIndex > 0) { + newSelected = newSelected.concat( + selected.slice(0, selectedIndex), + selected.slice(selectedIndex + 1), + ); + } + + setSelected(newSelected); + }; + + const handleChangePage = (event: unknown, newPage: number) => { + setPage(newPage); + }; + + const handleChangeRowsPerPage = (event: React.ChangeEvent) => { + setRowsPerPage(parseInt(event.target.value, 10)); + setPage(0); + }; + + const handleChangeDense = (event: React.ChangeEvent) => { + setDense(event.target.checked); + }; + + const isSelected = (name: string) => selected.indexOf(name) !== -1; + + const emptyRows = rowsPerPage - Math.min(rowsPerPage, rows.length - page * rowsPerPage); + + return ( +
    + + + + + + + {stableSort(rows, getComparator(order, orderBy)) + .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + .map((row, index) => { + const isItemSelected = isSelected(row.name); + const labelId = `enhanced-table-checkbox-${index}`; + + return ( + handleClick(event, row.name)} + role="checkbox" + aria-checked={isItemSelected} + tabIndex={-1} + key={row.name} + selected={isItemSelected} + > + + + + + {row.name} + + {row.calories} + {row.fat} + {row.carbs} + {row.protein} + + ); + })} + {emptyRows > 0 && ( + + + + )} + +
    +
    + +
    + } + label="Dense padding" + /> +
    + ); +} + +export default { + title: "Material-ui|tables|EnhancedTable.stories" +}; diff --git a/examples/storybook/stories/material-ui/tables/MaterialTableDemo.stories.tsx b/examples/storybook/stories/material-ui/tables/MaterialTableDemo.stories.tsx new file mode 100644 index 00000000000000..d5c4811891b829 --- /dev/null +++ b/examples/storybook/stories/material-ui/tables/MaterialTableDemo.stories.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import MaterialTable, { Column } from 'material-table'; + +interface Row { + name: string; + surname: string; + birthYear: number; + birthCity: number; +} + +interface TableState { + columns: Array>; + data: Row[]; +} + +export function MaterialTableDemo() { + const [state, setState] = React.useState({ + columns: [ + { title: 'Name', field: 'name' }, + { title: 'Surname', field: 'surname' }, + { title: 'Birth Year', field: 'birthYear', type: 'numeric' }, + { + title: 'Birth Place', + field: 'birthCity', + lookup: { 34: 'İstanbul', 63: 'Şanlıurfa' }, + }, + ], + data: [ + { name: 'Mehmet', surname: 'Baran', birthYear: 1987, birthCity: 63 }, + { + name: 'Zerya Betül', + surname: 'Baran', + birthYear: 2017, + birthCity: 34, + }, + ], + }); + + return ( + + new Promise((resolve) => { + setTimeout(() => { + resolve(); + setState((prevState) => { + const data = [...prevState.data]; + data.push(newData); + return { ...prevState, data }; + }); + }, 600); + }), + onRowUpdate: (newData, oldData) => + new Promise((resolve) => { + setTimeout(() => { + resolve(); + if (oldData) { + setState((prevState) => { + const data = [...prevState.data]; + data[data.indexOf(oldData)] = newData; + return { ...prevState, data }; + }); + } + }, 600); + }), + onRowDelete: (oldData) => + new Promise((resolve) => { + setTimeout(() => { + resolve(); + setState((prevState) => { + const data = [...prevState.data]; + data.splice(data.indexOf(oldData), 1); + return { ...prevState, data }; + }); + }, 600); + }), + }} + /> + ); +} + +export default { + title: "Material-ui|tables|MaterialTableDemo.stories" +}; diff --git a/examples/storybook/stories/material-ui/tables/ReactVirtualizedTable.stories.tsx b/examples/storybook/stories/material-ui/tables/ReactVirtualizedTable.stories.tsx new file mode 100644 index 00000000000000..24cae4c1e49b7d --- /dev/null +++ b/examples/storybook/stories/material-ui/tables/ReactVirtualizedTable.stories.tsx @@ -0,0 +1,241 @@ +import React from 'react'; +import clsx from 'clsx'; +import { createStyles, Theme, withStyles, WithStyles } from '@material-ui/core/styles'; +import TableCell from '@material-ui/core/TableCell'; +import Paper from '@material-ui/core/Paper'; +import { AutoSizer, Column, Table, TableCellRenderer, TableHeaderProps } from 'react-virtualized'; + +declare module '@material-ui/core/styles/withStyles' { + // Augment the BaseCSSProperties so that we can control jss-rtl + interface BaseCSSProperties { + /* + * Used to control if the rule-set should be affected by rtl transformation + */ + flip?: boolean; + } +} + +const styles = (theme: Theme) => + createStyles({ + flexContainer: { + display: 'flex', + alignItems: 'center', + boxSizing: 'border-box', + }, + table: { + // temporary right-to-left patch, waiting for + // https://github.com/bvaughn/react-virtualized/issues/454 + '& .ReactVirtualized__Table__headerRow': { + flip: false, + paddingRight: theme.direction === 'rtl' ? '0 !important' : undefined, + }, + }, + tableRow: { + cursor: 'pointer', + }, + tableRowHover: { + '&:hover': { + backgroundColor: theme.palette.grey[200], + }, + }, + tableCell: { + flex: 1, + }, + noClick: { + cursor: 'initial', + }, + }); + +interface ColumnData { + dataKey: string; + label: string; + numeric?: boolean; + width: number; +} + +interface Row { + index: number; +} + +interface MuiVirtualizedTableProps extends WithStyles { + columns: ColumnData[]; + headerHeight?: number; + onRowClick?: () => void; + rowCount: number; + rowGetter: (row: Row) => Data; + rowHeight?: number; +} + +class MuiVirtualizedTable extends React.PureComponent { + static defaultProps = { + headerHeight: 48, + rowHeight: 48, + }; + + getRowClassName = ({ index }: Row) => { + const { classes, onRowClick } = this.props; + + return clsx(classes.tableRow, classes.flexContainer, { + [classes.tableRowHover]: index !== -1 && onRowClick != null, + }); + }; + + cellRenderer: TableCellRenderer = ({ cellData, columnIndex }) => { + const { columns, classes, rowHeight, onRowClick } = this.props; + return ( + + {cellData} + + ); + }; + + headerRenderer = ({ label, columnIndex }: TableHeaderProps & { columnIndex: number }) => { + const { headerHeight, columns, classes } = this.props; + + return ( + + {label} + + ); + }; + + render() { + const { classes, columns, rowHeight, headerHeight, ...tableProps } = this.props; + return ( + + {({ height, width }) => ( + + {columns.map(({ dataKey, ...other }, index) => { + return ( + + this.headerRenderer({ + ...headerProps, + columnIndex: index, + }) + } + className={classes.flexContainer} + cellRenderer={this.cellRenderer} + dataKey={dataKey} + {...other} + /> + ); + })} +
    + )} +
    + ); + } +} + +const VirtualizedTable = withStyles(styles)(MuiVirtualizedTable); + +// --- + +interface Data { + calories: number; + carbs: number; + dessert: string; + fat: number; + id: number; + protein: number; +} +type Sample = [string, number, number, number, number]; + +const sample: Sample[] = [ + ['Frozen yoghurt', 159, 6.0, 24, 4.0], + ['Ice cream sandwich', 237, 9.0, 37, 4.3], + ['Eclair', 262, 16.0, 24, 6.0], + ['Cupcake', 305, 3.7, 67, 4.3], + ['Gingerbread', 356, 16.0, 49, 3.9], +]; + +function createData( + id: number, + dessert: string, + calories: number, + fat: number, + carbs: number, + protein: number, +): Data { + return { id, dessert, calories, fat, carbs, protein }; +} + +const rows: Data[] = []; + +for (let i = 0; i < 200; i += 1) { + const randomSelection = sample[Math.floor(Math.random() * sample.length)]; + rows.push(createData(i, ...randomSelection)); +} + +export function ReactVirtualizedTable() { + return ( + + rows[index]} + columns={[ + { + width: 200, + label: 'Dessert', + dataKey: 'dessert', + }, + { + width: 120, + label: 'Calories\u00A0(g)', + dataKey: 'calories', + numeric: true, + }, + { + width: 120, + label: 'Fat\u00A0(g)', + dataKey: 'fat', + numeric: true, + }, + { + width: 120, + label: 'Carbs\u00A0(g)', + dataKey: 'carbs', + numeric: true, + }, + { + width: 120, + label: 'Protein\u00A0(g)', + dataKey: 'protein', + numeric: true, + }, + ]} + /> + + ); +} + +export default { + title: "Material-ui|tables|ReactVirtualizedTable.stories" +}; diff --git a/examples/storybook/stories/material-ui/tables/SimpleTable.stories.tsx b/examples/storybook/stories/material-ui/tables/SimpleTable.stories.tsx new file mode 100644 index 00000000000000..fe794f76d922c7 --- /dev/null +++ b/examples/storybook/stories/material-ui/tables/SimpleTable.stories.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableContainer from '@material-ui/core/TableContainer'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import Paper from '@material-ui/core/Paper'; + +const useStyles = makeStyles({ + table: { + minWidth: 650, + }, +}); + +function createData(name: string, calories: number, fat: number, carbs: number, protein: number) { + return { name, calories, fat, carbs, protein }; +} + +const rows = [ + createData('Frozen yoghurt', 159, 6.0, 24, 4.0), + createData('Ice cream sandwich', 237, 9.0, 37, 4.3), + createData('Eclair', 262, 16.0, 24, 6.0), + createData('Cupcake', 305, 3.7, 67, 4.3), + createData('Gingerbread', 356, 16.0, 49, 3.9), +]; + +export function SimpleTable() { + const classes = useStyles(); + + return ( + + + + + Dessert (100g serving) + Calories + Fat (g) + Carbs (g) + Protein (g) + + + + {rows.map((row) => ( + + + {row.name} + + {row.calories} + {row.fat} + {row.carbs} + {row.protein} + + ))} + +
    +
    + ); +} + +export default { + title: "Material-ui|tables|SimpleTable.stories" +}; diff --git a/examples/storybook/stories/material-ui/tables/SpanningTable.stories.tsx b/examples/storybook/stories/material-ui/tables/SpanningTable.stories.tsx new file mode 100644 index 00000000000000..8d725242389de8 --- /dev/null +++ b/examples/storybook/stories/material-ui/tables/SpanningTable.stories.tsx @@ -0,0 +1,104 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableContainer from '@material-ui/core/TableContainer'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import Paper from '@material-ui/core/Paper'; + +const TAX_RATE = 0.07; + +const useStyles = makeStyles({ + table: { + minWidth: 700, + }, +}); + +function ccyFormat(num: number) { + return `${num.toFixed(2)}`; +} + +function priceRow(qty: number, unit: number) { + return qty * unit; +} + +function createRow(desc: string, qty: number, unit: number) { + const price = priceRow(qty, unit); + return { desc, qty, unit, price }; +} + +interface Row { + desc: string; + qty: number; + unit: number; + price: number; +} + +function subtotal(items: Row[]) { + return items.map(({ price }) => price).reduce((sum, i) => sum + i, 0); +} + +const rows = [ + createRow('Paperclips (Box)', 100, 1.15), + createRow('Paper (Case)', 10, 45.99), + createRow('Waste Basket', 2, 17.99), +]; + +const invoiceSubtotal = subtotal(rows); +const invoiceTaxes = TAX_RATE * invoiceSubtotal; +const invoiceTotal = invoiceTaxes + invoiceSubtotal; + +export function SpanningTable() { + const classes = useStyles(); + + return ( + + + + + + Details + + Price + + + Desc + Qty. + Unit + Sum + + + + {rows.map((row) => ( + + {row.desc} + {row.qty} + {row.unit} + {ccyFormat(row.price)} + + ))} + + + Subtotal + {ccyFormat(invoiceSubtotal)} + + + Tax + {`${(TAX_RATE * 100).toFixed(0)} %`} + {ccyFormat(invoiceTaxes)} + + + Total + {ccyFormat(invoiceTotal)} + + +
    +
    + ); +} + +export default { + title: "Material-ui|tables|SpanningTable.stories" +}; diff --git a/examples/storybook/stories/material-ui/tables/StickyHeadTable.stories.tsx b/examples/storybook/stories/material-ui/tables/StickyHeadTable.stories.tsx new file mode 100644 index 00000000000000..736438d298acc0 --- /dev/null +++ b/examples/storybook/stories/material-ui/tables/StickyHeadTable.stories.tsx @@ -0,0 +1,150 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Paper from '@material-ui/core/Paper'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableContainer from '@material-ui/core/TableContainer'; +import TableHead from '@material-ui/core/TableHead'; +import TablePagination from '@material-ui/core/TablePagination'; +import TableRow from '@material-ui/core/TableRow'; + +interface Column { + id: 'name' | 'code' | 'population' | 'size' | 'density'; + label: string; + minWidth?: number; + align?: 'right'; + format?: (value: number) => string; +} + +const columns: Column[] = [ + { id: 'name', label: 'Name', minWidth: 170 }, + { id: 'code', label: 'ISO\u00a0Code', minWidth: 100 }, + { + id: 'population', + label: 'Population', + minWidth: 170, + align: 'right', + format: (value: number) => value.toLocaleString('en-US'), + }, + { + id: 'size', + label: 'Size\u00a0(km\u00b2)', + minWidth: 170, + align: 'right', + format: (value: number) => value.toLocaleString('en-US'), + }, + { + id: 'density', + label: 'Density', + minWidth: 170, + align: 'right', + format: (value: number) => value.toFixed(2), + }, +]; + +interface Data { + name: string; + code: string; + population: number; + size: number; + density: number; +} + +function createData(name: string, code: string, population: number, size: number): Data { + const density = population / size; + return { name, code, population, size, density }; +} + +const rows = [ + createData('India', 'IN', 1324171354, 3287263), + createData('China', 'CN', 1403500365, 9596961), + createData('Italy', 'IT', 60483973, 301340), + createData('United States', 'US', 327167434, 9833520), + createData('Canada', 'CA', 37602103, 9984670), + createData('Australia', 'AU', 25475400, 7692024), + createData('Germany', 'DE', 83019200, 357578), + createData('Ireland', 'IE', 4857000, 70273), + createData('Mexico', 'MX', 126577691, 1972550), + createData('Japan', 'JP', 126317000, 377973), + createData('France', 'FR', 67022000, 640679), + createData('United Kingdom', 'GB', 67545757, 242495), + createData('Russia', 'RU', 146793744, 17098246), + createData('Nigeria', 'NG', 200962417, 923768), + createData('Brazil', 'BR', 210147125, 8515767), +]; + +const useStyles = makeStyles({ + root: { + width: '100%', + }, + container: { + maxHeight: 440, + }, +}); + +export function StickyHeadTable() { + const classes = useStyles(); + const [page, setPage] = React.useState(0); + const [rowsPerPage, setRowsPerPage] = React.useState(10); + + const handleChangePage = (event: unknown, newPage: number) => { + setPage(newPage); + }; + + const handleChangeRowsPerPage = (event: React.ChangeEvent) => { + setRowsPerPage(+event.target.value); + setPage(0); + }; + + return ( + + + + + + {columns.map((column) => ( + + {column.label} + + ))} + + + + {rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((row) => { + return ( + + {columns.map((column) => { + const value = row[column.id]; + return ( + + {column.format && typeof value === 'number' ? column.format(value) : value} + + ); + })} + + ); + })} + +
    +
    + +
    + ); +} + +export default { + title: "Material-ui|tables|StickyHeadTable.stories" +}; diff --git a/examples/storybook/stories/material-ui/tabs/AccessibleTabs.stories.tsx b/examples/storybook/stories/material-ui/tabs/AccessibleTabs.stories.tsx new file mode 100644 index 00000000000000..0566183c51f25f --- /dev/null +++ b/examples/storybook/stories/material-ui/tabs/AccessibleTabs.stories.tsx @@ -0,0 +1,104 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import Typography from '@material-ui/core/Typography'; +import Box from '@material-ui/core/Box'; + +interface TabPanelProps { + children?: React.ReactNode; + index: number; + value: number; +} + +function TabPanel(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +interface DemoTabsProps { + labelId: string; + onChange: (event: unknown, value: number) => void; + selectionFollowsFocus?: boolean; + value: number; +} +function DemoTabs(props: DemoTabsProps) { + const { labelId, onChange, selectionFollowsFocus, value } = props; + + return ( + + + + + + + + ); +} + +const useStyles = makeStyles({ + root: { + flexGrow: 1, + }, +}); + +export function AccessibleTabs() { + const classes = useStyles(); + + const [value, setValue] = React.useState(0); + const handleChange = (event: unknown, newValue: number) => { + setValue(newValue); + }; + + return ( +
    + + Tabs where selection follows focus + + + + Tabs where each tab needs to be selected manually + + + + Item One + + + Item Two + + + Item Three + +
    + ); +} + +export default { + title: "Material-ui|tabs|AccessibleTabs.stories" +}; diff --git a/examples/storybook/stories/material-ui/tabs/CenteredTabs.stories.tsx b/examples/storybook/stories/material-ui/tabs/CenteredTabs.stories.tsx new file mode 100644 index 00000000000000..a457352c5e34e7 --- /dev/null +++ b/examples/storybook/stories/material-ui/tabs/CenteredTabs.stories.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Paper from '@material-ui/core/Paper'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; + +const useStyles = makeStyles({ + root: { + flexGrow: 1, + }, +}); + +export function CenteredTabs() { + const classes = useStyles(); + const [value, setValue] = React.useState(0); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => { + setValue(newValue); + }; + + return ( + + + + + + + + ); +} + +export default { + title: "Material-ui|tabs|CenteredTabs.stories" +}; diff --git a/examples/storybook/stories/material-ui/tabs/CustomizedTabs.stories.tsx b/examples/storybook/stories/material-ui/tabs/CustomizedTabs.stories.tsx new file mode 100644 index 00000000000000..ad8549d1190750 --- /dev/null +++ b/examples/storybook/stories/material-ui/tabs/CustomizedTabs.stories.tsx @@ -0,0 +1,135 @@ +import React from 'react'; +import { makeStyles, withStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import Typography from '@material-ui/core/Typography'; + +const AntTabs = withStyles({ + root: { + borderBottom: '1px solid #e8e8e8', + }, + indicator: { + backgroundColor: '#1890ff', + }, +})(Tabs); + +const AntTab = withStyles((theme: Theme) => + createStyles({ + root: { + textTransform: 'none', + minWidth: 72, + fontWeight: theme.typography.fontWeightRegular, + marginRight: theme.spacing(4), + fontFamily: [ + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', + 'Roboto', + '"Helvetica Neue"', + 'Arial', + 'sans-serif', + '"Apple Color Emoji"', + '"Segoe UI Emoji"', + '"Segoe UI Symbol"', + ].join(','), + '&:hover': { + color: '#40a9ff', + opacity: 1, + }, + '&$selected': { + color: '#1890ff', + fontWeight: theme.typography.fontWeightMedium, + }, + '&:focus': { + color: '#40a9ff', + }, + }, + selected: {}, + }), +)((props: StyledTabProps) => ); + +interface StyledTabsProps { + value: number; + onChange: (event: React.ChangeEvent<{}>, newValue: number) => void; +} + +const StyledTabs = withStyles({ + indicator: { + display: 'flex', + justifyContent: 'center', + backgroundColor: 'transparent', + '& > span': { + maxWidth: 40, + width: '100%', + backgroundColor: '#635ee7', + }, + }, +})((props: StyledTabsProps) => }} />); + +interface StyledTabProps { + label: string; +} + +const StyledTab = withStyles((theme: Theme) => + createStyles({ + root: { + textTransform: 'none', + color: '#fff', + fontWeight: theme.typography.fontWeightRegular, + fontSize: theme.typography.pxToRem(15), + marginRight: theme.spacing(1), + '&:focus': { + opacity: 1, + }, + }, + }), +)((props: StyledTabProps) => ); + +const useStyles = makeStyles((theme: Theme) => ({ + root: { + flexGrow: 1, + }, + padding: { + padding: theme.spacing(3), + }, + demo1: { + backgroundColor: theme.palette.background.paper, + }, + demo2: { + backgroundColor: '#2e1534', + }, +})); + +export function CustomizedTabs() { + const classes = useStyles(); + const [value, setValue] = React.useState(0); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => { + setValue(newValue); + }; + + return ( +
    +
    + + + + + + +
    +
    + + + + + + +
    +
    + ); +} + +export default { + title: "Material-ui|tabs|CustomizedTabs.stories" +}; diff --git a/examples/storybook/stories/material-ui/tabs/DisabledTabs.stories.tsx b/examples/storybook/stories/material-ui/tabs/DisabledTabs.stories.tsx new file mode 100644 index 00000000000000..a8b944e368a61f --- /dev/null +++ b/examples/storybook/stories/material-ui/tabs/DisabledTabs.stories.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import Paper from '@material-ui/core/Paper'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; + +export function DisabledTabs() { + const [value, setValue] = React.useState(2); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => { + setValue(newValue); + }; + + return ( + + + + + + + + ); +} + +export default { + title: "Material-ui|tabs|DisabledTabs.stories" +}; diff --git a/examples/storybook/stories/material-ui/tabs/FullWidthTabs.stories.tsx b/examples/storybook/stories/material-ui/tabs/FullWidthTabs.stories.tsx new file mode 100644 index 00000000000000..19bfdfcadcf29e --- /dev/null +++ b/examples/storybook/stories/material-ui/tabs/FullWidthTabs.stories.tsx @@ -0,0 +1,101 @@ +import React from 'react'; +import SwipeableViews from 'react-swipeable-views'; +import { makeStyles, Theme, useTheme } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import Typography from '@material-ui/core/Typography'; +import Box from '@material-ui/core/Box'; + +interface TabPanelProps { + children?: React.ReactNode; + dir?: string; + index: any; + value: any; +} + +function TabPanel(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +function a11yProps(index: any) { + return { + id: `full-width-tab-${index}`, + 'aria-controls': `full-width-tabpanel-${index}`, + }; +} + +const useStyles = makeStyles((theme: Theme) => ({ + root: { + backgroundColor: theme.palette.background.paper, + width: 500, + }, +})); + +export function FullWidthTabs() { + const classes = useStyles(); + const theme = useTheme(); + const [value, setValue] = React.useState(0); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => { + setValue(newValue); + }; + + const handleChangeIndex = (index: number) => { + setValue(index); + }; + + return ( +
    + + + + + + + + + + Item One + + + Item Two + + + Item Three + + +
    + ); +} + +export default { + title: "Material-ui|tabs|FullWidthTabs.stories" +}; diff --git a/examples/storybook/stories/material-ui/tabs/IconLabelTabs.stories.tsx b/examples/storybook/stories/material-ui/tabs/IconLabelTabs.stories.tsx new file mode 100644 index 00000000000000..b541b36c0c0dd6 --- /dev/null +++ b/examples/storybook/stories/material-ui/tabs/IconLabelTabs.stories.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import Paper from '@material-ui/core/Paper'; +import { makeStyles } from '@material-ui/core/styles'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import PhoneIcon from '@material-ui/icons/Phone'; +import FavoriteIcon from '@material-ui/icons/Favorite'; +import PersonPinIcon from '@material-ui/icons/PersonPin'; + +const useStyles = makeStyles({ + root: { + flexGrow: 1, + maxWidth: 500, + }, +}); + +export function IconLabelTabs() { + const classes = useStyles(); + const [value, setValue] = React.useState(0); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => { + setValue(newValue); + }; + + return ( + + + } label="RECENTS" /> + } label="FAVORITES" /> + } label="NEARBY" /> + + + ); +} + +export default { + title: "Material-ui|tabs|IconLabelTabs.stories" +}; diff --git a/examples/storybook/stories/material-ui/tabs/IconTabs.stories.tsx b/examples/storybook/stories/material-ui/tabs/IconTabs.stories.tsx new file mode 100644 index 00000000000000..628d14c409500d --- /dev/null +++ b/examples/storybook/stories/material-ui/tabs/IconTabs.stories.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Paper from '@material-ui/core/Paper'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import PhoneIcon from '@material-ui/icons/Phone'; +import FavoriteIcon from '@material-ui/icons/Favorite'; +import PersonPinIcon from '@material-ui/icons/PersonPin'; + +const useStyles = makeStyles({ + root: { + flexGrow: 1, + maxWidth: 500, + }, +}); + +export function IconTabs() { + const classes = useStyles(); + const [value, setValue] = React.useState(0); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => { + setValue(newValue); + }; + + return ( + + + } aria-label="phone" /> + } aria-label="favorite" /> + } aria-label="person" /> + + + ); +} + +export default { + title: "Material-ui|tabs|IconTabs.stories" +}; diff --git a/examples/storybook/stories/material-ui/tabs/LabTabs.stories.tsx b/examples/storybook/stories/material-ui/tabs/LabTabs.stories.tsx new file mode 100644 index 00000000000000..cec5fd222c3d4b --- /dev/null +++ b/examples/storybook/stories/material-ui/tabs/LabTabs.stories.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Tab from '@material-ui/core/Tab'; +import TabContext from '@material-ui/lab/TabContext'; +import TabList from '@material-ui/lab/TabList'; +import TabPanel from '@material-ui/lab/TabPanel'; + +const useStyles = makeStyles((theme: Theme) => ({ + root: { + flexGrow: 1, + backgroundColor: theme.palette.background.paper, + }, +})); + +export function LabTabs() { + const classes = useStyles(); + const [value, setValue] = React.useState('1'); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: string) => { + setValue(newValue); + }; + + return ( +
    + + + + + + + + + Item One + Item Two + Item Three + +
    + ); +} + +export default { + title: "Material-ui|tabs|LabTabs.stories" +}; diff --git a/examples/storybook/stories/material-ui/tabs/NavTabs.stories.tsx b/examples/storybook/stories/material-ui/tabs/NavTabs.stories.tsx new file mode 100644 index 00000000000000..0e5a90f914bc8a --- /dev/null +++ b/examples/storybook/stories/material-ui/tabs/NavTabs.stories.tsx @@ -0,0 +1,103 @@ +import React from 'react'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import Typography from '@material-ui/core/Typography'; +import Box from '@material-ui/core/Box'; + +interface TabPanelProps { + children?: React.ReactNode; + index: any; + value: any; +} + +function TabPanel(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +function a11yProps(index: any) { + return { + id: `nav-tab-${index}`, + 'aria-controls': `nav-tabpanel-${index}`, + }; +} + +interface LinkTabProps { + label?: string; + href?: string; +} + +function LinkTab(props: LinkTabProps) { + return ( + ) => { + event.preventDefault(); + }} + {...props} + /> + ); +} + +const useStyles = makeStyles((theme: Theme) => ({ + root: { + flexGrow: 1, + backgroundColor: theme.palette.background.paper, + }, +})); + +export function NavTabs() { + const classes = useStyles(); + const [value, setValue] = React.useState(0); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => { + setValue(newValue); + }; + + return ( +
    + + + + + + + + + Page One + + + Page Two + + + Page Three + +
    + ); +} + +export default { + title: "Material-ui|tabs|NavTabs.stories" +}; diff --git a/examples/storybook/stories/material-ui/tabs/ScrollableTabsButtonAuto.stories.tsx b/examples/storybook/stories/material-ui/tabs/ScrollableTabsButtonAuto.stories.tsx new file mode 100644 index 00000000000000..9d020f21111083 --- /dev/null +++ b/examples/storybook/stories/material-ui/tabs/ScrollableTabsButtonAuto.stories.tsx @@ -0,0 +1,106 @@ +import React from 'react'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import Typography from '@material-ui/core/Typography'; +import Box from '@material-ui/core/Box'; + +interface TabPanelProps { + children?: React.ReactNode; + index: any; + value: any; +} + +function TabPanel(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +function a11yProps(index: any) { + return { + id: `scrollable-auto-tab-${index}`, + 'aria-controls': `scrollable-auto-tabpanel-${index}`, + }; +} + +const useStyles = makeStyles((theme: Theme) => ({ + root: { + flexGrow: 1, + width: '100%', + backgroundColor: theme.palette.background.paper, + }, +})); + +export function ScrollableTabsButtonAuto() { + const classes = useStyles(); + const [value, setValue] = React.useState(0); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => { + setValue(newValue); + }; + + return ( +
    + + + + + + + + + + + + + Item One + + + Item Two + + + Item Three + + + Item Four + + + Item Five + + + Item Six + + + Item Seven + +
    + ); +} + +export default { + title: "Material-ui|tabs|ScrollableTabsButtonAuto.stories" +}; diff --git a/examples/storybook/stories/material-ui/tabs/ScrollableTabsButtonForce.stories.tsx b/examples/storybook/stories/material-ui/tabs/ScrollableTabsButtonForce.stories.tsx new file mode 100644 index 00000000000000..4525a82b0473e0 --- /dev/null +++ b/examples/storybook/stories/material-ui/tabs/ScrollableTabsButtonForce.stories.tsx @@ -0,0 +1,113 @@ +import React from 'react'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import PhoneIcon from '@material-ui/icons/Phone'; +import FavoriteIcon from '@material-ui/icons/Favorite'; +import PersonPinIcon from '@material-ui/icons/PersonPin'; +import HelpIcon from '@material-ui/icons/Help'; +import ShoppingBasket from '@material-ui/icons/ShoppingBasket'; +import ThumbDown from '@material-ui/icons/ThumbDown'; +import ThumbUp from '@material-ui/icons/ThumbUp'; +import Typography from '@material-ui/core/Typography'; +import Box from '@material-ui/core/Box'; + +interface TabPanelProps { + children?: React.ReactNode; + index: any; + value: any; +} + +function TabPanel(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +function a11yProps(index: any) { + return { + id: `scrollable-force-tab-${index}`, + 'aria-controls': `scrollable-force-tabpanel-${index}`, + }; +} + +const useStyles = makeStyles((theme: Theme) => ({ + root: { + flexGrow: 1, + width: '100%', + backgroundColor: theme.palette.background.paper, + }, +})); + +export function ScrollableTabsButtonForce() { + const classes = useStyles(); + const [value, setValue] = React.useState(0); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => { + setValue(newValue); + }; + + return ( +
    + + + } {...a11yProps(0)} /> + } {...a11yProps(1)} /> + } {...a11yProps(2)} /> + } {...a11yProps(3)} /> + } {...a11yProps(4)} /> + } {...a11yProps(5)} /> + } {...a11yProps(6)} /> + + + + Item One + + + Item Two + + + Item Three + + + Item Four + + + Item Five + + + Item Six + + + Item Seven + +
    + ); +} + +export default { + title: "Material-ui|tabs|ScrollableTabsButtonForce.stories" +}; diff --git a/examples/storybook/stories/material-ui/tabs/ScrollableTabsButtonPrevent.stories.tsx b/examples/storybook/stories/material-ui/tabs/ScrollableTabsButtonPrevent.stories.tsx new file mode 100644 index 00000000000000..eabd1443794ffc --- /dev/null +++ b/examples/storybook/stories/material-ui/tabs/ScrollableTabsButtonPrevent.stories.tsx @@ -0,0 +1,111 @@ +import React from 'react'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import PhoneIcon from '@material-ui/icons/Phone'; +import FavoriteIcon from '@material-ui/icons/Favorite'; +import PersonPinIcon from '@material-ui/icons/PersonPin'; +import HelpIcon from '@material-ui/icons/Help'; +import ShoppingBasket from '@material-ui/icons/ShoppingBasket'; +import ThumbDown from '@material-ui/icons/ThumbDown'; +import ThumbUp from '@material-ui/icons/ThumbUp'; +import Typography from '@material-ui/core/Typography'; +import Box from '@material-ui/core/Box'; + +interface TabPanelProps { + children?: React.ReactNode; + index: any; + value: any; +} + +function TabPanel(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +function a11yProps(index: any) { + return { + id: `scrollable-prevent-tab-${index}`, + 'aria-controls': `scrollable-prevent-tabpanel-${index}`, + }; +} + +const useStyles = makeStyles((theme: Theme) => ({ + root: { + flexGrow: 1, + width: '100%', + backgroundColor: theme.palette.background.paper, + }, +})); + +export function ScrollableTabsButtonPrevent() { + const classes = useStyles(); + const [value, setValue] = React.useState(0); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => { + setValue(newValue); + }; + + return ( +
    + + + } aria-label="phone" {...a11yProps(0)} /> + } aria-label="favorite" {...a11yProps(1)} /> + } aria-label="person" {...a11yProps(2)} /> + } aria-label="help" {...a11yProps(3)} /> + } aria-label="shopping" {...a11yProps(4)} /> + } aria-label="up" {...a11yProps(5)} /> + } aria-label="down" {...a11yProps(6)} /> + + + + Item One + + + Item Two + + + Item Three + + + Item Four + + + Item Five + + + Item Six + + + Item Seven + +
    + ); +} + +export default { + title: "Material-ui|tabs|ScrollableTabsButtonPrevent.stories" +}; diff --git a/examples/storybook/stories/material-ui/tabs/SimpleTabs.stories.tsx b/examples/storybook/stories/material-ui/tabs/SimpleTabs.stories.tsx new file mode 100644 index 00000000000000..0e0477edd130ba --- /dev/null +++ b/examples/storybook/stories/material-ui/tabs/SimpleTabs.stories.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import Typography from '@material-ui/core/Typography'; +import Box from '@material-ui/core/Box'; + +interface TabPanelProps { + children?: React.ReactNode; + index: any; + value: any; +} + +function TabPanel(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +function a11yProps(index: any) { + return { + id: `simple-tab-${index}`, + 'aria-controls': `simple-tabpanel-${index}`, + }; +} + +const useStyles = makeStyles((theme: Theme) => ({ + root: { + flexGrow: 1, + backgroundColor: theme.palette.background.paper, + }, +})); + +export function SimpleTabs() { + const classes = useStyles(); + const [value, setValue] = React.useState(0); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => { + setValue(newValue); + }; + + return ( +
    + + + + + + + + + Item One + + + Item Two + + + Item Three + +
    + ); +} + +export default { + title: "Material-ui|tabs|SimpleTabs.stories" +}; diff --git a/examples/storybook/stories/material-ui/tabs/TabsWrappedLabel.stories.tsx b/examples/storybook/stories/material-ui/tabs/TabsWrappedLabel.stories.tsx new file mode 100644 index 00000000000000..09432ab0b5b2ad --- /dev/null +++ b/examples/storybook/stories/material-ui/tabs/TabsWrappedLabel.stories.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import Typography from '@material-ui/core/Typography'; +import Box from '@material-ui/core/Box'; + +interface TabPanelProps { + children?: React.ReactNode; + index: any; + value: any; +} + +function TabPanel(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +function a11yProps(index: any) { + return { + id: `wrapped-tab-${index}`, + 'aria-controls': `wrapped-tabpanel-${index}`, + }; +} + +const useStyles = makeStyles((theme: Theme) => ({ + root: { + flexGrow: 1, + backgroundColor: theme.palette.background.paper, + }, +})); + +export function TabsWrappedLabel() { + const classes = useStyles(); + const [value, setValue] = React.useState('one'); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: string) => { + setValue(newValue); + }; + + return ( +
    + + + + + + + + + Item One + + + Item Two + + + Item Three + +
    + ); +} + +export default { + title: "Material-ui|tabs|TabsWrappedLabel.stories" +}; diff --git a/examples/storybook/stories/material-ui/tabs/VerticalTabs.stories.tsx b/examples/storybook/stories/material-ui/tabs/VerticalTabs.stories.tsx new file mode 100644 index 00000000000000..a96be0baf55bff --- /dev/null +++ b/examples/storybook/stories/material-ui/tabs/VerticalTabs.stories.tsx @@ -0,0 +1,106 @@ +import React from 'react'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import Typography from '@material-ui/core/Typography'; +import Box from '@material-ui/core/Box'; + +interface TabPanelProps { + children?: React.ReactNode; + index: any; + value: any; +} + +function TabPanel(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +function a11yProps(index: any) { + return { + id: `vertical-tab-${index}`, + 'aria-controls': `vertical-tabpanel-${index}`, + }; +} + +const useStyles = makeStyles((theme: Theme) => ({ + root: { + flexGrow: 1, + backgroundColor: theme.palette.background.paper, + display: 'flex', + height: 224, + }, + tabs: { + borderRight: `1px solid ${theme.palette.divider}`, + }, +})); + +export function VerticalTabs() { + const classes = useStyles(); + const [value, setValue] = React.useState(0); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => { + setValue(newValue); + }; + + return ( +
    + + + + + + + + + + + Item One + + + Item Two + + + Item Three + + + Item Four + + + Item Five + + + Item Six + + + Item Seven + +
    + ); +} + +export default { + title: "Material-ui|tabs|VerticalTabs.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/BasicTextFields.stories.tsx b/examples/storybook/stories/material-ui/text-fields/BasicTextFields.stories.tsx new file mode 100644 index 00000000000000..40c252b2830c42 --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/BasicTextFields.stories.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import TextField from '@material-ui/core/TextField'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + width: '25ch', + }, + }, + }), +); + +export function BasicTextFields() { + const classes = useStyles(); + + return ( +
    + + + + + ); +} + +export default { + title: "Material-ui|text-fields|BasicTextFields.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/ColorTextFields.stories.tsx b/examples/storybook/stories/material-ui/text-fields/ColorTextFields.stories.tsx new file mode 100644 index 00000000000000..76c968d057fc6a --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/ColorTextFields.stories.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import TextField from '@material-ui/core/TextField'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + width: '25ch', + }, + }, + }), +); + +export function ColorTextFields() { + const classes = useStyles(); + + return ( +
    + + + + + ); +} + +export default { + title: "Material-ui|text-fields|ColorTextFields.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/ComposedTextField.stories.tsx b/examples/storybook/stories/material-ui/text-fields/ComposedTextField.stories.tsx new file mode 100644 index 00000000000000..d91af0b93bacb0 --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/ComposedTextField.stories.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import FilledInput from '@material-ui/core/FilledInput'; +import FormControl from '@material-ui/core/FormControl'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import Input from '@material-ui/core/Input'; +import InputLabel from '@material-ui/core/InputLabel'; +import OutlinedInput from '@material-ui/core/OutlinedInput'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +export function ComposedTextField() { + const [name, setName] = React.useState('Composed TextField'); + const classes = useStyles(); + + const handleChange = (event: React.ChangeEvent) => { + setName(event.target.value); + }; + + return ( +
    + + Name + + + + Name + + Some important helper text + + + Name + + Disabled + + + Name + + Error + + + Name + + + + Name + + +
    + ); +} + +export default { + title: "Material-ui|text-fields|ComposedTextField.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/CustomizedInputBase.stories.tsx b/examples/storybook/stories/material-ui/text-fields/CustomizedInputBase.stories.tsx new file mode 100644 index 00000000000000..d984baf8d5878c --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/CustomizedInputBase.stories.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Paper from '@material-ui/core/Paper'; +import InputBase from '@material-ui/core/InputBase'; +import Divider from '@material-ui/core/Divider'; +import IconButton from '@material-ui/core/IconButton'; +import MenuIcon from '@material-ui/icons/Menu'; +import SearchIcon from '@material-ui/icons/Search'; +import DirectionsIcon from '@material-ui/icons/Directions'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + padding: '2px 4px', + display: 'flex', + alignItems: 'center', + width: 400, + }, + input: { + marginLeft: theme.spacing(1), + flex: 1, + }, + iconButton: { + padding: 10, + }, + divider: { + height: 28, + margin: 4, + }, + }), +); + +export function CustomizedInputBase() { + const classes = useStyles(); + + return ( + + + + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|text-fields|CustomizedInputBase.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/CustomizedInputs.stories.tsx b/examples/storybook/stories/material-ui/text-fields/CustomizedInputs.stories.tsx new file mode 100644 index 00000000000000..b1f03542514530 --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/CustomizedInputs.stories.tsx @@ -0,0 +1,201 @@ +import React from 'react'; +import { + createStyles, + fade, + Theme, + ThemeProvider, + withStyles, + makeStyles, + createMuiTheme, +} from '@material-ui/core/styles'; +import InputBase from '@material-ui/core/InputBase'; +import InputLabel from '@material-ui/core/InputLabel'; +import TextField, { TextFieldProps } from '@material-ui/core/TextField'; +import FormControl from '@material-ui/core/FormControl'; +import { green } from '@material-ui/core/colors'; +import { OutlinedInputProps } from '@material-ui/core/OutlinedInput'; + +const CssTextField = withStyles({ + root: { + '& label.Mui-focused': { + color: 'green', + }, + '& .MuiInput-underline:after': { + borderBottomColor: 'green', + }, + '& .MuiOutlinedInput-root': { + '& fieldset': { + borderColor: 'red', + }, + '&:hover fieldset': { + borderColor: 'yellow', + }, + '&.Mui-focused fieldset': { + borderColor: 'green', + }, + }, + }, +})(TextField); + +const BootstrapInput = withStyles((theme: Theme) => + createStyles({ + root: { + 'label + &': { + marginTop: theme.spacing(3), + }, + }, + input: { + borderRadius: 4, + position: 'relative', + backgroundColor: theme.palette.common.white, + border: '1px solid #ced4da', + fontSize: 16, + width: 'auto', + padding: '10px 12px', + transition: theme.transitions.create(['border-color', 'box-shadow']), + // Use the system font instead of the default Roboto font. + fontFamily: [ + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', + 'Roboto', + '"Helvetica Neue"', + 'Arial', + 'sans-serif', + '"Apple Color Emoji"', + '"Segoe UI Emoji"', + '"Segoe UI Symbol"', + ].join(','), + '&:focus': { + boxShadow: `${fade(theme.palette.primary.main, 0.25)} 0 0 0 0.2rem`, + borderColor: theme.palette.primary.main, + }, + }, + }), +)(InputBase); + +const useStylesReddit = makeStyles((theme: Theme) => + createStyles({ + root: { + border: '1px solid #e2e2e1', + overflow: 'hidden', + borderRadius: 4, + backgroundColor: '#fcfcfb', + transition: theme.transitions.create(['border-color', 'box-shadow']), + '&:hover': { + backgroundColor: '#fff', + }, + '&$focused': { + backgroundColor: '#fff', + boxShadow: `${fade(theme.palette.primary.main, 0.25)} 0 0 0 2px`, + borderColor: theme.palette.primary.main, + }, + }, + focused: {}, + }), +); + +function RedditTextField(props: TextFieldProps) { + const classes = useStylesReddit(); + + return ( + } + {...props} + /> + ); +} + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + flexWrap: 'wrap', + }, + margin: { + margin: theme.spacing(1), + }, + }), +); + +const ValidationTextField = withStyles({ + root: { + '& input:valid + fieldset': { + borderColor: 'green', + borderWidth: 2, + }, + '& input:invalid + fieldset': { + borderColor: 'red', + borderWidth: 2, + }, + '& input:valid:focus + fieldset': { + borderLeftWidth: 6, + padding: '4px !important', // override inline-style + }, + }, +})(TextField); + +const theme = createMuiTheme({ + palette: { + primary: green, + }, +}); + +export function CustomizedInputs() { + const classes = useStyles(); + + return ( +
    + + + + + + + + + Bootstrap + + + + + + + + ); +} + +export default { + title: "Material-ui|text-fields|CustomizedInputs.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/FormPropsTextFields.stories.tsx b/examples/storybook/stories/material-ui/text-fields/FormPropsTextFields.stories.tsx new file mode 100644 index 00000000000000..bb2ab93d7b1e4a --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/FormPropsTextFields.stories.tsx @@ -0,0 +1,158 @@ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& .MuiTextField-root': { + margin: theme.spacing(1), + width: '25ch', + }, + }, + }), +); + +export function FormPropsTextFields() { + const classes = useStyles(); + + return ( +
    +
    + + + + + + + +
    +
    + + + + + + + +
    +
    + + + + + + + +
    +
    + ); +} + +export default { + title: "Material-ui|text-fields|FormPropsTextFields.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/FormattedInputs.stories.tsx b/examples/storybook/stories/material-ui/text-fields/FormattedInputs.stories.tsx new file mode 100644 index 00000000000000..ff3f9f4738ff64 --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/FormattedInputs.stories.tsx @@ -0,0 +1,115 @@ +import React from 'react'; +import MaskedInput from 'react-text-mask'; +import NumberFormat from 'react-number-format'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import Input from '@material-ui/core/Input'; +import InputLabel from '@material-ui/core/InputLabel'; +import TextField from '@material-ui/core/TextField'; +import FormControl from '@material-ui/core/FormControl'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +interface TextMaskCustomProps { + inputRef: (ref: HTMLInputElement | null) => void; +} + +function TextMaskCustom(props: TextMaskCustomProps) { + const { inputRef, ...other } = props; + + return ( + { + inputRef(ref ? ref.inputElement : null); + }} + mask={['(', /[1-9]/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/]} + placeholderChar={'\u2000'} + showMask + /> + ); +} + +interface NumberFormatCustomProps { + inputRef: (instance: NumberFormat | null) => void; + onChange: (event: { target: { name: string; value: string } }) => void; + name: string; +} + +function NumberFormatCustom(props: NumberFormatCustomProps) { + const { inputRef, onChange, ...other } = props; + + return ( + { + onChange({ + target: { + name: props.name, + value: values.value, + }, + }); + }} + thousandSeparator + isNumericString + prefix="$" + /> + ); +} + +interface State { + textmask: string; + numberformat: string; +} + +export function FormattedInputs() { + const classes = useStyles(); + const [values, setValues] = React.useState({ + textmask: '(1  )    -    ', + numberformat: '1320', + }); + + const handleChange = (event: React.ChangeEvent) => { + setValues({ + ...values, + [event.target.name]: event.target.value, + }); + }; + + return ( +
    + + react-text-mask + + + +
    + ); +} + +export default { + title: "Material-ui|text-fields|FormattedInputs.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/InputAdornments.stories.tsx b/examples/storybook/stories/material-ui/text-fields/InputAdornments.stories.tsx new file mode 100644 index 00000000000000..40b911eec22898 --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/InputAdornments.stories.tsx @@ -0,0 +1,235 @@ +import React from 'react'; +import clsx from 'clsx'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import IconButton from '@material-ui/core/IconButton'; +import Input from '@material-ui/core/Input'; +import FilledInput from '@material-ui/core/FilledInput'; +import OutlinedInput from '@material-ui/core/OutlinedInput'; +import InputLabel from '@material-ui/core/InputLabel'; +import InputAdornment from '@material-ui/core/InputAdornment'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import FormControl from '@material-ui/core/FormControl'; +import TextField from '@material-ui/core/TextField'; +import Visibility from '@material-ui/icons/Visibility'; +import VisibilityOff from '@material-ui/icons/VisibilityOff'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + flexWrap: 'wrap', + }, + margin: { + margin: theme.spacing(1), + }, + withoutLabel: { + marginTop: theme.spacing(3), + }, + textField: { + width: '25ch', + }, + }), +); + +interface State { + amount: string; + password: string; + weight: string; + weightRange: string; + showPassword: boolean; +} + +export function InputAdornments() { + const classes = useStyles(); + const [values, setValues] = React.useState({ + amount: '', + password: '', + weight: '', + weightRange: '', + showPassword: false, + }); + + const handleChange = (prop: keyof State) => (event: React.ChangeEvent) => { + setValues({ ...values, [prop]: event.target.value }); + }; + + const handleClickShowPassword = () => { + setValues({ ...values, showPassword: !values.showPassword }); + }; + + const handleMouseDownPassword = (event: React.MouseEvent) => { + event.preventDefault(); + }; + + return ( +
    +
    + Kg, + }} + /> + + Kg} + aria-describedby="standard-weight-helper-text" + inputProps={{ + 'aria-label': 'weight', + }} + /> + Weight + + + Password + + + {values.showPassword ? : } + + + } + /> + + + Amount + $} + /> + +
    +
    + Kg, + }} + variant="filled" + /> + + Kg} + aria-describedby="filled-weight-helper-text" + inputProps={{ + 'aria-label': 'weight', + }} + /> + Weight + + + Password + + + {values.showPassword ? : } + + + } + /> + + + Amount + $} + /> + +
    +
    + Kg, + }} + variant="outlined" + /> + + Kg} + aria-describedby="outlined-weight-helper-text" + inputProps={{ + 'aria-label': 'weight', + }} + labelWidth={0} + /> + Weight + + + Password + + + {values.showPassword ? : } + + + } + labelWidth={70} + /> + + + Amount + $} + labelWidth={60} + /> + +
    +
    + ); +} + +export default { + title: "Material-ui|text-fields|InputAdornments.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/InputWithIcon.stories.tsx b/examples/storybook/stories/material-ui/text-fields/InputWithIcon.stories.tsx new file mode 100644 index 00000000000000..e6ab56aa6b8cb9 --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/InputWithIcon.stories.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import Input from '@material-ui/core/Input'; +import InputLabel from '@material-ui/core/InputLabel'; +import InputAdornment from '@material-ui/core/InputAdornment'; +import FormControl from '@material-ui/core/FormControl'; +import TextField from '@material-ui/core/TextField'; +import Grid from '@material-ui/core/Grid'; +import AccountCircle from '@material-ui/icons/AccountCircle'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + margin: { + margin: theme.spacing(1), + }, + }), +); + +export function InputWithIcon() { + const classes = useStyles(); + + return ( +
    + + With a start adornment + + + + } + /> + + + + + ), + }} + /> +
    + + + + + + + + +
    +
    + ); +} + +export default { + title: "Material-ui|text-fields|InputWithIcon.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/Inputs.stories.tsx b/examples/storybook/stories/material-ui/text-fields/Inputs.stories.tsx new file mode 100644 index 00000000000000..9032a8979b239f --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/Inputs.stories.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import Input from '@material-ui/core/Input'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +export function Inputs() { + const classes = useStyles(); + + return ( +
    + + + + +
    + ); +} + +export default { + title: "Material-ui|text-fields|Inputs.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/LayoutTextFields.stories.tsx b/examples/storybook/stories/material-ui/text-fields/LayoutTextFields.stories.tsx new file mode 100644 index 00000000000000..f3ff3231c08e4f --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/LayoutTextFields.stories.tsx @@ -0,0 +1,149 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import TextField from '@material-ui/core/TextField'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + flexWrap: 'wrap', + }, + textField: { + marginLeft: theme.spacing(1), + marginRight: theme.spacing(1), + width: '25ch', + }, + }), +); + +export function LayoutTextFields() { + const classes = useStyles(); + + return ( +
    +
    + + + + +
    +
    + + + + +
    +
    + + + + +
    +
    + ); +} + +export default { + title: "Material-ui|text-fields|LayoutTextFields.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/MultilineTextFields.stories.tsx b/examples/storybook/stories/material-ui/text-fields/MultilineTextFields.stories.tsx new file mode 100644 index 00000000000000..3d9c1abb7d5192 --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/MultilineTextFields.stories.tsx @@ -0,0 +1,107 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import TextField from '@material-ui/core/TextField'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& .MuiTextField-root': { + margin: theme.spacing(1), + width: '25ch', + }, + }, + }), +); + +export function MultilineTextFields() { + const classes = useStyles(); + const [value, setValue] = React.useState('Controlled'); + + const handleChange = (event: React.ChangeEvent) => { + setValue(event.target.value); + }; + + return ( +
    +
    + + + +
    +
    + + + +
    +
    + + + +
    +
    + ); +} + +export default { + title: "Material-ui|text-fields|MultilineTextFields.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/SelectTextFields.stories.tsx b/examples/storybook/stories/material-ui/text-fields/SelectTextFields.stories.tsx new file mode 100644 index 00000000000000..392f26e349ccd0 --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/SelectTextFields.stories.tsx @@ -0,0 +1,155 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import TextField from '@material-ui/core/TextField'; +import MenuItem from '@material-ui/core/MenuItem'; + +const currencies = [ + { + value: 'USD', + label: '$', + }, + { + value: 'EUR', + label: '€', + }, + { + value: 'BTC', + label: '฿', + }, + { + value: 'JPY', + label: '¥', + }, +]; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& .MuiTextField-root': { + margin: theme.spacing(1), + width: '25ch', + }, + }, + }), +); + +export function MultilineTextFields() { + const classes = useStyles(); + const [currency, setCurrency] = React.useState('EUR'); + + const handleChange = (event: React.ChangeEvent) => { + setCurrency(event.target.value); + }; + + return ( +
    +
    + + {currencies.map((option) => ( + + {option.label} + + ))} + + + {currencies.map((option) => ( + + ))} + +
    +
    + + {currencies.map((option) => ( + + {option.label} + + ))} + + + {currencies.map((option) => ( + + ))} + +
    +
    + + {currencies.map((option) => ( + + {option.label} + + ))} + + + {currencies.map((option) => ( + + ))} + +
    +
    + ); +} + +export default { + title: "Material-ui|text-fields|SelectTextFields.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/StateTextFields.stories.tsx b/examples/storybook/stories/material-ui/text-fields/StateTextFields.stories.tsx new file mode 100644 index 00000000000000..a66ae45c805204 --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/StateTextFields.stories.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& .MuiTextField-root': { + margin: theme.spacing(1), + width: '25ch', + }, + }, + }), +); + +export function StateTextFields() { + const classes = useStyles(); + const [name, setName] = React.useState('Cat in the Hat'); + const handleChange = (event: React.ChangeEvent) => { + setName(event.target.value); + }; + + return ( +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + ); +} + +export default { + title: "Material-ui|text-fields|StateTextFields.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/TextFieldSizes.stories.tsx b/examples/storybook/stories/material-ui/text-fields/TextFieldSizes.stories.tsx new file mode 100644 index 00000000000000..47d8c5c00247ed --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/TextFieldSizes.stories.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import TextField from '@material-ui/core/TextField'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& .MuiTextField-root': { + margin: theme.spacing(1), + width: 200, + }, + }, + }), +); + +export function TextFieldSizes() { + const classes = useStyles(); + + return ( +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + ); +} + +export default { + title: "Material-ui|text-fields|TextFieldSizes.stories" +}; diff --git a/examples/storybook/stories/material-ui/text-fields/ValidationTextFields.stories.tsx b/examples/storybook/stories/material-ui/text-fields/ValidationTextFields.stories.tsx new file mode 100644 index 00000000000000..367e91da80808f --- /dev/null +++ b/examples/storybook/stories/material-ui/text-fields/ValidationTextFields.stories.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + '& .MuiTextField-root': { + margin: theme.spacing(1), + width: 200, + }, + }, + }), +); + +export function ValidationTextFields() { + const classes = useStyles(); + + return ( +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + ); +} + +export default { + title: "Material-ui|text-fields|ValidationTextFields.stories" +}; diff --git a/examples/storybook/stories/material-ui/textarea-autosize/EmptyTextarea.stories.tsx b/examples/storybook/stories/material-ui/textarea-autosize/EmptyTextarea.stories.tsx new file mode 100644 index 00000000000000..b98ce28eb90841 --- /dev/null +++ b/examples/storybook/stories/material-ui/textarea-autosize/EmptyTextarea.stories.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import TextareaAutosize from '@material-ui/core/TextareaAutosize'; + +export function EmptyTextarea() { + return ; +} + +export default { + title: "Material-ui|textarea-autosize|EmptyTextarea.stories" +}; diff --git a/examples/storybook/stories/material-ui/textarea-autosize/MaxHeightTextarea.stories.tsx b/examples/storybook/stories/material-ui/textarea-autosize/MaxHeightTextarea.stories.tsx new file mode 100644 index 00000000000000..2e3b5074959fd2 --- /dev/null +++ b/examples/storybook/stories/material-ui/textarea-autosize/MaxHeightTextarea.stories.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import TextareaAutosize from '@material-ui/core/TextareaAutosize'; + +export function MaxHeightTextarea() { + return ( + + ); +} + +export default { + title: "Material-ui|textarea-autosize|MaxHeightTextarea.stories" +}; diff --git a/examples/storybook/stories/material-ui/textarea-autosize/MinHeightTextarea.stories.tsx b/examples/storybook/stories/material-ui/textarea-autosize/MinHeightTextarea.stories.tsx new file mode 100644 index 00000000000000..66900da324234e --- /dev/null +++ b/examples/storybook/stories/material-ui/textarea-autosize/MinHeightTextarea.stories.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import TextareaAutosize from '@material-ui/core/TextareaAutosize'; + +export function MinHeightTextarea() { + return ; +} + +export default { + title: "Material-ui|textarea-autosize|MinHeightTextarea.stories" +}; diff --git a/examples/storybook/stories/material-ui/toggle-button/CustomizedDividers.stories.tsx b/examples/storybook/stories/material-ui/toggle-button/CustomizedDividers.stories.tsx new file mode 100644 index 00000000000000..d96673186eb7b4 --- /dev/null +++ b/examples/storybook/stories/material-ui/toggle-button/CustomizedDividers.stories.tsx @@ -0,0 +1,108 @@ +import React from 'react'; +import { makeStyles, withStyles, Theme, createStyles } from '@material-ui/core/styles'; +import FormatAlignLeftIcon from '@material-ui/icons/FormatAlignLeft'; +import FormatAlignCenterIcon from '@material-ui/icons/FormatAlignCenter'; +import FormatAlignRightIcon from '@material-ui/icons/FormatAlignRight'; +import FormatAlignJustifyIcon from '@material-ui/icons/FormatAlignJustify'; +import FormatBoldIcon from '@material-ui/icons/FormatBold'; +import FormatItalicIcon from '@material-ui/icons/FormatItalic'; +import FormatUnderlinedIcon from '@material-ui/icons/FormatUnderlined'; +import FormatColorFillIcon from '@material-ui/icons/FormatColorFill'; +import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; +import Divider from '@material-ui/core/Divider'; +import Paper from '@material-ui/core/Paper'; +import ToggleButton from '@material-ui/lab/ToggleButton'; +import ToggleButtonGroup from '@material-ui/lab/ToggleButtonGroup'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + paper: { + display: 'flex', + border: `1px solid ${theme.palette.divider}`, + flexWrap: 'wrap', + }, + divider: { + margin: theme.spacing(1, 0.5), + }, + }), +); + +const StyledToggleButtonGroup = withStyles((theme) => ({ + grouped: { + margin: theme.spacing(0.5), + border: 'none', + '&:not(:first-child)': { + borderRadius: theme.shape.borderRadius, + }, + '&:first-child': { + borderRadius: theme.shape.borderRadius, + }, + }, +}))(ToggleButtonGroup); + +export function CustomizedDividers() { + const [alignment, setAlignment] = React.useState('left'); + const [formats, setFormats] = React.useState(() => ['italic']); + + const handleFormat = (event: React.MouseEvent, newFormats: string[]) => { + setFormats(newFormats); + }; + + const handleAlignment = (event: React.MouseEvent, newAlignment: string) => { + setAlignment(newAlignment); + }; + + const classes = useStyles(); + + return ( +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|toggle-button|CustomizedDividers.stories" +}; diff --git a/examples/storybook/stories/material-ui/toggle-button/StandaloneToggleButton.stories.tsx b/examples/storybook/stories/material-ui/toggle-button/StandaloneToggleButton.stories.tsx new file mode 100644 index 00000000000000..dfc9414a5f208d --- /dev/null +++ b/examples/storybook/stories/material-ui/toggle-button/StandaloneToggleButton.stories.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import CheckIcon from '@material-ui/icons/Check'; +import ToggleButton from '@material-ui/lab/ToggleButton'; + +export function StandaloneToggleButton() { + const [selected, setSelected] = React.useState(false); + + return ( + { + setSelected(!selected); + }} + > + + + ); +} + +export default { + title: "Material-ui|toggle-button|StandaloneToggleButton.stories" +}; diff --git a/examples/storybook/stories/material-ui/toggle-button/ToggleButtonNotEmpty.stories.tsx b/examples/storybook/stories/material-ui/toggle-button/ToggleButtonNotEmpty.stories.tsx new file mode 100644 index 00000000000000..4e17d5406f9d07 --- /dev/null +++ b/examples/storybook/stories/material-ui/toggle-button/ToggleButtonNotEmpty.stories.tsx @@ -0,0 +1,84 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import FormatAlignLeftIcon from '@material-ui/icons/FormatAlignLeft'; +import FormatAlignCenterIcon from '@material-ui/icons/FormatAlignCenter'; +import FormatAlignRightIcon from '@material-ui/icons/FormatAlignRight'; +import FormatAlignJustifyIcon from '@material-ui/icons/FormatAlignJustify'; +import LaptopIcon from '@material-ui/icons/Laptop'; +import TvIcon from '@material-ui/icons/Tv'; +import PhoneAndroidIcon from '@material-ui/icons/PhoneAndroid'; +import Grid from '@material-ui/core/Grid'; +import ToggleButton from '@material-ui/lab/ToggleButton'; +import ToggleButtonGroup from '@material-ui/lab/ToggleButtonGroup'; + +const useStyles = makeStyles((theme) => ({ + toggleContainer: { + margin: theme.spacing(2, 0), + }, +})); + +export function ToggleButtonNotEmpty() { + const [alignment, setAlignment] = React.useState('left'); + const [formats, setFormats] = React.useState(() => ['phone']); + + const handleFormat = (event: React.MouseEvent, newFormats: string[]) => { + if (newFormats.length) { + setFormats(newFormats); + } + }; + + const handleAlignment = (event: React.MouseEvent, newAlignment: string | null) => { + if (newAlignment !== null) { + setAlignment(newAlignment); + } + }; + + const classes = useStyles(); + + return ( + + +
    + + + + + + + + + + + + + + +
    +
    + +
    + + + + + + + + + + + +
    +
    +
    + ); +} + +export default { + title: "Material-ui|toggle-button|ToggleButtonNotEmpty.stories" +}; diff --git a/examples/storybook/stories/material-ui/toggle-button/ToggleButtonSizes.stories.tsx b/examples/storybook/stories/material-ui/toggle-button/ToggleButtonSizes.stories.tsx new file mode 100644 index 00000000000000..230b4a81522b73 --- /dev/null +++ b/examples/storybook/stories/material-ui/toggle-button/ToggleButtonSizes.stories.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import FormatAlignLeftIcon from '@material-ui/icons/FormatAlignLeft'; +import FormatAlignCenterIcon from '@material-ui/icons/FormatAlignCenter'; +import FormatAlignRightIcon from '@material-ui/icons/FormatAlignRight'; +import FormatAlignJustifyIcon from '@material-ui/icons/FormatAlignJustify'; +import Grid from '@material-ui/core/Grid'; +import ToggleButton from '@material-ui/lab/ToggleButton'; +import ToggleButtonGroup from '@material-ui/lab/ToggleButtonGroup'; + +export function ToggleButtonSizes() { + const [alignment, setAlignment] = React.useState('left'); + + const handleChange = (event: React.MouseEvent, newAlignment: string) => { + setAlignment(newAlignment); + }; + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|toggle-button|ToggleButtonSizes.stories" +}; diff --git a/examples/storybook/stories/material-ui/toggle-button/ToggleButtons.stories.tsx b/examples/storybook/stories/material-ui/toggle-button/ToggleButtons.stories.tsx new file mode 100644 index 00000000000000..d760e5f0fd4ba8 --- /dev/null +++ b/examples/storybook/stories/material-ui/toggle-button/ToggleButtons.stories.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import FormatAlignLeftIcon from '@material-ui/icons/FormatAlignLeft'; +import FormatAlignCenterIcon from '@material-ui/icons/FormatAlignCenter'; +import FormatAlignRightIcon from '@material-ui/icons/FormatAlignRight'; +import FormatAlignJustifyIcon from '@material-ui/icons/FormatAlignJustify'; +import ToggleButton from '@material-ui/lab/ToggleButton'; +import ToggleButtonGroup from '@material-ui/lab/ToggleButtonGroup'; + +export function ToggleButtons() { + const [alignment, setAlignment] = React.useState('left'); + + const handleAlignment = (event: React.MouseEvent, newAlignment: string | null) => { + setAlignment(newAlignment); + }; + + return ( + + + + + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|toggle-button|ToggleButtons.stories" +}; diff --git a/examples/storybook/stories/material-ui/toggle-button/ToggleButtonsMultiple.stories.tsx b/examples/storybook/stories/material-ui/toggle-button/ToggleButtonsMultiple.stories.tsx new file mode 100644 index 00000000000000..6eab13c08f98a1 --- /dev/null +++ b/examples/storybook/stories/material-ui/toggle-button/ToggleButtonsMultiple.stories.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import FormatBoldIcon from '@material-ui/icons/FormatBold'; +import FormatItalicIcon from '@material-ui/icons/FormatItalic'; +import FormatUnderlinedIcon from '@material-ui/icons/FormatUnderlined'; +import FormatColorFillIcon from '@material-ui/icons/FormatColorFill'; +import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; +import ToggleButton from '@material-ui/lab/ToggleButton'; +import ToggleButtonGroup from '@material-ui/lab/ToggleButtonGroup'; + +export function ToggleButtonsMultiple() { + const [formats, setFormats] = React.useState(() => ['bold', 'italic']); + + const handleFormat = (event: React.MouseEvent, newFormats: string[]) => { + setFormats(newFormats); + }; + + return ( + + + + + + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|toggle-button|ToggleButtonsMultiple.stories" +}; diff --git a/examples/storybook/stories/material-ui/toggle-button/VerticalToggleButtons.stories.tsx b/examples/storybook/stories/material-ui/toggle-button/VerticalToggleButtons.stories.tsx new file mode 100644 index 00000000000000..645b777784a2e2 --- /dev/null +++ b/examples/storybook/stories/material-ui/toggle-button/VerticalToggleButtons.stories.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import ViewListIcon from '@material-ui/icons/ViewList'; +import ViewModuleIcon from '@material-ui/icons/ViewModule'; +import ViewQuiltIcon from '@material-ui/icons/ViewQuilt'; +import ToggleButton from '@material-ui/lab/ToggleButton'; +import ToggleButtonGroup from '@material-ui/lab/ToggleButtonGroup'; + +export function VerticalToggleButtons() { + const [view, setView] = React.useState('list'); + + const handleChange = (event: React.MouseEvent, nextView: string) => { + setView(nextView); + }; + + return ( + + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|toggle-button|VerticalToggleButtons.stories" +}; diff --git a/examples/storybook/stories/material-ui/tooltips/ArrowTooltips.stories.tsx b/examples/storybook/stories/material-ui/tooltips/ArrowTooltips.stories.tsx new file mode 100644 index 00000000000000..80a6646767e4c9 --- /dev/null +++ b/examples/storybook/stories/material-ui/tooltips/ArrowTooltips.stories.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Tooltip from '@material-ui/core/Tooltip'; + +export function ArrowTooltips() { + return ( + + + + ); +} + +export default { + title: "Material-ui|tooltips|ArrowTooltips.stories" +}; diff --git a/examples/storybook/stories/material-ui/tooltips/ControlledTooltips.stories.tsx b/examples/storybook/stories/material-ui/tooltips/ControlledTooltips.stories.tsx new file mode 100644 index 00000000000000..55451b290a5c90 --- /dev/null +++ b/examples/storybook/stories/material-ui/tooltips/ControlledTooltips.stories.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Tooltip from '@material-ui/core/Tooltip'; + +export function ControlledTooltips() { + const [open, setOpen] = React.useState(false); + + const handleClose = () => { + setOpen(false); + }; + + const handleOpen = () => { + setOpen(true); + }; + + return ( + + + + ); +} + +export default { + title: "Material-ui|tooltips|ControlledTooltips.stories" +}; diff --git a/examples/storybook/stories/material-ui/tooltips/CustomizedTooltips.stories.tsx b/examples/storybook/stories/material-ui/tooltips/CustomizedTooltips.stories.tsx new file mode 100644 index 00000000000000..ac8909a97f0907 --- /dev/null +++ b/examples/storybook/stories/material-ui/tooltips/CustomizedTooltips.stories.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import { withStyles, Theme, makeStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Tooltip, { TooltipProps } from '@material-ui/core/Tooltip'; +import Typography from '@material-ui/core/Typography'; + +const LightTooltip = withStyles((theme: Theme) => ({ + tooltip: { + backgroundColor: theme.palette.common.white, + color: 'rgba(0, 0, 0, 0.87)', + boxShadow: theme.shadows[1], + fontSize: 11, + }, +}))(Tooltip); + +const useStylesBootstrap = makeStyles((theme: Theme) => ({ + arrow: { + color: theme.palette.common.black, + }, + tooltip: { + backgroundColor: theme.palette.common.black, + }, +})); + +function BootstrapTooltip(props: TooltipProps) { + const classes = useStylesBootstrap(); + + return ; +} + +const HtmlTooltip = withStyles((theme: Theme) => ({ + tooltip: { + backgroundColor: '#f5f5f9', + color: 'rgba(0, 0, 0, 0.87)', + maxWidth: 220, + fontSize: theme.typography.pxToRem(12), + border: '1px solid #dadde9', + }, +}))(Tooltip); + +export function CustomizedTooltips() { + return ( +
    + + + + + + + + Tooltip with HTML + {"And here's"} {'some'} {'amazing content'}.{' '} + {"It's very engaging. Right?"} + + } + > + + +
    + ); +} + +export default { + title: "Material-ui|tooltips|CustomizedTooltips.stories" +}; diff --git a/examples/storybook/stories/material-ui/tooltips/DelayTooltips.stories.tsx b/examples/storybook/stories/material-ui/tooltips/DelayTooltips.stories.tsx new file mode 100644 index 00000000000000..601d12c5dae71f --- /dev/null +++ b/examples/storybook/stories/material-ui/tooltips/DelayTooltips.stories.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Tooltip from '@material-ui/core/Tooltip'; + +export function DelayTooltips() { + return ( + + + + ); +} + +export default { + title: "Material-ui|tooltips|DelayTooltips.stories" +}; diff --git a/examples/storybook/stories/material-ui/tooltips/DisabledTooltips.stories.tsx b/examples/storybook/stories/material-ui/tooltips/DisabledTooltips.stories.tsx new file mode 100644 index 00000000000000..949541e19a837b --- /dev/null +++ b/examples/storybook/stories/material-ui/tooltips/DisabledTooltips.stories.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Tooltip from '@material-ui/core/Tooltip'; + +export function DisabledTooltips() { + return ( + + + + + + ); +} + +export default { + title: "Material-ui|tooltips|DisabledTooltips.stories" +}; diff --git a/examples/storybook/stories/material-ui/tooltips/InteractiveTooltips.stories.tsx b/examples/storybook/stories/material-ui/tooltips/InteractiveTooltips.stories.tsx new file mode 100644 index 00000000000000..b6296f0dbec2db --- /dev/null +++ b/examples/storybook/stories/material-ui/tooltips/InteractiveTooltips.stories.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Tooltip from '@material-ui/core/Tooltip'; + +export function InteractiveTooltips() { + return ( + + + + ); +} + +export default { + title: "Material-ui|tooltips|InteractiveTooltips.stories" +}; diff --git a/examples/storybook/stories/material-ui/tooltips/PositionedTooltips.stories.tsx b/examples/storybook/stories/material-ui/tooltips/PositionedTooltips.stories.tsx new file mode 100644 index 00000000000000..c4d37b9da45e2d --- /dev/null +++ b/examples/storybook/stories/material-ui/tooltips/PositionedTooltips.stories.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Grid from '@material-ui/core/Grid'; +import Button from '@material-ui/core/Button'; +import Tooltip from '@material-ui/core/Tooltip'; + +const useStyles = makeStyles({ + root: { + width: 500, + }, +}); + +export function PositionedTooltips() { + const classes = useStyles(); + + return ( +
    + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + +
    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|tooltips|PositionedTooltips.stories" +}; diff --git a/examples/storybook/stories/material-ui/tooltips/SimpleTooltips.stories.tsx b/examples/storybook/stories/material-ui/tooltips/SimpleTooltips.stories.tsx new file mode 100644 index 00000000000000..a065bdd1d57221 --- /dev/null +++ b/examples/storybook/stories/material-ui/tooltips/SimpleTooltips.stories.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import AddIcon from '@material-ui/icons/Add'; +import Fab from '@material-ui/core/Fab'; +import DeleteIcon from '@material-ui/icons/Delete'; +import IconButton from '@material-ui/core/IconButton'; +import Tooltip from '@material-ui/core/Tooltip'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + fab: { + margin: theme.spacing(2), + }, + absolute: { + position: 'absolute', + bottom: theme.spacing(2), + right: theme.spacing(3), + }, + }), +); + +export function SimpleTooltips() { + const classes = useStyles(); + + return ( +
    + + + + + + + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|tooltips|SimpleTooltips.stories" +}; diff --git a/examples/storybook/stories/material-ui/tooltips/TransitionsTooltips.stories.tsx b/examples/storybook/stories/material-ui/tooltips/TransitionsTooltips.stories.tsx new file mode 100644 index 00000000000000..224dc493d93f79 --- /dev/null +++ b/examples/storybook/stories/material-ui/tooltips/TransitionsTooltips.stories.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Tooltip from '@material-ui/core/Tooltip'; +import Fade from '@material-ui/core/Fade'; +import Zoom from '@material-ui/core/Zoom'; + +export function TransitionsTooltips() { + return ( +
    + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|tooltips|TransitionsTooltips.stories" +}; diff --git a/examples/storybook/stories/material-ui/tooltips/TriggersTooltips.stories.tsx b/examples/storybook/stories/material-ui/tooltips/TriggersTooltips.stories.tsx new file mode 100644 index 00000000000000..7500837cc8cdc4 --- /dev/null +++ b/examples/storybook/stories/material-ui/tooltips/TriggersTooltips.stories.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import Grid from '@material-ui/core/Grid'; +import Button from '@material-ui/core/Button'; +import Tooltip from '@material-ui/core/Tooltip'; +import ClickAwayListener from '@material-ui/core/ClickAwayListener'; + +export function TriggersTooltips() { + const [open, setOpen] = React.useState(false); + + const handleTooltipClose = () => { + setOpen(false); + }; + + const handleTooltipOpen = () => { + setOpen(true); + }; + + return ( +
    + + + + + + + + + + + + + + + + + + +
    + + + +
    +
    +
    +
    +
    + ); +} + +export default { + title: "Material-ui|tooltips|TriggersTooltips.stories" +}; diff --git a/examples/storybook/stories/material-ui/tooltips/VariableWidth.stories.tsx b/examples/storybook/stories/material-ui/tooltips/VariableWidth.stories.tsx new file mode 100644 index 00000000000000..b696e9f525eb9e --- /dev/null +++ b/examples/storybook/stories/material-ui/tooltips/VariableWidth.stories.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Tooltip from '@material-ui/core/Tooltip'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + button: { + margin: theme.spacing(1), + }, + customWidth: { + maxWidth: 500, + }, + noMaxWidth: { + maxWidth: 'none', + }, + }), +); + +const longText = ` +Aliquam eget finibus ante, non facilisis lectus. Sed vitae dignissim est, vel aliquam tellus. +Praesent non nunc mollis, fermentum neque at, semper arcu. +Nullam eget est sed sem iaculis gravida eget vitae justo. +`; + +export function VariableWidth() { + const classes = useStyles(); + + return ( +
    + + + + + + + + + +
    + ); +} + +export default { + title: "Material-ui|tooltips|VariableWidth.stories" +}; diff --git a/examples/storybook/stories/material-ui/transfer-list/SelectAllTransferList.stories.tsx b/examples/storybook/stories/material-ui/transfer-list/SelectAllTransferList.stories.tsx new file mode 100644 index 00000000000000..1b89c6ddd4832e --- /dev/null +++ b/examples/storybook/stories/material-ui/transfer-list/SelectAllTransferList.stories.tsx @@ -0,0 +1,164 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Grid from '@material-ui/core/Grid'; +import List from '@material-ui/core/List'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import Checkbox from '@material-ui/core/Checkbox'; +import Button from '@material-ui/core/Button'; +import Divider from '@material-ui/core/Divider'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + margin: 'auto', + }, + cardHeader: { + padding: theme.spacing(1, 2), + }, + list: { + width: 200, + height: 230, + backgroundColor: theme.palette.background.paper, + overflow: 'auto', + }, + button: { + margin: theme.spacing(0.5, 0), + }, + }), +); + +function not(a: number[], b: number[]) { + return a.filter((value) => b.indexOf(value) === -1); +} + +function intersection(a: number[], b: number[]) { + return a.filter((value) => b.indexOf(value) !== -1); +} + +function union(a: number[], b: number[]) { + return [...a, ...not(b, a)]; +} + +export function TransferList() { + const classes = useStyles(); + const [checked, setChecked] = React.useState([]); + const [left, setLeft] = React.useState([0, 1, 2, 3]); + const [right, setRight] = React.useState([4, 5, 6, 7]); + + const leftChecked = intersection(checked, left); + const rightChecked = intersection(checked, right); + + const handleToggle = (value: number) => () => { + const currentIndex = checked.indexOf(value); + const newChecked = [...checked]; + + if (currentIndex === -1) { + newChecked.push(value); + } else { + newChecked.splice(currentIndex, 1); + } + + setChecked(newChecked); + }; + + const numberOfChecked = (items: number[]) => intersection(checked, items).length; + + const handleToggleAll = (items: number[]) => () => { + if (numberOfChecked(items) === items.length) { + setChecked(not(checked, items)); + } else { + setChecked(union(checked, items)); + } + }; + + const handleCheckedRight = () => { + setRight(right.concat(leftChecked)); + setLeft(not(left, leftChecked)); + setChecked(not(checked, leftChecked)); + }; + + const handleCheckedLeft = () => { + setLeft(left.concat(rightChecked)); + setRight(not(right, rightChecked)); + setChecked(not(checked, rightChecked)); + }; + + const customList = (title: React.ReactNode, items: number[]) => ( + + + } + title={title} + subheader={`${numberOfChecked(items)}/${items.length} selected`} + /> + + + {items.map((value: number) => { + const labelId = `transfer-list-all-item-${value}-label`; + + return ( + + + + + + + ); + })} + + + + ); + + return ( + + {customList('Choices', left)} + + + + + + + {customList('Chosen', right)} + + ); +} + +export default { + title: "Material-ui|transfer-list|SelectAllTransferList.stories" +}; diff --git a/examples/storybook/stories/material-ui/transfer-list/TransferList.stories.tsx b/examples/storybook/stories/material-ui/transfer-list/TransferList.stories.tsx new file mode 100644 index 00000000000000..53f96623ffa185 --- /dev/null +++ b/examples/storybook/stories/material-ui/transfer-list/TransferList.stories.tsx @@ -0,0 +1,159 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import Grid from '@material-ui/core/Grid'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import Checkbox from '@material-ui/core/Checkbox'; +import Button from '@material-ui/core/Button'; +import Paper from '@material-ui/core/Paper'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + margin: 'auto', + }, + paper: { + width: 200, + height: 230, + overflow: 'auto', + }, + button: { + margin: theme.spacing(0.5, 0), + }, + }), +); + +function not(a: number[], b: number[]) { + return a.filter((value) => b.indexOf(value) === -1); +} + +function intersection(a: number[], b: number[]) { + return a.filter((value) => b.indexOf(value) !== -1); +} + +export function TransferList() { + const classes = useStyles(); + const [checked, setChecked] = React.useState([]); + const [left, setLeft] = React.useState([0, 1, 2, 3]); + const [right, setRight] = React.useState([4, 5, 6, 7]); + + const leftChecked = intersection(checked, left); + const rightChecked = intersection(checked, right); + + const handleToggle = (value: number) => () => { + const currentIndex = checked.indexOf(value); + const newChecked = [...checked]; + + if (currentIndex === -1) { + newChecked.push(value); + } else { + newChecked.splice(currentIndex, 1); + } + + setChecked(newChecked); + }; + + const handleAllRight = () => { + setRight(right.concat(left)); + setLeft([]); + }; + + const handleCheckedRight = () => { + setRight(right.concat(leftChecked)); + setLeft(not(left, leftChecked)); + setChecked(not(checked, leftChecked)); + }; + + const handleCheckedLeft = () => { + setLeft(left.concat(rightChecked)); + setRight(not(right, rightChecked)); + setChecked(not(checked, rightChecked)); + }; + + const handleAllLeft = () => { + setLeft(left.concat(right)); + setRight([]); + }; + + const customList = (items: number[]) => ( + + + {items.map((value: number) => { + const labelId = `transfer-list-item-${value}-label`; + + return ( + + + + + + + ); + })} + + + + ); + + return ( + + {customList(left)} + + + + + + + + + {customList(right)} + + ); +} + +export default { + title: "Material-ui|transfer-list|TransferList.stories" +}; diff --git a/examples/storybook/stories/material-ui/transitions/SimpleCollapse.stories.tsx b/examples/storybook/stories/material-ui/transitions/SimpleCollapse.stories.tsx new file mode 100644 index 00000000000000..aaf322cfe343d0 --- /dev/null +++ b/examples/storybook/stories/material-ui/transitions/SimpleCollapse.stories.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Switch from '@material-ui/core/Switch'; +import Paper from '@material-ui/core/Paper'; +import Collapse from '@material-ui/core/Collapse'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + height: 180, + }, + container: { + display: 'flex', + }, + paper: { + margin: theme.spacing(1), + }, + svg: { + width: 100, + height: 100, + }, + polygon: { + fill: theme.palette.common.white, + stroke: theme.palette.divider, + strokeWidth: 1, + }, + }), +); + +export function SimpleCollapse() { + const classes = useStyles(); + const [checked, setChecked] = React.useState(false); + + const handleChange = () => { + setChecked((prev) => !prev); + }; + + return ( +
    + } + label="Show" + /> +
    + + + + + + + + + + + + + + +
    +
    + ); +} + +export default { + title: "Material-ui|transitions|SimpleCollapse.stories" +}; diff --git a/examples/storybook/stories/material-ui/transitions/SimpleFade.stories.tsx b/examples/storybook/stories/material-ui/transitions/SimpleFade.stories.tsx new file mode 100644 index 00000000000000..6be08438f735e9 --- /dev/null +++ b/examples/storybook/stories/material-ui/transitions/SimpleFade.stories.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Switch from '@material-ui/core/Switch'; +import Paper from '@material-ui/core/Paper'; +import Fade from '@material-ui/core/Fade'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + height: 180, + }, + container: { + display: 'flex', + }, + paper: { + margin: theme.spacing(1), + }, + svg: { + width: 100, + height: 100, + }, + polygon: { + fill: theme.palette.common.white, + stroke: theme.palette.divider, + strokeWidth: 1, + }, + }), +); + +export function SimpleFade() { + const classes = useStyles(); + const [checked, setChecked] = React.useState(false); + + const handleChange = () => { + setChecked((prev) => !prev); + }; + + return ( +
    + } + label="Show" + /> +
    + + + + + + + +
    +
    + ); +} + +export default { + title: "Material-ui|transitions|SimpleFade.stories" +}; diff --git a/examples/storybook/stories/material-ui/transitions/SimpleGrow.stories.tsx b/examples/storybook/stories/material-ui/transitions/SimpleGrow.stories.tsx new file mode 100644 index 00000000000000..253a0e78ff8950 --- /dev/null +++ b/examples/storybook/stories/material-ui/transitions/SimpleGrow.stories.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Switch from '@material-ui/core/Switch'; +import Paper from '@material-ui/core/Paper'; +import Grow from '@material-ui/core/Grow'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + height: 180, + }, + container: { + display: 'flex', + }, + paper: { + margin: theme.spacing(1), + }, + svg: { + width: 100, + height: 100, + }, + polygon: { + fill: theme.palette.common.white, + stroke: theme.palette.divider, + strokeWidth: 1, + }, + }), +); + +export function SimpleGrow() { + const classes = useStyles(); + const [checked, setChecked] = React.useState(false); + + const handleChange = () => { + setChecked((prev) => !prev); + }; + + return ( +
    + } + label="Show" + /> +
    + + + + + + + + {/* Conditionally applies the timeout prop to change the entry speed. */} + + + + + + + +
    +
    + ); +} + +export default { + title: "Material-ui|transitions|SimpleGrow.stories" +}; diff --git a/examples/storybook/stories/material-ui/transitions/SimpleSlide.stories.tsx b/examples/storybook/stories/material-ui/transitions/SimpleSlide.stories.tsx new file mode 100644 index 00000000000000..12fab8b51c24e3 --- /dev/null +++ b/examples/storybook/stories/material-ui/transitions/SimpleSlide.stories.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import Switch from '@material-ui/core/Switch'; +import Paper from '@material-ui/core/Paper'; +import Slide from '@material-ui/core/Slide'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + height: 180, + }, + wrapper: { + width: 100 + theme.spacing(2), + }, + paper: { + zIndex: 1, + position: 'relative', + margin: theme.spacing(1), + }, + svg: { + width: 100, + height: 100, + }, + polygon: { + fill: theme.palette.common.white, + stroke: theme.palette.divider, + strokeWidth: 1, + }, + }), +); + +export function SimpleSlide() { + const classes = useStyles(); + const [checked, setChecked] = React.useState(false); + + const handleChange = () => { + setChecked((prev) => !prev); + }; + + return ( +
    +
    + } + label="Show" + /> + + + + + + + +
    +
    + ); +} + +export default { + title: "Material-ui|transitions|SimpleSlide.stories" +}; diff --git a/examples/storybook/stories/material-ui/transitions/SimpleZoom.stories.tsx b/examples/storybook/stories/material-ui/transitions/SimpleZoom.stories.tsx new file mode 100644 index 00000000000000..085da280fc0675 --- /dev/null +++ b/examples/storybook/stories/material-ui/transitions/SimpleZoom.stories.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import Switch from '@material-ui/core/Switch'; +import Paper from '@material-ui/core/Paper'; +import Zoom from '@material-ui/core/Zoom'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + height: 180, + }, + container: { + display: 'flex', + }, + paper: { + margin: theme.spacing(1), + }, + svg: { + width: 100, + height: 100, + }, + polygon: { + fill: theme.palette.common.white, + stroke: theme.palette.divider, + strokeWidth: 1, + }, + }), +); + +export function SimpleZoom() { + const classes = useStyles(); + const [checked, setChecked] = React.useState(false); + + const handleChange = () => { + setChecked((prev) => !prev); + }; + + return ( +
    + } + label="Show" + /> +
    + + + + + + + + + + + + + + +
    +
    + ); +} + +export default { + title: "Material-ui|transitions|SimpleZoom.stories" +}; diff --git a/examples/storybook/stories/material-ui/tree-view/ControlledTreeView.stories.tsx b/examples/storybook/stories/material-ui/tree-view/ControlledTreeView.stories.tsx new file mode 100644 index 00000000000000..6d57e0e76e49c0 --- /dev/null +++ b/examples/storybook/stories/material-ui/tree-view/ControlledTreeView.stories.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import TreeView from '@material-ui/lab/TreeView'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import ChevronRightIcon from '@material-ui/icons/ChevronRight'; +import TreeItem from '@material-ui/lab/TreeItem'; + +const useStyles = makeStyles({ + root: { + height: 216, + flexGrow: 1, + maxWidth: 400, + }, +}); + +export function ControlledTreeView() { + const classes = useStyles(); + const [expanded, setExpanded] = React.useState([]); + const [selected, setSelected] = React.useState([]); + + const handleToggle = (event: React.ChangeEvent<{}>, nodeIds: string[]) => { + setExpanded(nodeIds); + }; + + const handleSelect = (event: React.ChangeEvent<{}>, nodeIds: string[]) => { + setSelected(nodeIds); + }; + + return ( + } + defaultExpandIcon={} + expanded={expanded} + selected={selected} + onNodeToggle={handleToggle} + onNodeSelect={handleSelect} + > + + + + + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|tree-view|ControlledTreeView.stories" +}; diff --git a/examples/storybook/stories/material-ui/tree-view/CustomizedTreeView.stories.tsx b/examples/storybook/stories/material-ui/tree-view/CustomizedTreeView.stories.tsx new file mode 100644 index 00000000000000..62d19cce934830 --- /dev/null +++ b/examples/storybook/stories/material-ui/tree-view/CustomizedTreeView.stories.tsx @@ -0,0 +1,106 @@ +import React from 'react'; +import SvgIcon, { SvgIconProps } from '@material-ui/core/SvgIcon'; +import { fade, makeStyles, withStyles, Theme, createStyles } from '@material-ui/core/styles'; +import TreeView from '@material-ui/lab/TreeView'; +import TreeItem, { TreeItemProps } from '@material-ui/lab/TreeItem'; +import Collapse from '@material-ui/core/Collapse'; +import { useSpring, animated } from 'react-spring/web.cjs'; // web.cjs is required for IE 11 support +import { TransitionProps } from '@material-ui/core/transitions'; + +function MinusSquare(props: SvgIconProps) { + return ( + + {/* tslint:disable-next-line: max-line-length */} + + + ); +} + +function PlusSquare(props: SvgIconProps) { + return ( + + {/* tslint:disable-next-line: max-line-length */} + + + ); +} + +function CloseSquare(props: SvgIconProps) { + return ( + + {/* tslint:disable-next-line: max-line-length */} + + + ); +} + +function TransitionComponent(props: TransitionProps) { + const style = useSpring({ + from: { opacity: 0, transform: 'translate3d(20px,0,0)' }, + to: { opacity: props.in ? 1 : 0, transform: `translate3d(${props.in ? 0 : 20}px,0,0)` }, + }); + + return ( + + + + ); +} + +const StyledTreeItem = withStyles((theme: Theme) => + createStyles({ + iconContainer: { + '& .close': { + opacity: 0.3, + }, + }, + group: { + marginLeft: 7, + paddingLeft: 18, + borderLeft: `1px dashed ${fade(theme.palette.text.primary, 0.4)}`, + }, + }), +)((props: TreeItemProps) => ); + +const useStyles = makeStyles( + createStyles({ + root: { + height: 264, + flexGrow: 1, + maxWidth: 400, + }, + }), +); + +export function CustomizedTreeView() { + const classes = useStyles(); + + return ( + } + defaultExpandIcon={} + defaultEndIcon={} + > + + + + + + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|tree-view|CustomizedTreeView.stories" +}; diff --git a/examples/storybook/stories/material-ui/tree-view/FileSystemNavigator.stories.tsx b/examples/storybook/stories/material-ui/tree-view/FileSystemNavigator.stories.tsx new file mode 100644 index 00000000000000..d06b996c507536 --- /dev/null +++ b/examples/storybook/stories/material-ui/tree-view/FileSystemNavigator.stories.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import TreeView from '@material-ui/lab/TreeView'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import ChevronRightIcon from '@material-ui/icons/ChevronRight'; +import TreeItem from '@material-ui/lab/TreeItem'; + +const useStyles = makeStyles({ + root: { + height: 240, + flexGrow: 1, + maxWidth: 400, + }, +}); + +export function FileSystemNavigator() { + const classes = useStyles(); + + return ( + } + defaultExpandIcon={} + > + + + + + + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|tree-view|FileSystemNavigator.stories" +}; diff --git a/examples/storybook/stories/material-ui/tree-view/GmailTreeView.stories.tsx b/examples/storybook/stories/material-ui/tree-view/GmailTreeView.stories.tsx new file mode 100644 index 00000000000000..23007ff6f2df22 --- /dev/null +++ b/examples/storybook/stories/material-ui/tree-view/GmailTreeView.stories.tsx @@ -0,0 +1,182 @@ +import React from 'react'; +import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; +import TreeView from '@material-ui/lab/TreeView'; +import TreeItem, { TreeItemProps } from '@material-ui/lab/TreeItem'; +import Typography from '@material-ui/core/Typography'; +import MailIcon from '@material-ui/icons/Mail'; +import DeleteIcon from '@material-ui/icons/Delete'; +import Label from '@material-ui/icons/Label'; +import SupervisorAccountIcon from '@material-ui/icons/SupervisorAccount'; +import InfoIcon from '@material-ui/icons/Info'; +import ForumIcon from '@material-ui/icons/Forum'; +import LocalOfferIcon from '@material-ui/icons/LocalOffer'; +import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; +import ArrowRightIcon from '@material-ui/icons/ArrowRight'; +import { SvgIconProps } from '@material-ui/core/SvgIcon'; + +declare module 'csstype' { + interface Properties { + '--tree-view-color'?: string; + '--tree-view-bg-color'?: string; + } +} + +type StyledTreeItemProps = TreeItemProps & { + bgColor?: string; + color?: string; + labelIcon: React.ElementType; + labelInfo?: string; + labelText: string; +}; + +const useTreeItemStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + color: theme.palette.text.secondary, + '&:hover > $content': { + backgroundColor: theme.palette.action.hover, + }, + '&:focus > $content, &$selected > $content': { + backgroundColor: `var(--tree-view-bg-color, ${theme.palette.grey[400]})`, + color: 'var(--tree-view-color)', + }, + '&:focus > $content $label, &:hover > $content $label, &$selected > $content $label': { + backgroundColor: 'transparent', + }, + }, + content: { + color: theme.palette.text.secondary, + borderTopRightRadius: theme.spacing(2), + borderBottomRightRadius: theme.spacing(2), + paddingRight: theme.spacing(1), + fontWeight: theme.typography.fontWeightMedium, + '$expanded > &': { + fontWeight: theme.typography.fontWeightRegular, + }, + }, + group: { + marginLeft: 0, + '& $content': { + paddingLeft: theme.spacing(2), + }, + }, + expanded: {}, + selected: {}, + label: { + fontWeight: 'inherit', + color: 'inherit', + }, + labelRoot: { + display: 'flex', + alignItems: 'center', + padding: theme.spacing(0.5, 0), + }, + labelIcon: { + marginRight: theme.spacing(1), + }, + labelText: { + fontWeight: 'inherit', + flexGrow: 1, + }, + }), +); + +function StyledTreeItem(props: StyledTreeItemProps) { + const classes = useTreeItemStyles(); + const { labelText, labelIcon: LabelIcon, labelInfo, color, bgColor, ...other } = props; + + return ( + + + + {labelText} + + + {labelInfo} + +
    + } + style={{ + '--tree-view-color': color, + '--tree-view-bg-color': bgColor, + }} + classes={{ + root: classes.root, + content: classes.content, + expanded: classes.expanded, + selected: classes.selected, + group: classes.group, + label: classes.label, + }} + {...other} + /> + ); +} + +const useStyles = makeStyles( + createStyles({ + root: { + height: 264, + flexGrow: 1, + maxWidth: 400, + }, + }), +); + +export function GmailTreeView() { + const classes = useStyles(); + + return ( + } + defaultExpandIcon={} + defaultEndIcon={
    } + > + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|tree-view|GmailTreeView.stories" +}; diff --git a/examples/storybook/stories/material-ui/tree-view/MultiSelectTreeView.stories.tsx b/examples/storybook/stories/material-ui/tree-view/MultiSelectTreeView.stories.tsx new file mode 100644 index 00000000000000..a474113b343bbf --- /dev/null +++ b/examples/storybook/stories/material-ui/tree-view/MultiSelectTreeView.stories.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import TreeView from '@material-ui/lab/TreeView'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import ChevronRightIcon from '@material-ui/icons/ChevronRight'; +import TreeItem from '@material-ui/lab/TreeItem'; + +const useStyles = makeStyles({ + root: { + height: 216, + flexGrow: 1, + maxWidth: 400, + }, +}); + +export function MultiSelectTreeView() { + const classes = useStyles(); + + return ( + } + defaultExpandIcon={} + multiSelect + > + + + + + + + + + + + + + + + ); +} + +export default { + title: "Material-ui|tree-view|MultiSelectTreeView.stories" +}; diff --git a/examples/storybook/stories/material-ui/tree-view/RecursiveTreeView.stories.tsx b/examples/storybook/stories/material-ui/tree-view/RecursiveTreeView.stories.tsx new file mode 100644 index 00000000000000..4e141e848327a8 --- /dev/null +++ b/examples/storybook/stories/material-ui/tree-view/RecursiveTreeView.stories.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import TreeView from '@material-ui/lab/TreeView'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import ChevronRightIcon from '@material-ui/icons/ChevronRight'; +import TreeItem from '@material-ui/lab/TreeItem'; + +interface RenderTree { + id: string; + name: string; + children?: RenderTree[]; +} + +const data: RenderTree = { + id: 'root', + name: 'Parent', + children: [ + { + id: '1', + name: 'Child - 1', + }, + { + id: '3', + name: 'Child - 3', + children: [ + { + id: '4', + name: 'Child - 4', + }, + ], + }, + ], +}; + +const useStyles = makeStyles({ + root: { + height: 110, + flexGrow: 1, + maxWidth: 400, + }, +}); + +export function RecursiveTreeView() { + const classes = useStyles(); + + const renderTree = (nodes: RenderTree) => ( + + {Array.isArray(nodes.children) ? nodes.children.map((node) => renderTree(node)) : null} + + ); + + return ( + } + defaultExpanded={['root']} + defaultExpandIcon={} + > + {renderTree(data)} + + ); +} + +export default { + title: "Material-ui|tree-view|RecursiveTreeView.stories" +}; diff --git a/examples/storybook/stories/material-ui/typography/Types.stories.tsx b/examples/storybook/stories/material-ui/typography/Types.stories.tsx new file mode 100644 index 00000000000000..0a1cba8beb44b9 --- /dev/null +++ b/examples/storybook/stories/material-ui/typography/Types.stories.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles({ + root: { + width: '100%', + maxWidth: 500, + }, +}); + +export function Types() { + const classes = useStyles(); + + return ( +
    + + h1. Heading + + + h2. Heading + + + h3. Heading + + + h4. Heading + + + h5. Heading + + + h6. Heading + + + subtitle1. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos blanditiis tenetur + + + subtitle2. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos blanditiis tenetur + + + body1. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos blanditiis tenetur + unde suscipit, quam beatae rerum inventore consectetur, neque doloribus, cupiditate numquam + dignissimos laborum fugiat deleniti? Eum quasi quidem quibusdam. + + + body2. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos blanditiis tenetur + unde suscipit, quam beatae rerum inventore consectetur, neque doloribus, cupiditate numquam + dignissimos laborum fugiat deleniti? Eum quasi quidem quibusdam. + + + button text + + + caption text + + + overline text + +
    + ); +} + +export default { + title: "Material-ui|typography|Types.stories" +}; diff --git a/examples/storybook/stories/material-ui/typography/TypographyTheme.stories.tsx b/examples/storybook/stories/material-ui/typography/TypographyTheme.stories.tsx new file mode 100644 index 00000000000000..e404836c4a28bd --- /dev/null +++ b/examples/storybook/stories/material-ui/typography/TypographyTheme.stories.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + ...theme.typography.button, + backgroundColor: theme.palette.background.paper, + padding: theme.spacing(1), + }, + }), +); + +export function TypographyTheme() { + const classes = useStyles(); + + return
    {"This div's text looks like that of a button."}
    ; +} + +export default { + title: "Material-ui|typography|TypographyTheme.stories" +}; diff --git a/examples/storybook/stories/material-ui/use-media-query/JavaScriptMedia.stories.tsx b/examples/storybook/stories/material-ui/use-media-query/JavaScriptMedia.stories.tsx new file mode 100644 index 00000000000000..4a8a5ef1729c9a --- /dev/null +++ b/examples/storybook/stories/material-ui/use-media-query/JavaScriptMedia.stories.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import json2mq from 'json2mq'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; + +export function JavaScriptMedia() { + const matches = useMediaQuery( + json2mq({ + minWidth: 600, + }), + ); + + return {`{ minWidth: 600 } matches: ${matches}`}; +} + +export default { + title: "Material-ui|use-media-query|JavaScriptMedia.stories" +}; diff --git a/examples/storybook/stories/material-ui/use-media-query/ServerSide.stories.tsx b/examples/storybook/stories/material-ui/use-media-query/ServerSide.stories.tsx new file mode 100644 index 00000000000000..1e89e108d33622 --- /dev/null +++ b/examples/storybook/stories/material-ui/use-media-query/ServerSide.stories.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import mediaQuery from 'css-mediaquery'; +import { ThemeProvider, Theme } from '@material-ui/core/styles'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; + +function MyComponent() { + const matches = useMediaQuery('(min-width:600px)'); + + return {`(min-width:600px) matches: ${matches}`}; +} + +export function ServerSide() { + const ssrMatchMedia = (query: string) => ({ + matches: mediaQuery.match(query, { + // The estimated CSS width of the browser. + width: 800, + }), + }); + + return ( + + theme={{ + props: { + // Change the default options of useMediaQuery + MuiUseMediaQuery: { ssrMatchMedia }, + }, + }} + > + + + ); +} + +export default { + title: "Material-ui|use-media-query|ServerSide.stories" +}; diff --git a/examples/storybook/stories/material-ui/use-media-query/SimpleMediaQuery.stories.tsx b/examples/storybook/stories/material-ui/use-media-query/SimpleMediaQuery.stories.tsx new file mode 100644 index 00000000000000..146770baeaf9d1 --- /dev/null +++ b/examples/storybook/stories/material-ui/use-media-query/SimpleMediaQuery.stories.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; + +export function SimpleMediaQuery() { + const matches = useMediaQuery('(min-width:600px)'); + + return {`(min-width:600px) matches: ${matches}`}; +} + +export default { + title: "Material-ui|use-media-query|SimpleMediaQuery.stories" +}; diff --git a/examples/storybook/stories/material-ui/use-media-query/ThemeHelper.stories.tsx b/examples/storybook/stories/material-ui/use-media-query/ThemeHelper.stories.tsx new file mode 100644 index 00000000000000..18d9c610bb5bfb --- /dev/null +++ b/examples/storybook/stories/material-ui/use-media-query/ThemeHelper.stories.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { createMuiTheme, ThemeProvider, useTheme } from '@material-ui/core/styles'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; + +function MyComponent() { + const theme = useTheme(); + const matches = useMediaQuery(theme.breakpoints.up('sm')); + + return {`theme.breakpoints.up('sm') matches: ${matches}`}; +} + +const theme = createMuiTheme(); + +export function ThemeHelper() { + return ( + + + + ); +} + +export default { + title: "Material-ui|use-media-query|ThemeHelper.stories" +}; diff --git a/examples/storybook/stories/material-ui/use-media-query/UseWidth.stories.tsx b/examples/storybook/stories/material-ui/use-media-query/UseWidth.stories.tsx new file mode 100644 index 00000000000000..abf581792c62d1 --- /dev/null +++ b/examples/storybook/stories/material-ui/use-media-query/UseWidth.stories.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { Theme, ThemeProvider, useTheme, createMuiTheme } from '@material-ui/core/styles'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; +import { Breakpoint } from '@material-ui/core/styles/createBreakpoints'; + +type BreakpointOrNull = Breakpoint | null; + +/** + * Be careful using this hook. It only works because the number of + * breakpoints in theme is static. It will break once you change the number of + * breakpoints. See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level + */ +function useWidth() { + const theme: Theme = useTheme(); + const keys: Breakpoint[] = [...theme.breakpoints.keys].reverse(); + return ( + keys.reduce((output: BreakpointOrNull, key: Breakpoint) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const matches = useMediaQuery(theme.breakpoints.up(key)); + return !output && matches ? key : output; + }, null) || 'xs' + ); +} + +function MyComponent() { + const width = useWidth(); + return {`width: ${width}`}; +} + +const theme = createMuiTheme(); + +export function UseWidth() { + return ( + + + + ); +} + +export default { + title: "Material-ui|use-media-query|UseWidth.stories" +};