Skip to content
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
38 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
a630082
Add os id and data source section (partial impl)
VadimKovalenkoSNF Feb 28, 2026
43a4655
Adjust styles, add tooltip and toogle switch
VadimKovalenkoSNF Mar 2, 2026
4fd3cce
Align styles across components (partial impl)
VadimKovalenkoSNF Mar 2, 2026
173413a
Create frontend.md docs
VadimKovalenkoSNF Mar 2, 2026
ed9e9b4
Minor style fix, update tooltip text
VadimKovalenkoSNF Mar 2, 2026
89610eb
Merge branch 'main' into OSDEV-2375-location-name-os-id-understanging…
VadimKovalenkoSNF Mar 3, 2026
a111202
Fix lint issue
VadimKovalenkoSNF Mar 3, 2026
e080926
Update styles and layout, use rem units for body fonts
VadimKovalenkoSNF Mar 3, 2026
6aa0e18
Merge branch 'main' into OSDEV-2375-location-name-os-id-understanging…
VadimKovalenkoSNF Mar 3, 2026
84ba6ab
Add unit tests
VadimKovalenkoSNF Mar 3, 2026
11c9a02
Coercing embed to a boolean when passing it to ProductionLocationDeta…
VadimKovalenkoSNF Mar 3, 2026
154aa72
interactive attr is no longer set to MUI’s Tooltip
VadimKovalenkoSNF Mar 3, 2026
544ee14
Apply set of minor html fixes
VadimKovalenkoSNF Mar 3, 2026
df43954
Fix lint issues
VadimKovalenkoSNF Mar 3, 2026
bc34dc9
Fix icon data source section on small devices
VadimKovalenkoSNF Mar 3, 2026
52ff8ef
Update colors for the data source section
VadimKovalenkoSNF Mar 3, 2026
e1e90a3
Update release notes
VadimKovalenkoSNF Mar 3, 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
154 changes: 154 additions & 0 deletions doc/frontend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Typography Guide

How to use typography across the application: which shared styles to use, how to combine them with MUI `Typography`, and when to use each.

---

## 1. Source of truth

- **File:** `src/react/src/util/typographyStyles.js`
- **Usage:** Call `getTypographyStyles(theme)` in your component's `withStyles` (or `makeStyles`) and spread the returned keys into your class objects; then pass those classes to MUI `Typography` via `className={classes.xyz}`.
- **Rule:** Prefer these tokens over hardcoding font sizes/weights so headings, labels, body text, and links stay consistent.

---

## 2. Semantic roles and which style to use

| Role | When to use | Style key | Typical MUI usage |
|------|-------------|-----------|-------------------|
| **Page title** | Main title of a page (e.g. facility/location name) | (custom from `formLabelTight`) | `<Typography component="h1" variant="h1" className={classes.title} />` |
| **Major section** | Big section blocks (e.g. claim intro) | (custom as needed) | `component="h2"`, `variant="h2"` |
| **Section title** | Subsections ("Understanding Data Sources", "Partner Data", etc.) | `sectionTitle` | `component="h3"`, `variant="h3"`, `className` with `...typography.sectionTitle` |
| **Field / UI label** | Form labels, bold labels ("OS ID:", "CLAIMED PROFILE", "Claimed", "Crowdsourced") | `formLabel` or `formLabelTight` | `component="span"` or `"label"`, `variant="body1"`, `className` with `...typography.formLabelTight` |
| **Body / paragraph** | Normal paragraphs, descriptions, "Show more" labels | `bodyText` | `component="p"` or `"span"`, `variant="body1"`, `className` with `...typography.bodyText` |
| **Section description** | Intro or explanatory block under a section | `sectionDescription` | `component="p"`, `variant="body1"`, `className` with `...typography.sectionDescription` |
| **Inline highlight** | Short emphasized bits (name, date, ID value) inline in text | `inlineHighlight` | `component="span"`, `className` with `...typography.inlineHighlight` |

---

## 3. Style attributes (from `typographyStyles.js`)

- **formLabel / formLabelRoot / formLabelTight**
Base: `fontSize: '21px'`, `fontWeight: 600`.
`formLabel` adds `margin: '24px 0 8px 0'`; `formLabelRoot` uses `marginTop: 0`, `marginBottom: '8px'`; `formLabelTight` has no extra margin.
Use for field labels and strong short labels.

- **sectionTitle**
`fontSize: '24px'`, `fontWeight: theme.typography.fontWeightSemiBold`, `marginTop: '25px'`.
Use for h3-level section titles.

- **sectionDescription**
`fontSize: '18px'`, `marginBottom: '10px'`.
Use for the first paragraph or intro under a section.

- **bodyText**
`fontSize: '18px'`, `color: theme.palette.text.secondary`.
Use for regular body and secondary text.

- **inlineHighlight**
`fontWeight: 500`, `color: theme.palette.text.primary`, `display: 'inline'`.
Use for emphasized inline pieces (e.g. facility name, date, OS ID value).

---

## 4. Headings

- **h1 – Page title**
One per page (e.g. facility/location name).
Example: `component="h1"`, `variant="h1"`, `className` from styles that extend `formLabelTight` (e.g. 28px, 700 for prominence).

