diff --git a/packages/core/src/layout/ContentHeader/ContentHeader.js b/packages/core/src/layout/ContentHeader/ContentHeader.js
deleted file mode 100644
index dac44354a4..0000000000
--- a/packages/core/src/layout/ContentHeader/ContentHeader.js
+++ /dev/null
@@ -1,102 +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, Fragment } from 'react';
-import { Typography, withStyles } from '@material-ui/core';
-import Helmet from 'react-helmet';
-// import FavoriteButton from 'shared/components/layout/FavoriteButton';
-
-const styles = theme => ({
- container: {
- width: '100%',
- display: 'flex',
- flexDirection: 'row',
- flexWrap: 'wrap',
- justifyContent: 'flex-end',
- alignItems: 'center',
- },
- leftItemsBox: {
- flex: '1 1 auto',
- marginBottom: theme.spacing(1),
- minWidth: 0,
- overflow: 'visible',
- },
- rightItemsBox: {
- flex: '0 1 auto',
- display: 'flex',
- flexDirection: 'row',
- flexWrap: 'wrap',
- alignItems: 'center',
- marginLeft: theme.spacing(1),
- marginBottom: theme.spacing(1),
- minWidth: 0,
- overflow: 'visible',
- },
- description: {},
- title: {
- display: 'inline-flex',
- },
-});
-
-class ContentHeader extends Component {
- static defaultProps = {
- favoriteable: true,
- title: 'Unknown page',
- titleComponent: undefined,
- };
-
- render() {
- const {
- title,
- description,
- /* favoriteable,*/ children,
- classes,
- } = this.props;
- const TitleComponent = this.props.titleComponent;
- const renderedTitle =
- TitleComponent !== undefined ? (
-
- ) : (
-
- {title}
-
- );
-
- return (
-
-
-
-
- {renderedTitle}
- {/* favoriteable && */}
- {description && (
-
- {description}
-
- )}
-
-
{children}
-
-
- );
- }
-}
-
-export default withStyles(styles)(ContentHeader);
diff --git a/packages/core/src/layout/ContentHeader/ContentHeader.test.tsx b/packages/core/src/layout/ContentHeader/ContentHeader.test.tsx
new file mode 100644
index 0000000000..50b4f921ac
--- /dev/null
+++ b/packages/core/src/layout/ContentHeader/ContentHeader.test.tsx
@@ -0,0 +1,49 @@
+/*
+ * 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 from 'react';
+import { render } from '@testing-library/react';
+import ContentHeader from './ContentHeader';
+import { wrapInThemedTestApp } from '../../testUtils';
+
+jest.mock('react-helmet', () => {
+ return ({ defaultTitle }) =>
defaultTitle: {defaultTitle}
;
+});
+
+describe(' ', () => {
+ it('should render with title', () => {
+ const rendered = render(
+ wrapInThemedTestApp( ),
+ );
+ rendered.getByText('Title');
+ });
+
+ it('should render with titleComponent', () => {
+ const title = 'Custom title';
+ const titleComponent = () => {title} ;
+ const rendered = render(
+ wrapInThemedTestApp( ),
+ );
+ rendered.getByText(title);
+ });
+
+ it('should render with description', () => {
+ const rendered = render(
+ wrapInThemedTestApp( ),
+ );
+ rendered.getByText('description');
+ });
+});
diff --git a/packages/core/src/layout/ContentHeader/ContentHeader.tsx b/packages/core/src/layout/ContentHeader/ContentHeader.tsx
new file mode 100644
index 0000000000..0f83f54c25
--- /dev/null
+++ b/packages/core/src/layout/ContentHeader/ContentHeader.tsx
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+/**
+ * TODO favoriteable capability
+ */
+
+import React, { ComponentType, Fragment, FC } from 'react';
+import { Typography, makeStyles } from '@material-ui/core';
+import Helmet from 'react-helmet';
+import { BackstageTheme } from '../../theme/theme';
+
+const useStyles = makeStyles(theme => ({
+ container: {
+ width: '100%',
+ display: 'flex',
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ justifyContent: 'flex-end',
+ alignItems: 'center',
+ },
+ leftItemsBox: {
+ flex: '1 1 auto',
+ marginBottom: theme.spacing(1),
+ minWidth: 0,
+ overflow: 'visible',
+ },
+ rightItemsBox: {
+ flex: '0 1 auto',
+ display: 'flex',
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ alignItems: 'center',
+ marginLeft: theme.spacing(1),
+ marginBottom: theme.spacing(1),
+ minWidth: 0,
+ overflow: 'visible',
+ },
+ description: {},
+ title: {
+ display: 'inline-flex',
+ },
+}));
+
+type DefaultTitleProps = {
+ title?: string;
+ className: string;
+};
+
+const DefaultTitle: FC = ({
+ title = 'Unknown page',
+ className,
+}) => (
+
+ {title}
+
+);
+
+type ContentHeaderProps = {
+ title?: DefaultTitleProps['title'];
+ titleComponent?: ComponentType;
+ description?: string;
+};
+
+const ContentHeader: FC = ({
+ description,
+ title,
+ titleComponent: TitleComponent = undefined,
+ children,
+}) => {
+ const classes = useStyles();
+
+ const renderedTitle = TitleComponent ? (
+
+ ) : (
+
+ );
+ return (
+
+
+
+
+ {renderedTitle}
+ {description && (
+
+ {description}
+
+ )}
+
+
{children}
+
+
+ );
+};
+
+export default ContentHeader;
diff --git a/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.test.js b/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.test.tsx
similarity index 96%
rename from packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.test.js
rename to packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.test.tsx
index 1b90cad6f8..3a32d9371e 100644
--- a/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.test.js
+++ b/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.test.tsx
@@ -41,7 +41,7 @@ describe(' ', () => {
'aria-disabled',
'true',
);
- fireEvent.click(rendered.queryByText('Some label'));
+ fireEvent.click(rendered.queryByText('Some label') as Node);
expect(onClickFunction).toHaveBeenCalled();
// We do not expect the dropdown to disappear after click
expect(rendered.queryByText('Some label')).toBeInTheDocument();
@@ -84,7 +84,7 @@ describe(' ', () => {
expect(onClickFunction).not.toHaveBeenCalled();
fireEvent.click(rendered.getByTestId('header-action-menu'));
expect(onClickFunction).not.toHaveBeenCalled();
- fireEvent.click(rendered.queryByText('Secondary label'));
+ fireEvent.click(rendered.queryByText('Secondary label') as Node);
expect(onClickFunction).toHaveBeenCalled();
// We do not expect the dropdown to disappear after click
expect(rendered.queryByText('Some label')).toBeInTheDocument();
diff --git a/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.js b/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.tsx
similarity index 75%
rename from packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.js
rename to packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.tsx
index 409338caeb..4799654cee 100644
--- a/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.js
+++ b/packages/core/src/layout/HeaderActionMenu/HeaderActionMenu.tsx
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import React, { Fragment } from 'react';
+import React, { Fragment, ReactElement, FC, ComponentType } from 'react';
import {
IconButton,
List,
@@ -22,10 +22,20 @@ import {
ListItemIcon,
ListItemText,
Popover,
+ ListItemTextProps,
} from '@material-ui/core';
import { default as KebabMenuIcon } from './MenuVertical';
-const ActionItem = ({
+type ActionItemProps = {
+ label?: ListItemTextProps['primary'];
+ secondaryLabel?: ListItemTextProps['secondary'];
+ icon?: ReactElement;
+ disabled?: boolean;
+ onClick?: (event: React.MouseEvent) => void;
+ WrapperComponent?: ComponentType;
+};
+
+const ActionItem: FC = ({
label,
secondaryLabel,
icon,
@@ -52,7 +62,11 @@ const ActionItem = ({
);
};
-const HeaderActionMenu = ({ actionItems }) => {
+export type HeaderActionMenuProps = {
+ actionItems: ActionItemProps[];
+};
+
+const HeaderActionMenu: FC = ({ actionItems }) => {
const [open, setOpen] = React.useState(false);
const anchorElRef = React.useRef(null);
@@ -80,8 +94,10 @@ const HeaderActionMenu = ({ actionItems }) => {
onClose={() => setOpen(false)}
>
- {actionItems.map(actionItem => {
- return ;
+ {actionItems.map((actionItem, i) => {
+ return (
+
+ );
})}
diff --git a/packages/core/src/layout/HeaderLabel/HeaderLabel.test.js b/packages/core/src/layout/HeaderLabel/HeaderLabel.test.tsx
similarity index 83%
rename from packages/core/src/layout/HeaderLabel/HeaderLabel.test.js
rename to packages/core/src/layout/HeaderLabel/HeaderLabel.test.tsx
index 2d9e97bb51..727c890e35 100644
--- a/packages/core/src/layout/HeaderLabel/HeaderLabel.test.js
+++ b/packages/core/src/layout/HeaderLabel/HeaderLabel.test.tsx
@@ -30,6 +30,13 @@ describe(' ', () => {
expect(rendered.getByText('')).toBeInTheDocument();
});
+ it('should say unknown when passing null as value prop', () => {
+ const rendered = render(
+ wrapInThemedTestApp( ),
+ );
+ expect(rendered.getByText('')).toBeInTheDocument();
+ });
+
it('should have value', () => {
const rendered = render(
wrapInThemedTestApp( ),
@@ -43,7 +50,7 @@ describe(' ', () => {
,
),
);
- const anchor = rendered.container.querySelector('a');
+ const anchor = rendered.container.querySelector('a') as HTMLAnchorElement;
expect(rendered.getByText('Value')).toBeInTheDocument();
expect(anchor.href).toBe('http://localhost/test');
});
diff --git a/packages/core/src/layout/HeaderLabel/HeaderLabel.js b/packages/core/src/layout/HeaderLabel/HeaderLabel.tsx
similarity index 50%
rename from packages/core/src/layout/HeaderLabel/HeaderLabel.js
rename to packages/core/src/layout/HeaderLabel/HeaderLabel.tsx
index 23c3f9507a..4349399194 100644
--- a/packages/core/src/layout/HeaderLabel/HeaderLabel.js
+++ b/packages/core/src/layout/HeaderLabel/HeaderLabel.tsx
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { Typography, withStyles } from '@material-ui/core';
+import React, { FC } from 'react';
+import { Typography, makeStyles } from '@material-ui/core';
import { Link } from '@material-ui/core';
+import { BackstageTheme } from '../../theme/theme';
-const style = theme => ({
+const useStyles = makeStyles(theme => ({
root: {
textAlign: 'left',
margin: theme.spacing(2),
@@ -40,27 +40,38 @@ const style = theme => ({
fontSize: 14,
height: '16px',
},
-});
+}));
-class HeaderLabel extends Component {
- static propTypes = {
- label: PropTypes.string.isRequired,
- value: PropTypes.node,
- url: PropTypes.string,
- };
+type HeaderLabelContentProps = {
+ value: React.ReactNode;
+ className: string;
+};
- render() {
- const { label, value, url, classes } = this.props;
- const content = (
- {value || ''}
- );
- return (
-
- {label}
- {url ? {content} : content}
-
- );
- }
-}
+const HeaderLabelContent: FC = ({
+ value,
+ className,
+}) => {value} ;
-export default withStyles(style)(HeaderLabel);
+type HeaderLabelProps = {
+ label: string;
+ value?: HeaderLabelContentProps['value'];
+ url?: string;
+};
+
+const HeaderLabel: FC = ({ label, value, url }) => {
+ const classes = useStyles();
+ const content = (
+ '}
+ />
+ );
+ return (
+
+ {label}
+ {url ? {content} : content}
+
+ );
+};
+
+export default HeaderLabel;