Skip to content

Commit

Permalink
Merge pull request #1626 from terrestris/displayfield-links
Browse files Browse the repository at this point in the history
Create anchor elements for linklike values in DisplayField
  • Loading branch information
KaiVolland authored May 31, 2024
2 parents 8c28db7 + 959d914 commit 6965c10
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 6 deletions.
36 changes: 36 additions & 0 deletions src/components/DisplayField/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import React from 'react';

import {
render,
screen
} from '@testing-library/react';

import DisplayField from '.';

describe('<DisplayField />', () => {
Expand All @@ -6,4 +13,33 @@ describe('<DisplayField />', () => {
expect(DisplayField).toBeDefined();
});

it('renders urls as links', () => {
const linkValues = [
'http://example.com',
'https://example.com',
'http://localhost:8080',
'ftp://example.com',
'H:\\special\\windäws\\with spaces\\and, tons of, commas and .dots\\file.txt',
'file://///server/share/path/to/file.txt',
'mailto:[email protected]'
];

for (const linkValue of linkValues) {
render(<DisplayField value={linkValue} />);
const link = screen.getByText(linkValue);
expect(link).toBeVisible();
expect(link).toBeInstanceOf(HTMLAnchorElement);
expect(link).toHaveAttribute('href', linkValue);
}

const noneLinkValues = [ 'Peter', 123 ];

for (const noneLinkValue of noneLinkValues) {
render(<DisplayField value={noneLinkValue} />);
const noneLink = screen.queryByText(noneLinkValue);
expect(noneLink).toBeVisible();
expect(noneLink).not.toBeInstanceOf(HTMLAnchorElement);
}
});

});
46 changes: 40 additions & 6 deletions src/components/DisplayField/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
UploadFile
} from 'antd';

import isString from 'lodash/isString';
import {
isMoment
} from 'moment';
Expand Down Expand Up @@ -52,10 +53,10 @@ export const DisplayField: React.FC<DisplayFieldProps> = ({
i18n
} = useTranslation();

let displayText: string = '';
let displayValue: React.ReactNode = '';

if (typeof value === 'string') {
displayText = value;
displayValue = value;
}

if (typeof value === 'boolean' || value === 'false' || value === 'true') {
Expand All @@ -68,17 +69,46 @@ export const DisplayField: React.FC<DisplayFieldProps> = ({
}

if (Number.isFinite(value)) {
displayText = new Intl.NumberFormat(i18n.language, {
displayValue = new Intl.NumberFormat(i18n.language, {
useGrouping: false
}).format(Number(value));
}

if (isMoment(value)) {
displayText = value.format(format);
displayValue = value.format(format);
}

if (Array.isArray(value)) {
displayText = value.join(', ');
displayValue = value.join(', ');
}

const isUrl = (candidate: string) => {
const lowerCandidate = candidate.toLowerCase();
const protocols = ['http://', 'https://', 'ftp://', 'file://', 'mailto:'];
if (protocols.some(protocol => lowerCandidate.startsWith(protocol))) {
return true;
}
// UNC path
if (candidate.startsWith('\\')) {
return true;
}
// windows drive letter
if (candidate.match(/^[a-zA-Z]:\\/)) {
return true;
}
return false;
};

if (isString(value) && isUrl(value)) {
displayValue = (
<a
href={value}
target="_blank"
rel='noreferrer'
>
{value}
</a>
);
}

const getUpload = (val: ValueType | ValueType[]): UploadFile<ShogunFile>[] | null => {
Expand Down Expand Up @@ -142,12 +172,16 @@ export const DisplayField: React.FC<DisplayFieldProps> = ({
);
}

if (suffix) {
displayValue = <>{displayValue} {suffix}</>;
}

return (
<Typography.Text
className="displayfield"
{...passThroughProps}
>
{displayText}{displayText && suffix ? ` ${suffix}` : ''}
{displayValue}
</Typography.Text>
);
};
Expand Down

0 comments on commit 6965c10

Please sign in to comment.