Implement visibility toggle for account API keys

This commit is contained in:
James Nuanez
2020-07-26 23:24:13 -07:00
parent 52fcd29bae
commit ec4eeb84f5
3 changed files with 127 additions and 25 deletions
+53 -18
View File
@@ -11,6 +11,7 @@ import Loader from '../blocks/Loader.js';
import Modal from '../blocks/Modal.js';
import FormError from '../blocks/FormError.js';
import CopyableText from '../elements/CopyableText';
import ToggleText from '../blocks/ToggleText.js';
const Td = styled.td`
padding: 0.5rem 0;
@@ -52,8 +53,24 @@ const TableContent = props => {
newContent = newContent || content;
const sortedContent = [...newContent];
sortedContent.sort((a, b) => {
let valA = (a[column] && a[column].toLowerCase()) || '';
let valB = (b[column] && b[column].toLowerCase()) || '';
let valA;
let valB;
if (!a[column]) {
valA = '';
valB = '';
} else if (typeof a[column] === 'object') {
if (a[column].type === 'masked') {
valA = a[column].masked;
valB = b[column].masked;
}
if (a[column].type === 'normal') {
valA = a[column].content;
valB = b[column].content;
}
} else {
valA = (a[column] && a[column].toLowerCase()) || '';
valB = (b[column] && b[column].toLowerCase()) || '';
}
if (newSortOrder === 'asc') {
return valA > valB ? 1 : valA < valB ? -1 : 0;
} else {
@@ -279,6 +296,7 @@ const TableContent = props => {
{props.columns.map((c, i) => (
<th
key={c.key}
style={{ width: c.width }}
colSpan={!props.addContent && (i === props.columns.length - 1) ? '2' : null}
>
{selected.length && i === props.columns.length - 1 ? (
@@ -361,23 +379,40 @@ const TableContent = props => {
/>
</td>
)}
{props.columns.map((c, i) => (
<td key={c.key}>
{i === 0 && props.urlParam
? <span>
<Link
to={`/internal/${props.urlParam}/${a.sid}/edit`}
tabIndex={modalOpen ? '-1' : ''}
>
<span tabIndex="-1" title={a[c.key]}>
{a[c.key]}
</span>
</Link>
</span>
: <span title={a[c.key]}>{a[c.key]}</span>
{props.columns.map((c, i) => {
let columnContent = '';
let columnTitle = null;
if (a[c.key]) {
if (typeof a[c.key] === 'object') {
if (a[c.key].type === 'normal') {
columnContent = a[c.key].content;
columnTitle = columnContent;
} else if (a[c.key].type === 'masked') {
columnContent = <ToggleText masked={a[c.key].masked} revealed={a[c.key].revealed} />;
}
} else {
columnContent = a[c.key];
columnTitle = columnContent;
}
</td>
))}
}
return (
<td key={c.key} style={{ fontWeight: c.fontWeight }}>
{i === 0 && props.urlParam
? <span>
<Link
to={`/internal/${props.urlParam}/${a.sid}/edit`}
tabIndex={modalOpen ? '-1' : ''}
>
<span tabIndex="-1" title={columnTitle}>
{columnContent}
</span>
</Link>
</span>
: <span title={columnTitle}>{columnContent}</span>
}
</td>
);
})}
<td>
{props.rowsHaveDeleteButtons ? (
<Button
+60
View File
@@ -0,0 +1,60 @@
import React, { useState } from 'react';
import styled from 'styled-components/macro';
import { ReactComponent as ViewPassword } from '../../images/ViewPassword.svg';
import { ReactComponent as HidePassword } from '../../images/HidePassword.svg';
const Container = styled.span`
position: relative;
`;
const ToggleVisibilityButton = styled.button`
position: absolute;
top: -0.5rem;
left: 22rem;
width: 2.5rem;
cursor: pointer;
background: none;
border: 0;
outline: 0;
padding: 0;
& > span {
height: 2.25rem;
width: 2.5rem;
display: flex;
justify-content: center;
align-items: center;
outline: 0;
border-radius: 0.25rem;
fill: #767676;
}
&:hover > span {
fill: #565656;
}
&:focus > span {
box-shadow: inset 0 0 0 0.125rem #767676;
}
`;
const ToggleText = props => {
const [ mode, setMode ] = useState('masked');
return (
<Container>
{mode === 'masked' ? props.masked : props.revealed}
<ToggleVisibilityButton
onClick={() => setMode(mode === 'masked' ? 'revealed' : 'masked')}
>
<span tabIndex="-1">
{mode === 'masked'
? <ViewPassword />
: <HidePassword />
}
</span>
</ToggleVisibilityButton>
</Container>
);
};
export default ToggleText;
@@ -62,9 +62,16 @@ const AccountsAddEdit = () => {
}
return {
sid: a.api_key_sid,
token: maskedToken,
last_used: lastUsedString,
sid: a.api_key_sid,
token: {
type: 'masked',
masked: maskedToken,
revealed: token,
},
last_used: {
type: 'normal',
content: lastUsedString,
},
};
});
return(simplifiedApiKeys);
@@ -142,8 +149,8 @@ const AccountsAddEdit = () => {
//=============================================================================
const formatApiKeyToDelete = apiKey => {
const items = [
{ name: 'API Key:' , content: apiKey.token || '[none]' },
{ name: 'Last Used:' , content: apiKey.last_used || 'Never used' },
{ name: 'API Key:' , content: apiKey.token.masked || '[none]' },
{ name: 'Last Used:' , content: apiKey.last_used.content || 'Never used' },
];
return items;
};
@@ -200,8 +207,8 @@ const AccountsAddEdit = () => {
name="API key"
getContent={getApiKeys}
columns={[
{ header: 'API Key', key: 'token' },
{ header: 'Last Used', key: 'last_used' },
{ header: 'API Key', key: 'token', width: '27rem', fontWeight: 'normal' },
{ header: 'Last Used', key: 'last_used', width: '10rem' },
]}
addContent={createApiKey}
formatContentToDelete={formatApiKeyToDelete}