roadmap-milestone-card
roadmap
roadmap-page and test file
This commit is contained in:
Mark Xiong 2024-07-19 15:31:18 -05:00 коммит произвёл GitHub
Родитель 13183fd910
Коммит 16498d279b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
4 изменённых файлов: 150 добавлений и 97 удалений

Просмотреть файл

@ -1,5 +1,7 @@
import {SlPopup} from '@shoelace-style/shoelace';
import {LitElement, html, nothing} from 'lit';
import {ref, createRef} from 'lit/directives/ref.js';
import {customElement, property} from 'lit/decorators.js';
import {createRef, ref} from 'lit/directives/ref.js';
import {ROADMAP_MILESTONE_CARD_CSS} from '../css/elements/chromedash-roadmap-milestone-card-css.js';
const REMOVED_STATUS = ['Removed'];
@ -8,33 +10,37 @@ const ORIGIN_TRIAL = ['Origin trial'];
const BROWSER_INTERVENTION = ['Browser Intervention'];
const NO_FEATURE_STRING = 'NO FEATURES ARE PLANNED FOR THIS MILESTONE YET';
export interface TemplateContent {
channelLabel: string;
h1Class: string;
channelTag?: string;
dateText: string;
featureHeader: string;
}
@customElement('chromedash-roadmap-milestone-card')
class ChromedashRoadmapMilestoneCard extends LitElement {
infoPopupRef = createRef();
infoPopupRef = createRef<SlPopup>();
static styles = ROADMAP_MILESTONE_CARD_CSS;
static get properties() {
return {
starredFeatures: {type: Object},
highlightFeature: {type: Number},
templateContent: {type: Object},
channel: {type: Object},
showDates: {type: Boolean},
signedIn: {type: Boolean},
};
}
constructor() {
super();
this.starredFeatures = new Set();
}
@property({attribute: false})
starredFeatures = new Set<number>();
@property({attribute: false})
highlightFeature!: number;
@property({attribute: false})
templateContent!: TemplateContent;
@property({attribute: false})
channel; //TODO(markxiong0122): Type this as Channel when PR#4085 is merged
@property({type: Boolean})
showDates = false;
@property({type: Boolean})
signedIn = false;
/**
* Returns the number of days between a and b.
* @param {!Date} a
* @param {!Date} b
* @return {!{days: number, future: boolean}}
*/
_dateDiffInDays(a, b) {
_dateDiffInDays(a: Date, b: Date): {days: number; future: boolean} {
// Discard time and time-zone information.
const utc1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
const utc2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
@ -45,7 +51,7 @@ class ChromedashRoadmapMilestoneCard extends LitElement {
}
_computeDate(dateStr, addYear = false) {
const opts = {month: 'short', day: 'numeric'};
const opts: Intl.DateTimeFormatOptions = {month: 'short', day: 'numeric'};
if (addYear) {
opts.year = 'numeric';
}
@ -109,7 +115,7 @@ class ChromedashRoadmapMilestoneCard extends LitElement {
_computeDaysUntil(dateStr) {
const date = new Date(dateStr);
if (isNaN(date)) {
if (isNaN(date.getTime())) {
return nothing;
}
const today = new Date();
@ -134,7 +140,7 @@ class ChromedashRoadmapMilestoneCard extends LitElement {
if (
e.relatedTarget != this.renderRoot.querySelector('#detailed-info-link')
) {
this.infoPopupRef.value.active = null;
this.infoPopupRef.value!.active = false;
}
}
@ -144,7 +150,7 @@ class ChromedashRoadmapMilestoneCard extends LitElement {
name="info-circle"
id="info-button"
@click=${() =>
(this.infoPopupRef.value.active = !this.infoPopupRef.value.active)}
(this.infoPopupRef.value!.active = !this.infoPopupRef.value!.active)}
@focusout=${this.hidePopup}
></sl-icon-button>
@ -388,8 +394,3 @@ class ChromedashRoadmapMilestoneCard extends LitElement {
`;
}
}
customElements.define(
'chromedash-roadmap-milestone-card',
ChromedashRoadmapMilestoneCard
);

Просмотреть файл

@ -1,11 +1,15 @@
import {LitElement, css, html} from 'lit';
import {ref, createRef} from 'lit/directives/ref.js';
import './chromedash-roadmap';
import {customElement, property, state} from 'lit/decorators.js';
import {createRef, ref} from 'lit/directives/ref.js';
import {SHARED_STYLES} from '../css/shared-css.js';
import {User} from '../js-src/cs-client';
import './chromedash-roadmap';
import {ChromedashRoadmap} from './chromedash-roadmap';
@customElement('chromedash-roadmap-page')
export class ChromedashRoadmapPage extends LitElement {
sectionRef = createRef();
roadmapRef = createRef();
sectionRef = createRef<HTMLElement>();
roadmapRef = createRef<ChromedashRoadmap>();
static get styles() {
return [
@ -27,23 +31,16 @@ export class ChromedashRoadmapPage extends LitElement {
];
}
static get properties() {
return {
user: {type: Object},
cardWidth: {type: Number},
numColumns: {type: Number},
viewOffset: {type: Number},
};
}
@property({type: Object, attribute: false})
user!: User;
@state()
cardWidth = 0;
@state()
numColumns = 0;
@state()
viewOffset = 0;
constructor() {
super();
this.user = {};
this.cardWidth = 0;
this.numColumns = 0;
this.viewOffset = 0;
this.boundHandleResize = this.handleResize.bind(this);
}
boundHandleResize = this.handleResize.bind(this);
connectedCallback() {
super.connectedCallback();
@ -60,7 +57,7 @@ export class ChromedashRoadmapPage extends LitElement {
}
handleResize() {
const containerWidth = this.sectionRef.value.offsetWidth;
const containerWidth = this.sectionRef.value!.offsetWidth;
const margin = 16;
let numColumns = 3;
@ -70,14 +67,16 @@ export class ChromedashRoadmapPage extends LitElement {
numColumns = 2;
}
this.numColumns = numColumns;
this.cardWidth = containerWidth / numColumns - margin;
if (containerWidth) {
this.cardWidth = containerWidth / numColumns - margin;
}
this.updateRoadmapMargin(false);
}
handleMove(e) {
const roadmap = this.roadmapRef.value;
if (!roadmap.lastFutureFetchedOn) return;
if (!roadmap?.lastFutureFetchedOn) return;
if (e.target.id == 'next-button') {
this.viewOffset -= 1; // move to newer version
@ -99,6 +98,7 @@ export class ChromedashRoadmapPage extends LitElement {
updateRoadmapMargin(animated) {
const roadmap = this.roadmapRef.value;
if (!roadmap) return;
if (animated) {
roadmap.classList.add('animate');
} else {
@ -147,5 +147,3 @@ export class ChromedashRoadmapPage extends LitElement {
`;
}
}
customElements.define('chromedash-roadmap-page', ChromedashRoadmapPage);

Просмотреть файл

@ -1,8 +1,8 @@
import {html} from 'lit';
import {assert, fixture} from '@open-wc/testing';
import {ChromedashRoadmapPage} from './chromedash-roadmap-page';
import {ChromeStatusClient} from '../js-src/cs-client';
import {html} from 'lit';
import sinon from 'sinon';
import {ChromeStatusClient} from '../js-src/cs-client';
import {ChromedashRoadmapPage} from './chromedash-roadmap-page';
describe('chromedash-roadmap-page', () => {
const starsPromise = Promise.resolve([123456]);
@ -100,11 +100,11 @@ describe('chromedash-roadmap-page', () => {
assert.instanceOf(component, ChromedashRoadmapPage);
// subheader exists
const subheaderDiv = component.shadowRoot.querySelector('div#subheader');
const subheaderDiv = component.renderRoot.querySelector('div#subheader');
assert.exists(subheaderDiv);
// releases-section exists and contains the previous and next buttons
const releasesSec = component.shadowRoot.querySelector(
const releasesSec = component.renderRoot.querySelector(
'section#releases-section'
);
assert.exists(releasesSec);
@ -112,7 +112,7 @@ describe('chromedash-roadmap-page', () => {
assert.include(releasesSec.innerHTML, 'button id="next-button"');
// chromedash-roadmap component exists
const roadmap = component.shadowRoot.querySelector('chromedash-roadmap');
const roadmap = component.renderRoot.querySelector('chromedash-roadmap');
assert.exists(roadmap);
});
});

Просмотреть файл

@ -1,9 +1,43 @@
import {LitElement, html, css} from 'lit';
import {showToastMessage} from './utils.js';
import '@polymer/iron-icon';
import {LitElement, css, html} from 'lit';
import {customElement, property, state} from 'lit/decorators.js';
import {SHARED_STYLES} from '../css/shared-css.js';
import {Feature} from '../js-src/cs-client.js';
import {TemplateContent} from './chromedash-roadmap-milestone-card.js';
import {showToastMessage} from './utils.js';
const TEMPLATE_CONTENT = {
//TODO(markxiong0122): move this to cs-client.js after converting it to TypeScript
interface MilestoneDetails {
branch_point: string;
earliest_beta: string;
earliest_beta_chromeos: string;
earliest_beta_ios: string;
feature_freeze: string;
final_beta: string;
final_beta_cut: string;
late_stable_date: string;
latest_beta: string;
ldaps: Record<string, string>;
ltc_date: string;
ltr_date: string;
ltr_last_refresh_date: string;
mstone: number;
owners: Record<string, string>;
stable_cut: string;
stable_cut_ios: string;
stable_date: string;
stable_refresh_first: string;
stable_refresh_second: string;
stable_refresh_third: string;
version: number;
features: Feature[];
}
interface MilestoneInfo {
[milestone: number]: MilestoneDetails;
}
const TEMPLATE_CONTENT: Record<string, TemplateContent> = {
stable_minus_one: {
channelLabel: 'Released',
h1Class: '',
@ -49,7 +83,8 @@ const SHOW_DATES = true;
const compareFeatures = (a, b) =>
a.name.localeCompare(b.name, 'fr', {ignorePunctuation: true}); // comparator for sorting milestone features
class ChromedashRoadmap extends LitElement {
@customElement('chromedash-roadmap')
export class ChromedashRoadmap extends LitElement {
static get styles() {
return [
...SHARED_STYLES,
@ -63,37 +98,58 @@ class ChromedashRoadmap extends LitElement {
`,
];
}
static get properties() {
return {
channels: {attribute: false},
signedIn: {type: Boolean},
starredFeatures: {type: Object}, // will contain a set of starred features
numColumns: {type: Number},
cardWidth: {type: Number}, // width of each milestone card
lastFutureFetchedOn: {type: Number}, // milestone number rendering of which caused fetching of next milestones
lastPastFetchedOn: {type: Number}, // milestone number rendering of which caused fetching of previous milestones
lastMilestoneVisible: {type: Number}, // milestone number visible on screen to the user
futureMilestoneArray: {type: Array}, // array to store the milestone numbers fetched after dev channel
pastMilestoneArray: {type: Array}, // array to store the milestone numbers fetched before stable channel
milestoneInfo: {type: Object}, // object to store milestone details (features version etc.) fetched after dev channel
highlightFeature: {type: Number}, // feature to highlight
cardOffset: {type: Number}, // left margin value
};
}
constructor() {
super();
this.channels = {};
this.shownChannelNames = [];
this.numColumns = 0;
this.cardWidth = 0;
this.starredFeatures = new Set();
this.futureMilestoneArray = [];
this.pastMilestoneArray = [];
this.milestoneInfo = {};
this.cardOffset = 0;
}
@property({type: Boolean})
signedIn;
@property({type: Number, attribute: false})
numColumns = 0;
@property({type: Number, attribute: false})
cardWidth = 0;
@state()
channels; //TODO(markxiong0122): Type this as Channel when PR#4085 is merged
@state()
starredFeatures = new Set<number>();
/**
* The timestamp of the last fetched future milestone.
*/
@state()
lastFutureFetchedOn!: number;
/**
* The timestamp of the last fetched past milestone.
*/
@state()
lastPastFetchedOn!: number;
/**
* The milestone number currently visible on the screen to the user.
*/
@state()
lastMilestoneVisible!: number;
/**
* Array to store the milestone numbers fetched after the dev channel.
*/
@state()
futureMilestoneArray: number[] = [];
/**
* Array to store the milestone numbers fetched before the stable channel.
*/
@state()
pastMilestoneArray: number[] = [];
/**
* Object to store milestone details (features, version, etc.) fetched after the dev channel.
*/
@state()
milestoneInfo!: MilestoneInfo;
/**
* The feature to highlight.
*/
@state()
highlightFeature: number | undefined = undefined;
/**
* The left margin value.
*/
@state()
cardOffset = 0;
@state()
shownChannelNames: string[] = [];
connectedCallback() {
super.connectedCallback();
@ -305,5 +361,3 @@ class ChromedashRoadmap extends LitElement {
`;
}
}
customElements.define('chromedash-roadmap', ChromedashRoadmap);