From 2435d7a49bdba351355e6a8e3b0d39a9088d71ed Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Tue, 12 Oct 2021 12:54:44 -0500 Subject: [PATCH] Move HomepageTimer and use props over config (#7533) --- .changeset/wicked-clocks-kneel.md | 37 ++++++ .changeset/witty-dolls-walk.md | 5 + packages/app/src/components/home/HomePage.tsx | 36 ++++-- packages/core-components/api-report.md | 2 +- .../layout/HomepageTimer/HomepageTimer.tsx | 4 + plugins/home/api-report.md | 17 +++ .../HeaderWorldClock.test.tsx | 88 +++++++++++++++ .../HeaderWorldClock/HeaderWorldClock.tsx | 105 ++++++++++++++++++ .../src/components/HeaderWorldClock/index.ts | 17 +++ plugins/home/src/components/index.ts | 2 + plugins/home/src/index.ts | 3 +- 11 files changed, 304 insertions(+), 12 deletions(-) create mode 100644 .changeset/wicked-clocks-kneel.md create mode 100644 .changeset/witty-dolls-walk.md create mode 100644 plugins/home/src/components/HeaderWorldClock/HeaderWorldClock.test.tsx create mode 100644 plugins/home/src/components/HeaderWorldClock/HeaderWorldClock.tsx create mode 100644 plugins/home/src/components/HeaderWorldClock/index.ts diff --git a/.changeset/wicked-clocks-kneel.md b/.changeset/wicked-clocks-kneel.md new file mode 100644 index 0000000000..fc2ee54440 --- /dev/null +++ b/.changeset/wicked-clocks-kneel.md @@ -0,0 +1,37 @@ +--- +'@backstage/plugin-home': patch +--- + +Added HeaderWorldClock to the Home plugin which is a copy of the HomepageTimer from core-components that has been updated to use props over static config from app-config.yaml. To use HeaderWorldClock you'll need to create an array of ClockConfig like this: + +```ts +const clockConfigs: ClockConfig[] = [ + { + label: 'NYC', + timeZone: 'America/New_York', + }, + { + label: 'UTC', + timeZone: 'UTC', + }, + { + label: 'STO', + timeZone: 'Europe/Stockholm', + }, + { + label: 'TYO', + timeZone: 'Asia/Tokyo', + }, +]; +``` + +Then you can pass `clockConfigs` into the HeaderWorldClock like this: + +```ts + +
+ +
+ ... +
+``` diff --git a/.changeset/witty-dolls-walk.md b/.changeset/witty-dolls-walk.md new file mode 100644 index 0000000000..0c6532e725 --- /dev/null +++ b/.changeset/witty-dolls-walk.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Deprecated HomepageTimer in favor of HeaderWorldClock which is found in the [home plugin](https://github.com/backstage/backstage/tree/master/plugins/home) diff --git a/packages/app/src/components/home/HomePage.tsx b/packages/app/src/components/home/HomePage.tsx index 33ac1a7e8c..5a19cef6f6 100644 --- a/packages/app/src/components/home/HomePage.tsx +++ b/packages/app/src/components/home/HomePage.tsx @@ -15,26 +15,42 @@ */ import { - Content, - Header, - HomepageTimer, - Page, -} from '@backstage/core-components'; -import { - ComponentAccordion, - ComponentTab, - ComponentTabs, HomePageRandomJoke, + ComponentAccordion, + ComponentTabs, + ComponentTab, WelcomeTitle, + HeaderWorldClock, + ClockConfig, } from '@backstage/plugin-home'; +import { Content, Header, Page } from '@backstage/core-components'; import { HomePageSearchBar } from '@backstage/plugin-search'; import Grid from '@material-ui/core/Grid'; import React from 'react'; +const clockConfigs: ClockConfig[] = [ + { + label: 'NYC', + timeZone: 'America/New_York', + }, + { + label: 'UTC', + timeZone: 'UTC', + }, + { + label: 'STO', + timeZone: 'Europe/Stockholm', + }, + { + label: 'TYO', + timeZone: 'Asia/Tokyo', + }, +]; + export const HomePage = () => (
} pageTitleOverride="Home"> - +
diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 38fe19fc8b..dc8115287a 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -569,7 +569,7 @@ export function HelpIcon(props: IconComponentProps): JSX.Element; // Warning: (ae-missing-release-tag) "HomepageTimer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public @deprecated export function HomepageTimer(_props: {}): JSX.Element | null; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts diff --git a/packages/core-components/src/layout/HomepageTimer/HomepageTimer.tsx b/packages/core-components/src/layout/HomepageTimer/HomepageTimer.tsx index a78f4af8ca..a825d52c94 100644 --- a/packages/core-components/src/layout/HomepageTimer/HomepageTimer.tsx +++ b/packages/core-components/src/layout/HomepageTimer/HomepageTimer.tsx @@ -67,6 +67,10 @@ function getTimes(configApi: ConfigApi) { return clocks; } +/** + * Please use the HeaderWorldClock in the home plugin + * @deprecated in favor of the HeaderWorldClock which is found in the to home plugin + */ export function HomepageTimer(_props: {}) { const configApi = useApi(configApiRef); diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index a1cd98124a..57b0b899e2 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -10,6 +10,14 @@ import { Extension } from '@backstage/core-plugin-api'; import { ReactNode } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "ClockConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ClockConfig = { + label: string; + timeZone: string; +}; + // Warning: (ae-missing-release-tag) "ComponentAccordion" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -76,6 +84,15 @@ export function createCardExtension({ } & T) => JSX.Element >; +// Warning: (ae-missing-release-tag) "HeaderWorldClock" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const HeaderWorldClock: ({ + clockConfigs, +}: { + clockConfigs: ClockConfig[]; +}) => JSX.Element | null; + // Warning: (ae-missing-release-tag) "HomepageCompositionRoot" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/plugins/home/src/components/HeaderWorldClock/HeaderWorldClock.test.tsx b/plugins/home/src/components/HeaderWorldClock/HeaderWorldClock.test.tsx new file mode 100644 index 0000000000..9dbfceac81 --- /dev/null +++ b/plugins/home/src/components/HeaderWorldClock/HeaderWorldClock.test.tsx @@ -0,0 +1,88 @@ +/* + * 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 { renderInTestApp } from '@backstage/test-utils'; +import { HeaderWorldClock, ClockConfig } from './HeaderWorldClock'; +import React from 'react'; +import { lightTheme } from '@backstage/theme'; +import { ThemeProvider } from '@material-ui/core'; + +describe('HeaderWorldClock with valid Time Zones', () => { + it('displays Time Zones as expected', async () => { + const clockConfigs: ClockConfig[] = [ + { + label: 'NYC', + timeZone: 'America/New_York', + }, + { + label: 'UTC', + timeZone: 'UTC', + }, + { + label: 'STO', + timeZone: 'Europe/Stockholm', + }, + { + label: 'TYO', + timeZone: 'Asia/Tokyo', + }, + ]; + + const rendered = await renderInTestApp( + + + , + ); + + expect(rendered.getByText('NYC')).toBeInTheDocument(); + expect(rendered.getByText('UTC')).toBeInTheDocument(); + expect(rendered.getByText('STO')).toBeInTheDocument(); + expect(rendered.getByText('TYO')).toBeInTheDocument(); + }); +}); + +describe('HeaderWorldClock with no Time Zones provided', () => { + it('should not appear in output', async () => { + const clockConfigs: ClockConfig[] = []; + + const rendered = await renderInTestApp( + + + , + ); + + expect(rendered.container).toBeEmptyDOMElement(); + }); +}); + +describe('HeaderWorldClock with invalid Time Zone', () => { + it('uses GMT as fallback Time Zone', async () => { + const clockConfigs: ClockConfig[] = [ + { + label: 'New York', + timeZone: 'America/New_Pork', + }, + ]; + + const rendered = await renderInTestApp( + + + , + ); + + expect(rendered.getByText('GMT')).toBeInTheDocument(); + }); +}); diff --git a/plugins/home/src/components/HeaderWorldClock/HeaderWorldClock.tsx b/plugins/home/src/components/HeaderWorldClock/HeaderWorldClock.tsx new file mode 100644 index 0000000000..32bf305020 --- /dev/null +++ b/plugins/home/src/components/HeaderWorldClock/HeaderWorldClock.tsx @@ -0,0 +1,105 @@ +/* + * Copyright 2020 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 React from 'react'; +import { HeaderLabel } from '@backstage/core-components'; + +const timeFormat: Intl.DateTimeFormatOptions = { + hour: '2-digit', + minute: '2-digit', +}; + +type TimeObj = { + time: string; + label: string; +}; + +export type ClockConfig = { + label: string; + timeZone: string; +}; + +function getTimes(clockConfigs: ClockConfig[]) { + const d = new Date(); + const lang = window.navigator.language; + + const clocks: TimeObj[] = []; + + if (!clockConfigs) { + return clocks; + } + + for (const clockConfig of clockConfigs) { + let label = clockConfig.label; + + const options: Intl.DateTimeFormatOptions = { + timeZone: clockConfig.timeZone, + ...timeFormat, + }; + + try { + new Date().toLocaleString(lang, options); + } catch (e) { + // eslint-disable-next-line no-console + console.warn( + `The timezone ${options.timeZone} is invalid. Defaulting to GMT`, + ); + options.timeZone = 'GMT'; + label = 'GMT'; + } + + const time = d.toLocaleTimeString(lang, options); + clocks.push({ time, label }); + } + + return clocks; +} + +export const HeaderWorldClock = ({ + clockConfigs, +}: { + clockConfigs: ClockConfig[]; +}) => { + const defaultTimes: TimeObj[] = []; + const [clocks, setTimes] = React.useState(defaultTimes); + + React.useEffect(() => { + setTimes(getTimes(clockConfigs)); + + const intervalId = setInterval(() => { + setTimes(getTimes(clockConfigs)); + }, 1000); + + return () => { + clearInterval(intervalId); + }; + }, [clockConfigs]); + + if (clocks.length !== 0) { + return ( + <> + {clocks.map(clock => ( + + ))} + + ); + } + return null; +}; diff --git a/plugins/home/src/components/HeaderWorldClock/index.ts b/plugins/home/src/components/HeaderWorldClock/index.ts new file mode 100644 index 0000000000..0893c0586c --- /dev/null +++ b/plugins/home/src/components/HeaderWorldClock/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 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. + */ +export { HeaderWorldClock } from './HeaderWorldClock'; +export type { ClockConfig } from './HeaderWorldClock'; diff --git a/plugins/home/src/components/index.ts b/plugins/home/src/components/index.ts index aae0cbe26d..1408853f5f 100644 --- a/plugins/home/src/components/index.ts +++ b/plugins/home/src/components/index.ts @@ -16,3 +16,5 @@ export { HomepageCompositionRoot } from './HomepageCompositionRoot'; export { SettingsModal } from './SettingsModal'; +export { HeaderWorldClock } from './HeaderWorldClock'; +export type { ClockConfig } from './HeaderWorldClock'; diff --git a/plugins/home/src/index.ts b/plugins/home/src/index.ts index 0004cf5a23..a8b8e7ab14 100644 --- a/plugins/home/src/index.ts +++ b/plugins/home/src/index.ts @@ -29,5 +29,6 @@ export { ComponentTab, WelcomeTitle, } from './plugin'; -export { SettingsModal } from './components'; +export { SettingsModal, HeaderWorldClock } from './components'; +export type { ClockConfig } from './components'; export { createCardExtension } from './extensions';