Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions packages/dataviews/src/dataform-controls/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import toggle from './toggle';
import toggleGroup from './toggle-group';
import array from './array';
import color from './color';
import password from './password';

interface FormControls {
[ key: string ]: ComponentType< DataFormControlProps< any > >;
Expand All @@ -40,6 +41,7 @@ const FORM_CONTROLS: FormControls = {
telephone,
url,
integer,
password,
radio,
select,
text,
Expand Down
94 changes: 94 additions & 0 deletions packages/dataviews/src/dataform-controls/password.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* WordPress dependencies
*/
import {
Button,
privateApis,
__experimentalInputControlSuffixWrapper as InputControlSuffixWrapper,
} from '@wordpress/components';
import { useCallback, useState } from '@wordpress/element';
import { seen, unseen } from '@wordpress/icons';

/**
* Internal dependencies
*/
import type { DataFormControlProps } from '../types';
import { unlock } from '../lock-unlock';

const { ValidatedInputControl } = unlock( privateApis );

export default function Password< Item >( {
data,
field,
onChange,
hideLabelFromVision,
}: DataFormControlProps< Item > ) {
const { id, label, placeholder, description } = field;
const value = field.getValue( { item: data } );
const [ isVisible, setIsVisible ] = useState( false );
const [ customValidity, setCustomValidity ] =
useState<
React.ComponentProps<
typeof ValidatedInputControl
>[ 'customValidity' ]
>( undefined );

const onChangeControl = useCallback(
( newValue: string ) =>
onChange( {
[ id ]: newValue,
} ),
[ id, onChange ]
);

const toggleVisibility = useCallback( () => {
setIsVisible( ( prev ) => ! prev );
}, [] );

return (
<ValidatedInputControl
Copy link
Member

Choose a reason for hiding this comment

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

Can this use the utility/validated-text.tsx component instead? I understand it just needs to accept a suffix. It'd be great if the utility was renamed to utils/validated-input.tsx as well (we forgot to that recently).

required={ !! field.isValid?.required }
onValidate={ ( newValue: any ) => {
const message = field.isValid?.custom?.(
{
...data,
[ id ]: newValue,
},
field
);

if ( message ) {
setCustomValidity( {
type: 'invalid',
message,
} );
return;
}

setCustomValidity( undefined );
} }
customValidity={ customValidity }
label={ label }
placeholder={ placeholder }
value={ value ?? '' }
help={ description }
onChange={ onChangeControl }
hideLabelFromVision={ hideLabelFromVision }
type={ isVisible ? 'text' : 'password' }
suffix={
<InputControlSuffixWrapper variant="control">
<Button
icon={ isVisible ? unseen : seen }
onClick={ toggleVisibility }
size="small"
variant="tertiary"
aria-label={
isVisible ? 'Hide password' : 'Show password'
}
/>
</InputControlSuffixWrapper>
}
__next40pxDefaultSize
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export type DataFormValidatedTextControlProps< Item > =
/**
* The input type of the control.
*/
type?: 'text' | 'email' | 'tel' | 'url';
type?: 'text' | 'email' | 'tel' | 'url' | 'password';
/**
* Optional icon to display as prefix.
*/
Expand Down
5 changes: 5 additions & 0 deletions packages/dataviews/src/field-types/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { default as date } from './date';
import { default as boolean } from './boolean';
import { default as media } from './media';
import { default as array } from './array';
import { default as password } from './password';
import { default as telephone } from './telephone';
import { default as color } from './color';
import { default as url } from './url';
Expand Down Expand Up @@ -68,6 +69,10 @@ export default function getFieldTypeDefinition< Item >(
return array;
}

if ( 'password' === type ) {
return password;
}

if ( 'telephone' === type ) {
return telephone;
}
Expand Down
71 changes: 71 additions & 0 deletions packages/dataviews/src/field-types/password.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import type {
DataViewRenderFieldProps,
SortDirection,
NormalizedField,
FieldTypeDefinition,
} from '../types';
import { renderFromElements } from '../utils';
import {
OPERATOR_CONTAINS,
OPERATOR_IS,
OPERATOR_IS_ALL,
OPERATOR_IS_ANY,
OPERATOR_IS_NONE,
OPERATOR_IS_NOT,
OPERATOR_IS_NOT_ALL,
OPERATOR_NOT_CONTAINS,
OPERATOR_STARTS_WITH,
} from '../constants';

function sort( valueA: any, valueB: any, direction: SortDirection ) {
return direction === 'asc'
? valueA.localeCompare( valueB )
: valueB.localeCompare( valueA );
}

export default {
sort,
isValid: {
custom: ( item: any, field: NormalizedField< any > ) => {
const value = field.getValue( { item } );
if ( field?.elements ) {
const validValues = field.elements.map( ( f ) => f.value );
if ( ! validValues.includes( value ) ) {
return __( 'Value must be one of the elements.' );
}
}

return null;
},
},
Edit: 'password',
render: ( { item, field }: DataViewRenderFieldProps< any > ) => {
return field.elements
? renderFromElements( { item, field } )
: '•'.repeat( field.getValue( { item } ).length );
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps we can render a fixed number of dots instead of giving away the pass length?

},
enableSorting: true,
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps we should disable sorting by default, even though the field consumer can enable it.

filterBy: {
defaultOperators: [ OPERATOR_IS_ANY, OPERATOR_IS_NONE ],
Copy link
Member

Choose a reason for hiding this comment

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

Same here, perhaps we shouldn't offer any filtering for password by default.

validOperators: [
OPERATOR_IS,
OPERATOR_IS_NOT,
OPERATOR_CONTAINS,
OPERATOR_NOT_CONTAINS,
OPERATOR_STARTS_WITH,
// Multiple selection
OPERATOR_IS_ANY,
OPERATOR_IS_NONE,
OPERATOR_IS_ALL,
OPERATOR_IS_NOT_ALL,
],
},
} satisfies FieldTypeDefinition< any >;
40 changes: 40 additions & 0 deletions packages/dataviews/src/field-types/stories/index.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const meta = {
'datetime',
'email',
'integer',
'password',
'radio',
'select',
'telephone',
Expand Down Expand Up @@ -76,6 +77,8 @@ type DataType = {
colorWithElements: string;
url: string;
urlWithElements: string;
password: string;
passwordWithElements: string;
media: string;
mediaWithElements: string;
array: string[];
Expand Down Expand Up @@ -106,6 +109,8 @@ const data: DataType[] = [
colorWithElements: 'rgba(255, 165, 0, 0.8)',
url: 'https://example.com',
urlWithElements: 'https://example.com',
password: 'secretpassword123',
passwordWithElements: 'secretpassword123',
media: 'https://live.staticflickr.com/7398/9458193857_e1256123e3_z.jpg',
mediaWithElements:
'https://live.staticflickr.com/7398/9458193857_e1256123e3_z.jpg',
Expand Down Expand Up @@ -292,6 +297,23 @@ const fields: Field< DataType >[] = [
},
],
},
{
id: 'password',
type: 'password',
label: 'Password',
description: 'Help for password.',
},
{
id: 'passwordWithElements',
type: 'password',
label: 'Password (with elements)',
description: 'Help for password with elements.',
elements: [
{ value: 'secretpassword123', label: 'Secret Password' },
{ value: 'adminpass456', label: 'Admin Password' },
{ value: 'userpass789', label: 'User Password' },
],
},
{
id: 'media',
type: 'media',
Expand Down Expand Up @@ -376,6 +398,7 @@ type ControlTypes =
| 'datetime'
| 'email'
| 'integer'
| 'password'
| 'radio'
| 'select'
| 'telephone'
Expand Down Expand Up @@ -701,6 +724,23 @@ export const Array = ( {
);
};

export const Password = ( {
type,
Edit,
}: {
type: PanelTypes;
Edit: ControlTypes;
} ) => {
const passwordFields = useMemo(
() => fields.filter( ( field ) => field.type === 'password' ),
[]
);

return (
<FieldTypeStory fields={ passwordFields } type={ type } Edit={ Edit } />
);
};

export const NoType = ( {
type,
Edit,
Expand Down
1 change: 1 addition & 0 deletions packages/dataviews/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export type FieldType =
| 'media'
| 'boolean'
| 'email'
| 'password'
| 'telephone'
| 'color'
| 'url'
Expand Down
2 changes: 2 additions & 0 deletions packages/dataviews/src/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export function isItemValid< Item >(
( field.type === 'url' && isEmptyNullOrUndefined( value ) ) ||
( field.type === 'telephone' &&
isEmptyNullOrUndefined( value ) ) ||
( field.type === 'password' &&
isEmptyNullOrUndefined( value ) ) ||
( field.type === 'integer' &&
isEmptyNullOrUndefined( value ) ) ||
( field.type === undefined && isEmptyNullOrUndefined( value ) )
Expand Down
Loading