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();
});
});