- **h2 – Major section**
Top-level sections (e.g. claim intro).
`component="h2"`, `variant="h2"`, plus custom class if needed.

- **h3 – Section title**
Subsections ("Understanding Data Sources", "Partner Data", "Interactive map", etc.).
`component="h3"`, `variant="h3"`, `className` with `...typography.sectionTitle` (and margin overrides as needed, e.g. `marginTop: 0`).

- **h4 / h5 / h6**
Use sparingly for sub-subsections (e.g. ClaimFlag uses `h4` for "CLAIMED PROFILE").
Prefer a class that extends `formLabelTight` or `sectionTitle` so sizing stays consistent.

---

## 5. Regular text

- **Paragraphs:**
`component="p"`, `variant="body1"`, and a class that includes `...typography.bodyText` (and optionally `...typography.sectionDescription` for intro blocks).
Override `fontSize` or `marginBottom` only when needed (e.g. 16px for subsection text).

- **Inline text / labels:**
`component="span"`, `variant="body1"`, and either `formLabelTight` (labels) or `bodyText` (secondary inline).

---

## 6. Links

- **Color:** `theme.palette.primary.main`.
- **Implementation:**
Use `<a href="..." className={classes.link}>` or `<Link to="..." className={classes.link}>` with a style like `link: { color: theme.palette.primary.main }`.
For "Learn more →" in tooltips, inline `style={{ color: 'white' }}` is used for contrast on dark tooltip background; elsewhere use the theme primary color.
- **Optional:** Add `textDecoration: 'none'` and `'&:hover': { textDecoration: 'underline' }` for inline links (e.g. in DataSourcesInfo `learnMoreLink`).

---

## 7. Usage pattern

1. In the component's `styles.js`:
`import { getTypographyStyles } from '../../path/to/typographyStyles';`
then `const typography = getTypographyStyles(theme);` and use `...typography.sectionTitle`, etc., in your class objects.

2. In the JSX:
Use MUI `Typography` with both:
- **Semantic props:** `component` and `variant` (e.g. `component="h3"`, `variant="h3"` for section titles).
- **Visual style:** `className={classes.yourClass}` where `yourClass` spreads the right typography token and any local overrides.

3. Keep **one** clear hierarchy per page: one h1, then h2/h3 as needed; use the table in §2 to pick the right style key and component/variant.

---

## 8. Quick reference

| Use case | component | variant | Typography style key |
|----------|-----------|--------|----------------------|
| Page title (e.g. facility name) | h1 | h1 | formLabelTight (custom size/weight) |
| Section title | h3 | h3 | sectionTitle |
| Field / subsection label | span | body1 | formLabelTight |
| Paragraph / body | p | body1 | bodyText or sectionDescription |
| Inline emphasis (name, ID, date) | span | — | inlineHighlight |
| Links | a or Link | — | color: theme.palette.primary.main |

---

## 9. Container styles (commonStyles)

- **File:** `src/react/src/components/ProductionLocation/commonStyles.js`
- **Purpose:** Shared base styles for section containers (e.g. cards, content blocks) so background and shadow stay consistent across the Production Location UI.

### What it provides

`commonStyles(theme)` returns an object with a `container` key:

- **container:** `backgroundColor: theme.palette.background.white`, `boxShadow: '0 1px 3px rgba(0, 0, 0, 0.1)'`

### How to use it

Spread `commonStyles(theme).container` into your own container class and add any overrides (margin, padding, etc.). This keeps the same background and shadow while letting each component set its own spacing.

**Import in your styles file:**

```js
import commonStyles from '../../commonStyles'; // adjust path to your component
```

**Example – extend and override:**

```js
container: Object.freeze({
...commonStyles(theme).container,
marginBottom: spacing * 3,
padding: '20px 20px 20px 36px',
}),
```

Here the container gets the shared `backgroundColor` and `boxShadow` from `commonStyles`, plus component-specific `marginBottom` and `padding`. Use this pattern for any Production Location section that should look like a card (e.g. LocationTitle, DataSourcesInfo).
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Grid from '@material-ui/core/Grid';
import { withStyles } from '@material-ui/core/styles';
import InfoIcon from '@material-ui/icons/Info';

