Commit 2b2332c4 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Icons in footer links (#2809)

* Icons in footer links

Fixes #2539

* fix envs validator
parent ff04dc85
......@@ -580,6 +580,7 @@ const footerLinkSchema: yup.ObjectSchema<CustomLink> = yup
.object({
text: yup.string().required(),
url: yup.string().test(urlTest).required(),
iconUrl: yup.array().of(yup.string().required().test(urlTest)),
});
const footerLinkGroupSchema: yup.ObjectSchema<CustomLinksGroup> = yup
......
......@@ -17,7 +17,11 @@
"links": [
{
"text": "Develop",
"url": "https://example.com"
"url": "https://example.com",
"iconUrl": [
"https://example.com/mocks/image_s.jpg",
"https://example.com/mocks/image_svg.svg"
]
},
{
"text": "Grants",
......
......@@ -192,7 +192,7 @@ The app version shown in the footer is derived from build-time ENV variables `NE
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| title | `string` | Title of link group | Required | - | `Company` |
| links | `Array<{'text':string;'url':string;}>` | list of links | Required | - | `[{'text':'Homepage','url':'https://www.blockscout.com'}]` |
| links | `Array<{'text':string;'url':string;'iconUrl'?:[string,string]}>` | An array contains a list of links in the column. Each link can optionally have an `icon_url` property, which should include an array of two external image URLs for light and dark themes, respectively. If only one URL is provided, it will be used for both color schemes. We expect the icons to be square, with a minimum size of 40px by 40px or in SVG format. | Required | - | `[{'text':'Homepage','url':'https://www.blockscout.com'}]` |
&nbsp;
......
......@@ -79,6 +79,10 @@ export const FOOTER_LINKS: Array<CustomLinksGroup> = [
{
text: 'MetaDock',
url: 'https://blocksec.com/metadock',
iconUrl: [
'http://localhost:3000/mocks/image_s.jpg',
'http://localhost:3000/mocks/image_svg.svg',
],
},
{
text: 'Sourcify',
......
export type CustomLink = {
text: string;
url: string;
iconUrl?: Array<string>;
};
export type CustomLinksGroup = {
......
......@@ -9,7 +9,7 @@ import Footer from './Footer';
const FOOTER_LINKS_URL = 'https://localhost:3000/footer-links.json';
test.describe('with custom links, max cols', () => {
test.beforeEach(async({ render, mockApiResponse, mockConfigResponse, injectMetaMaskProvider, mockEnvs }) => {
test.beforeEach(async({ render, mockApiResponse, mockConfigResponse, injectMetaMaskProvider, mockEnvs, mockAssetResponse }) => {
await mockEnvs([
[ 'NEXT_PUBLIC_FOOTER_LINKS', FOOTER_LINKS_URL ],
]);
......@@ -21,6 +21,8 @@ test.describe('with custom links, max cols', () => {
indexed_internal_transactions_ratio: '0.1',
indexed_blocks_ratio: '0.1',
});
await mockAssetResponse(FOOTER_LINKS[3].links[0].iconUrl?.[0]!, './playwright/mocks/image_s.jpg');
await mockAssetResponse(FOOTER_LINKS[3].links[0].iconUrl?.[1]!, './playwright/mocks/image_svg.svg');
await render(<Footer/>);
});
......
import { Center } from '@chakra-ui/react';
import React from 'react';
import { useColorModeValue } from 'toolkit/chakra/color-mode';
import { Image } from 'toolkit/chakra/image';
import { Link } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton';
import type { IconName } from 'ui/shared/IconSvg';
......@@ -9,23 +11,55 @@ import IconSvg from 'ui/shared/IconSvg';
type Props = {
icon?: IconName;
iconSize?: string;
iconUrl?: Array<string>;
text: string;
url: string;
isLoading?: boolean;
};
const FooterLinkItem = ({ icon, iconSize, text, url, isLoading }: Props) => {
const FooterLinkItemIconExternal = ({ iconUrl, text }: { iconUrl: Array<string>; text: string }) => {
const [ lightIconUrl, darkIconUrl ] = iconUrl;
const imageSrc = useColorModeValue(lightIconUrl, darkIconUrl || lightIconUrl);
return (
<Image
src={ imageSrc }
alt={ `${ text } icon` }
boxSize={ 5 }
objectFit="contain"
/>
);
};
const FooterLinkItem = ({ icon, iconSize, iconUrl, text, url, isLoading }: Props) => {
if (isLoading) {
return <Skeleton loading my="3px">{ text }</Skeleton>;
}
const iconElement = (() => {
if (iconUrl && Array.isArray(iconUrl)) {
const [ lightIconUrl, darkIconUrl ] = iconUrl;
if (typeof lightIconUrl === 'string' && (typeof darkIconUrl === 'string' || !darkIconUrl)) {
return <FooterLinkItemIconExternal iconUrl={ iconUrl } text={ text }/>;
}
}
if (icon) {
return (
<Link href={ url } display="flex" alignItems="center" h="30px" variant="subtle" target="_blank" textStyle="xs">
{ icon && (
<Center minW={ 6 } mr={ 2 }>
<Center minW={ 6 }>
<IconSvg boxSize={ iconSize || 5 } name={ icon }/>
</Center>
) }
);
}
return null;
})();
return (
<Link href={ url } display="flex" alignItems="center" h="30px" variant="subtle" target="_blank" textStyle="xs" columnGap={ 2 }>
{ iconElement }
{ text }
</Link>
);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment