Skip to content

Commit

Permalink
feat: Visibility toggle for PasswordInput (#377)
Browse files Browse the repository at this point in the history
* Update fuselage.d.ts

* Update index.js

* Update spec.js

* Update index.js

* Update fuselage.d.ts

* Update index.js

* Update spec.js

* Update PasswordInput.stories.mdx

* Invert toggle state icon

* Convert PasswordInput to TypeScript

Co-authored-by: gabriellsh <40830821+gabriellsh@users.noreply.github.com>
Co-authored-by: Tasso Evangelista <tasso.evangelista@rocket.chat>
  • Loading branch information
3 people authored and Rocket.Chat committed Jun 28, 2021
1 parent 3333d7f commit 5ef1bec
Show file tree
Hide file tree
Showing 23 changed files with 117 additions and 145 deletions.
42 changes: 17 additions & 25 deletions packages/fuselage-hooks/src/useToggle.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
useState,
DispatchWithoutAction,
Dispatch,
SetStateAction,
} from 'react';
import { useState, SetStateAction } from 'react';

import { useMutableCallback } from './useMutableCallback';

Expand All @@ -14,11 +9,9 @@ import { useMutableCallback } from './useMutableCallback';
* @returns a state boolean value and a state toggler function
* @public
*/
export const useToggle = <
D extends DispatchWithoutAction | Dispatch<SetStateAction<boolean>>
>(
export const useToggle = (
initialValue?: boolean | (() => boolean)
): [boolean, D] => {
): [boolean, (forcedValue?: SetStateAction<boolean>) => void] => {
const [value, setValue] = useState(() => {
if (typeof initialValue === 'function') {
return !!initialValue();
Expand All @@ -27,23 +20,22 @@ export const useToggle = <
return !!initialValue;
});

const dispatch = useMutableCallback<
D extends DispatchWithoutAction ? [] : [SetStateAction<boolean>],
void
>((forcedValue?: SetStateAction<boolean>) => {
// uses value from scope to avoid multiple toggles in one render cycle
setValue(() => {
if (typeof forcedValue === 'boolean') {
return forcedValue;
}
const dispatch = useMutableCallback(
(forcedValue?: SetStateAction<boolean>) => {
// uses value from scope to avoid multiple toggles in one render cycle
setValue(() => {
if (typeof forcedValue === 'boolean') {
return forcedValue;
}

if (typeof forcedValue === 'function') {
return forcedValue(value);
}
if (typeof forcedValue === 'function') {
return forcedValue(value);
}

return !value;
});
}) as D;
return !value;
});
}
);

return [value, dispatch];
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom';

import { PasswordInput } from '.';
import PasswordInput from '.';

it('renders without crashing', () => {
const div = document.createElement('div');
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Meta, Story } from '@storybook/react';
import React from 'react';

import { Icon, PasswordInput } from '../..';
import PropsVariation from '../../.storybook/PropsVariation';

export default {
title: 'Forms/Inputs/PasswordInput',
component: PasswordInput,
parameters: { jest: ['PasswordInput.spec.tsx'] },
} as Meta;

export const Default: Story = () => <PasswordInput />;

export const WithValue: Story = () => <PasswordInput defaultValue='password' />;

export const WithIconAddon: Story = () => (
<PasswordInput addon={<Icon name='send' size={20} />} />
);

export const Invalid: Story = () => <PasswordInput error='Error' />;

export const Disabled: Story = () => <PasswordInput disabled />;

export const WithPlaceholder: Story = () => (
<PasswordInput placeholder='Placeholder' />
);

export const States: Story = () => (
<PropsVariation
component={PasswordInput}
common={{ onChange: () => undefined }}
xAxis={{
'default': {},
'with placeholder': { placeholder: 'Placeholder' },
'with value': { value: 'Value' },
'with icon': { addon: <Icon name='key' size='x20' />, value: 'Value' },
}}
yAxis={{
'default': {},
'hover': { className: 'hover' },
'active': { className: 'active' },
'focus': { className: 'focus' },
'disabled': { disabled: true },
'errored': { error: 'Error' },
'errored + hover': { className: 'hover', error: 'Error' },
'errored + active': { className: 'active', error: 'Error' },
'errored + focus': { className: 'focus', error: 'Error' },
}}
/>
);
36 changes: 36 additions & 0 deletions packages/fuselage/src/components/PasswordInput/PasswordInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useToggle } from '@rocket.chat/fuselage-hooks';
import React, { ComponentProps, forwardRef, Ref } from 'react';

import { Icon } from '../Icon';
import { InputBox } from '../InputBox';

// TODO: fix a11y issues

type PasswordInputProps = Omit<ComponentProps<typeof InputBox>, 'type'>;

const PasswordInput = forwardRef(function PasswordInput(
props: PasswordInputProps,
ref: Ref<HTMLInputElement>
) {
const [hidden, toggle] = useToggle(true);
const handleAddonClick = () => {
toggle();
};

return (
<InputBox
type={hidden ? 'password' : 'text'}
addon={
<Icon
name={hidden ? 'eye-off' : 'eye'}
size={20}
onClick={handleAddonClick}
/>
}
ref={ref}
{...props}
/>
);
});

export default PasswordInput;
8 changes: 0 additions & 8 deletions packages/fuselage/src/components/PasswordInput/index.d.ts

This file was deleted.

7 changes: 0 additions & 7 deletions packages/fuselage/src/components/PasswordInput/index.js

This file was deleted.

1 change: 1 addition & 0 deletions packages/fuselage/src/components/PasswordInput/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './PasswordInput';
2 changes: 1 addition & 1 deletion packages/fuselage/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export * from './NumberInput';
export * from './Options';
export * from './Options/Option';
export * from './Pagination';
export * from './PasswordInput';
export { default as PasswordInput } from './PasswordInput';
export * from './StatusBullet';
export * from './ProgressBar';
export * from './RadioButton';
Expand Down
10 changes: 5 additions & 5 deletions packages/onboarding-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
},
"devDependencies": {
"@rocket.chat/eslint-config-alt": "^0.26.0",
"@rocket.chat/fuselage-tokens": "workspace:packages/fuselage-tokens",
"@rocket.chat/fuselage-tokens": "^0.26.0",
"@rocket.chat/prettier-config": "^0.26.0",
"@storybook/addon-essentials": "^6.2.9",
"@storybook/addons": "^6.2.9",
Expand All @@ -73,10 +73,10 @@
"typescript": "^4.2.4"
},
"dependencies": {
"@rocket.chat/fuselage": "workspace:packages/fuselage",
"@rocket.chat/fuselage-hooks": "workspace:packages/fuselage-hooks",
"@rocket.chat/fuselage-polyfills": "workspace:packages/fuselage-polyfills",
"@rocket.chat/icons": "workspace:packages/icons",
"@rocket.chat/fuselage": "^0.26.0",
"@rocket.chat/fuselage-hooks": "^0.26.0",
"@rocket.chat/fuselage-polyfills": "^0.26.0",
"@rocket.chat/icons": "^0.26.0",
"tslib": "^2.2.0"
},
"peerDependencies": {
Expand Down
10 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4446,11 +4446,11 @@ __metadata:
resolution: "@rocket.chat/onboarding-ui@workspace:packages/onboarding-ui"
dependencies:
"@rocket.chat/eslint-config-alt": ^0.26.0
"@rocket.chat/fuselage": "workspace:packages/fuselage"
"@rocket.chat/fuselage-hooks": "workspace:packages/fuselage-hooks"
"@rocket.chat/fuselage-polyfills": "workspace:packages/fuselage-polyfills"
"@rocket.chat/fuselage-tokens": "workspace:packages/fuselage-tokens"
"@rocket.chat/icons": "workspace:packages/icons"
"@rocket.chat/fuselage": ^0.26.0
"@rocket.chat/fuselage-hooks": ^0.26.0
"@rocket.chat/fuselage-polyfills": ^0.26.0
"@rocket.chat/fuselage-tokens": ^0.26.0
"@rocket.chat/icons": ^0.26.0
"@rocket.chat/prettier-config": ^0.26.0
"@storybook/addon-essentials": ^6.2.9
"@storybook/addons": ^6.2.9
Expand Down

0 comments on commit 5ef1bec

Please sign in to comment.