diff --git a/.changeset/silver-needles-unite.md b/.changeset/silver-needles-unite.md new file mode 100644 index 0000000000..b5fed7e615 --- /dev/null +++ b/.changeset/silver-needles-unite.md @@ -0,0 +1,26 @@ +--- +'@backstage/plugin-home': patch +--- + +Added support for customizing the time format used in the `HeaderWorldClock` component + +Here's an example of how this can be used in the `HomePage.tsx` found in `\packages\app\src\components\home` to change the clock to be in the 24hr time format: + +```diff ++const timeFormat: Intl.DateTimeFormatOptions = { ++ hour: '2-digit', ++ minute: '2-digit', ++ hour12: false, ++}; + +export const homePage = ( + +
} pageTitleOverride="Home"> ++ +
+ + + + + +``` diff --git a/packages/app/src/components/home/HomePage.tsx b/packages/app/src/components/home/HomePage.tsx index 2cd5481c91..e432e1a00e 100644 --- a/packages/app/src/components/home/HomePage.tsx +++ b/packages/app/src/components/home/HomePage.tsx @@ -49,10 +49,19 @@ const clockConfigs: ClockConfig[] = [ }, ]; +const timeFormat: Intl.DateTimeFormatOptions = { + hour: '2-digit', + minute: '2-digit', + hour12: false, +}; + export const homePage = (
} pageTitleOverride="Home"> - +
diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index a65f8cda5c..452b3e1685 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -59,9 +59,10 @@ export function createCardExtension(options: { name?: string; }): Extension<(props: CardExtensionProps) => JSX.Element>; -// @public (undocumented) +// @public export const HeaderWorldClock: (props: { clockConfigs: ClockConfig[]; + customTimeFormat?: Intl.DateTimeFormatOptions | undefined; }) => JSX.Element | null; // @public diff --git a/plugins/home/src/components/index.ts b/plugins/home/src/components/index.ts index 1408853f5f..aae0cbe26d 100644 --- a/plugins/home/src/components/index.ts +++ b/plugins/home/src/components/index.ts @@ -16,5 +16,3 @@ export { HomepageCompositionRoot } from './HomepageCompositionRoot'; export { SettingsModal } from './SettingsModal'; -export { HeaderWorldClock } from './HeaderWorldClock'; -export type { ClockConfig } from './HeaderWorldClock'; diff --git a/plugins/home/src/homePageComponents/HeaderWorldClock/HeaderWorldClock.stories.tsx b/plugins/home/src/homePageComponents/HeaderWorldClock/HeaderWorldClock.stories.tsx new file mode 100644 index 0000000000..706dfa06ce --- /dev/null +++ b/plugins/home/src/homePageComponents/HeaderWorldClock/HeaderWorldClock.stories.tsx @@ -0,0 +1,97 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Header } from '@backstage/core-components'; +import { wrapInTestApp } from '@backstage/test-utils'; +import React, { ComponentType } from 'react'; +import { ClockConfig, HeaderWorldClock } from './HeaderWorldClock'; + +export default { + title: 'Plugins/Home/Components/HeaderWorldClock', + decorators: [(Story: ComponentType<{}>) => wrapInTestApp()], +}; + +export const Default = () => { + const clockConfigs: ClockConfig[] = [ + { + label: 'NYC', + timeZone: 'America/New_York', + }, + { + label: 'UTC', + timeZone: 'UTC', + }, + { + label: 'STO', + timeZone: 'Europe/Stockholm', + }, + { + label: 'TYO', + timeZone: 'Asia/Tokyo', + }, + ]; + + const timeFormat: Intl.DateTimeFormatOptions = { + hour: '2-digit', + minute: '2-digit', + hour12: true, + }; + + return ( +
+ +
+ ); +}; + +export const TwentyFourHourClocks = () => { + const clockConfigs: ClockConfig[] = [ + { + label: 'NYC', + timeZone: 'America/New_York', + }, + { + label: 'UTC', + timeZone: 'UTC', + }, + { + label: 'STO', + timeZone: 'Europe/Stockholm', + }, + { + label: 'TYO', + timeZone: 'Asia/Tokyo', + }, + ]; + + const timeFormat: Intl.DateTimeFormatOptions = { + hour: '2-digit', + minute: '2-digit', + hour12: false, + }; + + return ( +
+ +
+ ); +}; diff --git a/plugins/home/src/components/HeaderWorldClock/HeaderWorldClock.test.tsx b/plugins/home/src/homePageComponents/HeaderWorldClock/HeaderWorldClock.test.tsx similarity index 77% rename from plugins/home/src/components/HeaderWorldClock/HeaderWorldClock.test.tsx rename to plugins/home/src/homePageComponents/HeaderWorldClock/HeaderWorldClock.test.tsx index 9dbfceac81..3611f47787 100644 --- a/plugins/home/src/components/HeaderWorldClock/HeaderWorldClock.test.tsx +++ b/plugins/home/src/homePageComponents/HeaderWorldClock/HeaderWorldClock.test.tsx @@ -86,3 +86,32 @@ describe('HeaderWorldClock with invalid Time Zone', () => { expect(rendered.getByText('GMT')).toBeInTheDocument(); }); }); + +describe('HeaderWorldClock with custom Time Format', () => { + it('uses 24hr clock from custom Time Format', async () => { + const clockConfigs: ClockConfig[] = [ + { + label: 'UTC', + timeZone: 'UTC', + }, + ]; + + const timeFormat: Intl.DateTimeFormatOptions = { + hour: '2-digit', + minute: '2-digit', + hour12: false, + }; + + const rendered = await renderInTestApp( + + + , + ); + + const currentTime = `${new Date().getHours()}:${new Date().getMinutes()}`; + expect(rendered.getByText(currentTime)).toBeInTheDocument(); + }); +}); diff --git a/plugins/home/src/components/HeaderWorldClock/HeaderWorldClock.tsx b/plugins/home/src/homePageComponents/HeaderWorldClock/HeaderWorldClock.tsx similarity index 63% rename from plugins/home/src/components/HeaderWorldClock/HeaderWorldClock.tsx rename to plugins/home/src/homePageComponents/HeaderWorldClock/HeaderWorldClock.tsx index 7e3065cc7a..6672fe6a22 100644 --- a/plugins/home/src/components/HeaderWorldClock/HeaderWorldClock.tsx +++ b/plugins/home/src/homePageComponents/HeaderWorldClock/HeaderWorldClock.tsx @@ -32,7 +32,10 @@ export type ClockConfig = { timeZone: string; }; -function getTimes(clockConfigs: ClockConfig[]) { +function getTimes( + clockConfigs: ClockConfig[], + customTimeFormat?: Intl.DateTimeFormatOptions, +) { const d = new Date(); const lang = window.navigator.language; @@ -47,7 +50,7 @@ function getTimes(clockConfigs: ClockConfig[]) { const options: Intl.DateTimeFormatOptions = { timeZone: clockConfig.timeZone, - ...timeFormat, + ...(customTimeFormat ?? timeFormat), }; try { @@ -68,24 +71,57 @@ function getTimes(clockConfigs: ClockConfig[]) { return clocks; } -/** @public */ -export const HeaderWorldClock = (props: { clockConfigs: ClockConfig[] }) => { - const { clockConfigs } = props; +/** + * A component to display a configurable list of clocks for various time zones. + * + * @example + * Here's a simple example: + * ``` + * // This will give you a clock for the time zone that Stockholm is in + * // you can add more than one but keep in mind space may be limited + * const clockConfigs: ClockConfig[] = [ + * { + * label: 'STO', + * timeZone: 'Europe/Stockholm', + * }, + * ]; + * + * // Setting hour12 to false will make all the clocks show in the 24hr format + * const timeFormat: Intl.DateTimeFormatOptions = { + * hour: '2-digit', + * minute: '2-digit', + * hour12: false, + * }; + * + * // Here is the component in use: + * + * ``` + * + * @public + */ +export const HeaderWorldClock = (props: { + clockConfigs: ClockConfig[]; + customTimeFormat?: Intl.DateTimeFormatOptions; +}) => { + const { clockConfigs, customTimeFormat } = props; const defaultTimes: TimeObj[] = []; const [clocks, setTimes] = React.useState(defaultTimes); React.useEffect(() => { - setTimes(getTimes(clockConfigs)); + setTimes(getTimes(clockConfigs, customTimeFormat)); const intervalId = setInterval(() => { - setTimes(getTimes(clockConfigs)); + setTimes(getTimes(clockConfigs, customTimeFormat)); }, 1000); return () => { clearInterval(intervalId); }; - }, [clockConfigs]); + }, [clockConfigs, customTimeFormat]); if (clocks.length !== 0) { return ( diff --git a/plugins/home/src/components/HeaderWorldClock/index.ts b/plugins/home/src/homePageComponents/HeaderWorldClock/index.ts similarity index 100% rename from plugins/home/src/components/HeaderWorldClock/index.ts rename to plugins/home/src/homePageComponents/HeaderWorldClock/index.ts diff --git a/plugins/home/src/homePageComponents/index.ts b/plugins/home/src/homePageComponents/index.ts index 4d2802fb98..d318226dbd 100644 --- a/plugins/home/src/homePageComponents/index.ts +++ b/plugins/home/src/homePageComponents/index.ts @@ -16,3 +16,4 @@ export * from './CompanyLogo'; export * from './Toolkit'; +export type { ClockConfig } from './HeaderWorldClock'; diff --git a/plugins/home/src/index.ts b/plugins/home/src/index.ts index 8f137d589b..1776b5aa9c 100644 --- a/plugins/home/src/index.ts +++ b/plugins/home/src/index.ts @@ -31,9 +31,10 @@ export { ComponentTabs, ComponentTab, WelcomeTitle, + HeaderWorldClock, } from './plugin'; -export { SettingsModal, HeaderWorldClock } from './components'; +export { SettingsModal } from './components'; export * from './assets'; -export type { ClockConfig } from './components'; +export type { ClockConfig } from './homePageComponents'; export { createCardExtension } from './extensions'; export type { ComponentRenderer } from './extensions'; diff --git a/plugins/home/src/plugin.ts b/plugins/home/src/plugin.ts index 93110faffb..9b221ad8dc 100644 --- a/plugins/home/src/plugin.ts +++ b/plugins/home/src/plugin.ts @@ -136,3 +136,20 @@ export const HomePageStarredEntities = homePlugin.provide( components: () => import('./homePageComponents/StarredEntities'), }), ); + +/** + * A component to display a configurable list of clocks for various time zones. + * + * @public + */ +export const HeaderWorldClock = homePlugin.provide( + createComponentExtension({ + name: 'HeaderWorldClock', + component: { + lazy: () => + import('./homePageComponents/HeaderWorldClock').then( + m => m.HeaderWorldClock, + ), + }, + }), +);