Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
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: 1 addition & 1 deletion lib/index.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/index.css.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/index.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/tyk-ui.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/tyk-ui.css.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/tyk-ui.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/tyk-ui.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tyk-technologies/tyk-ui",
"version": "4.4.23",
"version": "4.4.24",
"description": "Tyk UI - ui reusable components",
"main": "src/index.js",
"scripts": {
Expand Down
29 changes: 29 additions & 0 deletions src/components/MaskSecret/MaskSecret.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.tyk-mask-secret {
display: inline-flex;
align-items: center;
gap: 8px;
}

.tyk-mask-secret__value {
font-family: var(--font-family-regular);
letter-spacing: 0.1em;
}

.tyk-mask-secret__toggle {
background: none;
border: none;
cursor: pointer;
color: var(--text-color, #666);
padding: 0;
display: flex;
align-items: center;
line-height: 1;
}

.tyk-mask-secret__toggle:focus {
outline: none;
}

.tyk-mask-secret__toggle:hover {
color: var(--color-primary-base, #333);
}
60 changes: 60 additions & 0 deletions src/components/MaskSecret/MaskSecret.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';
import MaskSecret from './index';

describe('MaskSecret', () => {
it('masks the value by default', () => {
cy.mount(<MaskSecret value="mysecret" />);

cy.get('.tyk-mask-secret__value').should('have.text', '••••••••');
cy.get('.tyk-mask-secret__toggle')
.should('have.attr', 'aria-label', 'Show secret');
cy.get('.tyk-mask-secret__toggle .fa-eye').should('exist');
});

it('reveals the value when the toggle is clicked', () => {
cy.mount(<MaskSecret value="mysecret" />);

cy.get('.tyk-mask-secret__toggle').click();

cy.get('.tyk-mask-secret__value').should('have.text', 'mysecret');
cy.get('.tyk-mask-secret__toggle')
.should('have.attr', 'aria-label', 'Hide secret');
cy.get('.tyk-mask-secret__toggle .fa-eye-slash').should('exist');
});

it('re-masks the value when the toggle is clicked a second time', () => {
cy.mount(<MaskSecret value="mysecret" />);

cy.get('.tyk-mask-secret__toggle').click();
cy.get('.tyk-mask-secret__toggle').click();

cy.get('.tyk-mask-secret__value').should('have.text', '••••••••');
cy.get('.tyk-mask-secret__toggle')
.should('have.attr', 'aria-label', 'Show secret');
});

it('masks bullet count matches value length', () => {
cy.mount(<MaskSecret value="abc" />);

cy.get('.tyk-mask-secret__value').should('have.text', '•••');
});

it('shows a dash and no toggle button when value is empty', () => {
cy.mount(<MaskSecret />);

cy.get('.tyk-mask-secret__value').should('have.text', '-');
cy.get('.tyk-mask-secret__toggle').should('not.exist');
});

it('applies an additional className to the root element', () => {
cy.mount(<MaskSecret value="secret" className="my-class" />);

cy.get('.tyk-mask-secret.my-class').should('exist');
});

it('is always masked on initial mount (simulating page refresh)', () => {
cy.mount(<MaskSecret value="refreshtest" />);

cy.get('.tyk-mask-secret__value').should('have.text', '•••••••••••');
});
});
13 changes: 13 additions & 0 deletions src/components/MaskSecret/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
The `MaskSecret` component displays a sensitive value (API token, shared secret, password, client secret, etc.) masked by default. A toggle button allows the user to reveal or re-mask the plaintext. Because the reveal state is not persisted, the secret is always masked again on page refresh.

Use this component for **display-only** secret values. For secret values inside a form, use `<Input type="password">` or `<Input2 type="password">` instead.

```js
<MaskSecret value="my-api-key-12345" />
```
```js
<MaskSecret value="a-very-long-shared-secret-token" className="custom-class" />
```
```js
<MaskSecret />
```
37 changes: 37 additions & 0 deletions src/components/MaskSecret/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';

import Icon from '../Icon';
import './MaskSecret.css';

function MaskSecret({ value, className }) {
const [revealed, setRevealed] = useState(false);

return (
<span className={['tyk-mask-secret', className].filter(Boolean).join(' ')}>
<span className="tyk-mask-secret__value">
{revealed ? (value || '-') : (value ? '•'.repeat(String(value).length) : '-')}
</span>
{value && (
<button
type="button"
className="tyk-mask-secret__toggle"
onClick={() => setRevealed((prev) => !prev)}
aria-label={revealed ? 'Hide secret' : 'Show secret'}
>
<Icon type={revealed ? 'eye-slash' : 'eye'} />
</button>
)}
</span>
);
}

MaskSecret.propTypes = {
className: PropTypes.string,
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
};

export default MaskSecret;
4 changes: 1 addition & 3 deletions src/form/components/Input/Readme.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
```js
<Input
label="This is a demo Input"
name="input"
Expand Down Expand Up @@ -116,5 +115,4 @@ import Icon from '../../../components/Icon';
onChange={ (e) => { console.log(e); }}
theme="default rounded-corners"
/>
</div>
```
</div>
2 changes: 1 addition & 1 deletion src/form/components/Input2/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,4 @@ import Icon from '../../../components/Icon';
onChange={ (e) => { console.log(e); }}
/>
</div>
```
```
23 changes: 23 additions & 0 deletions src/form/components/MaskedInput/MaskedInput.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.tyk-masked-input__toggle {
background: none;
border: none;
cursor: pointer;
color: var(--text-color, #666);
padding: 0 var(--form-control-addon-padding-right, 10px) 0 var(--form-control-addon-padding-left, 10px);
display: flex;
align-items: center;
line-height: 1;
}

.tyk-masked-input__toggle:focus {
outline: none;
}

.tyk-masked-input__toggle:hover {
color: var(--color-primary-base, #333);
}

.tyk-masked-input__toggle:disabled {
opacity: 0.4;
cursor: not-allowed;
}
Loading
Loading