Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 7 additions & 76 deletions src/react/src/__tests__/components/LocationTitle.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,6 @@ import { render, screen } from '@testing-library/react';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import ProductionLocationDetailsTitle from '../../components/ProductionLocation/Heading/LocationTitle/LocationTitle';

jest.mock('react-toastify', () => ({
toast: jest.fn(),
}));

jest.mock('../../components/CopySearch', () => {
function MockCopySearch({ children }) {
return <>{children}</>;
}
return MockCopySearch;
});

jest.mock('../../components/Contribute/DialogTooltip', () => {
function MockDialogTooltip({ childComponent }) {
return <>{childComponent}</>;
}
return MockDialogTooltip;
});

const theme = createMuiTheme();

const renderLocationTitle = (props = {}) =>
Expand All @@ -35,81 +17,30 @@ describe('ProductionLocation LocationTitle', () => {
renderLocationTitle({ data: null });

expect(screen.getByRole('heading', { level: 1 })).toBeInTheDocument();
expect(screen.getByRole('heading', { level: 2 })).toHaveTextContent(/OS ID:/);
expect(screen.getByText('Location Name')).toBeInTheDocument();
});

test('renders location name from data.properties.name', () => {
const data = {
properties: {
name: 'Test Facility Name',
os_id: 'CN2021250D1DTN7',
},
};

renderLocationTitle({ data });

expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('Test Facility Name');
});

test('renders OS ID from data.properties.os_id', () => {
const data = {
properties: {
name: 'Test Facility',
os_id: 'CN2021250D1DTN7',
},
};

renderLocationTitle({ data });

expect(screen.getByRole('heading', { level: 2 })).toHaveTextContent('OS ID: CN2021250D1DTN7');
});

test('shows Copy Link and Copy OS ID buttons when os_id is present', () => {
const data = {
properties: {
name: 'Test Facility',
os_id: 'CN2021250D1DTN7',
},
};

renderLocationTitle({ data });

expect(screen.getByRole('button', { name: /copy link/i })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /copy os id/i })).toBeInTheDocument();
expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent(
'Test Facility Name',
);
});

test('does not show copy buttons when os_id is missing', () => {
test('renders empty heading when data.properties.name is missing', () => {
const data = {
properties: {
name: 'Test Facility',
},
};

renderLocationTitle({ data });

expect(screen.queryByRole('button', { name: /copy link/i })).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: /copy os id/i })).not.toBeInTheDocument();
});

test('does not show copy buttons when data is null', () => {
renderLocationTitle({ data: null });

expect(screen.queryByRole('button', { name: /copy link/i })).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: /copy os id/i })).not.toBeInTheDocument();
});

test('renders info button for OS ID tooltip', () => {
const data = {
properties: {
name: 'Test',
os_id: 'US123',
},
properties: {},
};

renderLocationTitle({ data });

expect(
screen.getByRole('button', { name: /more information about os id/i }),
).toBeInTheDocument();
expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('');
});
});
78 changes: 78 additions & 0 deletions src/react/src/__tests__/components/OsIdBadge.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import ProductionLocationDetailsOsIdBadge from '../../components/ProductionLocation/Heading/osIdBadge/OsIdBadge';

jest.mock('react-toastify', () => ({
toast: jest.fn(),
}));

jest.mock('../../components/CopySearch', () => {
function MockCopySearch({ children }) {

Check warning on line 11 in src/react/src/__tests__/components/OsIdBadge.test.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'children' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=opensupplyhub_open-supply-hub&issues=AZy83Sa9TzgRrYV1OvD8&open=AZy83Sa9TzgRrYV1OvD8&pullRequest=901

Check warning on line 11 in src/react/src/__tests__/components/OsIdBadge.test.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Move function 'MockCopySearch' to the outer scope.

See more on https://sonarcloud.io/project/issues?id=opensupplyhub_open-supply-hub&issues=AZy83Sa9TzgRrYV1OvD7&open=AZy83Sa9TzgRrYV1OvD7&pullRequest=901
return <>{children}</>;
}
return MockCopySearch;
});

