Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions src/stable/components/News/News.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/stable/components/News/News.css.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 78 additions & 0 deletions src/stable/components/News/News.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import styles from '!!raw-loader!./News.css';

const template = document.createElement('template');
template.innerHTML = `
<style>
${styles}
</style>

<div class="news-card">

<div class="news-header">
<h3 class="news-title">
<slot name="news-title" id="newsTitleSlot">Default news title: Lorem ipsum dolor sit amet.</slot>
<svg class="chevron-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
<polyline points="9 6 15 12 9 18"></polyline>
</svg>
</h3>
</div>


<div class="news-meta">
<span id="news-date"></span>
<span class="news-tags">
<slot name="tags" id="tagsSlot"></slot>
</span>
</div>

</div>
`;

class News extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.appendChild(template.content.cloneNode(true));
}

static get observedAttributes() {
return ['datetime'];
}

attributeChangedCallback(name, oldValue, newValue) {
if (name === 'datetime') {
this.formatDate(newValue);
}
}

connectedCallback() {
const date = this.getAttribute('datetime');
this.formatDate(date);
}

formatDate(dateStr) {
// Parse the string into a Date object
const date = new Date(dateStr);

// Validate the date
if (isNaN(date.getTime())) {
this.shadowRoot.querySelector('#news-date').textContent = 'Invalid date';
return;
}

// Format the date — e.g., "June 1, 2024"
const formatted = date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
});

// Insert the formatted date into the DOM
const dateElement = this.shadowRoot.querySelector('#news-date');
if (dateElement) {
dateElement.textContent = formatted;
}
}
}

export { News as default };
86 changes: 86 additions & 0 deletions src/stable/components/News/News.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,300;0,400&display=swap');

.news-card {
min-width: 340px;
width: 100%;
box-sizing: border-box;
background: #fff;
border-radius: 8px;
padding: 20px 24px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}

.news-header {
display: block;
margin-bottom: 4px;
}

.news-title {
font-family: 'Montserrat', sans-serif;
font-weight: 400;
font-size: 16px;
line-height: 1;
letter-spacing: 0;
margin: 0;
display: inline;
color: #222;

// Chevron hover effect
&:hover .chevron-icon,
.chevron-icon:hover {
transform: translateX(4px);
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
cursor: pointer;
}

.chevron-icon {
stroke: #333;
width: 1em;
height: 1em;
vertical-align: -0.15em;
margin-left: 0.15em;
display: inline-block;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
pointer-events: auto;
cursor: pointer;
}

.news-meta {
margin-top: 10px;
display: flex;
align-items: center;
gap: 14px;
}

#news-date {
font-family: 'Montserrat', sans-serif;
font-weight: 300;
font-style: italic;
font-size: 14px;
line-height: 1;
letter-spacing: 0;
color: #444;
cursor: pointer;

// Chevron hover effect when hovering date
&:hover ~ .news-title .chevron-icon {
transform: translateX(4px);
}
}

.news-tags {
font-family: 'Montserrat', sans-serif;
font-weight: 300;
font-style: italic;
font-size: 14px;
line-height: 1;
letter-spacing: 0;
color: #444;
cursor: pointer;

// Chevron hover effect when hovering tags
&:hover ~ .news-title .chevron-icon {
transform: translateX(4px);
}
}
2 changes: 2 additions & 0 deletions src/stable/components/News/cod-news.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import News from './News';
customElements.define('cod-news', News);
94 changes: 94 additions & 0 deletions src/stable/docs/News.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {
Meta,
Title,
Subtitle,
Description,
Primary,
Controls,
Stories,
Story,
Source,
} from '@storybook/blocks';

import * as NewsStories from '../stories/news.stories';

import '../../../.storybook/docs'; // Import all documentation components

<Meta of={NewsStories} />

# News

<div className="component-summary">
{' '}
The **News** component displays a stylized news card with a title, date, and optional
tags slot. It formats the `datetime` attribute automatically.
</div>

## Examples

### Basic

<Description of={NewsStories.Basic} />
<Story of={NewsStories.Basic} inline={true} />
<Source of={NewsStories.Basic} />

## Slots

<doc-slots-table
data={JSON.stringify([
{"name": "news-title", "description": "The main title of the news component."},
{"name": "tags", "description": "A placeholder for the tag component."},
])}>
</doc-slots-table>

## HTML Attributes / JS Properties

Supported HTML attributes. If the attribute is reflected in a JS property, that attribute will have a 'reflects' tag next to the name.

<doc-js-properties-table
data={JSON.stringify([
{
"name": "datetime",
"description": "An ISO date string. The component formats and displays it in a readable format (e.g., 'June 1, 2024').",
"type": "string",
"default": "-",
"reflects": false
}
])}>
</doc-js-properties-table>

## Events

<doc-events-table
data={JSON.stringify([])}>
</doc-events-table>

## Methods

<doc-methods-table
data={JSON.stringify([])}>
</doc-methods-table>

## Custom CSS Properties

<doc-css-custom-properties-table
data={JSON.stringify([])}>
</doc-css-custom-properties-table>

## CSS Parts

<doc-css-parts-table
data={JSON.stringify([])}>
</doc-css-parts-table>

## Dependencies

<doc-dependencies-list data={JSON.stringify([])}></doc-dependencies-list>

## Accessibility

The News component uses semantic HTML:

- The title is wrapped in a `<h3>` for hierarchy.
- Date text is plain text.
- The chevron icon uses `aria-hidden="true"` and is not focusable.
1 change: 1 addition & 0 deletions src/stable/index-stable.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ import './components/GovBanner/cod-gov-banner.js';
import './components/SectionNavigation/cod-section-navigation.js';
import './components/ServiceButton/cod-service-button.js';
import './components/Tag/cod-tag.js';
import './components/News/cod-news.js';
Loading
Loading