From 16498d279b2de7633ddf88bf72f92719b59c7df9 Mon Sep 17 00:00:00 2001 From: Mark Xiong Date: Fri, 19 Jul 2024 15:31:18 -0500 Subject: [PATCH] Typescript Migration: roadmap (#4097) roadmap-milestone-card roadmap roadmap-page and test file --- ...s => chromedash-roadmap-milestone-card.ts} | 59 ++++---- ...map-page.js => chromedash-roadmap-page.ts} | 48 ++++--- ...est.js => chromedash-roadmap-page_test.ts} | 12 +- ...edash-roadmap.js => chromedash-roadmap.ts} | 128 +++++++++++++----- 4 files changed, 150 insertions(+), 97 deletions(-) rename client-src/elements/{chromedash-roadmap-milestone-card.js => chromedash-roadmap-milestone-card.ts} (90%) rename client-src/elements/{chromedash-roadmap-page.js => chromedash-roadmap-page.ts} (80%) rename client-src/elements/{chromedash-roadmap-page_test.js => chromedash-roadmap-page_test.ts} (95%) rename client-src/elements/{chromedash-roadmap.js => chromedash-roadmap.ts} (78%) diff --git a/client-src/elements/chromedash-roadmap-milestone-card.js b/client-src/elements/chromedash-roadmap-milestone-card.ts similarity index 90% rename from client-src/elements/chromedash-roadmap-milestone-card.js rename to client-src/elements/chromedash-roadmap-milestone-card.ts index b69445ac..adf6e190 100644 --- a/client-src/elements/chromedash-roadmap-milestone-card.js +++ b/client-src/elements/chromedash-roadmap-milestone-card.ts @@ -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(); 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(); + @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} > @@ -388,8 +394,3 @@ class ChromedashRoadmapMilestoneCard extends LitElement { `; } } - -customElements.define( - 'chromedash-roadmap-milestone-card', - ChromedashRoadmapMilestoneCard -); diff --git a/client-src/elements/chromedash-roadmap-page.js b/client-src/elements/chromedash-roadmap-page.ts similarity index 80% rename from client-src/elements/chromedash-roadmap-page.js rename to client-src/elements/chromedash-roadmap-page.ts index ab412882..db70f5a2 100644 --- a/client-src/elements/chromedash-roadmap-page.js +++ b/client-src/elements/chromedash-roadmap-page.ts @@ -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(); + roadmapRef = createRef(); 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); diff --git a/client-src/elements/chromedash-roadmap-page_test.js b/client-src/elements/chromedash-roadmap-page_test.ts similarity index 95% rename from client-src/elements/chromedash-roadmap-page_test.js rename to client-src/elements/chromedash-roadmap-page_test.ts index dbb8c64c..76d6026a 100644 --- a/client-src/elements/chromedash-roadmap-page_test.js +++ b/client-src/elements/chromedash-roadmap-page_test.ts @@ -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); }); }); diff --git a/client-src/elements/chromedash-roadmap.js b/client-src/elements/chromedash-roadmap.ts similarity index 78% rename from client-src/elements/chromedash-roadmap.js rename to client-src/elements/chromedash-roadmap.ts index 73a09494..79007003 100644 --- a/client-src/elements/chromedash-roadmap.js +++ b/client-src/elements/chromedash-roadmap.ts @@ -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; + ltc_date: string; + ltr_date: string; + ltr_last_refresh_date: string; + mstone: number; + owners: Record; + 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 = { 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(); + /** + * 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);