mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-11 18:33:53 +00:00
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:
+1
-3
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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) {
|
||||
|
||||
+14
-15
@@ -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);
|
||||
});
|
||||
+7
-6
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user