Skip to content

Commit 8e1b2db

Browse files
complete test
1 parent 4c782e1 commit 8e1b2db

File tree

8 files changed

+106
-39
lines changed

8 files changed

+106
-39
lines changed

src/components/ComponentForms/ImageForm.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export const ImageForm = ({
2424
id="src"
2525
name="src"
2626
type="text"
27-
value={alt}
28-
onChange={e => setAlt(e.target.value)}
27+
value={src}
28+
onChange={e => setSrc(e.target.value)}
2929
/>
3030
<label className="component-field__label" htmlFor="alt">
3131
alt
@@ -36,8 +36,8 @@ export const ImageForm = ({
3636
id="alt"
3737
name="alt"
3838
type="text"
39-
value={src}
40-
onChange={e => setSrc(e.target.value)}
39+
value={alt}
40+
onChange={e => setAlt(e.target.value)}
4141
/>
4242
</div>
4343
<Actions>

src/components/Components/A.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,26 @@
1-
export const A = () => {}
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
export const A = ({ values = {} }) => (
5+
<a
6+
href={values.href || '#'}
7+
target="_blank"
8+
rel="noopener noreferrer"
9+
>
10+
{values.label || ''}
11+
</a>
12+
);
13+
14+
A.propTypes = {
15+
values: PropTypes.shape({
16+
href: PropTypes.string,
17+
label: PropTypes.string,
18+
}),
19+
};
20+
21+
A.defaultProps = {
22+
values: {
23+
href: '#',
24+
label: '',
25+
},
26+
};

src/components/Components/Img.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,16 @@
1-
export const Img = () => {}
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
export const Img = ({ values }) => (
5+
<img
6+
src={values.src}
7+
alt={values.alt}
8+
/>
9+
);
10+
11+
Img.propTypes = {
12+
values: PropTypes.shape({
13+
src: PropTypes.string.isRequired,
14+
alt: PropTypes.string.isRequired,
15+
}).isRequired,
16+
};

src/components/ComponentsPicker/components-picker.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
2-
import { useDispatch } from 'react-redux';
2+
import { useSelector, useDispatch } from 'react-redux';
3+
import PropTypes from 'prop-types';
34

45
import { uuid } from '../../utils/uuid';
56
import { componentsActions } from '../../store/components';
@@ -9,15 +10,20 @@ import './components-picker.css'
910

1011
export const ComponentsPicker = () => {
1112
const dispatch = useDispatch();
13+
const currentlyEdited = useSelector(state => state.components?.currentlyEdited);
14+
const lockedPicker = Boolean(currentlyEdited);
15+
1216
const onComponentClick = layout => dispatch(componentsActions.addComponent({id: uuid(), layout}));
1317

1418
return (
1519
<div className="components-picker">
1620
{AvailableComponents.map(component => (
1721
<div
18-
className="components-picker__component"
22+
className={`components-picker__component ${
23+
lockedPicker ? 'components-picker__component--disabled' : ''
24+
}`}
1925
key={component.layout}
20-
onClick={() => onComponentClick(component.layout)}
26+
onClick={!lockedPicker ? () => onComponentClick(component.layout) : undefined}
2127
>
2228
<span className="components-picker__component-label">
2329
{component.label}
@@ -27,3 +33,7 @@ export const ComponentsPicker = () => {
2733
</div>
2834
);
2935
}
36+
37+
ComponentsPicker.propTypes = {
38+
lockedPicker: PropTypes.bool.isRequired,
39+
};

src/components/ComponentsWrapper/components-wrapper.js

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,37 +9,36 @@ export const ComponentsWrapper = ({
99
isEdited,
1010
layout,
1111
onComponentEdit,
12+
values = {},
1213
...props
1314
}) => {
1415
if (!layout) {
1516
return null;
1617
}
1718

18-
const Component = AvailableComponents.find(comp => comp.layout === layout).component
19+
const Component = AvailableComponents.find(comp => comp.layout === layout)?.component;
1920

2021
const wrapperClass = classNames(
2122
'components-wrapper',
2223
{ 'components-wrapper--active': isEdited },
2324
);
2425

25-
return Component
26-
? (
27-
<div
28-
className={wrapperClass}
29-
>
30-
<Component layout={layout} {...props} />
31-
<span className="edit" onClick={onComponentEdit}>click to edit</span>
32-
</div>
33-
)
34-
: null;
26+
return Component ? (
27+
<div className={wrapperClass}>
28+
<Component layout={layout} values={values} {...props} />
29+
<span className="edit" onClick={onComponentEdit}>click to edit</span>
30+
</div>
31+
) : null;
3532
};
3633

3734
ComponentsWrapper.propTypes = {
3835
isEdited: PropTypes.bool.isRequired,
3936
layout: PropTypes.string,
4037
onComponentEdit: PropTypes.func.isRequired,
38+
values: PropTypes.object,
4139
};
4240

4341
ComponentsWrapper.defaultProps = {
4442
layout: null,
43+
values: {},
4544
};

src/components/Layout/layout.js

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,25 @@ export const Layout = () => {
1414
const onRemoveClick = id => dispatch(componentsActions.removeComponent({id}));
1515
const onSubmit = (id, values) => dispatch(componentsActions.updateComponent({id, data: { values }}));
1616

17-
return <>
18-
<Navbar />
19-
<div className="d-flex">
20-
<ComponentsPreview />
21-
<ComponentsPicker />
22-
</div>
23-
{currentlyEdited && (
24-
<EditedComponent
25-
layout={currentlyEdited.layout}
26-
onRemoveClick={() => onRemoveClick(currentlyEdited.id)}
27-
onSubmit={values => onSubmit(currentlyEdited.id, values)}
28-
values={currentlyEdited.values}
29-
/>
30-
)}
31-
</>
17+
return (
18+
<>
19+
<Navbar />
20+
<div className="d-flex">
21+
<ComponentsPreview />
22+
<ComponentsPicker lockedPicker={Boolean(currentlyEdited)} />
23+
</div>
24+
{currentlyEdited && (
25+
<EditedComponent
26+
layout={currentlyEdited.layout}
27+
onRemoveClick={() => onRemoveClick(currentlyEdited.id)}
28+
onSubmit={values => onSubmit(currentlyEdited.id, values)}
29+
values={currentlyEdited.values}
30+
/>
31+
)}
32+
</>
33+
);
3234
}
3335

3436
Layout.defaultProps = {
3537
currentlyEdited: null,
36-
};
38+
};

src/store/components.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,29 @@ export const componentsSlice = createSlice({
1010
initialState,
1111
reducers: {
1212
addComponent: (state, action) => {
13+
const { id, layout } = action.payload;
14+
const newComponent = { id, layout, values: {} };
15+
state.items.push(newComponent);
16+
state.currentlyEdited = newComponent;
1317
},
1418
updateComponent: (state, action) => {
19+
const { id, data } = action.payload;
20+
const index = state.items.findIndex(item => item.id === id);
21+
if (index !== -1) {
22+
state.items[index] = { ...state.items[index], ...data };
23+
}
24+
state.currentlyEdited = null;
1525
},
1626
removeComponent: (state, action) => {
27+
const { id } = action.payload;
28+
state.items = state.items.filter(item => item.id !== id);
29+
state.currentlyEdited = null;
1730
},
1831
setEditedComponent: (state, action) => {
32+
state.currentlyEdited = action.payload.component;
1933
},
2034
},
2135
})
2236

2337
export const componentsActions = componentsSlice.actions
24-
export const componentsReducer = componentsSlice.reducer
38+
export const componentsReducer = componentsSlice.reducer

src/store/index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@ import { configureStore } from '@reduxjs/toolkit'
22
import { componentsReducer } from './components';
33

44
export const getStore = () => configureStore({
5-
reducer: {}
6-
})
5+
reducer: {
6+
components: componentsReducer
7+
}
8+
})

0 commit comments

Comments
 (0)