Skip to content

Commit

Permalink
Merge pull request #20 from provectus/feature/14-add-custom-params-fo…
Browse files Browse the repository at this point in the history
…r-topics-creation

Feature/14 add custom params for topics creation
  • Loading branch information
Gataniel committed Apr 13, 2020
2 parents bdea709 + e4dbebb commit ea9426e
Show file tree
Hide file tree
Showing 11 changed files with 429 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import CustomParamButton, { CustomParamButtonType } from './CustomParamButton';
import { isFirstParam } from './CustomParams';

interface Props {
index: string;
onAdd: (event: React.MouseEvent<HTMLButtonElement>) => void;
onRemove: (index: string) => void;
}

const CustomParamAction: React.FC<Props> = ({
index,
onAdd,
onRemove,
}) => (
<>
<label className='label'>&nbsp;</label>
{
isFirstParam(index)
? <CustomParamButton className="is-success" type={CustomParamButtonType.plus} onClick={onAdd} />
: <CustomParamButton className="is-danger" type={CustomParamButtonType.minus} onClick={() => onRemove(index)} />
}
</>
)

export default CustomParamAction;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';

export enum CustomParamButtonType {
plus = 'fa-plus',
minus = 'fa-minus',
}

interface Props {
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void,
className: string;
type: CustomParamButtonType;
}

const CustomParamButton: React.FC<Props> = ({
onClick,
className,
type,
}) => (
<button className={`button ${className} is-outlined`} onClick={onClick}>
<span className="icon">
<i className={`fas fa-lg ${type}`}></i>
</span>
</button>
)

export default CustomParamButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { TopicCustomParamOption } from 'redux/interfaces';
import { CUSTOM_PARAMS_OPTIONS } from './customParamsOptions';

interface Props {};

const CustomParamOptions: React.FC<Props> = () => (
<>
<option value=''>Select</option>
{
Object.values(CUSTOM_PARAMS_OPTIONS).map((opt: TopicCustomParamOption) => (
<option key={opt.name} value={opt.name}>
{opt.name}
</option>
))
}
</>
);

export default React.memo(CustomParamOptions);
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from 'react';
import { useFormContext, ErrorMessage } from 'react-hook-form';
import CustomParamOptions from './CustomParamOptions';
import { isFirstParam, INDEX_PREFIX } from './CustomParams';
import { TopicFormCustomParam } from 'redux/interfaces';

interface Props {
isDisabled: boolean;
index: string;
name: string;
}

const CustomParamSelect: React.FC<Props> = ({
isDisabled,
index,
name,
}) => {

const { register, unregister, errors, getValues, triggerValidation } = useFormContext();
const optInputName = `${index}[name]`;

React.useEffect(
() => { if (isFirstParam(index)) { unregister(optInputName) } },
);

const selectedMustBeUniq = (selected: string) => {
const values = getValues({ nest: true });
const customParamsValues: TopicFormCustomParam = values.customParams;

let valid = true;

for (const [key, customParam] of Object.entries(customParamsValues)) {
if (`${INDEX_PREFIX}.${key}` === index) { continue; }
if (selected === customParam.name) {
valid = false;
break;
};
}

return valid ? true : 'Custom Parameter must be unique';
};

return (
<>
<label className="label">Custom Parameter</label>
<div className="select is-block">
<select
name={optInputName}
ref={register({
required: 'Custom Parameter is required.',
validate: { unique: selected => selectedMustBeUniq(selected) },
})}
onChange={() => triggerValidation(optInputName)}
disabled={isDisabled}
defaultValue={name}
>
<CustomParamOptions />
</select>
<p className="help is-danger">
<ErrorMessage errors={errors} name={optInputName}/>
</p>
</div>
</>
);
};

export default React.memo(CustomParamSelect);
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';
import { useFormContext, ErrorMessage } from 'react-hook-form';
import { CUSTOM_PARAMS_OPTIONS } from './customParamsOptions';
import { isFirstParam } from './CustomParams';

