From 5a4719b461684ea6d6a608aeaae8535cad6f29c9 Mon Sep 17 00:00:00 2001 From: violet Date: Thu, 5 Nov 2026 16:30:07 -0500 Subject: [PATCH 1/2] Update the public records conference from the partner's revised questions The summary now opens with the reworded paragraph and adds a second one on Question 1, with its link written inline because the summary renders as plain text. The first point where the bills match is reworded to ask how requests for the Legislature's records would work. Verified: the YAML loads, its summary and every row match the revised document by text comparison, and prettier passes. Not verified: the page was not rendered, and nothing was synced. Co-Authored-By: Claude Sonnet 5.5 --- conferences/194/public-records.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/conferences/194/public-records.yaml b/conferences/194/public-records.yaml index f6ffdaa3a..2c7b8f703 100644 --- a/conferences/194/public-records.yaml +++ b/conferences/194/public-records.yaml @@ -2,7 +2,7 @@ id: public-records name: Public Records court: 194 title: An Act promoting transparency and public access in state government -gist: Both bills would let the public request listed categories of the Legislature's records through a separate process run by a records officer in each chamber, and both bring the Governor's office under the public records law for records from January 2027 on. They split on how courts review denials, whether the State Auditor may audit the Legislature (House only), which exemptions apply, and a journalist shield law (Senate only). +gist: Both bills would let the public request a set list of the Legislature's records through a separate process run by a records officer in each chamber, and both would bring the Governor's office under the public records law. They differ most on how courts review denials, whether the State Auditor may audit the Legislature (House only), which exemptions apply, and a journalist shield law (Senate only). Both bills were written before voters approved Question 1 (https://www.sec.state.ma.us/divisions/elections/research-and-statistics/2026-Information-for-voters/2026-ballot-question1.htm) on Nov. 3, 2026. Starting Jan. 1, 2027, that law puts the Legislature and the Governor's office under the general public records law, with new exemptions for constituent casework and for documents about developing policy. A final bill based on either version would replace that approach for the Legislature's records. senateBillId: S3244 houseBillId: H5469 canonicalBillId: H5469 @@ -50,8 +50,8 @@ differences: - heading: Should the bill include money to carry out the new rules? house: "Yes, $1,000,000 for fiscal year 2026: $250,000 each for the Governor's office, the Senate, the House and joint legislative operations. That fiscal year has ended. (§§1–2)" similarities: - - heading: Would the Legislature have to answer public records requests? - body: Yes, but only for a set list of final legislative records, through its own process. That process is the only way to get them; the general public records law would not apply to the Legislature. + - heading: How would requests for the Legislature's records work? + body: Only a set list of final legislative records would be available, through the Legislature's own process. That process would be the only way to get them; the general public records law would not apply to the Legislature. - heading: Which Legislature records would be covered? body: Bills, amendments, summaries and fiscal notes, with their sponsors; hearing notices, attendance and recordings or transcripts; written testimony (subject to committee rules on sensitive information); committee votes and reports; rules, calendars, journals and roll calls; leadership, committee and office assignments; employee manuals and policies; appointment notices; reports required by law; spending records sent to the Comptroller, including salaries and payments to vendors; final internal audits; ethics disclosures; the State House press list; and approved district maps. - heading: Who would handle requests? From 01ed43d599b3d75f38f226344210248c239673ed Mon Sep 17 00:00:00 2001 From: violet Date: Thu, 5 Nov 2026 16:40:12 -0500 Subject: [PATCH 2/2] Write a conference's gist as markdown, so it can carry a link The gist rendered as one plain paragraph, which left the public records conference's note on Question 1 as a bare address with no break before it. It is now markdown, read with react-markdown as the ballot question alert is, with links in the page's dotted style opening in a new tab. The feedback line beneath is styled by its place after the gist's text rather than as any second paragraph, so a gist of several paragraphs does not take its look. The public records gist is rewritten as two paragraphs with a real link; the other eight are single plain sentences and read the same. Verified: the conference and ballot question jest suites pass, including a new test that a gist link opens in a new tab; prettier and eslint are clean on the touched files; the real react-markdown renders the public records gist as two paragraphs with a link and the pet equity gist as one. Not verified: the page was not rendered in a browser, so the link style, the paragraph spacing and the feedback line beneath were not looked at, and nothing was synced. The production build was not run. Co-Authored-By: Claude Sonnet 5.5 --- .../ConferenceDetails.switching.test.tsx | 2 ++ .../conference/ConferenceDetails.test.tsx | 36 +++++++++++++++++++ .../conference/ConferenceDifferences.tsx | 28 +++++++++++++-- components/conference/types.ts | 2 +- conferences/194/public-records.yaml | 5 ++- docs/updating-conferences.md | 2 +- functions/src/conferences/types.ts | 3 +- 7 files changed, 72 insertions(+), 6 deletions(-) diff --git a/components/conference/ConferenceDetails.switching.test.tsx b/components/conference/ConferenceDetails.switching.test.tsx index 0bff6494f..3150ed7bc 100644 --- a/components/conference/ConferenceDetails.switching.test.tsx +++ b/components/conference/ConferenceDetails.switching.test.tsx @@ -19,6 +19,8 @@ jest.mock("next/router", () => ({ useRouter: () => ({ asPath: "/conferences/194/a" }) })) jest.mock("usehooks-ts", () => ({ useMediaQuery: () => false })) +// react-markdown ships ESM only, and the gist here is empty. +jest.mock("react-markdown", () => ({ __esModule: true, default: () => null })) jest.mock("../featureFlags", () => ({ useFlags: () => ({ legislators: false, hearingsAndTranscriptions: true }) })) diff --git a/components/conference/ConferenceDetails.test.tsx b/components/conference/ConferenceDetails.test.tsx index cc8debd35..de8e75ebf 100644 --- a/components/conference/ConferenceDetails.test.tsx +++ b/components/conference/ConferenceDetails.test.tsx @@ -47,6 +47,29 @@ const routeChangeStart = (url: string) => jest.mocked(Router.events.on).mock.calls[0][1](url) jest.mock("usehooks-ts", () => ({ useMediaQuery: () => false })) +// react-markdown ships ESM only. This reads one link, as the gist writes it. +jest.mock("react-markdown", () => ({ + __esModule: true, + default: ({ + children, + components + }: { + children: string + components?: { a?: React.ComponentType } + }) => { + const match = children.match(/\[([^\]]+)\]\(([^)]+)\)/) + const Link = components?.a ?? "a" + return match ? ( +

+ {children.slice(0, match.index)} + {match[1]} + {children.slice((match.index ?? 0) + match[0].length)} +

+ ) : ( +

{children}

+ ) + } +})) let mockLegislators = false jest.mock("../featureFlags", () => ({ @@ -607,6 +630,19 @@ describe("ConferenceDetails", () => { expect(screen.getByText("Bans use.")).toBeInTheDocument() }) + it("links a markdown link in the gist, out to a new tab", () => { + render( + + ) + const link = screen.getByRole("link", { name: "Question 1" }) + expect(link).toHaveAttribute("href", "https://example.org/q1") + expect(link).toHaveAttribute("target", "_blank") + expect(link).toHaveAttribute("rel", "noopener noreferrer") + }) + it("opens a term's note on hover", () => { render() fireEvent.pointerEnter(screen.getByText("Chapter 93M")) diff --git a/components/conference/ConferenceDifferences.tsx b/components/conference/ConferenceDifferences.tsx index 21d980a49..673ea5654 100644 --- a/components/conference/ConferenceDifferences.tsx +++ b/components/conference/ConferenceDifferences.tsx @@ -1,5 +1,6 @@ import { Fragment, KeyboardEvent, useRef, useState } from "react" import { Trans, useTranslation } from "next-i18next" +import ReactMarkdown from "react-markdown" import styled from "styled-components" import { FEEDBACK_URL } from "../common" import { shortName } from "../formatting" @@ -80,13 +81,22 @@ const Gist = styled.div` line-height: 1.65; color: ${ink.mid}; } - p + p { + /* The feedback line beneath the gist. */ + > div + p { margin-top: 8px; font-size: 0.8125rem; color: ${ink.faint}; } ` +/** The gist's own text, written as markdown, so its paragraphs are told + * apart from the feedback line. */ +const GistText = styled.div` + p + p { + margin-top: 8px; + } +` + const DottedLink = styled.a` font-weight: 600; color: var(--maple-brand-primary); @@ -378,7 +388,21 @@ export function ConferenceDifferences({ {gist && ( -

{gist}

+ + ( + + ) + }} + > + {gist} + +