Refactor/vite (#209)

* refactor(vite): add dependencies
* refactor(vite): necessary code migrations
* refactor(vite): convert file to tsx
* refactor(vite): config
* refactor(vite): doctype casing
This commit is contained in:
Carlos Valente
2022-09-28 21:13:20 +02:00
committed by GitHub
parent d86afee477
commit 562cb769c5
30 changed files with 1572 additions and 7013 deletions
+1 -3
View File
@@ -11,7 +11,7 @@ import theme from './theme/theme';
import AppRouter from './AppRouter';
// Load Open Sans typeface
require('typeface-open-sans');
import('typeface-open-sans');
export const ontimeQueryClient = new QueryClient();
function App() {
@@ -42,8 +42,6 @@ function App() {
};
}, [handleKeyPress]);
return (
<ChakraProvider resetCSS theme={theme}>
<SocketProvider>
+2 -6
View File
@@ -19,12 +19,8 @@ export const TIMER = ['ontime-timer'];
* @description finds server path given the current location, it
* @return {*}
*/
export const calculateServer = () => {
if (process.env?.NODE_ENV === 'development') {
return `http://localhost:${STATIC_PORT}`;
}
return window.location.origin;
};
export const calculateServer = () =>
import.meta.env.DEV ? `http://localhost:${STATIC_PORT}` : window.location.origin;
export const serverURL = calculateServer();
export const eventURL = `${serverURL}/${EVENT_TABLE}`;
@@ -2,8 +2,6 @@ import { IconButton } from '@chakra-ui/button';
import { IconButtonProps } from '@chakra-ui/react';
import { Tooltip } from '@chakra-ui/tooltip';
export type Sizes = 'xs' | 'sm' | 'md' | 'lg';
interface TooltipActionBtnProps extends IconButtonProps {
clickHandler: () => void;
tooltip: string;
@@ -1,11 +1,10 @@
/* eslint-disable react/destructuring-assignment */
import React from 'react';
import { version as appVersion } from '../../../../package.json';
import { LoggingContext } from '../../context/LoggingContext';
import style from './ErrorBoundary.module.scss';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const appVersion = require('../../../../package.json').version;
class ErrorBoundary extends React.Component {
static contextType = LoggingContext;
@@ -1,35 +0,0 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import EditableTimer from '../EditableTimer';
const renderEditableTimer = (
name = 'test',
actionHandler = () => undefined,
validate = () => undefined
) => render(<EditableTimer name={name} actionHandler={actionHandler} validate={validate} />);
describe('test EditableTimer component', () => {
const testName = "test";
const actionHandler = jest.fn();
const validate = jest.fn();
renderEditableTimer(testName, actionHandler, validate);
const editableTimer = screen.getByTestId('editable-timer');
const editableInput = screen.getByTestId('editable-timer-input');
// skipping for now as error seems to come from beta library
it.skip('renders correctly', () => {
expect(editableTimer).toBeInTheDocument();
expect(editableInput).toBeInTheDocument();
const myTypedString = 'verylongandcool'
userEvent.type(editableInput, myTypedString);
expect(editableInput).toHaveValue(myTypedString);
userEvent.type(editableInput, '{enter}');
// no previous is given, defaults to 0
expect(validate).toHaveBeenCalledWith(testName, 0);
});
});
+177
View File
@@ -0,0 +1,177 @@
import {
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
} from 'react';
export type TLogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';
export type TOptions = {
logLevel?: TLogLevel;
maxFontSize?: number;
minFontSize?: number;
onFinish?: (fontSize: number) => void;
onStart?: () => void;
resolution?: number;
};
const LOG_LEVEL: Record<TLogLevel, number> = {
debug: 10,
info: 20,
warn: 30,
error: 40,
none: 100,
};
const useFitText = ({
logLevel: logLevelOption = 'info',
maxFontSize = 100,
minFontSize = 20,
onFinish,
onStart,
resolution = 5,
}: TOptions = {}) => {
const logLevel = LOG_LEVEL[logLevelOption];
const initState = useCallback(() => {
return {
calcKey: 0,
fontSize: maxFontSize,
fontSizePrev: minFontSize,
fontSizeMax: maxFontSize,
fontSizeMin: minFontSize,
};
}, [maxFontSize, minFontSize]);
const ref = useRef<HTMLDivElement>(null);
const innerHtmlPrevRef = useRef<string | null>();
const isCalculatingRef = useRef(false);
const [state, setState] = useState(initState);
const { calcKey, fontSize, fontSizeMax, fontSizeMin, fontSizePrev } = state;
// Monitor div size changes and recalculate on resize
let animationFrameId: number | null = null;
const [ro] = useState(
() =>
new ResizeObserver(() => {
animationFrameId = window.requestAnimationFrame(() => {
if (isCalculatingRef.current) {
return;
}
onStart && onStart();
isCalculatingRef.current = true;
// `calcKey` is used in the dependencies array of
// `useIsoLayoutEffect` below. It is incremented so that the font size
// will be recalculated even if the previous state didn't change (e.g.
// when the text fit initially).
setState({
...initState(),
calcKey: calcKey + 1,
});
});
}),
);
useEffect(() => {
if (ref.current) {
ro.observe(ref.current);
}
return () => {
animationFrameId && window.cancelAnimationFrame(animationFrameId);
ro.disconnect();
};
}, [animationFrameId, ro]);
// Recalculate when the div contents change
const innerHtml = ref.current && ref.current.innerHTML;
useEffect(() => {
if (calcKey === 0 || isCalculatingRef.current) {
return;
}
if (innerHtml !== innerHtmlPrevRef.current) {
onStart && onStart();
setState({
...initState(),
calcKey: calcKey + 1,
});
}
innerHtmlPrevRef.current = innerHtml;
}, [calcKey, initState, innerHtml, onStart]);
// Check overflow and resize font
useLayoutEffect(() => {
// Don't start calculating font size until the `resizeKey` is incremented
// above in the `ResizeObserver` callback. This avoids an extra resize
// on initialization.
if (calcKey === 0) {
return;
}
const isWithinResolution = Math.abs(fontSize - fontSizePrev) <= resolution;
const isOverflow =
!!ref.current &&
(ref.current.scrollHeight > ref.current.offsetHeight ||
ref.current.scrollWidth > ref.current.offsetWidth);
const isFailed = isOverflow && fontSize === fontSizePrev;
const isAsc = fontSize > fontSizePrev;
// Return if the font size has been adjusted "enough" (change within `resolution`)
// reduce font size by one increment if it's overflowing.
if (isWithinResolution) {
if (isFailed) {
isCalculatingRef.current = false;
if (logLevel <= LOG_LEVEL.info) {
console.info(
`[use-fit-text] reached \`minFontSize = ${minFontSize}\` without fitting text`,
);
}
} else if (isOverflow) {
setState({
fontSize: isAsc ? fontSizePrev : fontSizeMin,
fontSizeMax,
fontSizeMin,
fontSizePrev,
calcKey,
});
} else {
isCalculatingRef.current = false;
onFinish && onFinish(fontSize);
}
return;
}
// Binary search to adjust font size
let delta: number;
let newMax = fontSizeMax;
let newMin = fontSizeMin;
if (isOverflow) {
delta = isAsc ? fontSizePrev - fontSize : fontSizeMin - fontSize;
newMax = Math.min(fontSizeMax, fontSize);
} else {
delta = isAsc ? fontSizeMax - fontSize : fontSizePrev - fontSize;
newMin = Math.max(fontSizeMin, fontSize);
}
setState({
calcKey,
fontSize: fontSize + delta / 2,
fontSizeMax: newMax,
fontSizeMin: newMin,
fontSizePrev: fontSize,
});
}, [
calcKey,
fontSize,
fontSizeMax,
fontSizeMin,
fontSizePrev,
onFinish,
ref,
resolution,
]);
return { fontSize: `${fontSize}%`, ref };
};
export default useFitText;
@@ -16,7 +16,7 @@ test('generate 100 with less than 110 attempts', () => {
expect(attempts).toBeLessThan(105);
});
describe('generate 1000 with less than 1020 attempts', () => {
test('generate 1000 with less than 1020 attempts', () => {
const ids = new Set();
let attempts = 1;
while (ids.size < 1000) {
@@ -3,17 +3,25 @@ import { IconButton } from '@chakra-ui/button';
import { Editable, EditableInput, EditablePreview } from '@chakra-ui/editable';
import { Tooltip } from '@chakra-ui/tooltip';
import { IoSunny } from '@react-icons/all-files/io5/IoSunny';
import PropTypes from 'prop-types';
import { tooltipDelayMid } from '../../../ontimeConfig';
import style from './MessageControl.module.scss';
export default function InputRow(props) {
const { label, placeholder, text, visible, actionHandler, changeHandler } = props;
const [inputText, setInputText] = useState(text || '');
interface InputRowProps {
label: string;
placeholder: string;
text: string;
visible: boolean;
actionHandler: (action: string, payload: object) => void;
changeHandler: (newValue: string) => void;
}
const handleInputChange = (newValue) => {
export default function InputRow(props: InputRowProps) {
const { label, placeholder, text, visible, actionHandler, changeHandler } = props;
const [inputText, setInputText] = useState<string>(text || '');
const handleInputChange = (newValue: string) => {
setInputText(newValue);
changeHandler(newValue);
};
@@ -26,7 +34,7 @@ export default function InputRow(props) {
return (
<div className={`${visible ? style.inputRowActive: ''}`}>
<div className={`${visible ? style.inputRowActive : ''}`}>
<span className={style.label}>{label}</span>
<div className={style.inputItems}>
<Editable
@@ -53,12 +61,3 @@ export default function InputRow(props) {
</div>
);
}
InputRow.propTypes = {
label: PropTypes.string,
placeholder: PropTypes.string,
text: PropTypes.string,
visible: PropTypes.bool,
actionHandler: PropTypes.func.isRequired,
changeHandler: PropTypes.func.isRequired,
};
@@ -1,24 +0,0 @@
import { QueryClientProvider } from '@tanstack/react-query';
import { render, screen } from '@testing-library/react';
import { queryClientMock } from '../../../__mocks__/QueryClient.mock';
import MenuBar from '../MenuBar';
const onOpenHandler = jest.fn();
const onCloseHandler = jest.fn();
const renderInMock = () => {
render(
<QueryClientProvider client={queryClientMock}>
<MenuBar onOpen={onOpenHandler} onClose={onCloseHandler} />
</QueryClientProvider>
);
};
test('check that menu bar renders correctly', () => {
// need to inject the react query provider
renderInMock();
const nButtons = screen.getAllByRole('button').length;
expect(nButtons).toBe(7);
});
@@ -1,10 +1,11 @@
import { render, screen } from '@testing-library/react';
import { vi } from 'vitest';
import MenuActionButtons from "../MenuActionButtons";
import MenuActionButtons from '../MenuActionButtons';
const actionHandler = jest.fn();
const actionHandler = vi.fn();
const renderInMock = () => {
render(<MenuActionButtons actionHandler={actionHandler} />)
render(<MenuActionButtons actionHandler={actionHandler} />);
};
test('check that menu bar renders correctly', () => {
@@ -12,8 +13,8 @@ test('check that menu bar renders correctly', () => {
renderInMock();
const b = screen.getByRole('button', {
name: /create menu/i
})
name: /create menu/i,
});
expect(b).toBeInTheDocument();
});
});
@@ -1,19 +1,22 @@
import { QueryClientProvider } from '@tanstack/react-query';
import { render, screen } from '@testing-library/react';
import { vi } from 'vitest';
import { queryClientMock } from '../../../__mocks__/QueryClient.mock';
import MenuBar from '../MenuBar';
const onOpenHandler = jest.fn();
const onCloseHandler = jest.fn();
const onOpenHandler = vi.fn();
const onCloseHandler = vi.fn();
const isOpen = false;
const onUploadOpenHandler = jest.fn();
const onUploadOpenHandler = vi.fn();
const renderInMock = () => {
render(
<QueryClientProvider client={queryClientMock}>
<MenuBar onOpen={onOpenHandler} onClose={onCloseHandler} isOpen={isOpen} onUploadOpen={onUploadOpenHandler} />
</QueryClientProvider>
<MenuBar isSettingsOpen={isOpen} onSettingsOpen={onOpenHandler}
onSettingsClose={onCloseHandler} isUploadOpen={isOpen}
onUploadOpen={onUploadOpenHandler} />
</QueryClientProvider>,
);
};
@@ -17,6 +17,7 @@ import { getSettings, ontimePlaceholderSettings, postSettings } from 'common/api
import { useFetch } from 'common/hooks/useFetch';
import { useAtom } from 'jotai';
import { version } from '../../../package.json';
import { eventSettingsAtom } from '../../common/atoms/LocalEventSettings';
import TooltipActionBtn from '../../common/components/buttons/TooltipActionBtn';
import { LoggingContext } from '../../common/context/LoggingContext';
@@ -26,9 +27,6 @@ import SubmitContainer from './SubmitContainer';
import style from './Modals.module.scss';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const version = require('../../../package.json').version;
export default function AppSettingsModal() {
const { data, status, refetch } = useFetch(APP_SETTINGS, getSettings);
const { emitError, emitWarning } = useContext(LoggingContext);
@@ -1,4 +1,52 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1
exports[`makeTable() > returns array of arrays with given fields 1`] = `
[
[
"Ontime · Schedule Template",
],
[
"Event Name",
"",
],
[
"Event URL",
"",
],
[],
[
"Time Start",
"Time End",
"Event Title",
"Presenter Name",
"Event Subtitle",
"Is Public? (x)",
"Notes",
"Colour",
"user0:test",
],
[
"00:00:00",
"00:00:00",
"test title 1",
"",
"",
"x",
"",
"",
"test",
"test",
"",
"",
"",
"",
"",
"",
"",
"",
],
]
`;
exports[`makeTable() returns array of arrays with given fields 1`] = `
Array [
@@ -1,9 +1,9 @@
import { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import useFitText from 'use-fit-text';
import { overrideStylesURL } from '../../../common/api/apiConstants';
import NavLogo from '../../../common/components/nav/NavLogo';
import useFitText from '../../../common/hooks/useFitText';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { formatDisplay } from '../../../common/utils/dateConfig';
import {
+4 -5
View File
@@ -1,5 +1,4 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
import matchers from "@testing-library/jest-dom/matchers";
import { expect } from "vitest";
expect.extend(matchers);