Skip to content

Commit d0bb090

Browse files
jseminckCompuIves
authored andcommitted
Introduce delete file modal (codesandbox#147)
* Introduce delete file modal for confirming the file deletion * Update cancel button styles * Make title and body props * Rename component to Alert and move it to components directory * Use Alert also for sandbox deletion * Fix button tests * Remove async/await * Prettify using new settings
1 parent 23d5338 commit d0bb090

File tree

6 files changed

+211
-27
lines changed

6 files changed

+211
-27
lines changed

src/app/components/Alert.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
import Button from 'app/components/buttons/Button';
4+
5+
const Container = styled.div`
6+
display: flex;
7+
flex-direction: column;
8+
background-color: ${props => props.theme.background};
9+
color: rgba(255, 255, 255, 0.8);
10+
padding: 0.75rem;
11+
`;
12+
13+
const Title = styled.div`
14+
display: flex;
15+
justify-content: center;
16+
font-weight: 500;
17+
font-size: 1.125rem;
18+
color: rgba(255, 255, 255, 0.9);
19+
margin-top: 0 !important;
20+
margin-bottom: 1rem;
21+
text-transform: uppercase;
22+
`;
23+
24+
const Text = styled.div`
25+
font-size: 14px;
26+
font-weight: 0;
27+
`;
28+
29+
const Buttons = styled.div`
30+
display: flex;
31+
justify-content: center;
32+
margin-top: 1rem;
33+
button {
34+
display: flex;
35+
justify-content: center;
36+
width: 6rem;
37+
margin: 0.5rem;
38+
}
39+
`;
40+
41+
type Props = {
42+
title: string,
43+
body: string | Object,
44+
onCancel: Function,
45+
onDelete: Function,
46+
};
47+
48+
const Alert = ({ title, body, onCancel, onDelete }: Props) => (
49+
<Container>
50+
<Title>{title}</Title>
51+
52+
<Text>{body}</Text>
53+
54+
<Buttons>
55+
<Button small block secondary onClick={onCancel}>
56+
Cancel
57+
</Button>
58+
<Button small block primary onClick={onDelete}>
59+
Delete
60+
</Button>
61+
</Buttons>
62+
</Container>
63+
);
64+
65+
export default Alert;

src/app/components/buttons/Button.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,25 @@ import styled, { css, keyframes } from 'styled-components';
44

55
import theme from 'common/theme';
66

7-
const getBackgroundColor = ({ disabled, red }) => {
7+
const getBackgroundColor = ({ disabled, red, secondary }) => {
88
if (disabled) return `background: ${theme.background2.darken(0.1)()}`;
9+
if (secondary) return `background: #3A4B5D`;
910
if (red)
1011
return `background-image: linear-gradient(270deg, #F27777, #400000);`;
1112
return `background-image: linear-gradient(270deg, #fed29d, #A58B66, #7abae8, #56a0d6);`;
1213
};
1314

14-
const getColor = ({ disabled }) => {
15+
const getColor = ({ disabled, secondary }) => {
1516
if (disabled) return theme.background2.lighten(1.5)();
17+
if (secondary) return `#56a0d6`;
1618
return 'white';
1719
};
1820

21+
const getBorder = ({ secondary }) => {
22+
if (secondary) return `1px solid #56a0d6`;
23+
return 'none';
24+
};
25+
1926
const forward = keyframes`
2027
0%{background-position:0% 50%}
2128
100%{background-position:100% 50%}
@@ -37,6 +44,7 @@ const styles = css`
3744
${props => getBackgroundColor(props)};
3845
background-size: 720%;
3946
47+
border: ${props => getBorder(props)};
4048
border-radius: 4px;
4149
4250
box-sizing: border-box;

src/app/components/buttons/Button.test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@ describe('Button', () => {
3131
it('renders disabled', () => {
3232
testRender(<Button disabled>Test</Button>);
3333
});
34+
35+
it('renders secondary', () => {
36+
testRender(<Button secondary>Test</Button>);
37+
});
3438
});

src/app/components/buttons/__snapshots__/Button.test.js.snap

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ exports[`Button renders 1`] = `
1414
outline: none;
1515
background-image: linear-gradient(270deg,#fed29d,#A58B66,#7abae8,#56a0d6);
1616
background-size: 720%;
17+
border: none;
1718
border-radius: 4px;
1819
box-sizing: border-box;
1920
font-size: 1.125rem;
@@ -76,6 +77,7 @@ exports[`Button renders disabled 1`] = `
7677
outline: none;
7778
background: rgb(25,29,31);
7879
background-size: 720%;
80+
border: none;
7981
border-radius: 4px;
8082
box-sizing: border-box;
8183
font-size: 1.125rem;
@@ -113,6 +115,7 @@ exports[`Button renders href 1`] = `
113115
outline: none;
114116
background-image: linear-gradient(270deg,#fed29d,#A58B66,#7abae8,#56a0d6);
115117
background-size: 720%;
118+
border: none;
116119
border-radius: 4px;
117120
box-sizing: border-box;
118121
font-size: 1.125rem;
@@ -176,6 +179,7 @@ exports[`Button renders onClick 1`] = `
176179
outline: none;
177180
background-image: linear-gradient(270deg,#fed29d,#A58B66,#7abae8,#56a0d6);
178181
background-size: 720%;
182+
border: none;
179183
border-radius: 4px;
180184
box-sizing: border-box;
181185
font-size: 1.125rem;
@@ -239,6 +243,7 @@ exports[`Button renders properties 1`] = `
239243
outline: none;
240244
background-image: linear-gradient(270deg,#fed29d,#A58B66,#7abae8,#56a0d6);
241245
background-size: 720%;
246+
border: none;
242247
border-radius: 4px;
243248
box-sizing: border-box;
244249
font-size: 1.125rem;
@@ -288,6 +293,69 @@ exports[`Button renders properties 1`] = `
288293
</button>
289294
`;
290295

296+
exports[`Button renders secondary 1`] = `
297+
.c0 {
298+
-webkit-transition: 0.3s ease all;
299+
transition: 0.3s ease all;
300+
-webkit-animation-name: cUPaoU;
301+
animation-name: cUPaoU;
302+
-webkit-animation-duration: 300ms;
303+
animation-duration: 300ms;
304+
-webkit-animation-timing-function: ease;
305+
animation-timing-function: ease;
306+
border: none;
307+
outline: none;
308+
background: #3A4B5D;
309+
background-size: 720%;
310+
border: 1px solid #56a0d6;
311+
border-radius: 4px;
312+
box-sizing: border-box;
313+
font-size: 1.125rem;
314+
text-align: center;
315+
color: #56a0d6;
316+
font-weight: 400;
317+
box-shadow: 0 3px 3px rgba(0,0,0,0.5);
318+
width: inherit;
319+
padding: 0.65rem 2.25rem;
320+
-webkit-user-select: none;
321+
-moz-user-select: none;
322+
-ms-user-select: none;
323+
user-select: none;
324+
text-decoration: none;
325+
cursor: pointer;
326+
}
327+
328+
.c0:hover {
329+
-webkit-animation-name: eEnBCQ;
330+
animation-name: eEnBCQ;
331+
-webkit-animation-duration: 300ms;
332+
animation-duration: 300ms;
333+
-webkit-animation-timing-function: ease;
334+
animation-timing-function: ease;
335+
-webkit-animation-direction: normal;
336+
animation-direction: normal;
337+
-webkit-animation-fill-mode: forwards;
338+
animation-fill-mode: forwards;
339+
box-shadow: 0 7px 10px rgba(0,0,0,0.5);
340+
-webkit-transform: translateY(-1px);
341+
-ms-transform: translateY(-1px);
342+
transform: translateY(-1px);
343+
}
344+
345+
.c0:active {
346+
-webkit-transform: translateY(1px);
347+
-ms-transform: translateY(1px);
348+
transform: translateY(1px);
349+
box-shadow: 0 0 0 rgba(0,0,0,0.5);
350+
}
351+
352+
<button
353+
className="c0"
354+
>
355+
Test
356+
</button>
357+
`;
358+
291359
exports[`Button renders to 1`] = `
292360
.c0 {
293361
-webkit-transition: 0.3s ease all;
@@ -302,6 +370,7 @@ exports[`Button renders to 1`] = `
302370
outline: none;
303371
background-image: linear-gradient(270deg,#fed29d,#A58B66,#7abae8,#56a0d6);
304372
background-size: 720%;
373+
border: none;
305374
border-radius: 4px;
306375
box-sizing: border-box;
307376
font-size: 1.125rem;

src/app/pages/Sandbox/Editor/Workspace/Files/DirectoryEntry/index.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import sandboxActionCreators from 'app/store/entities/sandboxes/actions';
1010
import { validateTitle } from 'app/store/entities//sandboxes/modules/validator';
1111
import contextMenuActionCreators from 'app/store/context-menu/actions';
1212
import { getModuleParents } from 'app/store/entities/sandboxes/modules/selectors';
13+
import modalActionCreators from 'app/store/modal/actions';
14+
import Alert from 'app/components/Alert';
1315

1416
import Entry from './Entry';
1517
import DirectoryChildren from './DirectoryChildren';
@@ -34,6 +36,7 @@ const Opener = styled.div`
3436
const mapDispatchToProps = dispatch => ({
3537
sandboxActions: bindActionCreators(sandboxActionCreators, dispatch),
3638
openMenu: bindActionCreators(contextMenuActionCreators, dispatch).openMenu,
39+
modalActions: bindActionCreators(modalActionCreators, dispatch),
3740
});
3841
type Props = {
3942
id: string,
@@ -49,6 +52,7 @@ type Props = {
4952
depth: ?number,
5053
openMenu: (e: Event) => void,
5154
sandboxActions: typeof sandboxActionCreators,
55+
modalActions: typeof modalActionCreators,
5256
currentModuleId: ?string,
5357
isInProjectView: boolean,
5458
};
@@ -118,17 +122,28 @@ class DirectoryEntry extends React.PureComponent {
118122
};
119123

120124
deleteModule = (id: string) => {
121-
const { sandboxId, modules, sandboxActions } = this.props;
125+
const { sandboxId, modules, sandboxActions, modalActions } = this.props;
122126
const module = modules.find(m => m.id === id);
123127

124-
const confirmed = confirm(
125-
`Are you sure you want to delete ${module.title}?`
126-
);
127-
128-
if (confirmed) {
129-
sandboxActions.deleteModule(sandboxId, id);
130-
}
131-
return true;
128+
modalActions.openModal({
129+
Body: (
130+
<Alert
131+
title="Delete File"
132+
body={
133+
<span>
134+
Are you sure you want to delete <b>{module.title}</b>?
135+
<br />
136+
The file will be permanently removed.
137+
</span>
138+
}
139+
onCancel={modalActions.closeModal}
140+
onDelete={() => {
141+
modalActions.closeModal();
142+
sandboxActions.deleteModule(sandboxId, id);
143+
}}
144+
/>
145+
),
146+
});
132147
};
133148

134149
onCreateDirectoryClick = () => {

src/app/pages/Sandbox/Editor/Workspace/SandboxActions/index.js

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
11
import React from 'react';
2+
import { connect } from 'react-redux';
3+
import { bindActionCreators } from 'redux';
24
import styled from 'styled-components';
35

46
import Button from 'app/components/buttons/Button';
57

8+
import modalActionCreators from 'app/store/modal/actions';
9+
import Alert from 'app/components/Alert';
10+
611
import WorkspaceInputContainer from '../WorkspaceInputContainer';
712

813
import WorkspaceSubtitle from '../WorkspaceSubtitle';
914

10-
type Props = {
11-
id: string,
12-
deleteSandbox: (id: string) => void,
13-
newSandboxUrl: () => void,
14-
setSandboxPrivacy: (id: string, privacy: number) => void,
15-
isPatron: boolean,
16-
privacy: 0 | 1 | 2,
17-
};
18-
1915
const PrivacySelect = styled.select`
2016
background-color: rgba(0, 0, 0, 0.3);
2117
color: rgba(255, 255, 255, 0.8);
@@ -28,18 +24,43 @@ const PrivacySelect = styled.select`
2824
box-sizing: border-box;
2925
`;
3026

31-
export default class SandboxSettings extends React.PureComponent {
27+
const mapDispatchToProps = dispatch => ({
28+
modalActions: bindActionCreators(modalActionCreators, dispatch),
29+
});
30+
31+
type Props = {
32+
id: string,
33+
deleteSandbox: (id: string) => void,
34+
newSandboxUrl: () => void,
35+
setSandboxPrivacy: (id: string, privacy: number) => void,
36+
isPatron: boolean,
37+
privacy: 0 | 1 | 2,
38+
modalActions: typeof modalActionCreators,
39+
};
40+
41+
class SandboxSettings extends React.PureComponent {
3242
props: Props;
3343
state = {
3444
loading: false,
3545
};
3646

37-
handleDeleteSandbox = async () => {
38-
const really = confirm('Are you sure you want to delete this sandbox?'); // TODO: confirm???
39-
if (really) {
40-
await this.props.deleteSandbox(this.props.id);
41-
await this.props.newSandboxUrl();
42-
}
47+
handleDeleteSandbox = () => {
48+
const { modalActions } = this.props;
49+
50+
modalActions.openModal({
51+
Body: (
52+
<Alert
53+
title="Delete File"
54+
body={<span>Are you sure you want to delete this sandbox?</span>}
55+
onCancel={modalActions.closeModal}
56+
onDelete={() => {
57+
this.props.deleteSandbox(this.props.id);
58+
this.props.newSandboxUrl();
59+
modalActions.closeModal();
60+
}}
61+
/>
62+
),
63+
});
4364
};
4465

4566
updateSandboxPrivacy = async e => {
@@ -95,3 +116,5 @@ export default class SandboxSettings extends React.PureComponent {
95116
);
96117
}
97118
}
119+
120+
export default connect(undefined, mapDispatchToProps)(SandboxSettings);

0 commit comments

Comments
 (0)