diff --git a/nextjs-app/__tests__/components/SchoolRow.test.tsx b/nextjs-app/__tests__/components/SchoolRow.test.tsx
index dcdab0e..1911d31 100644
--- a/nextjs-app/__tests__/components/SchoolRow.test.tsx
+++ b/nextjs-app/__tests__/components/SchoolRow.test.tsx
@@ -34,3 +34,21 @@ describe('SchoolRow religious character', () => {
expect(screen.getByText('Church of England')).toBeInTheDocument();
});
});
+
+describe('SchoolRow shares the school page flags', () => {
+ it("prints the type in the search filter's terms", () => {
+ render();
+ expect(screen.getByText('State school')).toBeInTheDocument();
+ expect(screen.queryByText('Free schools')).not.toBeInTheDocument();
+ });
+
+ it('flags a nursery class', () => {
+ render();
+ expect(screen.getByText('Nursery class')).toBeInTheDocument();
+ });
+
+ it("flags a boys' school", () => {
+ render();
+ expect(screen.getByText("Boys' school")).toBeInTheDocument();
+ });
+});
diff --git a/nextjs-app/__tests__/components/SecondarySchoolRow.test.tsx b/nextjs-app/__tests__/components/SecondarySchoolRow.test.tsx
index e3a25ac..9503a84 100644
--- a/nextjs-app/__tests__/components/SecondarySchoolRow.test.tsx
+++ b/nextjs-app/__tests__/components/SecondarySchoolRow.test.tsx
@@ -79,7 +79,7 @@ describe('SecondarySchoolRow admissions tag', () => {
expect(screen.queryByText('Selective')).not.toBeInTheDocument();
});
- it.each(['None', 'Does not apply', '', null])(
+ it.each(['None', 'Does not apply'])(
'gives no faith tag when the religious character is %p',
(religious_denomination) => {
render(
@@ -87,16 +87,32 @@ describe('SecondarySchoolRow admissions tag', () => {
school={{ ...base, admissions_policy: 'Not applicable', religious_denomination }}
/>,
);
+ expect(screen.queryByText(religious_denomination)).not.toBeInTheDocument();
expect(screen.queryByText('Faith priority')).not.toBeInTheDocument();
},
);
- it('tags a school with a religious character', () => {
+ it('tags the religious character the register records, not "Faith priority"', () => {
render(
,
);
- expect(screen.getByText('Faith priority')).toBeInTheDocument();
+ expect(screen.getByText('Church of England')).toBeInTheDocument();
+ expect(screen.queryByText('Faith priority')).not.toBeInTheDocument();
+ });
+});
+
+describe('SecondarySchoolRow shares the school page flags', () => {
+ it("prints the type in the search filter's terms", () => {
+ render();
+ expect(screen.getByText('State school')).toBeInTheDocument();
+ expect(screen.queryByText('Academy')).not.toBeInTheDocument();
+ });
+
+ it("flags a girls' school and fees", () => {
+ render();
+ expect(screen.getByText("Girls' school")).toBeInTheDocument();
+ expect(screen.getByText('Fee-paying')).toBeInTheDocument();
});
});
diff --git a/nextjs-app/components/SchoolRow.module.css b/nextjs-app/components/SchoolRow.module.css
index ce61578..4348519 100644
--- a/nextjs-app/components/SchoolRow.module.css
+++ b/nextjs-app/components/SchoolRow.module.css
@@ -100,6 +100,19 @@
color: var(--text-secondary);
}
+/* Changes who can apply or what it costs. Outlined, not tinted: a fact, not
+ a verdict. An inset ring keeps the box the size of its neighbours. */
+.conditionTag {
+ display: inline-block;
+ padding: 0.0625rem 0.4rem;
+ font-size: 0.75rem;
+ font-weight: 600;
+ line-height: 1.4;
+ border-radius: 4px;
+ box-shadow: inset 0 0 0 1px rgba(var(--ink-rgb), 0.4);
+ color: var(--text-primary);
+}
+
/* Line 3: stats */
.line3 {
display: flex;
diff --git a/nextjs-app/components/SchoolRow.tsx b/nextjs-app/components/SchoolRow.tsx
index fe167b0..f250975 100644
--- a/nextjs-app/components/SchoolRow.tsx
+++ b/nextjs-app/components/SchoolRow.tsx
@@ -3,13 +3,14 @@
* Four-line row for primary school search results
*
* Line 1: School name · Ofsted badge (framework-aware)
- * Line 2: School type · Age range · Denomination · Gender
+ * Line 2: Phase · Type · Age range · the school page's flags (lib/schoolFacts)
* Line 3: Reading, Writing & Maths % · trend arrow · vs-national delta · Pupils
* Line 4: Local authority · Distance
*/
import type { School } from '@/lib/types';
-import { formatPercentage, calculateTrend, getPhaseStyle, schoolUrl, buildOfstedListBadge, formatAgeRange, hasReligiousCharacter, isProposedToClose, isSpecialSchool, listRwmValue } from '@/lib/utils';
+import { formatPercentage, calculateTrend, getPhaseStyle, schoolUrl, buildOfstedListBadge, formatAgeRange, isProposedToClose, isSpecialSchool, listRwmValue } from '@/lib/utils';
+import { schoolFlags, schoolTypeLabel } from '@/lib/schoolFacts';
import styles from './SchoolRow.module.css';
interface SchoolRowProps {
@@ -33,8 +34,8 @@ export function SchoolRow({
const phase = getPhaseStyle(school.phase);
const ofstedBadge = buildOfstedListBadge(school);
- const showGender = school.gender && school.gender.toLowerCase() !== 'mixed';
- const showDenomination = hasReligiousCharacter(school.religious_denomination);
+ const typeLabel = schoolTypeLabel(school);
+ const flags = schoolFlags(school);
// The school's OWN figure and its year-over-year trend are same-school
// measures — shown whenever there's a real value (not the all-zero
@@ -77,10 +78,16 @@ export function SchoolRow({
{phase.label}
)}
- {school.school_type && {school.school_type}}
+ {typeLabel && {typeLabel}}
{school.age_range && {formatAgeRange(school.age_range)}}
- {showDenomination && {school.religious_denomination}}
- {showGender && {school.gender}}
+ {flags.map((flag) => (
+
+ {flag.label}
+
+ ))}
{isProposedToClose(school) && (
⚠ Proposed to close
)}
diff --git a/nextjs-app/components/SecondarySchoolRow.module.css b/nextjs-app/components/SecondarySchoolRow.module.css
index 2c82faa..1031f4c 100644
--- a/nextjs-app/components/SecondarySchoolRow.module.css
+++ b/nextjs-app/components/SecondarySchoolRow.module.css
@@ -183,9 +183,17 @@
white-space: nowrap;
}
-.selectiveTag {
- background: rgba(var(--status-below-rgb), 0.1);
- color: var(--status-below);
+/* Changes who can apply or what it costs. Outlined, not tinted: a fact, not
+ a verdict. An inset ring keeps the box the size of its neighbours. */
+.conditionTag {
+ display: inline-block;
+ padding: 0.0625rem 0.4rem;
+ font-size: 0.75rem;
+ font-weight: 600;
+ line-height: 1.4;
+ border-radius: 4px;
+ box-shadow: inset 0 0 0 1px rgba(var(--ink-rgb), 0.4);
+ color: var(--text-primary);
}
/* ── Ofsted badge ────────────────────────────────────── */
diff --git a/nextjs-app/components/SecondarySchoolRow.tsx b/nextjs-app/components/SecondarySchoolRow.tsx
index 762937d..2c5181e 100644
--- a/nextjs-app/components/SecondarySchoolRow.tsx
+++ b/nextjs-app/components/SecondarySchoolRow.tsx
@@ -3,7 +3,7 @@
* Four-line row for secondary school search results
*
* Line 1: School name · Ofsted badge
- * Line 2: School type · Age range · Gender · Sixth form · Admissions tag
+ * Line 2: Phase · Type · Age range · the school page's flags (lib/schoolFacts)
* Line 3: Attainment 8 (large) · ±LA avg delta · Pupils
* Line 4: LA name · distance
*/
@@ -11,22 +11,10 @@
'use client';
import type { School } from '@/lib/types';
-import { buildOfstedListBadge, getPhaseStyle, schoolUrl, formatAgeRange, hasReligiousCharacter, isProposedToClose, isSpecialSchool } from '@/lib/utils';
+import { buildOfstedListBadge, getPhaseStyle, schoolUrl, formatAgeRange, isProposedToClose, isSpecialSchool } from '@/lib/utils';
+import { schoolFlags, schoolTypeLabel } from '@/lib/schoolFacts';
import styles from './SecondarySchoolRow.module.css';
-function detectAdmissionsTag(school: School): string | null {
- // Exact match: "Non-selective" contains "selective", so a substring test
- // tagged every comprehensive as Selective.
- if (school.admissions_policy?.trim().toLowerCase() === 'selective') return 'Selective';
- if (hasReligiousCharacter(school.religious_denomination)) return 'Faith priority';
- return null;
-}
-
-function hasSixthForm(school: School): boolean {
- // GIAS OfficialSixthForm flag; missing (pipeline not yet re-run) => false.
- return school.has_sixth_form ?? false;
-}
-
interface SecondarySchoolRowProps {
school: School;
isLocationSearch?: boolean;
@@ -64,9 +52,8 @@ export function SecondarySchoolRow({
? att8 - laAvgAttainment8
: null;
- const admissionsTag = detectAdmissionsTag(school);
- const sixthForm = hasSixthForm(school);
- const showGender = school.gender && school.gender.toLowerCase() !== 'mixed';
+ const typeLabel = schoolTypeLabel(school);
+ const flags = schoolFlags(school);
return (
@@ -90,19 +77,16 @@ export function SecondarySchoolRow({
{phase.label}
)}
- {school.school_type && {school.school_type}}
+ {typeLabel && {typeLabel}}
{school.age_range && {formatAgeRange(school.age_range)}}
- {showGender && (
- {school.gender}
- )}
- {sixthForm && (
- Sixth form
- )}
- {admissionsTag && (
-
- {admissionsTag}
+ {flags.map((flag) => (
+
+ {flag.label}
- )}
+ ))}
{isProposedToClose(school) && (
⚠ Proposed to close
)}