Skip to content
Merged
4 changes: 1 addition & 3 deletions examples/form/components/SubmittedFields.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ export function SubmittedFields({ state, updateState }) {
return null;
}

delete value.dialog;

return (
<Dialog title="Dados Tratados" onClose={() => updateState({ dialog: { value: null } })}>
<pre>{JSON.stringify(value, null, 2)}</pre>
<pre>{JSON.stringify({ ...value, dialog: undefined }, null, 2)}</pre>
</Dialog>
);
}
2 changes: 1 addition & 1 deletion packages/helpers/src/dom.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ describe('helpers/dom', () => {
// Simulate the animation frame before the element is added
triggerAnimationFrame();

expect(window.requestAnimationFrame).toHaveBeenCalledTimes(1);
expect(window.requestAnimationFrame).toHaveBeenCalledOnce();
expect(el.scrollIntoView).toHaveBeenCalledWith({ behavior: 'auto' });
});

Expand Down
77 changes: 28 additions & 49 deletions packages/hooks/src/useTreeCollapse/useTreeCollapse.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getSubtreeSize } from '@barso/helpers';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useEffect, useReducer, useRef } from 'react';

/**
* @typedef {Object} TreeNode
Expand Down Expand Up @@ -41,68 +41,47 @@ export function useTreeCollapse({
additionalBudget = 10,
defaultExpandedId = null,
} = {}) {
const lastParamsRef = useRef({ totalBudget, minimalSubTree, defaultExpandedId });

const [nodeStates, setNodeStates] = useState(() =>
computeNodeStates({
minimalSubTree,
nodes,
totalBudget,
defaultExpandedId,
}),
const [nodeStates, dispatch] = useReducer(
(previousState, action = {}) => {
switch (action.type) {
case 'EXPAND_NODE':
return expandChildren({ additionalBudget, minimalSubTree, previousState, ...action });
case 'COLLAPSE_NODE':
return collapseChildren({ previousState, ...action });
case 'UPDATE_STATE':
return computeNodeStates({ defaultExpandedId, minimalSubTree, nodes, totalBudget, previousState, ...action });
case 'RESET_STATE':
default:
return computeNodeStates({ defaultExpandedId, minimalSubTree, nodes, totalBudget, ...action });
}
},
{ minimalSubTree, nodes, totalBudget, defaultExpandedId },
computeNodeStates,
);

const lastParamsRef = useRef();

useEffect(() => {
if (!lastParamsRef.current) {
lastParamsRef.current = { totalBudget, minimalSubTree, defaultExpandedId };
return;
}

const shouldUsePrevious =
lastParamsRef.current.totalBudget === totalBudget &&
lastParamsRef.current.minimalSubTree === minimalSubTree &&
lastParamsRef.current.defaultExpandedId === defaultExpandedId;

if (shouldUsePrevious) {
setNodeStates((previousState) =>
computeNodeStates({
minimalSubTree,
nodes,
previousState,
totalBudget,
defaultExpandedId,
}),
);
dispatch({ type: 'UPDATE_STATE' });
} else {
lastParamsRef.current = { totalBudget, minimalSubTree, defaultExpandedId };
setNodeStates(
computeNodeStates({
minimalSubTree,
nodes,
totalBudget,
defaultExpandedId,
}),
);
dispatch({ type: 'RESET_STATE' });
}
}, [defaultExpandedId, nodes, minimalSubTree, totalBudget]);

const handleExpand = useCallback(
(targetId) => {
setNodeStates((previousState) =>
expandChildren({
additionalBudget,
minimalSubTree,
previousState,
targetId,
}),
);
},
[additionalBudget, minimalSubTree],
);

const handleCollapse = useCallback((targetId) => {
setNodeStates((previousState) =>
collapseChildren({
previousState,
targetId,
}),
);
}, []);
const handleExpand = (targetId) => dispatch({ type: 'EXPAND_NODE', targetId });
const handleCollapse = (targetId) => dispatch({ type: 'COLLAPSE_NODE', targetId });

return {
handleCollapse,
Expand Down
22 changes: 10 additions & 12 deletions packages/infra/src/logger/axiom-transport.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ const mocks = vi.hoisted(() => {
const waitUntil = vi.fn().mockImplementation((promise) => promise);
const ingest = vi.fn();
const flush = vi.fn().mockResolvedValue();
const Axiom = vi.fn().mockImplementation(() => ({
ingest,
flush,
}));
const Axiom = vi.fn().mockImplementation(function () {
this.ingest = ingest;
this.flush = flush;
});

return {
ingest,
Expand Down Expand Up @@ -102,8 +102,7 @@ describe('axiomTransport', () => {

await transport.flush();

expect(mocks.waitUntil).toHaveBeenCalledWith(mocks.flush());
expect(mocks.waitUntil).toHaveBeenCalledOnce();
expect(mocks.waitUntil).toHaveBeenCalledExactlyOnceWith(mocks.flush());
});

it('should flush with logs', async () => {
Expand All @@ -121,8 +120,7 @@ describe('axiomTransport', () => {
expect.objectContaining({ level: 'error', msg: 'test log 2' }),
);

expect(mocks.waitUntil).toHaveBeenCalledWith(mocks.flush());
expect(mocks.waitUntil).toHaveBeenCalledOnce();
expect(mocks.waitUntil).toHaveBeenCalledExactlyOnceWith(mocks.flush());
});

it('should not ingest invalid logs', async () => {
Expand Down Expand Up @@ -169,10 +167,10 @@ describe('axiomTransport', () => {
it('should continue logging even if an error occurs with Axiom', async () => {
const error = new Error('test error');

mocks.Axiom.mockImplementationOnce(({ onError }) => ({
ingest: mocks.ingest.mockImplementationOnce(() => onError(error)),
flush: mocks.flush,
}));
mocks.Axiom.mockImplementationOnce(function ({ onError }) {
this.ingest = mocks.ingest.mockImplementationOnce(() => onError(error));
this.flush = mocks.flush;
});

const transport = axiomTransport({ dataset, token });

Expand Down
9 changes: 5 additions & 4 deletions packages/infra/src/logger/logger.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ const token = 'test-token';
describe('infra/logger', () => {
const mocks = vi.hoisted(() => ({
axiomIngest: vi.fn(),
flush: vi.fn().mockResolvedValue(),
}));

vi.mock('@axiomhq/js', () => ({
Axiom: vi.fn().mockImplementation(() => ({
ingest: mocks.axiomIngest,
flush: vi.fn().mockResolvedValue(),
})),
Axiom: vi.fn().mockImplementation(function () {
this.ingest = mocks.axiomIngest;
this.flush = mocks.flush;
}),
}));

let stdoutSpy;
Expand Down
3 changes: 2 additions & 1 deletion packages/ui/src/FormField/FormField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,11 @@ export const FormField = forwardRef(
const inputProps = {
validationStatus: error ? 'error' : isValid ? 'success' : null,
inputMode,
ref,
...props,
};

inputProps.ref = ref;

if (type === 'password') {
function focusAfterEnd() {
setTimeout(() => {
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/FormField/FormField.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ describe('ui', () => {
const handleClick = vi.fn();
render(<Suggestion suggestion={{ value: 'test', onClick: handleClick }} />);
fireEvent.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledTimes(1);
expect(handleClick).toHaveBeenCalledOnce();
});

it('calls ignoreClick when ignore button is clicked', () => {
Expand Down
17 changes: 7 additions & 10 deletions packages/ui/src/GoToTopButton/GoToTopButton.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { GoToTopButton } from './GoToTopButton.jsx';
const IntersectionObserver = global.IntersectionObserver;
let lastObserver = null;

const MockIntersectionObserver = vi.fn((callback) => {
const MockIntersectionObserver = vi.fn(function (callback) {
lastObserver = {
callback,
observe: vi.fn(),
Expand Down Expand Up @@ -48,8 +48,7 @@ describe('ui', () => {
);
expect(screen.queryByRole('button')).toBeNull();

expect(lastObserver.observe).toHaveBeenCalledTimes(1);
expect(lastObserver.observe).toHaveBeenCalledWith(screen.getByTestId('target'));
expect(lastObserver.observe).toHaveBeenCalledExactlyOnceWith(screen.getByTestId('target'));

act(() => lastObserver.callback([{ isIntersecting: false }]));
expect(screen.getByRole('button')).toBeVisible();
Expand All @@ -62,8 +61,7 @@ describe('ui', () => {

render(<GoToTopButton target={targetElement} />, { container: container });

expect(lastObserver.observe).toHaveBeenCalledTimes(1);
expect(lastObserver.observe).toHaveBeenCalledWith(targetElement);
expect(lastObserver.observe).toHaveBeenCalledExactlyOnceWith(targetElement);

act(() => lastObserver.callback([{ isIntersecting: false }]));
expect(screen.getByRole('button')).toBeVisible();
Expand Down Expand Up @@ -102,8 +100,7 @@ describe('ui', () => {

window.scrollTo = originalScrollTo;

expect(scrollToMock).toHaveBeenCalledTimes(1);
expect(scrollToMock).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' });
expect(scrollToMock).toHaveBeenCalledExactlyOnceWith({ top: 0, behavior: 'smooth' });
});

it('logs a warning if the target element is not found', () => {
Expand All @@ -123,12 +120,12 @@ describe('ui', () => {
</>,
);

expect(MockIntersectionObserver).toHaveBeenCalledTimes(1);
expect(lastObserver.observe).toHaveBeenCalledTimes(1);
expect(MockIntersectionObserver).toHaveBeenCalledOnce();
expect(lastObserver.observe).toHaveBeenCalledOnce();

unmount();

expect(lastObserver.disconnect).toHaveBeenCalledTimes(1);
expect(lastObserver.disconnect).toHaveBeenCalledOnce();
});
});
});
25 changes: 5 additions & 20 deletions packages/ui/src/Markdown/Markdown.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import mathLocale from '@bytemd/plugin-math/locales/pt_BR.json';
import mermaidPlugin from '@bytemd/plugin-mermaid';
import mermaidLocale from '@bytemd/plugin-mermaid/locales/pt_BR.json';
import { Editor as ByteMdEditor, Viewer as ByteMdViewer } from '@bytemd/react';
import { Box, useTheme } from '@primer/react';
import { useTheme } from '@primer/react';
import byteMDLocale from 'bytemd/locales/pt_BR.json';
import { useEffect, useMemo, useRef, useState } from 'react';
import { useEffect, useMemo, useRef } from 'react';

import {
anchorHeadersPlugin,
Expand Down Expand Up @@ -57,30 +57,15 @@ function usePlugins({ areLinksTrusted, clobberPrefix, shouldAddNofollow }) {
return plugins;
}

export function MarkdownViewer({ value: _value, areLinksTrusted, clobberPrefix, shouldAddNofollow, ...props }) {
export function MarkdownViewer({ areLinksTrusted, clobberPrefix, shouldAddNofollow, ...props }) {
clobberPrefix = clobberPrefix?.toLowerCase();
const bytemdPluginList = usePlugins({ areLinksTrusted, clobberPrefix, shouldAddNofollow });
const [value, setValue] = useState(_value);

useEffect(() => {
let timeout;

setValue((value) => {
timeout = setTimeout(() => setValue(value));
return value + '\n\u0160';
});

return () => clearTimeout(timeout);
}, [bytemdPluginList]);

useEffect(() => setValue(_value), [_value]);

return (
<ByteMdViewer
sanitize={sanitize({ clobberPrefix })}
remarkRehype={{ clobberPrefix }}
plugins={bytemdPluginList}
value={value}
{...props}
/>
);
Expand Down Expand Up @@ -115,7 +100,7 @@ export function MarkdownEditor({
}, []);

return (
<Box sx={{ width: '100%' }} ref={editorRef} className={isInvalid ? 'is-invalid' : ''}>
<div ref={editorRef} className={isInvalid ? 'is-invalid' : ''}>
<ByteMdEditor
plugins={bytemdPluginList}
mode={mode}
Expand All @@ -126,7 +111,7 @@ export function MarkdownEditor({
{...props}
/>
<EditorStyles height={initialHeight} mode={mode} />
</Box>
</div>
);
}

Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/Notifications/NotificationList.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,10 @@ describe('ui/Notifications', () => {

fireEvent.keyPress(item, { key: 'Enter', code: 'Enter', charCode: 13 });
expect(onItemSelect).toHaveBeenCalledWith(notifications[0]);
expect(onItemSelect).toHaveBeenCalledTimes(1);
expect(onItemSelect).toHaveBeenCalledOnce();

fireEvent.keyPress(item, { key: 'Escape', code: 'Escape', charCode: 27 });
expect(onItemSelect).toHaveBeenCalledTimes(1); // Escape should not trigger onItemSelect
expect(onItemSelect).toHaveBeenCalledOnce(); // Escape should not trigger onItemSelect

fireEvent.keyPress(item, { key: ' ', code: 'Space', charCode: 32 });
expect(onItemSelect).toHaveBeenCalledWith(notifications[0]);
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/Notifications/NotificationMenu.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ describe('ui/Notifications/NotificationMenu', () => {
fireEvent.click(closeButton);

expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
expect(onCloseMenu).toHaveBeenCalledTimes(1);
expect(onCloseMenu).toHaveBeenCalledOnce();
});
});

Expand Down
10 changes: 5 additions & 5 deletions packages/ui/src/_document.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ const hoisted = vi.hoisted(() => {
const collectStyles = vi.fn();
const getStyleElement = vi.fn(() => <style>Styled Components Styles</style>);
const seal = vi.fn();
const ServerStyleSheet = vi.fn(() => ({
collectStyles,
getStyleElement,
seal,
}));
const ServerStyleSheet = vi.fn(function () {
this.collectStyles = collectStyles;
this.getStyleElement = getStyleElement;
this.seal = seal;
});

const renderPage = vi.fn((App) => collectStyles(App));
const getInitialProps = vi.fn(() => ({}));
Expand Down
9 changes: 9 additions & 0 deletions tests/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,13 @@ if (typeof document !== 'undefined') {
removeListener: vi.fn(),
}),
});

// https://github.com/jsdom/jsdom/issues/3998
if (!document.adoptedStyleSheets) {
Object.defineProperty(document, 'adoptedStyleSheets', {
writable: true,
configurable: true,
value: [],
});
}
}
Loading