mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
SCSS Pipeline and style fixes (#229)
* Add SCSS pipeline and fix login style issues * Fix nav styles and make tests pass * Fix nav header styles and animations * Change font-size to 13px on nav * Fix duplicate specificity of styles
This commit is contained in:
parent
1d5596941a
commit
55307de42d
23 changed files with 220 additions and 134 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -9,6 +9,7 @@ node_modules
|
|||
|
||||
# generated artifacts
|
||||
assets/bundle.js
|
||||
assets/bundle.css
|
||||
server/bindata.go
|
||||
*.cover
|
||||
*.test
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ export default {
|
|||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
paddingTop: '80px',
|
||||
marginTop: '13vh',
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { isEqual, last } from 'lodash';
|
|||
import componentStyles from './styles';
|
||||
import kolideLogo from '../../../assets/images/kolide-logo.svg';
|
||||
import navItems from './navItems';
|
||||
import './styles.scss';
|
||||
|
||||
class SidePanel extends Component {
|
||||
static propTypes = {
|
||||
|
|
@ -41,6 +42,10 @@ class SidePanel extends Component {
|
|||
};
|
||||
}
|
||||
|
||||
setSubNavClass = (showSubItems) => {
|
||||
return showSubItems ? 'sub-nav sub-nav--expanded' : 'sub-nav';
|
||||
}
|
||||
|
||||
toggleShowSubItems = (showSubItems) => {
|
||||
return (evt) => {
|
||||
evt.preventDefault();
|
||||
|
|
@ -64,6 +69,7 @@ class SidePanel extends Component {
|
|||
orgNameStyles,
|
||||
usernameStyles,
|
||||
userStatusStyles,
|
||||
orgChevronStyles,
|
||||
} = componentStyles;
|
||||
|
||||
return (
|
||||
|
|
@ -76,6 +82,7 @@ class SidePanel extends Component {
|
|||
<h1 style={orgNameStyles}>Kolide, Inc.</h1>
|
||||
<div style={userStatusStyles(enabled)} />
|
||||
<h2 style={usernameStyles}>{username}</h2>
|
||||
<i style={orgChevronStyles} className="kolidecon-chevron-bold-down" />
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
|
@ -97,6 +104,7 @@ class SidePanel extends Component {
|
|||
<div style={navItemWrapperStyles(lastChild)} key={`nav-item-${name}`}>
|
||||
{active && <div style={navItemBeforeStyles} />}
|
||||
<li
|
||||
key={name}
|
||||
onClick={setActiveTab(name)}
|
||||
style={navItemStyles(active)}
|
||||
>
|
||||
|
|
@ -146,6 +154,7 @@ class SidePanel extends Component {
|
|||
>
|
||||
{active && <div style={subItemBeforeStyles} />}
|
||||
<li
|
||||
key={name}
|
||||
onClick={setActiveSubItem(name)}
|
||||
style={subItemStyles(active)}
|
||||
>
|
||||
|
|
@ -157,13 +166,11 @@ class SidePanel extends Component {
|
|||
|
||||
renderSubItems = (subItems) => {
|
||||
const { subItemListStyles, subItemsStyles } = componentStyles;
|
||||
const { renderCollapseSubItems, renderSubItem } = this;
|
||||
const { renderCollapseSubItems, renderSubItem, setSubNavClass } = this;
|
||||
const { showSubItems } = this.state;
|
||||
|
||||
if (!subItems.length) return false;
|
||||
|
||||
return (
|
||||
<div style={subItemsStyles(showSubItems)}>
|
||||
<div className={setSubNavClass(showSubItems)} style={subItemsStyles}>
|
||||
<ul style={subItemListStyles(showSubItems)}>
|
||||
{subItems.map(subItem => {
|
||||
return renderSubItem(subItem);
|
||||
|
|
@ -181,8 +188,8 @@ class SidePanel extends Component {
|
|||
const iconName = showSubItems ? 'kolidecon-chevron-bold-left' : 'kolidecon-chevron-bold-right';
|
||||
|
||||
return (
|
||||
<div style={collapseSubItemsWrapper}>
|
||||
<i className={iconName} style={{ color: '#FFF' }} onClick={toggleShowSubItems(!showSubItems)} />
|
||||
<div style={collapseSubItemsWrapper} onClick={toggleShowSubItems(!showSubItems)}>
|
||||
<i className={iconName} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,21 +5,43 @@ const { border, color, font, padding } = Styles;
|
|||
const componentStyles = {
|
||||
companyLogoStyles: {
|
||||
position: 'absolute',
|
||||
left: '16px',
|
||||
height: '44px',
|
||||
left: '0',
|
||||
top: '23px',
|
||||
height: '42px',
|
||||
marginRight: '10px',
|
||||
borderColor: color.accentMedium,
|
||||
borderStyle: 'solid',
|
||||
borderWidth: '1px',
|
||||
borderRadius: '100%',
|
||||
'@media (max-width: 760px)': {
|
||||
left: '4px',
|
||||
left: '5px',
|
||||
},
|
||||
},
|
||||
headerStyles: {
|
||||
borderBottomColor: color.accentLight,
|
||||
borderBottomStyle: 'solid',
|
||||
borderBottomWidth: '1px',
|
||||
height: '67px',
|
||||
marginBottom: padding.half,
|
||||
marginRight: padding.medium,
|
||||
height: '62px',
|
||||
cursor: 'pointer',
|
||||
paddingLeft: '54px',
|
||||
paddingTop: '26px',
|
||||
marginRight: '16px',
|
||||
position: 'relative',
|
||||
},
|
||||
orgChevronStyles: {
|
||||
color: color.accentMedium,
|
||||
fontSize: '12px',
|
||||
position: 'absolute',
|
||||
top: '50px',
|
||||
right: '35px',
|
||||
'@media (max-width: 760px)': {
|
||||
top: 'auto',
|
||||
left: '0',
|
||||
right: '0',
|
||||
bottom: '6px',
|
||||
textAlign: 'center',
|
||||
display: 'block',
|
||||
},
|
||||
},
|
||||
iconStyles: {
|
||||
position: 'relative',
|
||||
|
|
@ -28,7 +50,9 @@ const componentStyles = {
|
|||
top: '4px',
|
||||
left: 0,
|
||||
'@media (max-width: 760px)': {
|
||||
left: '5px',
|
||||
display: 'block',
|
||||
textAlign: 'center',
|
||||
marginRight: 0,
|
||||
},
|
||||
},
|
||||
navItemBeforeStyles: {
|
||||
|
|
@ -36,8 +60,8 @@ const componentStyles = {
|
|||
width: '6px',
|
||||
height: '50px',
|
||||
position: 'absolute',
|
||||
left: '-24px',
|
||||
top: '2px',
|
||||
left: '-16px',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: '#9a61c6',
|
||||
'@media (max-width: 760px)': {
|
||||
|
|
@ -60,10 +84,11 @@ const componentStyles = {
|
|||
const activeStyles = {
|
||||
color: color.brand,
|
||||
borderBottom: 'none',
|
||||
transition: 'none',
|
||||
':hover': {
|
||||
color: color.brandDark,
|
||||
},
|
||||
'@media (max-width: 760px)': {
|
||||
borderBottom: '8px solid #9a61c6',
|
||||
textAlign: 'center',
|
||||
borderBottom: '6px solid #9a61c6',
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -72,12 +97,13 @@ const componentStyles = {
|
|||
position: 'relative',
|
||||
color: color.textLight,
|
||||
cursor: 'pointer',
|
||||
fontSize: font.small,
|
||||
fontSize: '13px',
|
||||
letterSpacing: '0.5px',
|
||||
textTransform: 'uppercase',
|
||||
paddingTop: padding.half,
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
'@media (max-width: 760px)': {
|
||||
textAlign: 'center',
|
||||
transition: 'color 0.2s ease-in-out',
|
||||
':hover': {
|
||||
color: color.textDark,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -122,25 +148,24 @@ const componentStyles = {
|
|||
bottom: 0,
|
||||
boxShadow: '2px 0 8px 0 rgba(0, 0, 0, 0.1)',
|
||||
left: 0,
|
||||
paddingLeft: padding.large,
|
||||
paddingTop: padding.large,
|
||||
paddingLeft: '16px',
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
width: '216px',
|
||||
width: '223px',
|
||||
'@media (max-width: 760px)': {
|
||||
paddingLeft: 0,
|
||||
width: '54px',
|
||||
},
|
||||
},
|
||||
orgNameStyles: {
|
||||
fontSize: font.medium,
|
||||
fontSize: '16px',
|
||||
letterSpacing: '0.5px',
|
||||
margin: 0,
|
||||
overFlow: 'hidden',
|
||||
padding: 0,
|
||||
position: 'relative',
|
||||
textOverflow: 'ellipsis',
|
||||
top: '3px',
|
||||
top: '1px',
|
||||
whiteSpace: 'nowrap',
|
||||
'@media (max-width: 760px)': {
|
||||
display: 'none',
|
||||
|
|
@ -167,8 +192,12 @@ const componentStyles = {
|
|||
},
|
||||
subItemStyles: (active) => {
|
||||
const activeStyles = {
|
||||
fontSize: '13px',
|
||||
fontWeight: font.weight.bold,
|
||||
opacity: '1',
|
||||
':hover': {
|
||||
opacity: '1.0',
|
||||
},
|
||||
};
|
||||
|
||||
const baseStyles = {
|
||||
|
|
@ -184,6 +213,9 @@ const componentStyles = {
|
|||
position: 'relative',
|
||||
textTransform: 'none',
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
':hover': {
|
||||
opacity: '0.75',
|
||||
},
|
||||
};
|
||||
|
||||
if (active) {
|
||||
|
|
@ -195,54 +227,62 @@ const componentStyles = {
|
|||
|
||||
return baseStyles;
|
||||
},
|
||||
subItemsStyles: (expanded) => {
|
||||
return {
|
||||
backgroundColor: color.brand,
|
||||
boxShadow: 'inset 0 5px 8px 0 rgba(0, 0, 0, 0.12), inset 0 -5px 8px 0 rgba(0, 0, 0, 0.12)',
|
||||
marginBottom: 0,
|
||||
marginRight: 0,
|
||||
minHeight: '87px',
|
||||
paddingBottom: padding.half,
|
||||
paddingTop: padding.half,
|
||||
marginLeft: '-24px',
|
||||
marginTop: padding.medium,
|
||||
transition: 'width 0.1s ease-in-out',
|
||||
'@media (max-width: 760px)': {
|
||||
bottom: '-8px',
|
||||
left: '54px',
|
||||
marginLeft: 0,
|
||||
position: 'absolute',
|
||||
width: expanded ? '251px' : '18px',
|
||||
},
|
||||
};
|
||||
subItemsStyles: {
|
||||
backgroundColor: color.brand,
|
||||
boxShadow: 'inset 0 5px 8px 0 rgba(0, 0, 0, 0.12), inset 0 -5px 8px 0 rgba(0, 0, 0, 0.12)',
|
||||
marginRight: 0,
|
||||
marginBottom: '6px',
|
||||
paddingBottom: '3px',
|
||||
paddingTop: '3px',
|
||||
marginLeft: '-16px',
|
||||
position: 'relative',
|
||||
top: '10px',
|
||||
transition: 'width 0.1s ease-in-out',
|
||||
'@media (max-width: 760px)': {
|
||||
minHeight: '84px',
|
||||
borderTopRightRadius: '3px',
|
||||
borderBottomRightRadius: '3px',
|
||||
boxShadow: '2px 2px 8px rgba(0,0,0,0.1)',
|
||||
bottom: '-8px',
|
||||
left: '54px',
|
||||
marginLeft: 0,
|
||||
position: 'absolute',
|
||||
},
|
||||
},
|
||||
subItemListStyles: (expanded) => {
|
||||
return {
|
||||
listStyle: 'none',
|
||||
paddingLeft: '16px',
|
||||
'@media (max-width: 760px)': {
|
||||
borderRight: '1px solid rgba(0,0,0,0.16)',
|
||||
display: expanded ? 'inline-block' : 'none',
|
||||
padding: 0,
|
||||
textAlign: 'left',
|
||||
width: '211px',
|
||||
width: '166px',
|
||||
},
|
||||
};
|
||||
},
|
||||
collapseSubItemsWrapper: {
|
||||
position: 'absolute',
|
||||
right: '3px',
|
||||
top: '41%',
|
||||
right: '4px',
|
||||
top: '0',
|
||||
bottom: '0',
|
||||
lineHeight: '95px',
|
||||
color: '#fff',
|
||||
|
||||
'@media (min-width: 761px)': {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
usernameStyles: {
|
||||
position: 'relative',
|
||||
top: '3px',
|
||||
display: 'inline-block',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
fontSize: font.small,
|
||||
top: '-3px',
|
||||
left: '4px',
|
||||
fontSize: '13px',
|
||||
letterSpacing: '0.6px',
|
||||
textTransform: 'uppercase',
|
||||
'@media (max-width: 760px)': {
|
||||
display: 'none',
|
||||
|
|
@ -257,10 +297,8 @@ const componentStyles = {
|
|||
borderRadius: border.radius.circle,
|
||||
display: 'inline-block',
|
||||
height: size,
|
||||
left: '1px',
|
||||
marginRight: '6px',
|
||||
position: 'relative',
|
||||
top: '6px',
|
||||
width: size,
|
||||
'@media (max-width: 760px)': {
|
||||
display: 'none',
|
||||
|
|
|
|||
8
frontend/components/SidePanel/styles.scss
Normal file
8
frontend/components/SidePanel/styles.scss
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
@media (max-width: 760px) {
|
||||
.sub-nav {
|
||||
width: 22px;
|
||||
}
|
||||
.sub-nav--expanded {
|
||||
width: 188px;
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ class StackedWhiteBoxes extends Component {
|
|||
|
||||
return (
|
||||
<div style={exWrapperStyles}>
|
||||
<Link style={exStyles} to={previousLocation}>x</Link>
|
||||
<Link style={exStyles} to={previousLocation}>╳</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,16 +4,15 @@ const { border, color, font, padding } = styles;
|
|||
|
||||
export default {
|
||||
boxStyles: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: color.white,
|
||||
borderTopLeftRadius: border.radius.base,
|
||||
borderTopRightRadius: border.radius.base,
|
||||
borderRadius: border.radius.base,
|
||||
boxShadow: border.shadow.blur,
|
||||
minHeight: '370px',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: padding.base,
|
||||
width: '522px',
|
||||
padding: '30px',
|
||||
width: '524px',
|
||||
position: 'relative',
|
||||
fontWeight: 300,
|
||||
},
|
||||
containerStyles: {
|
||||
alignItems: 'center',
|
||||
|
|
@ -24,6 +23,10 @@ export default {
|
|||
exStyles: {
|
||||
color: color.lightGrey,
|
||||
textDecoration: 'none',
|
||||
position: 'absolute',
|
||||
top: '30px',
|
||||
right: '30px',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
exWrapperStyles: {
|
||||
textAlign: 'right',
|
||||
|
|
@ -53,6 +56,8 @@ export default {
|
|||
textStyles: {
|
||||
color: color.purpleGrey,
|
||||
fontSize: font.medium,
|
||||
lineHeight: '30px',
|
||||
letterSpacing: '0.64px',
|
||||
},
|
||||
smallTabStyles: {
|
||||
backgroundColor: color.white,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ export default {
|
|||
fontSize: font.large,
|
||||
fontWeight: '300',
|
||||
letterSpacing: '4px',
|
||||
marginTop: padding.base,
|
||||
padding: padding.medium,
|
||||
position: 'relative',
|
||||
textTransform: 'uppercase',
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ class ForgotPasswordForm extends Component {
|
|||
render () {
|
||||
const { error: serverError } = this.props;
|
||||
const { errors: clientErrors } = this.state;
|
||||
const { formStyles, inputStyles, submitButtonStyles } = componentStyles;
|
||||
const { formStyles, submitButtonContainerStyles, submitButtonStyles } = componentStyles;
|
||||
const { onFormSubmit, onInputFieldChange } = this;
|
||||
|
||||
return (
|
||||
|
|
@ -94,17 +94,18 @@ class ForgotPasswordForm extends Component {
|
|||
<InputFieldWithIcon
|
||||
autofocus
|
||||
error={clientErrors.email || serverError}
|
||||
iconName="envelope"
|
||||
iconName="kolidecon-email"
|
||||
name="email"
|
||||
onChange={onInputFieldChange}
|
||||
placeholder="Email Address"
|
||||
style={inputStyles}
|
||||
/>
|
||||
<GradientButton
|
||||
type="submit"
|
||||
style={submitButtonStyles}
|
||||
text="Reset Password"
|
||||
/>
|
||||
<div style={submitButtonContainerStyles}>
|
||||
<GradientButton
|
||||
type="submit"
|
||||
text="Reset Password"
|
||||
style={submitButtonStyles}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,15 @@ export default {
|
|||
formStyles: {
|
||||
width: '100%',
|
||||
},
|
||||
inputStyles: {
|
||||
width: '100%',
|
||||
submitButtonStyles: {
|
||||
':active': {
|
||||
boxShadow: '0 1px 0 #734893',
|
||||
},
|
||||
},
|
||||
submitButtonContainerStyles: {
|
||||
position: 'absolute',
|
||||
bottom: '30px',
|
||||
left: '30px',
|
||||
right: '30px',
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -140,14 +140,14 @@ class LoginForm extends Component {
|
|||
<InputFieldWithIcon
|
||||
autofocus
|
||||
error={errors.username}
|
||||
iconName="user"
|
||||
iconName="kolidecon-username"
|
||||
name="username"
|
||||
onChange={onInputChange('username')}
|
||||
placeholder="Username or Email"
|
||||
/>
|
||||
<InputFieldWithIcon
|
||||
error={errors.password}
|
||||
iconName="lock"
|
||||
iconName="kolidecon-password"
|
||||
name="password"
|
||||
onChange={onInputChange('password')}
|
||||
placeholder="Password"
|
||||
|
|
|
|||
|
|
@ -12,19 +12,21 @@ export default {
|
|||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: padding.base,
|
||||
padding: '30px',
|
||||
width: FORM_WIDTH,
|
||||
minHeight: '350px',
|
||||
fontWeight: '300',
|
||||
},
|
||||
forgotPasswordStyles: {
|
||||
fontSize: font.medium,
|
||||
letterSpacing: '1px',
|
||||
textDecoration: 'none',
|
||||
color: color.accentText,
|
||||
},
|
||||
forgotPasswordWrapperStyles: {
|
||||
marginTop: padding.base,
|
||||
textAlign: 'right',
|
||||
width: '378px',
|
||||
width: '100%',
|
||||
},
|
||||
formStyles: {
|
||||
boxShadow: '0 5px 30px 0 rgba(0,0,0,0.30)',
|
||||
|
|
@ -32,7 +34,6 @@ export default {
|
|||
submitButtonStyles: {
|
||||
borderTopLeftRadius: 0,
|
||||
borderTopRightRadius: 0,
|
||||
marginTop: 0,
|
||||
padding: padding.base,
|
||||
width: FORM_WIDTH,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import radium from 'radium';
|
||||
import Icon from '../../../icons/Icon';
|
||||
import componentStyles from './styles';
|
||||
|
||||
|
||||
class InputFieldWithIcon extends Component {
|
||||
static propTypes = {
|
||||
autofocus: PropTypes.bool,
|
||||
|
|
@ -45,17 +45,6 @@ class InputFieldWithIcon extends Component {
|
|||
return onChange(evt);
|
||||
}
|
||||
|
||||
iconVariant = () => {
|
||||
const { error } = this.props;
|
||||
const { value } = this.state;
|
||||
|
||||
if (error) return 'error';
|
||||
|
||||
if (value) return 'colored';
|
||||
|
||||
return 'default';
|
||||
}
|
||||
|
||||
renderHeading = () => {
|
||||
const { error, placeholder } = this.props;
|
||||
const { value } = this.state;
|
||||
|
|
@ -70,9 +59,9 @@ class InputFieldWithIcon extends Component {
|
|||
|
||||
render () {
|
||||
const { error, iconName, name, placeholder, style, type } = this.props;
|
||||
const { containerStyles, iconStyles, inputErrorStyles, inputStyles } = componentStyles;
|
||||
const { containerStyles, iconStyles, iconErrorStyles, inputErrorStyles, inputStyles } = componentStyles;
|
||||
const { value } = this.state;
|
||||
const { iconVariant, onInputChange } = this;
|
||||
const { onInputChange } = this;
|
||||
|
||||
return (
|
||||
<div style={containerStyles}>
|
||||
|
|
@ -80,12 +69,13 @@ class InputFieldWithIcon extends Component {
|
|||
<input
|
||||
name={name}
|
||||
onChange={onInputChange}
|
||||
className="input-with-icon"
|
||||
placeholder={placeholder}
|
||||
ref={(r) => { this.input = r; }}
|
||||
style={[inputStyles(value), inputErrorStyles(error), style]}
|
||||
style={[inputStyles(value, type), inputErrorStyles(error), style]}
|
||||
type={type}
|
||||
/>
|
||||
<Icon name={iconName} style={iconStyles} variant={iconVariant()} />
|
||||
<i className={iconName} style={[iconStyles(value), iconErrorStyles(error), style]} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,16 +6,38 @@ export default {
|
|||
containerStyles: {
|
||||
marginTop: padding.base,
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
},
|
||||
errorStyles: {
|
||||
color: color.alert,
|
||||
fontSize: font.small,
|
||||
textTransform: 'lowercase',
|
||||
},
|
||||
iconStyles: {
|
||||
position: 'absolute',
|
||||
right: '6px',
|
||||
top: '29px',
|
||||
iconStyles: (value) => {
|
||||
const baseStyles = {
|
||||
position: 'absolute',
|
||||
right: '6px',
|
||||
top: '28px',
|
||||
fontSize: '20px',
|
||||
color: color.accentText,
|
||||
};
|
||||
if (value) {
|
||||
return {
|
||||
...baseStyles,
|
||||
color: color.brand,
|
||||
};
|
||||
}
|
||||
|
||||
return baseStyles;
|
||||
},
|
||||
|
||||
iconErrorStyles: (error) => {
|
||||
if (error) {
|
||||
return {
|
||||
color: color.alert,
|
||||
};
|
||||
}
|
||||
return false;
|
||||
},
|
||||
inputErrorStyles: (error) => {
|
||||
if (error) {
|
||||
|
|
@ -26,21 +48,35 @@ export default {
|
|||
|
||||
return {};
|
||||
},
|
||||
inputStyles: (value) => {
|
||||
inputStyles: (value, type) => {
|
||||
const baseStyles = {
|
||||
borderLeft: 'none',
|
||||
borderRight: 'none',
|
||||
borderTop: 'none',
|
||||
borderBottomWidth: '1px',
|
||||
borderBottomWidth: '2px',
|
||||
fontSize: '20px',
|
||||
borderBottomStyle: 'solid',
|
||||
borderBottomColor: color.brand,
|
||||
borderBottomColor: color.brandUltralight,
|
||||
color: color.accentText,
|
||||
width: '378px',
|
||||
paddingRight: '30px',
|
||||
opacity: '1',
|
||||
textIndent: '1px',
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
boxSizing: 'border-box',
|
||||
':focus': {
|
||||
outline: 'none',
|
||||
},
|
||||
};
|
||||
|
||||
if (type === 'password' && value) {
|
||||
return {
|
||||
...baseStyles,
|
||||
letterSpacing: '7px',
|
||||
color: color.textUltradark,
|
||||
};
|
||||
}
|
||||
|
||||
if (value) {
|
||||
return {
|
||||
...baseStyles,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import ReactDOM from 'react-dom';
|
||||
import routes from './router';
|
||||
import './index.scss';
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
const { document } = global;
|
||||
|
|
|
|||
3
frontend/index.scss
Normal file
3
frontend/index.scss
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
@import "stylesheets/fonts.scss";
|
||||
@import "stylesheets/icons.scss";
|
||||
@import "stylesheets/forms.scss";
|
||||
|
|
@ -6,13 +6,11 @@ import componentStyles from './styles';
|
|||
import Icon from '../../components/icons/Icon';
|
||||
import paths from '../../router/paths';
|
||||
|
||||
const COUNTDOWN_INTERVAL = 1000;
|
||||
const REDIRECT_TIME = 3000;
|
||||
const REDIRECT_TIME = 1200;
|
||||
|
||||
class LoginSuccessfulPage extends Component {
|
||||
static propTypes = {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
user: PropTypes.object,
|
||||
};
|
||||
|
||||
constructor (props) {
|
||||
|
|
@ -27,42 +25,23 @@ class LoginSuccessfulPage extends Component {
|
|||
this.startRedirectCountdown();
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
const { interval } = this;
|
||||
|
||||
if (interval) clearInterval(interval);
|
||||
}
|
||||
|
||||
startRedirectCountdown = () => {
|
||||
const { dispatch } = this.props;
|
||||
const { HOME } = paths;
|
||||
const { redirectTime } = this.state;
|
||||
|
||||
this.interval = setInterval(() => {
|
||||
const { redirectTime } = this.state;
|
||||
|
||||
if (redirectTime > 0) {
|
||||
this.setState({
|
||||
redirectTime: redirectTime - COUNTDOWN_INTERVAL,
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
return dispatch(push(HOME));
|
||||
}, COUNTDOWN_INTERVAL);
|
||||
}, redirectTime);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { loginSuccessStyles, subtextStyles, whiteBoxStyles } = componentStyles;
|
||||
const { redirectTime } = this.state;
|
||||
const secondsToRedirect = redirectTime / 1000;
|
||||
|
||||
return (
|
||||
<div style={whiteBoxStyles}>
|
||||
<Icon name="check" />
|
||||
<p style={loginSuccessStyles}>Login successful</p>
|
||||
<p style={subtextStyles}>Hold on to your butts.</p>
|
||||
<p style={subtextStyles}>redirecting in {secondsToRedirect}</p>
|
||||
<p style={subtextStyles}>hold on to your butts...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
4
frontend/stylesheets/forms.scss
Normal file
4
frontend/stylesheets/forms.scss
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
.input-with-icon::placeholder {
|
||||
color: #A8B1CD;
|
||||
opacity: 1;
|
||||
}
|
||||
|
|
@ -2,8 +2,7 @@
|
|||
<html data-uuid="{{ .UUID }}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" type="text/css" href="/assets/stylesheets/fonts.css">
|
||||
<link rel="stylesheet" type="text/css" href="/assets/stylesheets/icons.css">
|
||||
<link rel="stylesheet" type="text/css" href="/assets/bundle.css">
|
||||
<title>Kolide</title>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
"jsdom": "^9.5.0",
|
||||
"lodash": "^4.3.0",
|
||||
"nock": "^8.0.0",
|
||||
"node-sass": "^3.10.0",
|
||||
"postcss-functions": "^2.1.0",
|
||||
"postcss-loader": "^0.8.0",
|
||||
"precss": "^1.4.0",
|
||||
|
|
@ -49,6 +50,7 @@
|
|||
"redux-mock-store": "^1.2.0",
|
||||
"redux-thunk": "^2.1.0",
|
||||
"require-hacker": "^2.1.4",
|
||||
"sass-loader": "^4.0.2",
|
||||
"style-loader": "^0.13.0",
|
||||
"stylus-loader": "1.5.1",
|
||||
"url-loader": "^0.5.7",
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ var autoprefixer = require('autoprefixer');
|
|||
var ExtractTextPlugin = require("extract-text-webpack-plugin");
|
||||
|
||||
var plugins = [
|
||||
new webpack.NoErrorsPlugin(),
|
||||
new webpack.optimize.DedupePlugin(),
|
||||
new webpack.NoErrorsPlugin(),
|
||||
new webpack.optimize.DedupePlugin(),
|
||||
new ExtractTextPlugin("bundle.css", {allChunks: false})
|
||||
];
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
|
|
@ -39,6 +40,10 @@ var config = {
|
|||
{test: /\.(png|gif)$/, loader: 'url-loader?name=[name]@[hash].[ext]&limit=6000'},
|
||||
{test: /\.(pdf|ico|jpg|svg|eot|otf|woff|ttf|mp4|webm)$/, loader: 'file-loader?name=[name]@[hash].[ext]'},
|
||||
{test: /\.json$/, loader: 'json-loader'},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
loader: ExtractTextPlugin.extract("style-loader", "css-loader!autoprefixer-loader!sass-loader")
|
||||
},
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
include: path.join(repo, 'frontend'),
|
||||
|
|
|
|||
Loading…
Reference in a new issue