Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WV-659] & [WV-660] VoterPositionEntryAndDisplay && VoterPositionEditNameAndPhotoModal #4228

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/img/global/icons/drop-down-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 13 additions & 13 deletions src/js/components/Activity/ActivityPostAdd.jsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import React, { Component, Suspense } from 'react';
import { Card, InputBase } from '@mui/material';
import styled from 'styled-components';
import withStyles from '@mui/styles/withStyles';
import withTheme from '@mui/styles/withTheme';
import PropTypes from 'prop-types';
import React, { Component, Suspense } from 'react';
import { isCordova } from '../../common/utils/isCordovaOrWebApp';
import { renderLog } from '../../common/utils/logging';
import VoterStore from '../../stores/VoterStore';
import { avatarGeneric } from '../../utils/applicationUtils';
import { cordovaNewsPaddingTop } from '../../utils/cordovaOffsets';

const ActivityPostModal = React.lazy(() => import(/* webpackChunkName: 'ActivityPostModal' */ './ActivityPostModal'));
const VoterPositionEntryAndDisplay = React.lazy(() => import(/* webpackChunkName: 'VoterPositionEntryAndDisplay' */ '../PositionItem/VoterPositionEntryAndDisplay'));

class ActivityPostAdd extends Component {
constructor (props) {
super(props);
this.state = {
showActivityPostModal: false,
showVoterPositionEntryAndDisplay: false,
statementText: '',
};
this.updateStatementTextToBeSaved = this.updateStatementTextToBeSaved.bind(this);
Expand Down Expand Up @@ -62,11 +62,11 @@ class ActivityPostAdd extends Component {
});
}

