diff --git a/src/components/ClearButton/ClearButton.test.tsx b/src/components/ClearButton/ClearButton.test.tsx index d7162a5..877c04b 100644 --- a/src/components/ClearButton/ClearButton.test.tsx +++ b/src/components/ClearButton/ClearButton.test.tsx @@ -6,16 +6,16 @@ import { resultExceptions } from '../Tree/types'; import { text } from '../../helpers'; import { render } from '../../utils/test-util'; import { useResultContext } from '../../contexts/ResultContext'; -import { useTextareaContext } from '../../contexts/TextareaContext'; +import { useTextareaRefContext } from '../../contexts/TextareaRefContext'; jest.mock('../../contexts/ResultContext'); -jest.mock('../../contexts/TextareaContext'); +jest.mock('../../contexts/TextareaRefContext'); const mockSetResult = jest.fn(); const mockSetTextareaValue = jest.fn(); (useResultContext as jest.Mock).mockReturnValue({ setResult: mockSetResult }); -(useTextareaContext as jest.Mock).mockReturnValue({ +(useTextareaRefContext as jest.Mock).mockReturnValue({ setTextareaValue: mockSetTextareaValue, }); diff --git a/src/components/ClearButton/ClearButton.tsx b/src/components/ClearButton/ClearButton.tsx index 44f1b2d..5670d23 100644 --- a/src/components/ClearButton/ClearButton.tsx +++ b/src/components/ClearButton/ClearButton.tsx @@ -2,12 +2,12 @@ import React from 'react'; import Button from '../common/Button'; import { text } from '../../helpers/index'; import { useResultContext } from '../../contexts/ResultContext'; -import { useTextareaContext } from '../../contexts/TextareaContext'; +import { useTextareaRefContext } from '../../contexts/TextareaRefContext'; import { resultExceptions } from '../Tree/types'; const ClearButton: React.FC = () => { const { setResult } = useResultContext(); - const { setTextareaValue } = useTextareaContext(); + const { setTextareaValue } = useTextareaRefContext(); const handleClear = () => { setTextareaValue(''); @@ -17,4 +17,4 @@ const ClearButton: React.FC = () => { return ; }; -export default React.memo(ClearButton); +export default ClearButton; diff --git a/src/components/CreateButton/CreateButton.test.tsx b/src/components/CreateButton/CreateButton.test.tsx index bea7ec9..6bea735 100644 --- a/src/components/CreateButton/CreateButton.test.tsx +++ b/src/components/CreateButton/CreateButton.test.tsx @@ -7,18 +7,19 @@ import { text } from '../../helpers'; import { render } from '../../utils/test-util'; import { buildTree } from '../../utils/buildTree'; import { useResultContext } from '../../contexts/ResultContext'; -import { useTextareaContext } from '../../contexts/TextareaContext'; - +import { useTextareaRefContext } from '../../contexts/TextareaRefContext'; jest.mock('../../contexts/ResultContext'); -jest.mock('../../contexts/TextareaContext'); +jest.mock('../../contexts/TextareaRefContext'); jest.mock('../../utils/buildTree'); const mockSetResult = jest.fn(); -const mockTextareaValue = ''; +const mockTextareaRef = { current: { value: '' } }; +const mockSetTextareaValue = jest.fn(); (useResultContext as jest.Mock).mockReturnValue({ setResult: mockSetResult }); -(useTextareaContext as jest.Mock).mockReturnValue({ - textareaValue: mockTextareaValue, +(useTextareaRefContext as jest.Mock).mockReturnValue({ + textareaRef: mockTextareaRef, + setTextareaValue: mockSetTextareaValue, }); describe('CreateButton Component', () => { @@ -32,8 +33,9 @@ describe('CreateButton Component', () => { }); it('handles create with empty textarea and example paths', () => { - (useTextareaContext as jest.Mock).mockReturnValueOnce({ - textareaValue: '', + (useTextareaRefContext as jest.Mock).mockReturnValueOnce({ + textareaRef: { current: { value: '' } }, + setTextareaValue: mockSetTextareaValue, }); (buildTree as jest.Mock).mockReturnValueOnce({}); @@ -47,8 +49,9 @@ describe('CreateButton Component', () => { it('handles create with empty textarea and non-empty tree', () => { const mockTree = { root: {} }; - (useTextareaContext as jest.Mock).mockReturnValueOnce({ - textareaValue: '', + (useTextareaRefContext as jest.Mock).mockReturnValueOnce({ + textareaRef: { current: { value: '' } }, + setTextareaValue: mockSetTextareaValue, }); (buildTree as jest.Mock).mockReturnValueOnce(mockTree); @@ -59,8 +62,9 @@ describe('CreateButton Component', () => { }); it('handles create with invalid JSON in textarea', () => { - (useTextareaContext as jest.Mock).mockReturnValueOnce({ - textareaValue: 'invalid JSON', + (useTextareaRefContext as jest.Mock).mockReturnValueOnce({ + textareaRef: { current: { value: 'invalid JSON' } }, + setTextareaValue: mockSetTextareaValue, }); const { getByText } = render(); @@ -70,8 +74,9 @@ describe('CreateButton Component', () => { }); it('handles create with valid JSON in textarea resulting in empty tree', () => { - (useTextareaContext as jest.Mock).mockReturnValueOnce({ - textareaValue: '[]', + (useTextareaRefContext as jest.Mock).mockReturnValueOnce({ + textareaRef: { current: { value: '[]' } }, + setTextareaValue: mockSetTextareaValue, }); (buildTree as jest.Mock).mockReturnValueOnce({}); @@ -83,8 +88,9 @@ describe('CreateButton Component', () => { it('handles create with valid JSON in textarea resulting in non-empty tree', () => { const mockTree = { root: {} }; - (useTextareaContext as jest.Mock).mockReturnValueOnce({ - textareaValue: '["path"]', + (useTextareaRefContext as jest.Mock).mockReturnValueOnce({ + textareaRef: { current: { value: '["path"]' } }, + setTextareaValue: mockSetTextareaValue, }); (buildTree as jest.Mock).mockReturnValueOnce(mockTree); diff --git a/src/components/CreateButton/CreateButton.tsx b/src/components/CreateButton/CreateButton.tsx index 217d782..0ba3210 100644 --- a/src/components/CreateButton/CreateButton.tsx +++ b/src/components/CreateButton/CreateButton.tsx @@ -5,17 +5,20 @@ import { text } from '../../helpers/index'; import { pathsExample } from '../../helpers/index'; import { buildTree } from '../../utils/buildTree'; import { useResultContext } from '../../contexts/ResultContext'; -import { useTextareaContext } from '../../contexts/TextareaContext'; +import { useTextareaRefContext } from '../../contexts/TextareaRefContext'; const CreateButton: React.FC = () => { const { setResult } = useResultContext(); - const { textareaValue } = useTextareaContext(); + const { textareaRef, setTextareaValue } = useTextareaRefContext(); const handleCreate = () => { + const textareaValue = textareaRef.current?.value; + try { let paths; if (textareaValue) { + setTextareaValue(textareaValue); paths = JSON.parse(textareaValue); } else { paths = pathsExample; diff --git a/src/components/Providers/Providers.tsx b/src/components/Providers/Providers.tsx index 10a6587..5751b6b 100644 --- a/src/components/Providers/Providers.tsx +++ b/src/components/Providers/Providers.tsx @@ -2,7 +2,7 @@ import React, { ReactNode } from 'react'; import { ThemeProvider } from 'styled-components'; import theme from '../../styles/styles-theme'; import { ResultProvider } from '../../contexts/ResultContext'; -import { TextareaProvider } from '../../contexts/TextareaContext'; +import { TextareaRefProvider } from '../../contexts/TextareaRefContext'; interface ProvidersProps { children: ReactNode; @@ -11,9 +11,9 @@ interface ProvidersProps { const Providers: React.FC = ({ children }) => { return ( - + {children} - + ); }; diff --git a/src/components/Textarea/Textarea.tsx b/src/components/Textarea/Textarea.tsx index d21f43e..e188e8f 100644 --- a/src/components/Textarea/Textarea.tsx +++ b/src/components/Textarea/Textarea.tsx @@ -1,6 +1,6 @@ -import React from 'react'; +import React, { useState } from 'react'; +import { useTextareaRefContext } from '../../contexts/TextareaRefContext'; import styled from 'styled-components'; -import { useTextareaContext } from '../../contexts/TextareaContext'; const StyledTextarea = styled.textarea` width: 100%; @@ -24,14 +24,21 @@ interface TextareaProps extends React.TextareaHTMLAttributes {} const Textarea: React.FC = (props) => { - const { textareaValue, setTextareaValue } = useTextareaContext(); + const [state, setState] = useState(''); + const { textareaRef } = useTextareaRefContext(); const handleChange = (e: React.ChangeEvent) => { - setTextareaValue(e.target.value); + setState(e.target.value); }; return ( - + ); }; diff --git a/src/components/Tree/Tree.tsx b/src/components/Tree/Tree.tsx index 8b6fe1f..dd23950 100644 --- a/src/components/Tree/Tree.tsx +++ b/src/components/Tree/Tree.tsx @@ -73,4 +73,4 @@ const Tree: React.FC = () => { return null; }; -export default React.memo(Tree); +export default Tree; diff --git a/src/components/common/Button/Button.tsx b/src/components/common/Button/Button.tsx index bb465ad..bcac413 100644 --- a/src/components/common/Button/Button.tsx +++ b/src/components/common/Button/Button.tsx @@ -24,4 +24,4 @@ const Button: React.FC = ({ children, ...props }) => { return {children}; }; -export default React.memo(Button); +export default Button; diff --git a/src/contexts/TextareaContext.tsx b/src/contexts/TextareaContext.tsx deleted file mode 100644 index 4fb2a80..0000000 --- a/src/contexts/TextareaContext.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React, { - createContext, - useContext, - useState, - ReactNode, - useMemo, -} from 'react'; - -interface TextareaContextType { - textareaValue: string; - setTextareaValue: (value: string) => void; -} - -const TextareaContext = createContext( - undefined -); - -export const TextareaProvider: React.FC<{ children: ReactNode }> = ({ - children, -}) => { - const [textareaValue, setTextareaValue] = useState(''); - - const contextValues = useMemo(() => { - return { - textareaValue, - setTextareaValue, - }; - }, [textareaValue, setTextareaValue]); - - return ( - - {children} - - ); -}; - -export const useTextareaContext = () => { - const context = useContext(TextareaContext); - if (context === undefined) { - throw new Error('useTextareaContext must be used inside TextareaProvider'); - } - return context; -}; diff --git a/src/contexts/TextareaRefContext.tsx b/src/contexts/TextareaRefContext.tsx new file mode 100644 index 0000000..016cf8a --- /dev/null +++ b/src/contexts/TextareaRefContext.tsx @@ -0,0 +1,46 @@ +import React, { + createContext, + useContext, + useRef, + ReactNode, + RefObject, +} from 'react'; + +interface TextareaRefContextType { + textareaRef: RefObject; + setTextareaValue: (value: string) => void; +} + +const TextareaRefContext = createContext( + undefined +); + +export const TextareaRefProvider: React.FC<{ children: ReactNode }> = ({ + children, +}) => { + const textareaRef = useRef(null); + + const setTextareaValue = (value: string) => { + if (textareaRef.current) { + textareaRef.current.value = value; + } + }; + + const contextValue = { textareaRef, setTextareaValue }; + + return ( + + {children} + + ); +}; + +export const useTextareaRefContext = () => { + const context = useContext(TextareaRefContext); + if (context === undefined) { + throw new Error( + 'useTextareaRefContext must be used within a TextareaRefProvider' + ); + } + return context; +}; diff --git a/src/utils/buildTree.ts b/src/utils/buildTree.ts index cf9f989..df86d37 100644 --- a/src/utils/buildTree.ts +++ b/src/utils/buildTree.ts @@ -59,13 +59,14 @@ const cleanTree = (node: TreeNodeType): void => { delete child.__isFile; } - const isFileHasChild = child.__isFile && Object.keys(child).length > 1; + const childArray = Object.keys(child).length > 1; + + const isFileHasChild = child.__isFile && childArray; if (isFileHasChild) { delete child.__isFile; } - const isEmptyHasChild = - child.__isEmptyFolder && Object.keys(child).length > 1; + const isEmptyHasChild = child.__isEmptyFolder && childArray; if (isEmptyHasChild) { delete child.__isEmptyFolder; }