interface Props {
isDisabled: boolean;
index: string;
name: string;
defaultValue: string;
}

const CustomParamValue: React.FC<Props> = ({
isDisabled,
index,
name,
defaultValue,
}) => {

const { register, unregister, errors, watch, setValue } = useFormContext();
const selectInputName: string = `${index}[name]`;
const valInputName: string = `${index}[value]`;
const selectedParamName = watch(selectInputName, name);

React.useEffect(
() => {
if (selectedParamName) {
setValue(valInputName, CUSTOM_PARAMS_OPTIONS[selectedParamName].defaultValue, true);
}
},
[selectedParamName],
);

React.useEffect(
() => { if (isFirstParam(index)) { unregister(valInputName) } },
);

return (
<>
<label className="label">Value</label>
<input
className="input"
placeholder="Value"
ref={register({
required: 'Value is required.',
})}
name={valInputName}
defaultValue={defaultValue}
autoComplete="off"
disabled={isDisabled}
/>
<p className="help is-danger">
<ErrorMessage errors={errors} name={valInputName}/>
</p>
</>
);
};

export default React.memo(CustomParamValue);
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React from 'react';
import { omit, reject } from 'lodash';

import { TopicFormCustomParams } from 'redux/interfaces';
import CustomParamSelect from './CustomParamSelect';
import CustomParamValue from './CustomParamValue';
import CustomParamAction from './CustomParamAction';

const DEFAULT_INDEX = 'default';
export const INDEX_PREFIX = 'customParams';
export const isFirstParam = (index: string) => (index === DEFAULT_INDEX);

interface Props {
isSubmitting: boolean;
}

const CustomParams: React.FC<Props> = ({
isSubmitting,
}) => {

const [formCustomParams, setFormCustomParams] = React.useState<TopicFormCustomParams>({
byIndex: { [DEFAULT_INDEX]: { name: '', value: '' } },
allIndexes: [DEFAULT_INDEX],
});

const onAdd = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();

const newIndex = `${INDEX_PREFIX}.${new Date().getTime()}`;

setFormCustomParams({
...formCustomParams,
byIndex: {
...formCustomParams.byIndex,
[newIndex]: { name: '', value: '' },
},
allIndexes: [
formCustomParams.allIndexes[0],
newIndex,
...formCustomParams.allIndexes.slice(1),
],
});
}

const onRemove = (index: string) => {
setFormCustomParams({
...formCustomParams,
byIndex: omit(formCustomParams.byIndex, index),
allIndexes: reject(formCustomParams.allIndexes, (i) => (i === index)),
});
}

return (
<>
{
formCustomParams.allIndexes.map((index) => (
<div className="columns is-centered" key={index}>
<div className="column">
<CustomParamSelect
index={index}
isDisabled={isFirstParam(index) || isSubmitting}
name={formCustomParams.byIndex[index].name}
/>
</div>

<div className="column">
<CustomParamValue
index={index}
isDisabled={isFirstParam(index) || isSubmitting}
name={formCustomParams.byIndex[index].name}
defaultValue={formCustomParams.byIndex[index].value}
/>
</div>

<div className="column is-narrow">
<CustomParamAction
index={index}
onAdd={onAdd}
onRemove={onRemove}
/>
</div>
</div>
))
}
</>
);
};

export default CustomParams;
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { connect } from 'react-redux';
import { RootState } from 'redux/interfaces';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import CustomParams from './CustomParams';

interface RouteProps {};

interface OwnProps extends RouteComponentProps<RouteProps> {
isSubmitting: boolean;
}

const mapStateToProps = (state: RootState, { isSubmitting }: OwnProps) => ({
isSubmitting,
})

export default withRouter(
connect(mapStateToProps)(CustomParams)
);
Loading

0 comments on commit ea9426e

Please sign in to comment.