diff --git a/packages/core/src/layout/InfoCard/BottomLink.js b/packages/core/src/layout/InfoCard/BottomLink.js deleted file mode 100644 index 5dafa72978..0000000000 --- a/packages/core/src/layout/InfoCard/BottomLink.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { Link } from '@material-ui/core'; -import { Divider, ListItemText, withStyles } from '@material-ui/core'; -import { ListItem, ListItemIcon } from '@material-ui/core'; -import ArrowIcon from '@material-ui/icons/ArrowForward'; -import grey from '@material-ui/core/colors/grey'; -import Box from '@material-ui/core/Box'; - -const styles = theme => ({ - root: { - maxWidth: 'fit-content', - padding: theme.spacing(2, 2, 2, 2.5) - }, - boxTitle: { - margin: 0, - color: grey[900] - } -}) -class BottomLink extends Component { - static propTypes = { - link: PropTypes.string, - title: PropTypes.string, - onClick: PropTypes.func, - }; - - render() { - const { link, title, onClick, classes } = this.props; - return ( -
- - - - - {title} - - - - - - -
- ); - } -} - -export default withStyles(styles)(BottomLink); \ No newline at end of file diff --git a/packages/core/src/layout/InfoCard/BottomLink.test.js b/packages/core/src/layout/InfoCard/BottomLink.test.tsx similarity index 100% rename from packages/core/src/layout/InfoCard/BottomLink.test.js rename to packages/core/src/layout/InfoCard/BottomLink.test.tsx diff --git a/packages/core/src/layout/InfoCard/BottomLink.tsx b/packages/core/src/layout/InfoCard/BottomLink.tsx new file mode 100644 index 0000000000..bf82f6cd11 --- /dev/null +++ b/packages/core/src/layout/InfoCard/BottomLink.tsx @@ -0,0 +1,65 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { FC } from 'react'; +import { Link } from '@material-ui/core'; +import { Divider, ListItemText, makeStyles } from '@material-ui/core'; +import { ListItem, ListItemIcon } from '@material-ui/core'; +import ArrowIcon from '@material-ui/icons/ArrowForward'; +import grey from '@material-ui/core/colors/grey'; +import Box from '@material-ui/core/Box'; +import { BackstageTheme } from '../../theme/theme'; + +const useStyles = makeStyles(theme => ({ + root: { + maxWidth: 'fit-content', + padding: theme.spacing(2, 2, 2, 2.5), + }, + boxTitle: { + margin: 0, + color: grey[900], + }, +})); + +export type Props = { + link: string; + title: string; + onClick?: (event: React.MouseEvent) => void; +}; + +const BottomLink: FC = ({ link, title, onClick }) => { + const classes = useStyles(); + + return ( +
+ + + + + + {title} + + + + + + + +
+ ); +}; + +export default BottomLink; diff --git a/packages/core/src/layout/InfoCard/InfoCard.js b/packages/core/src/layout/InfoCard/InfoCard.js deleted file mode 100644 index 1005a016af..0000000000 --- a/packages/core/src/layout/InfoCard/InfoCard.js +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { - Card, - CardActions, - CardContent, - CardHeader, - Divider, - withStyles, -} from '@material-ui/core'; -import ErrorBoundary from '../ErrorBoundary/ErrorBoundary'; -import BottomLink from './BottomLink'; - -import textContent from 'react-addons-text-content'; - -const styles = theme => ({ - header: { - padding: theme.spacing(2, 2, 2, 2.5) - } -}); - -const BoldHeader = withStyles({ - title: { fontWeight: '700' }, - subheader: { paddingTop: '2px' }, -})(CardHeader); - -const CardActionsTopRight = withStyles({ - root: { - display: 'inline-block', - paddingRight: '16px', - paddingTop: '16px', - float: 'right', - }, -})(CardActions); - -const VARIANT_STYLES = { - card: { - flex: { - display: 'flex', - flexDirection: 'column', - }, - widget: { - height: 430, - }, - fullHeight: { - height: '100%', - }, - height100: { - display: 'flex', - flexDirection: 'column', - height: 'calc(100% - 10px)', // for pages without content header - marginBottom: '10px', - }, - contentheader: { - height: 'calc(100% - 40px)', // for pages with content header - }, - contentheadertabs: { - height: 'calc(100% - 97px)', // for pages with content header and tabs (Tingle) - }, - noShrink: { - flexShrink: 0, - }, - minheight300: { - minHeight: 300, - overflow: 'initial', - }, - }, - cardContent: { - widget: { - overflowY: 'auto', - height: 332, - width: '100%', - }, - fullHeight: { - height: 'calc(100% - 50px)', - }, - height100: { - height: 'calc(100% - 50px)', - }, - contentRow: { - display: 'flex', - flexDirection: 'row', - }, - }, -}; - -/** - * InfoCard is used to display a paper-styled block on the screen, similar to a panel. - * - * You can custom style an InfoCard with the 'style' (outer container) and 'cardStyle' (inner container) - * styles. - * - * The InfoCard serves as an error boundary. As a result, if you provide a 'slackChannel' property this - * specifies the channel to display in the error component that is displayed if an error occurs - * in any descendent components. - * - * By default the InfoCard has no custom layout of its children, but is treated as a block element. A - * couple common variants are provided and can be specified via the variant property: - * - * Display the card full height suitable for DataGrid: - * - * ... - * - * Variants can be combined in a whitespace delimited list like so: - * - * ... - */ -class InfoCard extends Component { - static propTypes = { - title: PropTypes.oneOfType([PropTypes.element, PropTypes.string]), - subheader: PropTypes.oneOfType([PropTypes.node, PropTypes.string]), - divider: PropTypes.bool, - deepLink: PropTypes.object, - slackChannel: PropTypes.string, - variant: PropTypes.string, - }; - - render() { - const { - title, - subheader, - divider, - deepLink, - children, - actions, - actionsTopRight, - headerStyle, - headerProps, - classes, - slackChannel, - variant, - } = this.props; - - if (this.props.style) { - // eslint-disable-next-line no-console - console.warn( - 'InfoCard: using `style` property directly, consider migrating your style to variant in InfoCard', - ); - } - if (this.props.cardStyle) { - // eslint-disable-next-line no-console - console.warn( - 'InfoCard: using `cardStyle` property directly, consider migrating your style to variant in InfoCard', - ); - } - - /** - * If variant is specified, we build up styles for that particular variant for both - * the Card and the CardContent (since these need to be synced) - */ - let calculatedStyle = {}; - let calculatedCardStyle = {}; - - if (variant) { - const variants = variant.split(/[\s]+/g); - variants.forEach(name => { - calculatedStyle = { ...calculatedStyle, ...VARIANT_STYLES.card[name] }; - calculatedCardStyle = { - ...calculatedCardStyle, - ...VARIANT_STYLES.cardContent[name], - }; - }); - } - - // Apply the passed styles on top - const computedStyle = { ...calculatedStyle, ...this.props.style }; - const computedCardStyle = { - ...calculatedCardStyle, - ...this.props.cardStyle, - }; - - return ( - - - {title && ( - <> - - - - )} - {actionsTopRight && ( - {actionsTopRight} - )} - {divider && } - - {children} - - {actions && ( - - {actions} - - )} - {deepLink && } - - - ); - } -} - -export default withStyles(styles)(InfoCard); diff --git a/packages/core/src/layout/InfoCard/InfoCard.test.js b/packages/core/src/layout/InfoCard/InfoCard.test.tsx similarity index 100% rename from packages/core/src/layout/InfoCard/InfoCard.test.js rename to packages/core/src/layout/InfoCard/InfoCard.test.tsx diff --git a/packages/core/src/layout/InfoCard/InfoCard.tsx b/packages/core/src/layout/InfoCard/InfoCard.tsx new file mode 100644 index 0000000000..5b3ab1cfd1 --- /dev/null +++ b/packages/core/src/layout/InfoCard/InfoCard.tsx @@ -0,0 +1,206 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { FC, ReactNode } from 'react'; +import { + Card, + CardActions, + CardContent, + CardHeader, + Divider, + withStyles, + makeStyles, +} from '@material-ui/core'; +import ErrorBoundary from '../ErrorBoundary/ErrorBoundary'; +import BottomLink, { Props as BottomLinkProps } from './BottomLink'; +import { BackstageTheme } from '../../theme/theme'; + +const useStyles = makeStyles(theme => ({ + header: { + padding: theme.spacing(2, 2, 2, 2.5), + }, +})); + +const BoldHeader = withStyles(theme => ({ + title: { fontWeight: 700 }, + subheader: { paddingTop: theme.spacing(1) }, +}))(CardHeader); + +const CardActionsTopRight = withStyles(theme => ({ + root: { + display: 'inline-block', + padding: theme.spacing(8, 8, 0, 0), + float: 'right', + }, +}))(CardActions); + +const VARIANT_STYLES = { + card: { + flex: { + display: 'flex', + flexDirection: 'column', + }, + widget: { + height: 430, + }, + fullHeight: { + height: '100%', + }, + height100: { + display: 'flex', + flexDirection: 'column', + height: 'calc(100% - 10px)', // for pages without content header + marginBottom: '10px', + }, + contentheader: { + height: 'calc(100% - 40px)', // for pages with content header + }, + contentheadertabs: { + height: 'calc(100% - 97px)', // for pages with content header and tabs (Tingle) + }, + noShrink: { + flexShrink: 0, + }, + minheight300: { + minHeight: 300, + overflow: 'initial', + }, + }, + cardContent: { + widget: { + overflowY: 'auto', + height: 332, + width: '100%', + }, + fullHeight: { + height: 'calc(100% - 50px)', + }, + height100: { + height: 'calc(100% - 50px)', + }, + contentRow: { + display: 'flex', + flexDirection: 'row', + }, + }, +}; + +/** + * InfoCard is used to display a paper-styled block on the screen, similar to a panel. + * + * You can custom style an InfoCard with the 'style' (outer container) and 'cardStyle' (inner container) + * styles. + * + * The InfoCard serves as an error boundary. As a result, if you provide a 'slackChannel' property this + * specifies the channel to display in the error component that is displayed if an error occurs + * in any descendent components. + * + * By default the InfoCard has no custom layout of its children, but is treated as a block element. A + * couple common variants are provided and can be specified via the variant property: + * + * Display the card full height suitable for DataGrid: + * + * ... + * + * Variants can be combined in a whitespace delimited list like so: + * + * ... + */ +type Props = { + title?: ReactNode; + subheader?: ReactNode; + divider?: boolean; + deepLink?: BottomLinkProps; + slackChannel?: string; + variant?: string; + style?: object; + cardStyle?: object; + children?: ReactNode; + headerStyle?: object; + headerProps?: object; + actionsClassName?: string; + actions?: ReactNode; + cardClassName?: string; + actionsTopRight?: ReactNode; +}; + +const InfoCard: FC = ({ + title, + subheader, + divider, + deepLink, + slackChannel = '#backstage', + variant, + children, + headerStyle, + headerProps, + actionsClassName, + actions, + cardClassName, + actionsTopRight, +}) => { + const classes = useStyles(); + + /** + * If variant is specified, we build up styles for that particular variant for both + * the Card and the CardContent (since these need to be synced) + */ + let calculatedStyle = {}; + let calculatedCardStyle = {}; + + if (variant) { + const variants = variant.split(/[\s]+/g); + variants.forEach(name => { + calculatedStyle = { ...calculatedStyle, ...VARIANT_STYLES.card[name] }; + calculatedCardStyle = { + ...calculatedCardStyle, + ...VARIANT_STYLES.cardContent[name], + }; + }); + } + + return ( + + + {title && ( + <> + + + + )} + {actionsTopRight && ( + {actionsTopRight} + )} + {divider && } + + {children} + + {actions && ( + {actions} + )} + {deepLink && } + + + ); +}; + +export default InfoCard; diff --git a/packages/core/src/layout/InfoCard/index.js b/packages/core/src/layout/InfoCard/index.ts similarity index 100% rename from packages/core/src/layout/InfoCard/index.js rename to packages/core/src/layout/InfoCard/index.ts