Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
eeca1f1
Add basic classes for ClaimFlag (partial impl)
VadimKovalenkoSNF Feb 26, 2026
c83b677
Optimize class assigment
VadimKovalenkoSNF Feb 26, 2026
fb4d282
Minor style refactoring
VadimKovalenkoSNF Feb 26, 2026
b32a24c
Dialog tooltip optimization
VadimKovalenkoSNF Feb 26, 2026
b738170
Pass embed config flag (secure measure)
VadimKovalenkoSNF Feb 26, 2026
ee87028
Clear timeout for TooltipDialog popup
VadimKovalenkoSNF Feb 26, 2026
6304eab
Clear timeout for DialogTooltip popup, adjust user accessibility
VadimKovalenkoSNF Feb 26, 2026
5e3984d
Connect closure status badge
VadimKovalenkoSNF Feb 26, 2026
e2a475e
Guard React.cloneElement against non-element children in interactive …
VadimKovalenkoSNF Feb 27, 2026
af374f6
Add unit tests
VadimKovalenkoSNF Feb 27, 2026
1b60b17
Fix lint issues
VadimKovalenkoSNF Feb 27, 2026
e07a9ed
Add proptypes to the closure report component, fix minor issues
VadimKovalenkoSNF Feb 27, 2026
5be8398
Fix unit test
VadimKovalenkoSNF Feb 27, 2026
a8ddc91
Merge branch 'main' into OSDEV-2374-implement-claim-banner-with-statu…
VadimKovalenkoSNF Feb 27, 2026
3234ffe
Merge branch 'main' into OSDEV-2374-implement-claim-banner-with-statu…
VadimKovalenkoSNF Feb 27, 2026
31761c9
Fix post-merge errors
VadimKovalenkoSNF Feb 27, 2026
249ba92
Refactor DialogTooltip to pass href as a separate component
VadimKovalenkoSNF Feb 28, 2026
ee7fda9
Refactor DialogTooltip keyboard navigation
VadimKovalenkoSNF Feb 28, 2026
bfc273e
Refactor ClaimFlag util functions
VadimKovalenkoSNF Feb 28, 2026
84f9d9b
Split ClaimFlag into smaller inner components
VadimKovalenkoSNF Feb 28, 2026
5ecf343
Fix lint issues
VadimKovalenkoSNF Feb 28, 2026
0e6a972
Introduce InteractiveTrigger component
VadimKovalenkoSNF Mar 2, 2026
740ea07
Arrow function for redux methods, upd release notes
VadimKovalenkoSNF Mar 2, 2026
b239ca7
Move InteractiveTrigger into the separate file
VadimKovalenkoSNF Mar 2, 2026
71f8be8
Fix lint issues
VadimKovalenkoSNF Mar 2, 2026
34d46ea
Fix lint issues with long string
VadimKovalenkoSNF Mar 2, 2026
c4645f1
Update claim badge tooltip text
VadimKovalenkoSNF Mar 2, 2026
9b8ad7d
Increase font size for the inlineHighlight class
VadimKovalenkoSNF Mar 2, 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
1 change: 1 addition & 0 deletions doc/release/RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
* Previously opened facility pages at `/facilities/:osID` will redirect to `/production-locations/:osID` after page refresh.
* When the feature flag is disabled, accessing `/production-locations/:osID` routes will result in a "Not found" page with no automatic redirection to the legacy `/facilities/:osID` route.
* [OSDEV-2353](https://opensupplyhub.atlassian.net/browse/OSDEV-2353) - Created basic layout components for new Production Location page redesign.
* [OSDEV-2374](https://opensupplyhub.atlassian.net/browse/OSDEV-2374) - Created UI for Claim and Closure status banners.
* [OSDEV-2356](https://opensupplyhub.atlassian.net/browse/OSDEV-2356) - Added `GET api/partner-field-groups/` endpoint to retrieve partner field groups with pagination support and CDN caching for the endpoint (and additional endpoints for partner fields and contributors).
* [OSDEV-2369](https://opensupplyhub.atlassian.net/browse/OSDEV-2369) - As part of the Production Location page redesign, implemented the "Contribute to this profile" section in the sidebar. The section includes: Suggest Correction (link to the contribute flow), Report Duplicate and Dispute Claim (mailto links; Dispute Claim is shown only when the facility is claimed by someone else), and Report Closed / Report Reopened. Report Closed/Reopened opens a dialog where logged-in users can submit a reason; anonymous users see a prompt to log in.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { screen } from '@testing-library/react';
import renderWithProviders from '../../util/testUtils/renderWithProviders';
import ProductionLocationClaimFlag from '../../components/ProductionLocation/Heading/ClaimFlag/ClaimFlag';

describe('ProductionLocation ClaimFlag', () => {
const defaultProps = {
osId: 'US202510850SQCV',
isClaimed: false,
isPending: false,
isEmbed: false,
};

test('renders without crashing', () => {
renderWithProviders(
<Router>
<ProductionLocationClaimFlag
{...defaultProps}
isClaimed
/>
</Router>,
);

const banner = screen.getByTestId('claim-banner');
expect(banner).toBeInTheDocument();
});

test('shows CLAIMED PROFILE when claimed', () => {
renderWithProviders(
<Router>
<ProductionLocationClaimFlag
{...defaultProps}
isClaimed
/>
</Router>,
);

const banner = screen.getByTestId('claim-banner');
expect(banner).toHaveTextContent('CLAIMED PROFILE');
});

test('shows unclaimed message when not claimed', () => {
renderWithProviders(
<Router>
<ProductionLocationClaimFlag {...defaultProps} />
</Router>,
);

const banner = screen.getByTestId('claim-banner');
expect(banner).toHaveTextContent(
'This production location has not been claimed',
);
});

test('shows claim link when not claimed and not pending', () => {
renderWithProviders(
<Router>
<ProductionLocationClaimFlag {...defaultProps} />
</Router>,
);

const link = screen.getByRole('link', {
name: /I want to claim this production location/i,
});
expect(link).toBeInTheDocument();
});

test('shows pending claim message when pending', () => {
renderWithProviders(
<Router>
<ProductionLocationClaimFlag
{...defaultProps}
isPending
/>
</Router>,
);

const banner = screen.getByTestId('claim-banner');
expect(banner).toHaveTextContent(
'There is a pending claim for this production location',
);
});

test('returns null when embed is true', () => {
renderWithProviders(
<Router>
<ProductionLocationClaimFlag
{...defaultProps}
isEmbed
/>
</Router>,
);

const banner = screen.queryByTestId('claim-banner');
expect(banner).not.toBeInTheDocument();
});

test('shows claimed-by line when claimed with contributor and date', () => {
const claimInfo = {
contributor: { name: 'Acme Corp' },
approved_at: '2023-06-15T12:00:00Z',
};

renderWithProviders(
<Router>
<ProductionLocationClaimFlag
{...defaultProps}
isClaimed
claimInfo={claimInfo}
/>
</Router>,
);

expect(screen.getByText(/Claimed by/)).toBeInTheDocument();
expect(screen.getByText(/Acme Corp/)).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { screen } from '@testing-library/react';
import renderWithProviders from '../../util/testUtils/renderWithProviders';
import ClosureStatus from '../../components/ProductionLocation/Heading/ClosureStatus/ClosureStatus';

jest.mock('../../components/FeatureFlag', () => {
// eslint-disable-next-line global-require -- needed inside jest.mock factory
const PropTypes = require('prop-types');
const MockFeatureFlag = ({ children }) => <>{children}</>;
MockFeatureFlag.propTypes = { children: PropTypes.node };
MockFeatureFlag.defaultProps = { children: null };
return MockFeatureFlag;
});

describe('ProductionLocation ClosureStatus', () => {
const clearFacility = jest.fn();

afterEach(() => {
jest.clearAllMocks();
});

test('returns null when there is no activity report', () => {
const data = {
properties: {},
};

renderWithProviders(
<Router>
<ClosureStatus data={data} clearFacility={clearFacility} />
</Router>,
);

expect(screen.queryByText('Status pending')).not.toBeInTheDocument();
expect(
screen.queryByText(/This facility may be/),
).not.toBeInTheDocument();
});

test('returns null when report exists but not pending and not closed', () => {
const data = {
properties: {
activity_reports: [{ status: 'RESOLVED' }],
is_closed: false,
},
};

renderWithProviders(
<Router>
<ClosureStatus data={data} clearFacility={clearFacility} />
</Router>,
);

expect(screen.queryByText('Status pending')).not.toBeInTheDocument();
expect(
screen.queryByText(/This facility is closed/),
).not.toBeInTheDocument();
});

test('renders pending message when report status is PENDING', () => {
const data = {
properties: {
activity_reports: [
{ status: 'PENDING', closure_state: 'closed' },
],
is_closed: false,
},
};

renderWithProviders(
<Router>
<ClosureStatus data={data} clearFacility={clearFacility} />
</Router>,
);

expect(
screen.getByText(/This facility may be closed\./),
).toBeInTheDocument();
expect(screen.getByText('Status pending')).toBeInTheDocument();
});

test('renders closed message when facility is closed and no new OS ID', () => {
const data = {
properties: {
activity_reports: [{ status: 'RESOLVED' }],
is_closed: true,
},
};

renderWithProviders(
<Router>
<ClosureStatus data={data} clearFacility={clearFacility} />
</Router>,
);

expect(
screen.getByText(/This facility is closed\./),
).toBeInTheDocument();
});

test('renders moved-to link when closed with new_os_id', () => {
const data = {
properties: {
activity_reports: [{ status: 'RESOLVED' }],
is_closed: true,
new_os_id: 'US2025123456ABCD',
},
};

renderWithProviders(
<Router>
<ClosureStatus data={data} clearFacility={clearFacility} />
</Router>,
);

const link = screen.getByRole('link', { name: 'US2025123456ABCD' });
expect(link).toBeInTheDocument();
expect(
screen.getByText(/This facility has moved to/),
).toBeInTheDocument();
});
});
Loading