Skip to content

MNTOR-4298 - Announcements ADR #5866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open

MNTOR-4298 - Announcements ADR #5866

wants to merge 14 commits into from

Conversation

codemist
Copy link
Collaborator

@codemist codemist commented May 7, 2025

References:

Jira: MNTOR-4298

Description

Introduces a Fluent UI section and accompanying documentation to the Announcements admin page, giving Product and QA teams clearer context and improving usability for this feature.

Screenshot (if applicable)

image image

Not applicable.

How to test

Checklist (Definition of Done)

  • Localization strings (if needed) have been added.
  • Commits in this PR are minimal and have descriptive commit messages.
  • I've added or updated the relevant sections in readme and/or code comments
  • I've added a unit test to test for potential regressions of this bug.
  • If this PR implements a feature flag or experimentation, I've checked that it still works with the flag both on, and with the flag off.
  • If this PR implements a feature flag or experimentation, the Ship Behind Feature Flag status in Jira has been set
  • Product Owner accepted the User Story (demo of functionality completed) or waived the privilege.
  • All acceptance criteria are met.
  • Jira ticket has been updated (if needed) to match changes made during the development process.
  • Jira ticket has been updated (if needed) with suggestions for QA when this PR is deployed to stage.

Copy link

github-actions bot commented May 7, 2025

@@ -78,6 +78,7 @@
"@fluent/bundle": "^0.19.1",
"@fluent/langneg": "^0.7.0",
"@fluent/react": "^0.15.2",
"@fluent/syntax": "^0.19.0",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty safe dependency built for tooling around fluent:

It’s designed to parse, analyze, process, and serialize Fluent files.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That link goes to the docs for the Python implementation of Fluent - I think you mean this one :)

@codemist codemist marked this pull request as ready for review May 14, 2025 14:05
@codemist codemist requested a review from flozia May 14, 2025 14:05
@@ -31,6 +33,8 @@ export const AnnouncementsAdmin = (props: Props) => {
const [activeAnnouncementToEdit, setActiveAnnouncementToEdit] =
useState<AnnouncementRow | null>(null);

console.log(props.fluentStrings);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
console.log(props.fluentStrings);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alt="Fallback image"
width={500}
height={300}
key={activeAnnouncement.id}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The keys of the small and big images are duplicates: To make sure they don’t collide we should make them unique with for example:

Suggested change
key={activeAnnouncement.id}
key={`${activeAnnouncement.id}-small-fallback`}
key={`${activeAnnouncement.id}-small`}
key={`${activeAnnouncement.id-big-fallback`}
key={`${activeAnnouncement.id}-big`}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 326 to 347
{bigImageUnavailable ? (
<Image
alt="Fallback image"
width={500}
height={300}
key={activeAnnouncement.id}
className={styles.bigImage}
src="/images/announcements/fallback/big.svg"
onLoadingComplete={() => setBigImageIsLoading(false)}
/>
) : (
<Image
alt="Announcement preview"
width={500}
height={300}
key={activeAnnouncement.id}
src={bigImagePath}
className={styles.bigImage}
onLoadingComplete={() => setBigImageIsLoading(false)}
onError={() => setBigImageUnavailable(true)}
/>
)}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion (non-blocking): The fallback image logic is used a couple of times — it would be nice if we could unify this into a reusable component.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

36c6d22 Added ImageWithFallback which handles repeated logic.

Comment on lines 72 to 74
list-style: none;
margin-top: tokens.$spacing-md;
padding-left: 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For list resetting, we have the class .noList available that you could add to the <ul>.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 28 to 30
const ftlPath = path.resolve("locales-pending/announcements.ftl");
const raw = fs.readFileSync(ftlPath, "utf8");
const resource: Resource = parse(raw, { withSpans: false });
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: This part should be wrapped in a try {} catch {} to handle missing or invalid file contents when parsing the file.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 75 to 81
if (field === "title") {
result[id].title = value;
} else if (field === "description") {
result[id].description = value;
} else if (field === "cta-label") {
result[id].ctaLabel = value;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Instead of mapping the string IDs could we also leave them as they are (in kebab-case)? If we still need to map them: Since we are not handling any additional logic we could also map them like this:

Suggested change
if (field === "title") {
result[id].title = value;
} else if (field === "description") {
result[id].description = value;
} else if (field === "cta-label") {
result[id].ctaLabel = value;
}
const map: Record<string, keyof AnnouncementGroup> = {
title: "title",
description": "description",
"cta-label": "ctaLabel",
};
result[id][map[field]] = value;

… or maybe parse the IDs to camelCase if we expect them to match otherwise.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even when getFluentStrings is only used for the announcements admin panel, this function would benefit from some initial unit tests to validate the expected behavior. This would be especially helpful when making changes in the future.

Comment on lines +21 to +24
import { GroupedFluentAnnouncements } from "./getFluentStrings";
import { ImageWithFallback } from "./ImageWithFallback";
import { FluentStringsView } from "./FluentStringsView";
import { AnnouncementsDocsView } from "./DocsView";
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file was getting kind of unwieldy - I split out the tabs into separate components.

@codemist codemist requested a review from Vinnl June 18, 2025 13:19
Copy link
Collaborator

@Vinnl Vinnl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started out looking at the changed code, but it was so much, is never exposed to users, and it seems like Florian has also already done a pass on that, so I opted to stop my review of that and leave it to Florian to finish that up on Monday. I will say, though, that I'm impressed that you dove into the depths of the available Fluent packages to be able to display an interface over them :)

Speaking of, that interface looks useful, and having the docs there in context is as well. (Though small nit: it does not Record any Architectural Decisions, so it's not an ADR.) My main suggestion, though, would be to split it up in two: docs for admins using the UI (these docs), and docs for the parts that need to be done by an engineer, which could just live as a Markdown file under /docs/ in the repo (with a reference to it from the UI). Because right now, I imagine Tony would be a bit lost when trying to follow these docs.

@@ -78,6 +78,7 @@
"@fluent/bundle": "^0.19.1",
"@fluent/langneg": "^0.7.0",
"@fluent/react": "^0.15.2",
"@fluent/syntax": "^0.19.0",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That link goes to the docs for the Python implementation of Fluent - I think you mean this one :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't care so much because it's just an admin page, but I always do a double-take whenever I type margin, which I can highly recommend (just like e.g. for float) 🙂 There's probably times when they're justified, but most of the time they make the layout of the rest of the page unpredictable over time.

>
<div>
<p className={styles.title}>
<div className={styles.tabBar}>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, not a blocker since it's "just" an admin page, but react-aria implements tab behaviour - e.g. to make sure you can switch tabs using the arrow keys, and to announce them to screen readers. Just FYI for the future.

@codemist codemist requested a review from flozia June 24, 2025 14:51
Comment on lines +255 to +260
<button
className={styles.addButton}
onClick={() => setIsModalOpen(true)}
>
+ Add new announcement
</button>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A <button> as a direct child of <ul> is not valid and would need to be wrapped in an <li>.

<dt>Big Image</dt>
<dd>
<ImageWithFallback
src={smallImagePath}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be:

Suggested change
src={smallImagePath}
src={bigImagePath}

?

Copy link
Collaborator

@flozia flozia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering this is for admins only and the purpose of these additions, it looks good. There are some open pending comments that deserve a response before finishing this PR up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants