Skip to content

Commit

Permalink
chore(PasswordStrength): demo conversion to TS (#9518)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamviktora authored Aug 22, 2023
1 parent 95ecf33 commit 173a799
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 153 deletions.
154 changes: 1 addition & 153 deletions packages/react-core/src/demos/PasswordStrength.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,158 +14,6 @@ import CheckCircleIcon from '@patternfly/react-icons/dist/esm/icons/check-circle

Note, the validation and password strength rules are only examples, demonstrating the changes in the UI when certain conditions are met. We expect consumers will substitute their own, more robust, validation algorithm. In this demo the password strength is determined by how often validation rules are met. A good open-source password strength estimator, recommended by InfoSec, can be found here: https://github.com/dropbox/zxcvbn

```js
import React from 'react';
import {
Form,
FormGroup,
FormHelperText,
HelperText,
Popover,
HelperTextItem,
TextInput
} from '@patternfly/react-core';
import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
import ExclamationCircleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon';
import ExclamationTriangleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon';
import CheckCircleIcon from '@patternfly/react-icons/dist/esm/icons/check-circle-icon';

class PasswordStrengthDemo extends React.Component {
constructor(props) {
super(props);
this.state = {
password: '',
ruleLength: 'indeterminate',
ruleContent: 'indeterminate',
ruleCharacters: 'indeterminate',
passStrength: { variant: 'error', icon: <ExclamationCircleIcon />, text: 'Weak' }
};

this.handlePasswordInput = (_event, password) => {
this.setState({ password });
this.validate(password);
};

this.validate = (password) => {
if (password === '') {
this.setState({
ruleLength: 'indeterminate',
ruleContent: 'indeterminate',
ruleCharacters: 'indeterminate'
});
return;
}

if (password.length < 14) {
this.setState({ ruleLength: 'error' });
} else {
this.setState({ ruleLength: 'success' });
}

if (/redhat/gi.test(password)) {
this.setState({ ruleContent: 'error' });
} else {
this.setState({ ruleContent: 'success' });
}

let rulesCount = 0;
let strCount = 0;
if (/[a-z]/g.test(password)) {
rulesCount++;
}
if (/[A-Z]/g.test(password)) {
strCount += password.match(/[A-Z]/g).length;
rulesCount++;
}
if (/\d/g.test(password)) {
strCount += password.match(/\d/g).length;
rulesCount++;
}
if (/\W/g.test(password)) {
strCount += password.match(/\W/g).length;
rulesCount++;
}

if (rulesCount < 3) {
this.setState({ ruleCharacters: 'error' });
} else {
this.setState({ ruleCharacters: 'success' });
}

if (strCount < 3) {
this.setState({ passStrength: { variant: 'error', icon: <ExclamationCircleIcon />, text: 'Weak' } });
} else if (strCount < 5) {
this.setState({ passStrength: { variant: 'warning', icon: <ExclamationTriangleIcon />, text: 'Medium' } });
} else {
this.setState({ passStrength: { variant: 'success', icon: <CheckCircleIcon />, text: 'Strong' } });
}
};
}

render() {
const { password, ruleLength, ruleContent, ruleCharacters, passStrength } = this.state;

const iconPopover = (
<Popover headerContent={<div>Password Requirements</div>} bodyContent={<div>Password rules</div>}>
<button
type="button"
aria-label="More info for name field"
onClick={(e) => e.preventDefault()}
aria-describedby="password-field"
className="pf-v5-c-form__group-label-help"
>
<HelpIcon />
</button>
</Popover>
);

let passStrLabel = (
<HelperText>
<HelperTextItem variant={passStrength.variant} icon={passStrength.icon}>
{passStrength.text}
</HelperTextItem>
</HelperText>
);
```ts file="./examples/PasswordStrength/PasswordStrengthDemo.tsx"

return (
<Form>
<FormGroup
label="Password"
labelIcon={iconPopover}
isRequired
fieldId="password-field"
{...(ruleLength === 'success' &&
ruleContent === 'success' &&
ruleCharacters === 'success' && {
labelInfo: passStrLabel
})}
>
<TextInput
isRequired
type="text"
id="password-field"
name="password-field"
aria-describedby="password-field-helper"
aria-invalid={ruleLength === 'error' || ruleContent === 'error' || ruleCharacters === 'error'}
value={password}
onChange={this.handlePasswordInput}
/>
<FormHelperText component="div">
<HelperText component="ul" aria-live="polite" id="password-field-helper">
<HelperTextItem isDynamic variant={ruleLength} component="li">
Must be at least 14 characters
</HelperTextItem>
<HelperTextItem isDynamic variant={ruleContent} component="li">
Cannot contain the word "redhat"
</HelperTextItem>
<HelperTextItem isDynamic variant={ruleCharacters} component="li">
Must include at least 3 of the following: lowercase letter, uppercase letters, numbers, symbols
</HelperTextItem>
</HelperText>
</FormHelperText>
</FormGroup>
</Form>
);
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import React from 'react';
import {
Form,
FormGroup,
FormHelperText,
HelperText,
Popover,
HelperTextItem,
TextInput
} from '@patternfly/react-core';
import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
import ExclamationCircleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon';
import ExclamationTriangleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon';
import CheckCircleIcon from '@patternfly/react-icons/dist/esm/icons/check-circle-icon';

export const PasswordStrengthDemo: React.FunctionComponent = () => {
type HelperTextItemVariant = 'default' | 'indeterminate' | 'warning' | 'success' | 'error';
interface PassStrength {
variant: HelperTextItemVariant;
icon: JSX.Element;
text: string;
}

const [password, setPassword] = React.useState('');
const [ruleLength, setRuleLength] = React.useState<HelperTextItemVariant>('indeterminate');
const [ruleContent, setRuleContent] = React.useState<HelperTextItemVariant>('indeterminate');
const [ruleCharacters, setRuleCharacters] = React.useState<HelperTextItemVariant>('indeterminate');
const [passStrength, setPassStrength] = React.useState<PassStrength>({
variant: 'error',
icon: <ExclamationCircleIcon />,
text: 'Weak'
});

const handlePasswordInput = (_event: any, password: string) => {
setPassword(password);
validate(password);
};

const validate = (password: string) => {
if (password === '') {
setRuleLength('indeterminate');
setRuleContent('indeterminate');
setRuleCharacters('indeterminate');
return;
}

if (password.length < 14) {
setRuleLength('error');
} else {
setRuleLength('success');
}

if (/redhat/gi.test(password)) {
setRuleContent('error');
} else {
setRuleContent('success');
}

let rulesCount = 0;
let strCount = 0;
if (/[a-z]/g.test(password)) {
rulesCount++;
}
if (/[A-Z]/g.test(password)) {
strCount += password.match(/[A-Z]/g).length;
rulesCount++;
}
if (/\d/g.test(password)) {
strCount += password.match(/\d/g).length;
rulesCount++;
}
if (/\W/g.test(password)) {
strCount += password.match(/\W/g).length;
rulesCount++;
}

if (rulesCount < 3) {
setRuleCharacters('error');
} else {
setRuleCharacters('success');
}

if (strCount < 3) {
setPassStrength({ variant: 'error', icon: <ExclamationCircleIcon />, text: 'Weak' });
} else if (strCount < 5) {
setPassStrength({ variant: 'warning', icon: <ExclamationTriangleIcon />, text: 'Medium' });
} else {
setPassStrength({ variant: 'success', icon: <CheckCircleIcon />, text: 'Strong' });
}
};

const iconPopover = (
<Popover headerContent={<div>Password Requirements</div>} bodyContent={<div>Password rules</div>}>
<button
type="button"
aria-label="More info for name field"
onClick={(e) => e.preventDefault()}
aria-describedby="password-field"
className="pf-v5-c-form__group-label-help"
>
<HelpIcon />
</button>
</Popover>
);

const passStrLabel = (
<HelperText>
<HelperTextItem variant={passStrength.variant} icon={passStrength.icon}>
{passStrength.text}
</HelperTextItem>
</HelperText>
);

return (
<Form>
<FormGroup
label="Password"
labelIcon={iconPopover}
isRequired
fieldId="password-field"
{...(ruleLength === 'success' &&
ruleContent === 'success' &&
ruleCharacters === 'success' && {
labelInfo: passStrLabel
})}
>
<TextInput
isRequired
type="text"
id="password-field"
name="password-field"
aria-describedby="password-field-helper"
aria-invalid={ruleLength === 'error' || ruleContent === 'error' || ruleCharacters === 'error'}
value={password}
onChange={handlePasswordInput}
/>
<FormHelperText>
<HelperText component="ul" aria-live="polite" id="password-field-helper">
<HelperTextItem isDynamic variant={ruleLength} component="li">
Must be at least 14 characters
</HelperTextItem>
<HelperTextItem isDynamic variant={ruleContent} component="li">
Cannot contain the word "redhat"
</HelperTextItem>
<HelperTextItem isDynamic variant={ruleCharacters} component="li">
Must include at least 3 of the following: lowercase letter, uppercase letters, numbers, symbols
</HelperTextItem>
</HelperText>
</FormHelperText>
</FormGroup>
</Form>
);
};

0 comments on commit 173a799

Please sign in to comment.