feat(detail): move religious character into header details + mobile reveal #83
@@ -148,6 +148,24 @@ test('school detail page shows GIAS identity/contact details and drops the unwir
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('header details collapse behind a "Show all details" toggle on mobile', async ({ page }) => {
|
||||||
|
await page.setViewportSize({ width: 390, height: 844 });
|
||||||
|
const [urn] = await twoPrimaryUrns(page);
|
||||||
|
await page.goto(`/school/${urn}`);
|
||||||
|
await expect(page.locator('h1').first()).toBeVisible({ timeout: 15_000 });
|
||||||
|
|
||||||
|
// Collapsed by default on mobile/tablet …
|
||||||
|
const details = page.locator('#school-header-details');
|
||||||
|
await expect(details).toBeHidden();
|
||||||
|
const toggle = page.getByRole('button', { name: /show all details/i });
|
||||||
|
await expect(toggle).toBeVisible();
|
||||||
|
|
||||||
|
// … and the link reveals them (label flips to "Hide details").
|
||||||
|
await toggle.click();
|
||||||
|
await expect(details).toBeVisible();
|
||||||
|
await expect(page.getByRole('button', { name: /hide details/i })).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
test('a report-card school shows its report card, dated to the report-card inspection', async ({ page }) => {
|
test('a report-card school shows its report card, dated to the report-card inspection', async ({ page }) => {
|
||||||
// Detail views detected report cards via `framework`, which the API never
|
// Detail views detected report cards via `framework`, which the API never
|
||||||
// sets to "ReportCard" — so report-card schools rendered as legacy ratings
|
// sets to "ReportCard" — so report-card schools rendered as legacy ratings
|
||||||
|
|||||||
@@ -197,6 +197,22 @@
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* "Show all details" reveal — only rendered on mobile/tablet, where the
|
||||||
|
header details block is collapsed below the fold. Hidden on desktop. */
|
||||||
|
.detailsToggle {
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
padding: 0;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--accent-teal, #2d7d7d);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
/* Gender split card — sits in the Pupils & Inclusion heroStatGrid */
|
/* Gender split card — sits in the Pupils & Inclusion heroStatGrid */
|
||||||
.genderSplitValue {
|
.genderSplitValue {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1205,14 +1221,24 @@
|
|||||||
gap: 0.375rem;
|
gap: 0.375rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Secondary header info (headteacher, website, pupil count, trust)
|
/* Secondary header info (headteacher, website, pupil count, trust,
|
||||||
isn't needed above the fold on phones — pupil count lives in the
|
contact, area) isn't needed above the fold on phones/tablets, so it's
|
||||||
Pupils & Inclusion section, website is one scroll away. Reclaim
|
collapsed by default and revealed on demand via the "Show all details"
|
||||||
the ~3 vertical lines so the actual metrics surface sooner. */
|
link — reclaiming the vertical space so the metrics surface sooner. */
|
||||||
|
.detailsToggle {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
.headerDetails {
|
.headerDetails {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.headerDetailsOpen {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
.metricsGrid {
|
.metricsGrid {
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ export function SchoolDetailView({
|
|||||||
const heroMapRef = useRef<SchoolHeroMapHandle>(null);
|
const heroMapRef = useRef<SchoolHeroMapHandle>(null);
|
||||||
// "All ▾" jump menu listing every section.
|
// "All ▾" jump menu listing every section.
|
||||||
const [sectionsOpen, setSectionsOpen] = useState(false);
|
const [sectionsOpen, setSectionsOpen] = useState(false);
|
||||||
|
// Header details (headteacher, contact, trust, area) collapse behind a
|
||||||
|
// "Show all details" link on mobile/tablet, where they're below the fold.
|
||||||
|
const [detailsOpen, setDetailsOpen] = useState(false);
|
||||||
|
|
||||||
// Back returns to wherever the user came from; deep-links (no in-app history)
|
// Back returns to wherever the user came from; deep-links (no in-app history)
|
||||||
// fall back to search so the button never dead-ends or leaves the site.
|
// fall back to search so the button never dead-ends or leaves the site.
|
||||||
@@ -354,13 +357,6 @@ export function SchoolDetailView({
|
|||||||
{schoolInfo.age_range && (
|
{schoolInfo.age_range && (
|
||||||
<span className={styles.metaItem}>{formatAgeRange(schoolInfo.age_range)}</span>
|
<span className={styles.metaItem}>{formatAgeRange(schoolInfo.age_range)}</span>
|
||||||
)}
|
)}
|
||||||
{schoolInfo.religious_denomination && (
|
|
||||||
<span className={styles.metaItem}>
|
|
||||||
{['Does not apply', 'None'].includes(schoolInfo.religious_denomination)
|
|
||||||
? 'None'
|
|
||||||
: schoolInfo.religious_denomination}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{schoolInfo.nursery_provision && (
|
{schoolInfo.nursery_provision && (
|
||||||
<span className={styles.metaItem}>Nursery</span>
|
<span className={styles.metaItem}>Nursery</span>
|
||||||
)}
|
)}
|
||||||
@@ -391,7 +387,20 @@ export function SchoolDetailView({
|
|||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<div className={styles.headerDetails}>
|
<button
|
||||||
|
type="button"
|
||||||
|
className={styles.detailsToggle}
|
||||||
|
aria-expanded={detailsOpen}
|
||||||
|
aria-controls="school-header-details"
|
||||||
|
onClick={() => setDetailsOpen((o) => !o)}
|
||||||
|
>
|
||||||
|
{detailsOpen ? 'Hide details' : 'Show all details'}
|
||||||
|
<span aria-hidden="true">{detailsOpen ? '▴' : '▾'}</span>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
id="school-header-details"
|
||||||
|
className={`${styles.headerDetails}${detailsOpen ? ` ${styles.headerDetailsOpen}` : ''}`}
|
||||||
|
>
|
||||||
{schoolInfo.headteacher_name && (
|
{schoolInfo.headteacher_name && (
|
||||||
<span className={styles.headerDetail}>
|
<span className={styles.headerDetail}>
|
||||||
<strong>Headteacher:</strong> {schoolInfo.headteacher_name}
|
<strong>Headteacher:</strong> {schoolInfo.headteacher_name}
|
||||||
@@ -433,6 +442,14 @@ export function SchoolDetailView({
|
|||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{schoolInfo.religious_denomination && (
|
||||||
|
<span className={styles.headerDetail}>
|
||||||
|
<strong>Religious character:</strong>{' '}
|
||||||
|
{['Does not apply', 'None'].includes(schoolInfo.religious_denomination)
|
||||||
|
? 'None'
|
||||||
|
: schoolInfo.religious_denomination}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
{schoolInfo.county && (
|
{schoolInfo.county && (
|
||||||
<span className={styles.headerDetail}>
|
<span className={styles.headerDetail}>
|
||||||
<strong>County:</strong> {schoolInfo.county}
|
<strong>County:</strong> {schoolInfo.county}
|
||||||
|
|||||||
@@ -207,6 +207,22 @@
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* "Show all details" reveal — only rendered on mobile/tablet, where the
|
||||||
|
header details block is collapsed below the fold. Hidden on desktop. */
|
||||||
|
.detailsToggle {
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
padding: 0;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--accent-teal, #2d7d7d);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
@@ -1066,7 +1082,18 @@
|
|||||||
padding: 0.1rem 0.375rem;
|
padding: 0.1rem 0.375rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Collapsed below the fold on phones/tablets; revealed via "Show all
|
||||||
|
details" so the metrics surface sooner. */
|
||||||
|
.detailsToggle {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
.headerDetails {
|
.headerDetails {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerDetailsOpen {
|
||||||
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.375rem;
|
gap: 0.375rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ export function SecondarySchoolDetailView({
|
|||||||
const isInComparison = isSelected(schoolInfo.urn);
|
const isInComparison = isSelected(schoolInfo.urn);
|
||||||
|
|
||||||
const [activeSection, setActiveSection] = useState<string>('');
|
const [activeSection, setActiveSection] = useState<string>('');
|
||||||
|
// Header details collapse behind a "Show all details" link on mobile/tablet.
|
||||||
|
const [detailsOpen, setDetailsOpen] = useState(false);
|
||||||
|
|
||||||
const latestResults = yearlyData.length > 0 ? yearlyData[yearlyData.length - 1] : null;
|
const latestResults = yearlyData.length > 0 ? yearlyData[yearlyData.length - 1] : null;
|
||||||
|
|
||||||
@@ -246,13 +248,6 @@ export function SecondarySchoolDetailView({
|
|||||||
{schoolInfo.age_range && (
|
{schoolInfo.age_range && (
|
||||||
<span className={styles.badge}>{formatAgeRange(schoolInfo.age_range)}</span>
|
<span className={styles.badge}>{formatAgeRange(schoolInfo.age_range)}</span>
|
||||||
)}
|
)}
|
||||||
{schoolInfo.religious_denomination && (
|
|
||||||
<span className={styles.badge}>
|
|
||||||
{['Does not apply', 'None'].includes(schoolInfo.religious_denomination)
|
|
||||||
? 'None'
|
|
||||||
: schoolInfo.religious_denomination}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{schoolInfo.nursery_provision && (
|
{schoolInfo.nursery_provision && (
|
||||||
<span className={styles.badge}>Nursery</span>
|
<span className={styles.badge}>Nursery</span>
|
||||||
)}
|
)}
|
||||||
@@ -288,7 +283,20 @@ export function SecondarySchoolDetailView({
|
|||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<div className={styles.headerDetails}>
|
<button
|
||||||
|
type="button"
|
||||||
|
className={styles.detailsToggle}
|
||||||
|
aria-expanded={detailsOpen}
|
||||||
|
aria-controls="school-header-details"
|
||||||
|
onClick={() => setDetailsOpen((o) => !o)}
|
||||||
|
>
|
||||||
|
{detailsOpen ? 'Hide details' : 'Show all details'}
|
||||||
|
<span aria-hidden="true">{detailsOpen ? '▴' : '▾'}</span>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
id="school-header-details"
|
||||||
|
className={`${styles.headerDetails}${detailsOpen ? ` ${styles.headerDetailsOpen}` : ''}`}
|
||||||
|
>
|
||||||
{schoolInfo.headteacher_name && (
|
{schoolInfo.headteacher_name && (
|
||||||
<span className={styles.headerDetail}>
|
<span className={styles.headerDetail}>
|
||||||
<strong>Headteacher:</strong> {schoolInfo.headteacher_name}
|
<strong>Headteacher:</strong> {schoolInfo.headteacher_name}
|
||||||
@@ -326,6 +334,14 @@ export function SecondarySchoolDetailView({
|
|||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{schoolInfo.religious_denomination && (
|
||||||
|
<span className={styles.headerDetail}>
|
||||||
|
<strong>Religious character:</strong>{' '}
|
||||||
|
{['Does not apply', 'None'].includes(schoolInfo.religious_denomination)
|
||||||
|
? 'None'
|
||||||
|
: schoolInfo.religious_denomination}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
{schoolInfo.county && (
|
{schoolInfo.county && (
|
||||||
<span className={styles.headerDetail}>
|
<span className={styles.headerDetail}>
|
||||||
<strong>County:</strong> {schoolInfo.county}
|
<strong>County:</strong> {schoolInfo.county}
|
||||||
|
|||||||
Reference in New Issue
Block a user