Skip to content

News Page

Amy Wang edited this page Aug 11, 2025 · 2 revisions

Overview

The NEFAC News page displays news articles using GraphQL for data fetching and Apollo Client for state management. The page has a featured article at the top, followed by a grid of latest news articles with pagination support.

Architecture

/pages/news/index.tsx - Main news page component handling data fetching and layout

/components/news-page/NewsBubble.tsx - Reusable component for rendering individual news articles, and fetching data for featured article content

/components/news-page/NewsInterfaces.tsx - TypeScript interfaces defining data structures

GraphQL Implementation

Query Structure

The news page uses cursor-based pagination through GraphQL's connection pattern:

query GetNewsPosts($first: Int!, $after: String) {
  newsPosts(first: $first, after: $after) {
    pageInfo {
      hasNextPage
      endCursor
    }
    edges {
      node {
        id
        title
        date
        link
      }
    }
  }
}

Pagination

Initial load

  • Fetches 7 articles (INITIAL_BATCH_SIZE = 7)
  • First article becomes featured, remaining 6 display in grid

Subsequent loads

  • Fetches 6 articles (SUBSEQUENT_BATCH_SIZE = 6)
  • Maintains consistent grid layout without duplicating featured post

Cursor management

  • Uses endCursor from pageInfo to track pagination position
  • hasNextPage boolean determines if "Show More" button should display

Apollo Client Integration

const { data, loading, error, fetchMore } = useQuery(GET_NEWS, {
  variables: { first: INITIAL_BATCH_SIZE, after: null },
  notifyOnNetworkStatusChange: true,
});
  • fetchMore function handles pagination requests
  • notifyOnNetworkStatusChange provides loading states during pagination
updateQuery: (prev, { fetchMoreResult }) => {
  if (!fetchMoreResult) return prev;
  return {
    newsPosts: {
      ...fetchMoreResult.newsPosts,
      edges: [
        ...prev.newsPosts.edges,
        ...fetchMoreResult.newsPosts.edges,
      ],
    },
  };
}
  • updateQuery merges new results with existing data, appending new articles to the list of existing ones

Featured Post

Dynamic Content Fetching

Featured posts require additional content for preview generation. A separate GraphQL query in /NewsBubble.tsx fetches full content:

query GetNewsPostContent($id: ID!) {
  newsPost(id: $id) {
    content
  }
}
  • Query only executes for featured posts (skip: !featured)
  • Falls back to prop content if GraphQL fetch fails
  • Handles loading and error states

Content Preview Generation

The extractFirstSentenceFromHtml function processes fetched HTML content to create readable previews. It follows these steps to return preview text:

  1. Parse HTML content into DOM elements
  2. Extract text from paragraph elements
  3. Filter out image captions and author bylines
  4. Handle abbreviations (e.g. "U.S.") to prevent premature sentence breaks
  5. Collect up to 3 sentences with minimum 4 words each
  6. Return combined preview text

Previous Implementation

Overview

The previous implementation used WordPress REST API to fetch news articles directly from the NEFAC WordPress site. It had two separate functions for fetching the featured article and paginated news, both using _embed=true to include featured media metadata. Previous designs for the news page included each article preview containing an image, which was selected using a hierarchical process that prioritized the WordPress featured media, followed by images within the content, and lastly, a local placeholder image. The previous implementation also had traditional pagination with numbered controls and chevron navigation.

Image Extraction

The image extraction process in the previous implementation sourced images with the following priority:

  1. WordPress Featured Media (via _embedded['wp:featuredmedia'])
  2. Content-embedded images, via HTML parsing (extracting first <img> tag from article content
  3. Local placeholder fallback (/images/computer.png)
const getImageFromArticle = (article) => {
  const imgRegex = /<img[^>]+src="([^">]+)"/;
  const match = article.match(imgRegex);
  return match ? match[1] : null;
};

// Usage in component
image={
  article.featured_media && article._embedded?.['wp:featuredmedia']?.[0]?.source_url
    ? article._embedded?.['wp:featuredmedia']?.[0]?.source_url
    : getImageFromArticle(article.content.rendered) || '/images/computer.png'
}
Clone this wiki locally