import {
makeClaimFacilityLinkWithFeatureFlag,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react';
import PropTypes from 'prop-types';
import Grid from '@material-ui/core/Grid';
import Typography from '@material-ui/core/Typography';

const DataSourceItem = ({
classes,
Icon,
iconClassName,
title,
subsectionText,
showSubsectionInfo,
showLearnMore,
learnMoreUrl,
}) => (
<Grid item sm={12} md={4} className={classes.descriptionItem}>
<div className={classes.itemContent}>
<Icon className={iconClassName} aria-hidden />
<div className={classes.itemText}>
<Typography
component="span"
className={classes.label}
variant="body1"
>
{title}
</Typography>
{showSubsectionInfo && (
<Typography
component="p"
variant="body1"
className={classes.subsectionText}
>
{subsectionText}
{showLearnMore && learnMoreUrl && (
<>
{' '}
<a
href={learnMoreUrl}
target="_blank"
rel="noopener noreferrer"
className={classes.learnMoreLink}
>
Learn more →
</a>
</>
)}
</Typography>
)}
</div>
</div>
</Grid>
);

DataSourceItem.propTypes = {
classes: PropTypes.object.isRequired,
Icon: PropTypes.func.isRequired,
iconClassName: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
subsectionText: PropTypes.string.isRequired,
showSubsectionInfo: PropTypes.bool.isRequired,
showLearnMore: PropTypes.bool,
learnMoreUrl: PropTypes.string,
};

DataSourceItem.defaultProps = {
showLearnMore: false,
learnMoreUrl: null,
};

export default DataSourceItem;
Original file line number Diff line number Diff line change
@@ -1,16 +1,101 @@
import React from 'react';
import React, { useState } from 'react';
import Typography from '@material-ui/core/Typography';
import IconButton from '@material-ui/core/IconButton';
import Grid from '@material-ui/core/Grid';
import Switch from '@material-ui/core/Switch';
import { withStyles } from '@material-ui/core/styles';
import InfoIcon from '@material-ui/icons/Info';

import DialogTooltip from '../../../Contribute/DialogTooltip';
import DataSourceItem from './DataSourceItem';
import {
DATA_SOURCES_TOOLTIP_TEXT,
DATA_SOURCES_LEARN_MORE_URL,
DATA_SOURCES_ITEMS,
} from './constants';
import productionLocationDetailsDataSourcesInfoStyles from './styles';

const ProductionLocationDetailsDataSourcesInfo = ({ classes, className }) => (
<div className={`${classes.container} ${className || ''}`}>
<Typography variant="title" className={classes.title} component="h3">
Understanding Data Sources
</Typography>
</div>
);
const ProductionLocationDetailsDataSourcesInfo = ({ classes, className }) => {
const [showSubsectionInfo, setShowSubsectionInfo] = useState(false);

return (
<div className={`${classes.container} ${className || ''}`}>
<div className={classes.titleRow}>
<Typography
component="h3"
className={classes.sectionTitle}
variant="h3"
>
Understanding Data Sources
</Typography>
<DialogTooltip
text={DATA_SOURCES_TOOLTIP_TEXT}
textHref={
<p style={{ marginTop: 8, marginBottom: 0 }}>
<a
href={DATA_SOURCES_LEARN_MORE_URL}
target="_blank"
rel="noopener noreferrer"
style={{ color: 'white' }}
>
Learn more →
</a>
</p>
}
interactive
childComponent={
<IconButton
className={classes.infoButton}
size="small"
aria-label="More information about data sources"
disableRipple
>
<InfoIcon style={{ width: 16, height: 16 }} />
</IconButton>
}
/>
<div className={classes.switchWrap}>
<Typography
component="span"
className={classes.switchLabel}
>
Show more
</Typography>
<Switch
checked={showSubsectionInfo}
onChange={e => setShowSubsectionInfo(e.target.checked)}
color="primary"
size="small"
className={classes.switch}
inputProps={{
'aria-label':
'Show extra info under each data source',
}}
/>
</div>
</div>
<Grid container className={classes.descriptionList} spacing={2}>
{DATA_SOURCES_ITEMS.map(item => (
<DataSourceItem
key={item.title}
classes={classes}
Icon={item.Icon}
iconClassName={classes[item.iconClassNameKey]}
title={item.title}
subsectionText={item.subsectionText}
showSubsectionInfo={showSubsectionInfo}
showLearnMore={Boolean(item.showLearnMore)}
learnMoreUrl={
item.showLearnMore
? DATA_SOURCES_LEARN_MORE_URL
: null
}
/>
))}
</Grid>
</div>
);
};

export default withStyles(productionLocationDetailsDataSourcesInfoStyles)(
ProductionLocationDetailsDataSourcesInfo,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import CheckCircleOutline from '@material-ui/icons/CheckCircleOutline';
import People from '@material-ui/icons/People';
import GroupWork from '@material-ui/icons/GroupWork';

export const DATA_SOURCES_TOOLTIP_TEXT =
'Open Supply Hub is collaboratively mapping global supply chains. This model means that data comes into the platform in a few ways.';
export const DATA_SOURCES_LEARN_MORE_URL =
'https://info.opensupplyhub.org/resources/an-open-data-model';

export const DATA_SOURCES_ITEMS = Object.freeze([
Object.freeze({
Icon: CheckCircleOutline,
iconClassNameKey: 'iconClaimed',
title: 'Claimed',
subsectionText:
'General information & operational details submitted by production location',
}),
Object.freeze({
Icon: People,
iconClassNameKey: 'iconCrowdsourced',
title: 'Crowdsourced',
subsectionText:
"General information shared by supply chain stakeholders & OS Hub's research team",
showLearnMore: true,
}),
Object.freeze({
Icon: GroupWork,
iconClassNameKey: 'iconPartner',
title: 'Partner Data',
subsectionText:
'Additional social or environmental information shared by third party platforms',
showLearnMore: true,
}),
]);
Loading
Loading