Skip to content

Commit

Permalink
feat: inlineForm component
Browse files Browse the repository at this point in the history
  • Loading branch information
Katrin-kudryash committed Sep 1, 2023
1 parent 398a8c3 commit fea48bf
Show file tree
Hide file tree
Showing 3 changed files with 233 additions and 0 deletions.
122 changes: 122 additions & 0 deletions src/components/InlineForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React, { useEffect, useMemo, useReducer, useRef, useState } from 'react';
import styled from 'styled-components';
import { IconQuestionCircleOutline } from '@taskany/icons';

import { KeyCode, useKeyboard } from '../hooks/useKeyboard';
import { useClickOutside } from '../hooks/useClickOutside';
import { nullable } from '../utils';

import { Form } from './Form';
import { Popup } from './Popup';

interface RenderTriggerProps {
onClick: () => void;
}

interface InlineFormProps {
renderTrigger?: (props: RenderTriggerProps) => React.ReactNode;
onSubmit: () => void;
children: React.ReactNode;
tip?: React.ReactNode;
onReset: () => void;
isSubmitted?: boolean;
className?: string;
}

const StyledWrapper = styled.div`
display: contents;
& form {
background-color: transparent;
display: contents;
}
`;

const StyledFormWrapper = styled.div`
display: contents;
position: relative;
`;

const StyledQuestionIcon = styled(IconQuestionCircleOutline)`
margin-left: 4px;
`;

export const InlineForm: React.FC<InlineFormProps> = ({
renderTrigger,
onSubmit,
onReset,
isSubmitted,
children,
tip,
className,
}) => {
const wrapperRef = useRef<HTMLDivElement>(null);
const [visible, toggle] = useReducer((state) => !state, !renderTrigger);
const [hintVisible, toggleHintVisible] = useState(false);
const hintRef = useRef<HTMLAnchorElement>(null);
const trigger = useMemo(() => {
if (typeof renderTrigger === 'function') {
return renderTrigger({ onClick: toggle });
}

return null;
}, [renderTrigger]);

const [onESC] = useKeyboard(
[KeyCode.Escape],
() => {
if (visible) {
toggle();
}
},
{
capture: true,
},
);

useClickOutside(wrapperRef, () => {
if (visible) {
toggle();
}
});

useEffect(() => {
if (isSubmitted) {
toggle();
onReset();
}
}, [onReset, isSubmitted]);

useEffect(() => {
if (!visible) {
onReset();
}
}, [visible, onReset]);

return (
<StyledWrapper ref={wrapperRef} className={className} {...onESC}>
{!visible && trigger}
{visible && (
<Form onSubmit={onSubmit}>
<StyledFormWrapper>
{children}
{nullable(tip, (t: React.ReactNode) => (
<>
<a
ref={hintRef}
onMouseOver={() => toggleHintVisible(true)}
onMouseLeave={() => toggleHintVisible(false)}
>
<StyledQuestionIcon size="s" />
</a>
<Popup reference={hintRef} visible={hintVisible} placement="top-end">
{t}
</Popup>
</>
))}
</StyledFormWrapper>
</Form>
)}
</StyledWrapper>
);
};
1 change: 1 addition & 0 deletions src/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ export * from './Popup';
export * from './ListView';
export * from './ErrorPopup';
export * from './Spinner';
export * from './InlineForm';
110 changes: 110 additions & 0 deletions src/stories/InlineForm.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React, { useCallback } from 'react';
import { Meta, StoryObj } from '@storybook/react';
import styled from 'styled-components';
import { IconQuestionCircleOutline, IconTargetOutline } from '@taskany/icons';
import { gray7 } from '@taskany/colors';

import { InlineForm } from '../components/InlineForm';
import { Button } from '../components/Button';
import { TableCell } from '../components/Table';
import { Form } from '../components/Form';
import { Popup } from '../components/Popup';
import { FormInput } from '../components/FormInput';

const meta: Meta<typeof InlineForm> = {
title: 'InlineForm',
component: InlineForm,
};

export default meta;

type Story = StoryObj<typeof meta>;

const StyledWrapper = styled.div`
display: contents;
& form {
background-color: transparent;
display: contents;
}
`;

const StyledFormWrapper = styled.div`
display: contents;
position: relative;
`;

const StyledTableCell = styled(TableCell)`
flex-wrap: nowrap;
display: flex;
align-items: center;
`;

const StyledFormInput = styled(FormInput)`
font-size: 14px;
font-weight: normal;
padding: 5px 10px;
flex: 1;
border: 1px solid ${gray7};
box-sizing: border-box;
`;

const StyledQuestionIcon = styled(IconQuestionCircleOutline)`
margin-left: 4px;
`;

export const InlineFormComponent: Story = () => {
const onClick = () => {
console.log('Clicked');

Check warning on line 59 in src/stories/InlineForm.stories.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
};

const handleFormInputChange = useCallback(() => {
console.log('Change Click');
}, []);

return (
<StyledWrapper>
<Form onSubmit={() => console.log()}>
<StyledFormWrapper>
<StyledTableCell col={5} align="center">
<Button
type="button"
view={'default'}
brick="right"
outline
iconLeft={<IconTargetOutline size="xs" noWrap />}
onClick={onClick}
onMouseEnter={() => console.log('onMouseEnter')}
onMouseLeave={() => console.log('onMouseLeave')}
/>
<StyledFormInput
onChange={handleFormInputChange}
autoFocus
brick="center"
placeholder={'Enter criteria'}
/>
<StyledFormInput
onChange={handleFormInputChange}
autoComplete="off"
placeholder={'No more than 100'}
brick="center"
/>
<Button text={'Add'} brick="left" type="submit" view="primary" outline />
<>
<a
onMouseOver={() => console.log('onMouseOver')}
onMouseLeave={() => console.log('onMouseLeave')}
>
<StyledQuestionIcon size="s" />
</a>
<Popup placement="top-end">Hint</Popup>
</>
</StyledTableCell>
</StyledFormWrapper>
</Form>
</StyledWrapper>
);
};

InlineFormComponent.args = {};

0 comments on commit fea48bf

Please sign in to comment.