This is an automated email from the ASF dual-hosted git repository.

ryanahamilton pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/master by this push:
     new 43b6657  Expand date provider for new UI (#15725)
43b6657 is described below

commit 43b66579225dbee6e33a6fb52a88e188f4ea4dee
Author: Brent Bovenzi <[email protected]>
AuthorDate: Wed May 12 21:41:37 2021 -0400

    Expand date provider for new UI (#15725)
    
    * timezone dropdown components
    
    Copied the UX from the current UI on how to change timezone preferences.
    
    Working on a Chakra UI wrapper for React-Select.
    
    * clean up select ts
    
    * connect dropdown to timezone context
    
    * test dropdown
    
    * fix tests
    
    * update labels, select name & icons
    
    * 3rd party timezones, better dag rendering
    
    * check if timezone exists in a group
    
    * close menu on timezone selection
    
    * add a dateformat provider
    
    - create a provider for the format string to give to dayjs
    - initial example use-case is toggling between a 12 and 24 hour clock
    
    * merge timezone and dateformat providers
    
    * rename timezone provider in test
    
    * create dateFormat() wrapper around dayjs()
---
 .../ui/src/components/AppContainer/AppHeader.tsx   | 14 ++++++---
 .../components/AppContainer/TimezoneDropdown.tsx   | 16 ++++------
 .../{TimezoneProvider.tsx => DateProvider.tsx}     | 34 +++++++++++++++++-----
 airflow/ui/src/providers/auth/PrivateRoute.tsx     |  6 ++--
 airflow/ui/test/Login.test.tsx                     |  6 ----
 airflow/ui/test/TimezoneDropdown.test.tsx          | 10 +++----
 6 files changed, 50 insertions(+), 36 deletions(-)

diff --git a/airflow/ui/src/components/AppContainer/AppHeader.tsx 
b/airflow/ui/src/components/AppContainer/AppHeader.tsx
index cf2bb90..ff3fd54 100644
--- a/airflow/ui/src/components/AppContainer/AppHeader.tsx
+++ b/airflow/ui/src/components/AppContainer/AppHeader.tsx
@@ -36,17 +36,15 @@ import {
   MdBrightness2,
   MdAccountCircle,
   MdExitToApp,
+  MdQueryBuilder,
 } from 'react-icons/md';
-import dayjs from 'dayjs';
-import tz from 'dayjs/plugin/timezone';
 
 import { useAuthContext } from 'providers/auth/context';
+import { useDateContext, HOURS_24 } from 'providers/DateProvider';
 
 import ApacheAirflowLogo from 'components/icons/ApacheAirflowLogo';
 import TimezoneDropdown from './TimezoneDropdown';
 
-dayjs.extend(tz);
-
 interface Props {
   bodyBg: string;
   overlayBg: string;
@@ -55,6 +53,7 @@ interface Props {
 
 const AppHeader: React.FC<Props> = ({ bodyBg, overlayBg, breadcrumb }) => {
   const { toggleColorMode } = useColorMode();
+  const { dateFormat, toggle24Hour } = useDateContext();
   const headerHeight = '56px';
   const { hasValidAuthToken, logout } = useAuthContext();
   const darkLightIcon = useColorModeValue(MdBrightness2, MdWbSunny);
@@ -102,6 +101,13 @@ const AppHeader: React.FC<Props> = ({ bodyBg, overlayBg, 
breadcrumb }) => {
                 {darkLightText}
                 Mode
               </MenuItem>
+              {/* Clock config should move to User Profile Settings when that 
page exists */}
+              <MenuItem onClick={toggle24Hour}>
+                <Icon as={MdQueryBuilder} mr="2" />
+                Use
+                {dateFormat === HOURS_24 ? ' 12 hour ' : ' 24 hour '}
+                clock
+              </MenuItem>
               <MenuDivider />
               <MenuItem onClick={logout}>
                 <Icon as={MdExitToApp} mr="2" />
diff --git a/airflow/ui/src/components/AppContainer/TimezoneDropdown.tsx 
b/airflow/ui/src/components/AppContainer/TimezoneDropdown.tsx
index aae91cb..cb829f0 100644
--- a/airflow/ui/src/components/AppContainer/TimezoneDropdown.tsx
+++ b/airflow/ui/src/components/AppContainer/TimezoneDropdown.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import React, { useState, useRef } from 'react';
+import React, { useRef } from 'react';
 import {
   Box,
   Button,
@@ -26,20 +26,15 @@ import {
   MenuList,
   Tooltip,
 } from '@chakra-ui/react';
-import dayjs from 'dayjs';
-import tz from 'dayjs/plugin/timezone';
 import { getTimeZones } from '@vvo/tzdb';
 
 import Select from 'components/MultiSelect';
-import { useTimezoneContext } from 'providers/TimezoneProvider';
-
-dayjs.extend(tz);
+import { useDateContext } from 'providers/DateProvider';
 
 interface Option { value: string, label: string }
 
 const TimezoneDropdown: React.FC = () => {
-  const { timezone, setTimezone } = useTimezoneContext();
-  const [now, setNow] = useState(dayjs().tz(timezone));
+  const { timezone, setTimezone, formatDate } = useDateContext();
   const menuRef = useRef<HTMLButtonElement>(null);
 
   const timezones = getTimeZones();
@@ -54,7 +49,6 @@ const TimezoneDropdown: React.FC = () => {
   const onChangeTimezone = (newTimezone: Option | null) => {
     if (newTimezone) {
       setTimezone(newTimezone.value);
-      setNow(dayjs().tz(newTimezone.value));
       // Close the dropdown on a successful change
       menuRef?.current?.click();
     }
@@ -66,10 +60,10 @@ const TimezoneDropdown: React.FC = () => {
         <MenuButton as={Button} variant="ghost" mr="4" ref={menuRef}>
           <Box
             as="time"
-            dateTime={now.toString()}
+            dateTime={formatDate()}
             fontSize="md"
           >
-            {now.format('HH:mm Z')}
+            {formatDate()}
           </Box>
         </MenuButton>
       </Tooltip>
diff --git a/airflow/ui/src/providers/TimezoneProvider.tsx 
b/airflow/ui/src/providers/DateProvider.tsx
similarity index 66%
rename from airflow/ui/src/providers/TimezoneProvider.tsx
rename to airflow/ui/src/providers/DateProvider.tsx
index 193e8ce..6196fa0 100644
--- a/airflow/ui/src/providers/TimezoneProvider.tsx
+++ b/airflow/ui/src/providers/DateProvider.tsx
@@ -27,41 +27,61 @@ import dayjsTz from 'dayjs/plugin/timezone';
 dayjs.extend(utc);
 dayjs.extend(dayjsTz);
 
-interface TimezoneContextData {
+export const HOURS_24 = 'HH:mm Z';
+export const HOURS_12 = 'h:mmA Z';
+
+interface DateContextData {
   timezone: string;
   setTimezone: (value: string) => void;
+  dateFormat: string;
+  toggle24Hour: () => void;
+  formatDate: (date?: string | Date) => string;
 }
 
-export const TimezoneContext = createContext<TimezoneContextData>({
+export const DateContext = createContext<DateContextData>({
   timezone: 'UTC',
   setTimezone: () => {},
+  dateFormat: HOURS_24,
+  toggle24Hour: () => {},
+  formatDate: () => '',
 });
 
-export const useTimezoneContext = () => useContext(TimezoneContext);
+export const useDateContext = () => useContext(DateContext);
 
 type Props = {
   children: ReactNode;
 };
 
-const TimezoneProvider = ({ children }: Props): ReactElement => {
+const DateProvider = ({ children }: Props): ReactElement => {
   // TODO: add in default_timezone when GET /ui-metadata is available
   // guess timezone on browser or default to utc and don't guess when testing
   const isTest = process.env.NODE_ENV === 'test';
   const [timezone, setTimezone] = useState(isTest ? 'UTC' : dayjs.tz.guess());
+  const [dateFormat, setFormat] = useState(HOURS_24);
+
+  const toggle24Hour = () => {
+    setFormat(dateFormat === HOURS_24 ? HOURS_12 : HOURS_24);
+  };
+
   useEffect(() => {
     dayjs.tz.setDefault(timezone);
   }, [timezone]);
 
+  const formatDate = (date?: string | Date) => 
dayjs(date).tz(timezone).format(dateFormat);
+
   return (
-    <TimezoneContext.Provider
+    <DateContext.Provider
       value={{
         timezone,
         setTimezone,
+        dateFormat,
+        toggle24Hour,
+        formatDate,
       }}
     >
       {children}
-    </TimezoneContext.Provider>
+    </DateContext.Provider>
   );
 };
 
-export default TimezoneProvider;
+export default DateProvider;
diff --git a/airflow/ui/src/providers/auth/PrivateRoute.tsx 
b/airflow/ui/src/providers/auth/PrivateRoute.tsx
index 2fad7a7..377e128 100644
--- a/airflow/ui/src/providers/auth/PrivateRoute.tsx
+++ b/airflow/ui/src/providers/auth/PrivateRoute.tsx
@@ -23,13 +23,13 @@ import React, {
 import { Route, RouteProps } from 'react-router-dom';
 
 import Login from 'views/Login';
-// TimezoneProvider has to be used after authentication
-import TimezoneProvider from 'providers/TimezoneProvider';
+// DateProvider has to be used after authentication
+import DateProvider from 'providers/DateProvider';
 import { useAuthContext } from './context';
 
 const PrivateRoute: FC<RouteProps> = (props) => {
   const { hasValidAuthToken } = useAuthContext();
-  return hasValidAuthToken ? <TimezoneProvider><Route {...props} 
/></TimezoneProvider> : <Login />;
+  return hasValidAuthToken ? <DateProvider><Route {...props} /></DateProvider> 
: <Login />;
 };
 
 export default PrivateRoute;
diff --git a/airflow/ui/test/Login.test.tsx b/airflow/ui/test/Login.test.tsx
index 56bb75a..16de7be 100644
--- a/airflow/ui/test/Login.test.tsx
+++ b/airflow/ui/test/Login.test.tsx
@@ -23,9 +23,6 @@ import { render, fireEvent, waitFor } from 
'@testing-library/react';
 import { BrowserRouter } from 'react-router-dom';
 import nock from 'nock';
 import axios from 'axios';
-import dayjs from 'dayjs';
-import timezone from 'dayjs/plugin/timezone';
-import utc from 'dayjs/plugin/utc';
 
 import Login from 'views/Login';
 import App from 'App';
@@ -33,13 +30,10 @@ import AuthProvider from 'providers/auth/AuthProvider';
 
 import { url, defaultHeaders, QueryWrapper } from './utils';
 
-dayjs.extend(utc);
-dayjs.extend(timezone);
 axios.defaults.adapter = require('axios/lib/adapters/http');
 
 describe('test login component', () => {
   beforeAll(() => {
-    dayjs.tz.setDefault('UTC');
     nock(url)
       .defaultReplyHeaders(defaultHeaders)
       .persist()
diff --git a/airflow/ui/test/TimezoneDropdown.test.tsx 
b/airflow/ui/test/TimezoneDropdown.test.tsx
index 66051e6..60a9622 100644
--- a/airflow/ui/test/TimezoneDropdown.test.tsx
+++ b/airflow/ui/test/TimezoneDropdown.test.tsx
@@ -26,7 +26,7 @@ import timezone from 'dayjs/plugin/timezone';
 import utc from 'dayjs/plugin/utc';
 
 import TimezoneDropdown from 'components/AppContainer/TimezoneDropdown';
-import TimezoneProvider from 'providers/TimezoneProvider';
+import DateProvider, { HOURS_24 } from 'providers/DateProvider';
 import { ChakraWrapper } from './utils';
 
 dayjs.extend(utc);
@@ -35,13 +35,13 @@ dayjs.extend(timezone);
 describe('test timezone dropdown', () => {
   test('Can search for a new timezone and the date changes', () => {
     const { getByText } = render(
-      <TimezoneProvider>
+      <DateProvider>
         <TimezoneDropdown />
-      </TimezoneProvider>,
+      </DateProvider>,
       { wrapper: ChakraWrapper },
     );
 
-    const initialTime = dayjs().tz('UTC').format('HH:mm Z');
+    const initialTime = dayjs().tz('UTC').format(HOURS_24);
 
     expect(getByText(initialTime)).toBeInTheDocument();
     const button = getByText(initialTime);
@@ -54,6 +54,6 @@ describe('test timezone dropdown', () => {
     expect(option).toBeInTheDocument();
     fireEvent.click(option);
 
-    expect(getByText(dayjs().tz('America/Anchorage').format('HH:mm 
Z'))).toBeInTheDocument();
+    
expect(getByText(dayjs().tz('America/Anchorage').format(HOURS_24))).toBeInTheDocument();
   });
 });

Reply via email to