Skip to content
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
cc9d321
Introduced draft version of the data point and drawer components
vlad-shapik Feb 27, 2026
26a40fa
refactor(DataPoint): use Grid throughout, move tooltip icon between c…
vlad-shapik Feb 27, 2026
5b09dff
Refactored code
vlad-shapik Mar 2, 2026
564778a
Updated styles
vlad-shapik Mar 3, 2026
1c3238b
Updated styles
vlad-shapik Mar 3, 2026
2bd13ec
Updated styles
vlad-shapik Mar 3, 2026
2a3a79f
Simplified imports
vlad-shapik Mar 3, 2026
0213f88
refactor(PR 892): use component-named style imports and fix HelpToolt…
vlad-shapik Mar 3, 2026
6c66fc3
refactor(ProductionLocation): utils/constants, subfolders, direct par…
vlad-shapik Mar 3, 2026
61a7292
fix: use default exports in utils, remove comments, rename drawer uti…
vlad-shapik Mar 3, 2026
c713155
fix: import getInfoPromotedText from utils.jsx explicitly
vlad-shapik Mar 3, 2026
d42dc4b
fix: resolve production location page console warnings
vlad-shapik Mar 3, 2026
a576400
Remove unused hooks, update DataPoint styles
vlad-shapik Mar 3, 2026
0afe707
ContributionCard: source as profile link with OpenInNew icon, fix no-…
vlad-shapik Mar 4, 2026
4e9df08
Extract LearnMoreLink component; use IconComponent; refactor HelpTool…
vlad-shapik Mar 4, 2026
b1c88e3
LearnMoreLink: use destructured prop-types, move default link text to…
vlad-shapik Mar 4, 2026
2b9043e
IconComponent: inline utils, remove utils.js; LearnMoreLink updates
vlad-shapik Mar 4, 2026
2f257e1
Use typographyStyles and rem/units in PR 892 components (LearnMoreLin…
vlad-shapik Mar 5, 2026
9d65494
Fix console warnings and clean up DataPoint/LearnMoreLink/IconComponent
vlad-shapik Mar 5, 2026
38d3047
Rename contributorId to userId; make contributor name a profile link …
vlad-shapik Mar 5, 2026
060a33a
Remove DataPoint usage and mocks from ClaimDataContainer and Producti…
vlad-shapik Mar 5, 2026
2d59fc9
ProductionLocationDetailsGeneralFields: remove PropTypes
vlad-shapik Mar 5, 2026
8601afd
Move OS ID block to OsIdBadge; use IconComponent and LearnMoreLink in…
vlad-shapik Mar 5, 2026
3124b4c
ProductionLocationDetailsContent: fix OsIdBadge import path case (OsI…
vlad-shapik Mar 5, 2026
f85895d
Add OSDEV-2370 release note to 2.20.0 Code/API changes
vlad-shapik Mar 5, 2026
bf20cb1
fix: resolve ESLint errors in ContributionsDrawer, ContributionCard, …
vlad-shapik Mar 5, 2026
d1bc04a
style: adjust typography in LocationTitle and sidebar NavBar
vlad-shapik Mar 6, 2026
d3c8b14
refactor(ContributionsDrawer): extract DrawerSubtitle, InfoPromotedTe…
vlad-shapik Mar 6, 2026
0f3a467
fix(ProductionLocation): OsIdBadge folder case for case-sensitive fil…
vlad-shapik Mar 6, 2026
b53a51a
style(COLOURS): normalize chip background hex to uppercase
vlad-shapik Mar 6, 2026
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
4 changes: 4 additions & 0 deletions doc/release/RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
* Updated the GET `api/facilities/` and `api/facilities/{os_id}/` endpoints to include `contributor_type` (the raw type from the database) for both public and anonymous sources. Each contributor entry now also includes a `count` field (1 for public contributors and an aggregated count for anonymous entries of the same type), allowing the front end to display and sum counts by type (e.g., “18 Brands”, “9 Suppliers”).
* Additionally, updated GET `api/facilities/{os_id}/` to return the claim request creation date. All of this information is required for the redesigned Production Location page - specifically for the claim banner - as well as for the supply chain network.
* [OSDEV-2369](https://opensupplyhub.atlassian.net/browse/OSDEV-2369) - Moved single-facility data loading and redirect logic into the Production Location details container so the sidebar (including the "Contribute to this profile" section) and main content render with consistent facility data.
* [OSDEV-2370](https://opensupplyhub.atlassian.net/browse/OSDEV-2370) - Created reusable data point and drawer components for the Production Location page redesign:
* Introduced shared `IconComponent` (interactive tooltip with icon) and `LearnMoreLink`; refactored OS ID badge, Data Sources, and Claim status to use them.
* Added `DataPoint` component (label, value, status, contributor link, date, and optional "data sources" drawer trigger) and `ContributionsDrawer` with promoted source and list of contribution cards linking to contributor profiles.
* Claim form profile step and related tooltips now use `IconComponent`.

### Architecture/Environment changes
* Increased the CPU and memory allocation for the DedupeHub container to `8 CPU` and `40 GB` in the Terraform deployment configuration to address memory overload issues during production location reindexing for the `Test` environment.
Expand Down
135 changes: 135 additions & 0 deletions src/react/src/__tests__/components/ContributionsDrawer.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import React, { useState } from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';

import ContributionsDrawer from '../../components/ProductionLocation/ContributionsDrawer/ContributionsDrawer';

const theme = createMuiTheme();

const renderContributionsDrawer = (props = {}) =>
render(
<MemoryRouter>
<MuiThemeProvider theme={theme}>
<ContributionsDrawer
open
onClose={jest.fn()}
{...props}
/>
</MuiThemeProvider>
</MemoryRouter>,
);

function DrawerWithTrigger() {
const [open, setOpen] = useState(false);
return (
<MemoryRouter>
<MuiThemeProvider theme={theme}>
<button
type="button"
aria-label="Open drawer"
data-testid="open-drawer-trigger"
onClick={() => setOpen(true)}
/>
<ContributionsDrawer
open={open}
onClose={() => setOpen(false)}
/>
</MuiThemeProvider>
</MemoryRouter>
);
}

describe('ContributionsDrawer', () => {
test('renders without crashing when open with required props', () => {
renderContributionsDrawer({ open: true, onClose: () => {} });

expect(screen.getByTestId('contributions-drawer')).toBeInTheDocument();
});

test('drawer content is visible when open is true', () => {
renderContributionsDrawer({ open: true, onClose: () => {} });

expect(screen.getByTestId('contributions-drawer')).toBeInTheDocument();
expect(screen.getByTestId('contributions-drawer-title')).toBeInTheDocument();
});

test('calls onClose when close button is clicked', () => {
const onClose = jest.fn();
renderContributionsDrawer({ open: true, onClose });

fireEvent.click(screen.getByTestId('contributions-drawer-close'));

expect(onClose).toHaveBeenCalledTimes(1);
});

test('clicking trigger opens drawer and content is visible', () => {
render(<DrawerWithTrigger />);

expect(screen.queryByTestId('contributions-drawer')).not.toBeInTheDocument();

fireEvent.click(screen.getByTestId('open-drawer-trigger'));

expect(screen.getByTestId('contributions-drawer')).toBeInTheDocument();
expect(screen.getByTestId('contributions-drawer-title')).toBeInTheDocument();
});

test('does not render contribution list when contributions is empty', () => {
renderContributionsDrawer({
open: true,
onClose: () => {},
contributions: [],
});

expect(
screen.queryByTestId('contributions-drawer-list'),
).not.toBeInTheDocument();
});

test('does not render promoted card when promotedContribution is null', () => {
renderContributionsDrawer({
open: true,
onClose: () => {},
promotedContribution: null,
});

expect(
screen.queryByTestId('contribution-card-promoted'),
).not.toBeInTheDocument();
});

test('renders contribution list and cards when contributions provided', () => {
renderContributionsDrawer({
open: true,
onClose: () => {},
contributions: [
{
value: 'Value A',
sourceName: 'Source A',
date: '2022-01-01',
userId: 1,
},
],
});

expect(screen.getByTestId('contributions-drawer-list')).toBeInTheDocument();
expect(screen.getByTestId('contribution-card')).toBeInTheDocument();
});

test('renders promoted card when promotedContribution provided', () => {
renderContributionsDrawer({
open: true,
onClose: () => {},
promotedContribution: {
value: 'Promoted Value',
sourceName: 'Promoted Source',
date: '2023-01-01',
userId: 10,
},
});

expect(
screen.getByTestId('contribution-card-promoted'),
).toBeInTheDocument();
});
});
94 changes: 94 additions & 0 deletions src/react/src/__tests__/components/DataPoint.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';

import DataPoint from '../../components/ProductionLocation/DataPoint/DataPoint';
import { STATUS_CLAIMED } from '../../components/ProductionLocation/DataPoint/constants';

const theme = createMuiTheme();

const renderDataPoint = (props = {}) =>
render(
<MemoryRouter>
<MuiThemeProvider theme={theme}>
<DataPoint
label="Test Label"
value="Test Value"
{...props}
/>
</MuiThemeProvider>
</MemoryRouter>,
);

describe('DataPoint', () => {
test('renders without crashing with required props', () => {
renderDataPoint({ label: 'Name', value: 'Facility One' });

expect(screen.getByTestId('data-point')).toBeInTheDocument();
});

test('renders label and value via data-testid', () => {
renderDataPoint({ label: 'Name', value: 'Facility One' });

const label = screen.getByTestId('data-point-label');
const value = screen.getByTestId('data-point-value');
expect(label).toBeInTheDocument();
expect(label).toHaveTextContent('Name');
expect(value).toBeInTheDocument();
expect(value).toHaveTextContent('Facility One');
});

test('renders status chip when statusLabel is provided', () => {
renderDataPoint({
label: 'Name',
value: 'Value',
statusLabel: STATUS_CLAIMED,
});

const chip = screen.getByTestId('data-point-status-chip');
expect(chip).toBeInTheDocument();
});

test('renders contributor as link with correct href when userId is provided', () => {
renderDataPoint({
label: 'Name',
value: 'Value',
contributorName: 'Acme Corp',
userId: 42,
});

const contributor = screen.getByTestId('data-point-contributor');
expect(contributor).toBeInTheDocument();
const link = contributor.querySelector('a[href="/profile/42"]');
expect(link).toBeInTheDocument();
});

test('does not render status chip when statusLabel is null', () => {
renderDataPoint({ label: 'Name', value: 'Value' });

expect(screen.queryByTestId('data-point-status-chip')).not.toBeInTheDocument();
});

test('does not render contributor section when contributorName is null', () => {
renderDataPoint({
label: 'Name',
value: 'Value',
userId: 1,
});

expect(screen.queryByTestId('data-point-contributor')).not.toBeInTheDocument();
});

test('does not render sources button when drawerData has no contributions', () => {
renderDataPoint({
label: 'Name',
value: 'Value',
drawerData: { contributions: [], title: 'Title', subtitle: null },
onOpenDrawer: jest.fn(),
renderDrawer: () => null,
});

expect(screen.queryByTestId('data-point-sources-button')).not.toBeInTheDocument();
});
});
4 changes: 1 addition & 3 deletions src/react/src/__tests__/components/DataSourcesInfo.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,7 @@ describe('ProductionLocation DataSourcesInfo', () => {
renderDataSourcesInfo();

expect(
screen.getByRole('button', {
name: /more information about data sources/i,
}),
screen.getByTestId('data-sources-info-tooltip'),
).toBeInTheDocument();
});

Expand Down
8 changes: 2 additions & 6 deletions src/react/src/__tests__/components/OsIdBadge.test.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
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';
import ProductionLocationDetailsOsIdBadge from '../../components/ProductionLocation/Heading/OsIdBadge/OsIdBadge';

jest.mock('react-toastify', () => ({
toast: jest.fn(),
Expand Down Expand Up @@ -51,11 +51,7 @@ describe('ProductionLocationDetailsOsIdBadge', () => {
test('renders info button for OS ID tooltip', () => {
renderOsIdBadge({ osId: 'CN2021250D1DTN7' });

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

test('shows Copy Link and Copy OS ID buttons when osId is present', () => {
Expand Down
Loading