jest.mock('../../components/Contribute/DialogTooltip', () => {
function MockDialogTooltip({ childComponent }) {

Check warning on line 18 in src/react/src/__tests__/components/OsIdBadge.test.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'childComponent' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=opensupplyhub_open-supply-hub&issues=AZy83Sa9TzgRrYV1OvD-&open=AZy83Sa9TzgRrYV1OvD-&pullRequest=901

Check warning on line 18 in src/react/src/__tests__/components/OsIdBadge.test.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Move function 'MockDialogTooltip' to the outer scope.

See more on https://sonarcloud.io/project/issues?id=opensupplyhub_open-supply-hub&issues=AZy83Sa9TzgRrYV1OvD9&open=AZy83Sa9TzgRrYV1OvD9&pullRequest=901
return <>{childComponent}</>;
}
return MockDialogTooltip;
});

const theme = createMuiTheme();

const renderOsIdBadge = (props = {}) =>
render(
<MuiThemeProvider theme={theme}>
<ProductionLocationDetailsOsIdBadge
osId="CN2021250D1DTN7"
{...props}
/>
</MuiThemeProvider>,
);

describe('ProductionLocationDetailsOsIdBadge', () => {
test('renders without crashing', () => {
renderOsIdBadge({ osId: 'CN2021250D1DTN7' });

expect(screen.getByRole('heading', { level: 2 })).toBeInTheDocument();
});

test('renders OS ID label and value', () => {
renderOsIdBadge({ osId: 'CN2021250D1DTN7' });

expect(
screen.getByRole('heading', { level: 2 }),
).toHaveTextContent('OS ID: CN2021250D1DTN7');
});

test('renders info button for OS ID tooltip', () => {
renderOsIdBadge({ osId: 'CN2021250D1DTN7' });

expect(
screen.getByRole('button', {
name: /more information about os id/i,
}),
).toBeInTheDocument();
});

test('shows Copy Link and Copy OS ID buttons when osId is present', () => {
renderOsIdBadge({ osId: 'CN2021250D1DTN7' });

expect(
screen.getByRole('button', { name: /copy link/i }),
).toBeInTheDocument();
expect(
screen.getByRole('button', { name: /copy os id/i }),
).toBeInTheDocument();
});

test('does not show copy buttons when osId is empty', () => {
renderOsIdBadge({ osId: '' });

expect(screen.queryByRole('button', { name: /copy link/i })).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: /copy os id/i })).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ export default theme => {
return Object.freeze({
...typography,
root: {
marginTop: spacing * 3,
marginBottom: spacing * 3,
marginBottom: spacing * 2,
flexDirection: 'column',
padding: 0,
marginLeft: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default theme =>
borderRadius: 0,
display: 'flex',
justifyContent: 'flex-start',
marginBottom: theme.spacing.unit * 3,
marginBottom: theme.spacing.unit * 2,
},
contentContainer: {
width: '100%',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,116 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import IconButton from '@material-ui/core/IconButton';
import { withStyles } from '@material-ui/core/styles';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import { toast } from 'react-toastify';
import get from 'lodash/get';
import InfoIcon from '@material-ui/icons/Info';

import CopySearch from '../../../CopySearch';
import ContentCopyIcon from '../../../ContentCopyIcon';
import DialogTooltip from '../../../Contribute/DialogTooltip';

import productionLocationDetailsTitleStyles from './styles';

const OS_ID_TOOLTIP_TEXT =
'The OS ID is a free, unique identifier automatically assigned to each production location in OS Hub. Use it to track this location across systems, share it with partners, or reference it in compliance documentation.';
const OS_ID_LEARN_MORE_URL = 'https://info.opensupplyhub.org/resources/os-id';

const ProductionLocationDetailsTitle = ({ classes, data }) => {
const locationName = get(data, 'properties.name', '') || '';
const osId = get(data, 'properties.os_id', '') || '';

return (
<div className={classes.container}>
{/* h1: Page title per typographyStyles */}
<Typography component="span" className={classes.titleAccent}>
Location Name
</Typography>
<Typography
component="h1"
className={classes.title}
variant="headline"
>
{locationName}
</Typography>
<div className={classes.osIdRow}>
<span className={classes.osIdValueWithTooltip}>
<Typography
component="h2"
variant="title"
className={classes.osIdValue}
>
OS ID: {osId}
</Typography>
<DialogTooltip
text={OS_ID_TOOLTIP_TEXT}
textHref={
<p style={{ marginTop: 8, marginBottom: 0 }}>
<a
href={OS_ID_LEARN_MORE_URL}
target="_blank"
rel="noopener noreferrer"
style={{ color: 'white' }}
>
Learn more →
</a>
</p>
}
interactive
childComponent={
<IconButton
className={classes.osIdInfoButton}
size="small"
aria-label="More information about OS ID"
disableRipple
>
<InfoIcon style={{ width: 16, height: 16 }} />
</IconButton>
}
/>
</span>
{osId && (
<span className={classes.osIdActions}>
<span
className={`${classes.copyButtonWrap} ${classes.copyButtonWrapFirst}`}
>
<CopySearch toastText="Copied link">
<Button
variant="outlined"
size="small"
className={classes.copyButton}
aria-label="Copy link"
>
<ContentCopyIcon />
<span className={classes.buttonText}>
Copy Link
</span>
</Button>
</CopySearch>
</span>
<span className={classes.copyButtonWrap}>
<CopyToClipboard
text={osId}
onCopy={() =>
toast('Copied OS ID to clipboard')
}
>
<Button
variant="outlined"
size="small"
className={classes.copyButton}
aria-label="Copy OS ID"
>
<ContentCopyIcon />
<span className={classes.buttonText}>
Copy OS ID
</span>
</Button>
</CopyToClipboard>
</span>
</span>
)}
</div>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { getTypographyStyles } from '../../../../util/typographyStyles';
import commonStyles from '../../commonStyles';

export default theme => {
const typography = getTypographyStyles(theme);
const spacing = theme.spacing.unit ?? 8;
return Object.freeze({
container: Object.freeze({
...commonStyles(theme).container,
marginBottom: spacing * 3,
padding: '20px 20px 20px 36px',
padding: '0 20px 0 0',
}),
title: Object.freeze({
...typography.formLabelTight,
Expand All @@ -17,48 +14,8 @@ export default theme => {
marginTop: 0,
marginBottom: spacing,
}),
osIdRow: Object.freeze({
display: 'flex',
flexWrap: 'wrap',
alignItems: 'center',
gap: `${spacing}px ${spacing * 2}px`,
}),
osIdValueWithTooltip: Object.freeze({
display: 'inline-flex',
alignItems: 'center',
gap: spacing * 0.5,
}),
osIdValue: Object.freeze({
...typography.inlineHighlight,
fontSize: '21px',
}),
osIdInfoButton: Object.freeze({
padding: spacing * 0.5,
color: theme.palette.text.secondary,
'&:hover': {
color: theme.palette.text.primary,
backgroundColor: theme.palette.action.hover,
},
}),
osIdActions: Object.freeze({
display: 'inline-flex',
flexWrap: 'wrap',
marginLeft: 'auto',
}),
copyButtonWrap: Object.freeze({
display: 'inline-flex',
marginLeft: spacing * 2,
}),
copyButtonWrapFirst: Object.freeze({
marginLeft: 0,
}),
copyButton: Object.freeze({
textTransform: 'none',
minWidth: 'auto',
}),
buttonText: Object.freeze({
marginLeft: spacing * 0.5,
fontSize: '14px',
titleAccent: Object.freeze({
...typography.bodyText,
}),
});
};
Loading
Loading