Skip to content

Commit

Permalink
Merge pull request #16703 from strapi/feat/icon-picker
Browse files Browse the repository at this point in the history
Add icon picker to components and show component's icons
  • Loading branch information
Feranchz committed Jun 6, 2023
2 parents 9b56e4f + 420f300 commit 07ea89a
Show file tree
Hide file tree
Showing 24 changed files with 1,060 additions and 284 deletions.
4 changes: 4 additions & 0 deletions api-tests/core/content-type-builder/components.test.api.js
Expand Up @@ -68,6 +68,7 @@ describe('Content Type Builder - Components', () => {
component: {
category: 'default',
displayName: 'Some Component',
icon: 'calendar',
pluginOptions: {
pluginName: {
option: true,
Expand Down Expand Up @@ -107,6 +108,7 @@ describe('Content Type Builder - Components', () => {
body: {
component: {
category: 'default',
icon: 'calendar',
displayName: 'someComponent',
attributes: {},
},
Expand Down Expand Up @@ -172,6 +174,7 @@ describe('Content Type Builder - Components', () => {
category: 'default',
schema: {
displayName: 'Some Component',
icon: 'calendar',
description: '',
collectionName: 'components_default_some_components',
pluginOptions: {
Expand Down Expand Up @@ -254,6 +257,7 @@ describe('Content Type Builder - Components', () => {
body: {
component: {
category: 'default',
icon: 'calendar',
displayName: 'New Component',
attributes: {
name: {
Expand Down
@@ -1,49 +1,39 @@
import PropTypes from 'prop-types';
import React from 'react';
import styled from 'styled-components';

import { Flex } from '@strapi/design-system';
import { Flex, Icon } from '@strapi/design-system';
import { COMPONENT_ICONS } from './constants';

const WIDTH_S = 5;
const WIDTH_M = 8;

const Wrapper = styled(Flex)`
border-radius: ${({ showBackground }) => (showBackground ? `50%` : 0)};
color: ${({ theme }) => theme.colors.neutral600};
height: ${({ theme, size }) => theme.spaces[size === 'S' ? WIDTH_S : WIDTH_M]};
width: ${({ theme, size }) => theme.spaces[size === 'S' ? WIDTH_S : WIDTH_M]};
svg {
height: ${({ theme, size }) => theme.spaces[size === 'S' ? WIDTH_S - 2 : WIDTH_M - 3]};
width: ${({ theme, size }) => theme.spaces[size === 'S' ? WIDTH_S - 2 : WIDTH_M - 3]};
}
`;

export function ComponentIcon({ showBackground = true, size = 'M' }) {
export function ComponentIcon({ showBackground = true, size = 'M', icon }) {
return (
<Wrapper
<Flex
alignItems="center"
background={showBackground ? 'neutral200' : null}
justifyContent="center"
size={size}
showBackground={showBackground}
height={size === 'S' ? 5 : 8}
width={size === 'S' ? 5 : 8}
color="neutral600"
borderRadius={showBackground ? '50%' : 0}
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path
d="M216.3 2c4.8-2.6 10.5-2.6 15.3 0L422.3 106c5.1 2.8 8.3 8.2 8.3 14s-3.2 11.2-8.3 14L231.7 238c-4.8 2.6-10.5 2.6-15.3 0L25.7 134c-5.1-2.8-8.3-8.2-8.3-14s3.2-11.2 8.3-14L216.3 2zM23.7 170l176 96c5.1 2.8 8.3 8.2 8.3 14V496c0 5.6-3 10.9-7.8 13.8s-10.9 3-15.8 .3L8.3 414C3.2 411.2 0 405.9 0 400V184c0-5.6 3-10.9 7.8-13.8s10.9-3 15.8-.3zm400.7 0c5-2.7 11-2.6 15.8 .3s7.8 8.1 7.8 13.8V400c0 5.9-3.2 11.2-8.3 14l-176 96c-5 2.7-11 2.6-15.8-.3s-7.8-8.1-7.8-13.8V280c0-5.9 3.2-11.2 8.3-14l176-96z"
fill="currentColor"
/>
</svg>
</Wrapper>
<Icon
as={COMPONENT_ICONS[icon] || COMPONENT_ICONS.cube}
height={size === 'S' ? 3 : 5}
width={size === 'S' ? 3 : 5}
/>
</Flex>
);
}

ComponentIcon.defaultProps = {
showBackground: true,
size: 'M',
icon: 'Cube',
};

ComponentIcon.propTypes = {
showBackground: PropTypes.bool,
size: PropTypes.string,
icon: PropTypes.string,
};
@@ -0,0 +1,133 @@
import * as Icons from '@strapi/icons';

const COMPONENT_ICONS = {
alien: Icons.Alien,
apps: Icons.Apps,
archive: Icons.Archive,
arrowDown: Icons.ArrowDown,
arrowLeft: Icons.ArrowLeft,
arrowRight: Icons.ArrowRight,
arrowUp: Icons.ArrowUp,
attachment: Icons.Attachment,
bell: Icons.Bell,
bold: Icons.Bold,
book: Icons.Book,
briefcase: Icons.Briefcase,
brush: Icons.Brush,
bulletList: Icons.BulletList,
calendar: Icons.Calendar,
car: Icons.Car,
cast: Icons.Cast,
chartBubble: Icons.ChartBubble,
chartCircle: Icons.ChartCircle,
chartPie: Icons.ChartPie,
check: Icons.Check,
clock: Icons.Clock,
cloud: Icons.Cloud,
code: Icons.Code,
cog: Icons.Cog,
collapse: Icons.Collapse,
command: Icons.Command,
connector: Icons.Connector,
crop: Icons.Crop,
crown: Icons.Crown,
cube: Icons.Cube,
cup: Icons.Cup,
cursor: Icons.Cursor,
dashboard: Icons.Dashboard,
database: Icons.Database,
discuss: Icons.Discuss,
doctor: Icons.Doctor,
earth: Icons.Earth,
emotionHappy: Icons.EmotionHappy,
emotionUnhappy: Icons.EmotionUnhappy,
envelop: Icons.Envelop,
exit: Icons.Exit,
expand: Icons.Expand,
eye: Icons.Eye,
feather: Icons.Feather,
file: Icons.File,
fileError: Icons.FileError,
filePdf: Icons.FilePdf,
filter: Icons.Filter,
folder: Icons.Folder,
gate: Icons.Gate,
gift: Icons.Gift,
globe: Icons.Globe,
grid: Icons.Grid,
handHeart: Icons.HandHeart,
hashtag: Icons.Hashtag,
headphone: Icons.Headphone,
heart: Icons.Heart,
house: Icons.House,
information: Icons.Information,
italic: Icons.Italic,
key: Icons.Key,
landscape: Icons.Landscape,
layer: Icons.Layer,
layout: Icons.Layout,
lightbulb: Icons.Lightbulb,
link: Icons.Link,
lock: Icons.Lock,
magic: Icons.Magic,
manyToMany: Icons.ManyToMany,
manyToOne: Icons.ManyToOne,
manyWays: Icons.ManyWays,
medium: Icons.Medium,
message: Icons.Message,
microphone: Icons.Microphone,
monitor: Icons.Monitor,
moon: Icons.Moon,
music: Icons.Music,
oneToMany: Icons.OneToMany,
oneToOne: Icons.OneToOne,
oneWay: Icons.OneWay,
paint: Icons.Paint,
paintBrush: Icons.PaintBrush,
paperPlane: Icons.PaperPlane,
pencil: Icons.Pencil,
phone: Icons.Phone,
picture: Icons.Picture,
pin: Icons.Pin,
pinMap: Icons.PinMap,
plane: Icons.Plane,
play: Icons.Play,
plus: Icons.Plus,
priceTag: Icons.PriceTag,
puzzle: Icons.Puzzle,
question: Icons.Question,
quote: Icons.Quote,
refresh: Icons.Refresh,
repeat: Icons.Repeat,
restaurant: Icons.Restaurant,
rocket: Icons.Rocket,
rotate: Icons.Rotate,
scissors: Icons.Scissors,
search: Icons.Search,
seed: Icons.Seed,
server: Icons.Server,
shield: Icons.Shield,
shirt: Icons.Shirt,
shoppingCart: Icons.ShoppingCart,
slideshow: Icons.Slideshow,
stack: Icons.Stack,
star: Icons.Star,
store: Icons.Store,
strikeThrough: Icons.StrikeThrough,
sun: Icons.Sun,
television: Icons.Television,
thumbDown: Icons.ThumbDown,
thumbUp: Icons.ThumbUp,
train: Icons.Train,
twitter: Icons.Twitter,
typhoon: Icons.Typhoon,
underline: Icons.Underline,
user: Icons.User,
volumeMute: Icons.VolumeMute,
volumeUp: Icons.VolumeUp,
walk: Icons.Walk,
wheelchair: Icons.Wheelchair,
write: Icons.Write,
};

export { COMPONENT_ICONS };
@@ -0,0 +1,72 @@
/**
*
* ComponentCard
*
*/

import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';

import { Box, Typography, Flex } from '@strapi/design-system';
import { pxToRem } from '@strapi/helper-plugin';

import { ComponentIcon } from '../../ComponentIcon';

const ComponentBox = styled(Box)`
flex-shrink: 0;
height: ${pxToRem(84)};
border: 1px solid ${({ theme }) => theme.colors.neutral200};
background: ${({ theme }) => theme.colors.neutral100};
border-radius: ${({ theme }) => theme.borderRadius};
display: flex;
justify-content: center;
align-items: center;
&:focus,
&:hover {
border: 1px solid ${({ theme }) => theme.colors.primary200};
background: ${({ theme }) => theme.colors.primary100};
${Typography} {
color: ${({ theme }) => theme.colors.primary600};
}
/* > Flex > ComponentIcon */
> div > div:first-child {
background: ${({ theme }) => theme.colors.primary200};
color: ${({ theme }) => theme.colors.primary600};
svg {
path {
fill: ${({ theme }) => theme.colors.primary600};
}
}
}
}
`;

export default function ComponentCard({ children, onClick, icon }) {
return (
<ComponentBox as="button" type="button" onClick={onClick} hasRadius>
<Flex direction="column" gap={1} alignItems="center" justifyContent="center">
<ComponentIcon icon={icon} />

<Typography variant="pi" fontWeight="bold" textColor="neutral600">
{children}
</Typography>
</Flex>
</ComponentBox>
);
}

ComponentCard.defaultProps = {
onClick() {},
icon: 'Cube',
};

ComponentCard.propTypes = {
children: PropTypes.node.isRequired,
onClick: PropTypes.func,
icon: PropTypes.string,
};
Expand Up @@ -38,7 +38,7 @@ export const ComponentCategory = ({
<AccordionContent>
<Box paddingTop={4} paddingBottom={4} paddingLeft={3} paddingRight={3}>
<Grid>
{components.map(({ componentUid, info: { displayName } }) => (
{components.map(({ componentUid, info: { displayName, icon } }) => (
<ComponentBox
key={componentUid}
as="button"
Expand All @@ -52,7 +52,7 @@ export const ComponentCategory = ({
borderColor="neutral200"
>
<Flex direction="column" gap={1} alignItems="center" justifyContent="center">
<ComponentIcon />
<ComponentIcon icon={icon} />

<Typography variant="pi" fontWeight="bold" textColor="neutral600">
{formatMessage({ id: displayName, defaultMessage: displayName })}
Expand Down
Expand Up @@ -22,6 +22,7 @@ import { useContentTypeLayout, useDragAndDrop } from '../../../hooks';
import { composeRefs, getTrad, ItemTypes } from '../../../utils';

import FieldComponent from '../../FieldComponent';
import { ComponentIcon } from '../../ComponentIcon';

export const DynamicComponent = ({
componentUid,
Expand Down Expand Up @@ -206,6 +207,7 @@ export const DynamicComponent = ({
) : (
<Accordion expanded={isOpen} onToggle={handleToggle} size="S" error={errorMessage}>
<AccordionToggle
startIcon={<ComponentIcon icon={icon} showBackground={false} size="S" />}
action={accordionActions}
title={`${friendlyName}${mainValue}`}
togglePosition="left"
Expand Down
@@ -0,0 +1,25 @@
import React from 'react';
import { fireEvent, render } from '@testing-library/react';

import { ThemeProvider, lightTheme } from '@strapi/design-system';

import GlobalStyle from '../../../../../components/GlobalStyle';

import ComponentCard from '../ComponentCard';

describe('ComponentCard', () => {
const setup = (props) =>
render(
<ThemeProvider theme={lightTheme}>
<ComponentCard {...props}>test</ComponentCard>
<GlobalStyle />
</ThemeProvider>
);

it('should call the onClick handler when passed', () => {
const onClick = jest.fn();
const { getByText } = setup({ onClick });
fireEvent.click(getByText('test'));
expect(onClick).toHaveBeenCalled();
});
});
Expand Up @@ -25,13 +25,20 @@ const CustomLink = styled(Flex)`
> div:first-child {
background: ${({ theme }) => theme.colors.primary200};
color: ${({ theme }) => theme.colors.primary600};
svg {
path {
fill: ${({ theme }) => theme.colors.primary600};
}
}
}
}
`;

const DynamicZoneList = ({ components }) => {
const { componentLayouts } = useLayoutDnd();


return (
<Flex gap={2} overflow="scroll hidden" padding={3}>
{components.map((componentUid) => (
Expand All @@ -49,7 +56,7 @@ const DynamicZoneList = ({ components }) => {
as={Link}
to={`/content-manager/components/${componentUid}/configurations/edit`}
>
<ComponentIcon />
<ComponentIcon icon={componentLayouts?.[componentUid]?.info?.icon} />

<Box paddingTop={1}>
<Typography fontSize={1} textColor="neutral600" fontWeight="bold">
Expand Down

0 comments on commit 07ea89a

Please sign in to comment.