toggleActivityPostModal = () => {
const { showActivityPostModal } = this.state;
// console.log('toggleActivityPostModal showActivityPostModal:', showActivityPostModal);
toggleVoterPositionEntryAndDisplay = () => {
const { showVoterPositionEntryAndDisplay } = this.state;
// console.log('toggleVoterPositionEntryAndDisplay showVoterPositionEntryAndDisplay:', showVoterPositionEntryAndDisplay);
this.setState({
showActivityPostModal: !showActivityPostModal,
showVoterPositionEntryAndDisplay: !showVoterPositionEntryAndDisplay,
});
}

Expand All @@ -80,7 +80,7 @@ class ActivityPostAdd extends Component {
renderLog('ActivityPostAdd'); // Set LOG_RENDER_EVENTS to log all renders
const { classes, externalUniqueId, activityTidbitWeVoteId } = this.props;
const {
showActivityPostModal,
showVoterPositionEntryAndDisplay,
voterPhotoUrlMedium, statementText,
} = this.state;

Expand Down Expand Up @@ -137,19 +137,19 @@ class ActivityPostAdd extends Component {
inputRef={(tag) => { this.textarea = tag; }}
multiline
name="statementText"
onClick={this.toggleActivityPostModal}
onClick={this.toggleVoterPositionEntryAndDisplay}
onFocus={this.handleFocus}
placeholder={statementPlaceholderText}
rows="1"
/>
</InnerFlexWrapper>
{showActivityPostModal && (
{showVoterPositionEntryAndDisplay && (
<Suspense fallback={<></>}>
<ActivityPostModal
<VoterPositionEntryAndDisplay
activityTidbitWeVoteId={activityTidbitWeVoteId}
externalUniqueId={externalUniqueId}
show={showActivityPostModal}
toggleModal={this.toggleActivityPostModal}
show={showVoterPositionEntryAndDisplay}
toggleModal={this.toggleVoterPositionEntryAndDisplay}
/>
</Suspense>
)}
Expand Down
95 changes: 95 additions & 0 deletions src/js/components/Activity/ActivityPostPublicDropdown.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Select, MenuItem, FormControl, Typography } from '@mui/material';
import { withStyles } from '@mui/styles';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import DesignTokenColors from '../../common/components/Style/DesignTokenColors';

const ActivityPostPublicDropdown = (props) => {
const { visibilityIsPublic, onVisibilityChange, classes } = props;

const handleVisibilityChange = (event) => {
const { value } = event.target;
onVisibilityChange(value === 'Public');
};

return (
<FormControl className={classes.formControl} aria-labelledby="opinion-visibility-label">
<div className={classes.container}>
<Typography
id="opinion-visibility-label"
className={classes.label}
component="label"
>
Opinion visible to:
</Typography>
<Select
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this element is missing an accessible name or label. That makes it hard for people using screen readers or voice control to use the control.

value={visibilityIsPublic ? 'Public' : 'Friends Only'}
onChange={handleVisibilityChange}
className={classes.selectVisibility}
disableUnderline
IconComponent={ArrowDropDownIcon}
aria-label="Select visibility for your opinion"
>
<MenuItem value="Public" className={classes.menuItem}>
Public
</MenuItem>
<MenuItem value="Friends Only" className={classes.menuItem}>
My friends
</MenuItem>
</Select>
</div>
</FormControl>
);
};

ActivityPostPublicDropdown.propTypes = {
visibilityIsPublic: PropTypes.bool.isRequired,
onVisibilityChange: PropTypes.func.isRequired,
classes: PropTypes.object.isRequired,
};

const styles = () => ({
formControl: {
width: '100%',
},
container: {
display: 'flex',
alignItems: 'center',
},
label: {
fontFamily: 'Poppins',
fontSize: '13px',
fontWeight: '400',
lineHeight: '19.5px',
color: DesignTokenColors.neutralUI900,
marginRight: '8px',
},
selectVisibility: {
fontFamily: 'Nunito',
fontSize: '16px',
fontWeight: '400',
lineHeight: '21.82px',
color: DesignTokenColors.neutralUI900,
padding: '0 8px',
border: 'none',
boxShadow: 'none',
outline: 'none',
'&:focus': {
outline: 'none',
boxShadow: 'none',
},
'& .MuiOutlinedInput-notchedOutline': {
border: 'none',
},
},
menuItem: {
fontFamily: 'Nunito',
fontSize: '16px',
fontWeight: '400',
lineHeight: '21.82px',
color: DesignTokenColors.neutralUI900,
},
});

export default withStyles(styles)(ActivityPostPublicDropdown);
170 changes: 170 additions & 0 deletions src/js/components/PositionItem/VoterPositionEditNameAndPhotoModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import React, { useState } from 'react';
import { Dialog, DialogTitle, DialogContent, Button, TextField, IconButton } from '@mui/material';
import { withStyles } from '@mui/styles';
import PropTypes from 'prop-types';
import { Close as CloseIcon } from '@mui/icons-material';
import DesignTokenColors from '../../common/components/Style/DesignTokenColors';

const styles = {
modalContent: {
padding: '20px',
},
uploadSection: {
alignItems: 'center',
border: `3px dashed ${DesignTokenColors.neutral100}`,
borderRadius: '8px',
display: 'flex',
flexDirection: 'column',
marginBottom: '20px',
padding: '20px',
},
profilePhoto: {
alignItems: 'center',
backgroundColor: DesignTokenColors.neutral50,
borderRadius: '50%',
display: 'flex',
height: '80px',
justifyContent: 'center',
marginBottom: '10px',
width: '80px',
},
formField: {
color: DesignTokenColors.neutral100,
marginBottom: '15px',
},
a: {
color: DesignTokenColors.primary500,
},
closeButton: {
color: DesignTokenColors.neutral100,
position: 'absolute',
right: '8px',
top: '8px',
},
};

const VoterPositionEditNameAndPhotoModal = ({ show, toggleModal, classes }) => {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [website, setWebsite] = useState('');
const [description, setDescription] = useState('');
const [photo, setPhoto] = useState(null);

const handlePhotoUpload = (event) => {
const file = event.target.files[0];
if (file && file.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = (e) => setPhoto(e.target.result);
reader.readAsDataURL(file);
} else {
alert('Please upload a valid image file.');
}
};

const handleSave = () => {
if (!firstName.trim() || !lastName.trim()) {
alert('First and Last name are required.');
return;
}
if (website && !website.startsWith('http')) {
alert('Please enter a valid URL starting with http or https.');
return;
}
// Add logic to save details
toggleModal();
};

return (
<Dialog open={show} onClose={toggleModal} maxWidth="sm" fullWidth>
<DialogTitle>
Name & Photo Settings
<IconButton
aria-label="close"
className={classes.closeButton}
onClick={toggleModal}
>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent className={classes.modalContent}>
<p>
We are serious about protecting your information. We are non-profit and never sell information.
{' '}
<a href="/frequently-asked-questions" target="_blank" rel="noopener noreferrer" className={classes.a}>
Frequently Asked Questions.
</a>
</p>
<div className={classes.uploadSection}>
<div className={classes.profilePhoto}>
{photo ? <img src={photo} alt="Profile" style={{ width: '100%', borderRadius: '50%' }} /> : 'Upload'}
</div>
<input
type="file"
accept="image/*"
onChange={handlePhotoUpload}
style={{ display: 'none' }}
id="upload-photo"
/>
<label htmlFor="upload-photo">
<Button variant="outlined" component="span">
Upload Profile Photo
</Button>
</label>
</div>
<TextField
label="First Name"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
fullWidth
variant="outlined"
className={classes.formField}
/>
<TextField
label="Last Name"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
fullWidth
variant="outlined"
className={classes.formField}
/>
<TextField
label="Name Shown with Endorsements"
value={`${firstName} ${lastName}`}
fullWidth
variant="outlined"
className={classes.formField}
disabled
/>
<TextField
label="Your Website"
value={website}
onChange={(e) => setWebsite(e.target.value)}
fullWidth
variant="outlined"
className={classes.formField}
/>
<TextField
label="Description Shown with Endorsements"
value={description}
onChange={(e) => setDescription(e.target.value)}
fullWidth
variant="outlined"
className={classes.formField}
multiline
rows={3}
/>
<Button variant="contained" color="primary" onClick={handleSave} fullWidth>
Save
</Button>
</DialogContent>
</Dialog>
);
};

VoterPositionEditNameAndPhotoModal.propTypes = {
show: PropTypes.bool.isRequired,
toggleModal: PropTypes.func.isRequired,
classes: PropTypes.object.isRequired,
};

export default withStyles(styles)(VoterPositionEditNameAndPhotoModal);
Loading
Loading