chore: refines blocks drawer content

This commit is contained in:
Jacob Fletcher
2023-01-24 15:43:32 -05:00
parent 3d854f7724
commit 1ab8ccdc99
16 changed files with 196 additions and 184 deletions

View File

@@ -1,19 +1,28 @@
@import '../../../scss/styles.scss';
.upload-card {
.thumbnail-card {
@include shadow;
background: var(--theme-input-bg);
max-width: base(9);
margin-bottom: base(.5);
&__filename {
&__label {
padding: base(.5);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 600;
}
&--has-on-click {
cursor: pointer;
}
&--align-label-center {
text-align: center;
}
&__thumbnail {
display: flex;
align-items: center;
justify-content: center;
}
}

View File

@@ -0,0 +1,57 @@
import React, { Fragment } from 'react';
import { useTranslation } from 'react-i18next';
import { Props } from './types';
import Thumbnail from '../Thumbnail';
import './index.scss';
const baseClass = 'thumbnail-card';
export const ThumbnailCard: React.FC<Props> = (props) => {
const {
className,
onClick,
doc,
collection,
thumbnail,
label,
alignLabel,
onKeyDown,
} = props;
const { t } = useTranslation('general');
const classes = [
baseClass,
className,
typeof onClick === 'function' && `${baseClass}--has-on-click`,
alignLabel && `${baseClass}--align-label-${alignLabel}`,
].filter(Boolean).join(' ');
return (
<div
className={classes}
onClick={typeof onClick === 'function' ? onClick : undefined}
onKeyDown={typeof onKeyDown === 'function' ? onKeyDown : undefined}
>
<div className={`${baseClass}__thumbnail`}>
{thumbnail && thumbnail}
{!thumbnail && (collection && doc) && (
<Thumbnail
size="expand"
doc={doc}
collection={collection}
/>
)}
</div>
<div className={`${baseClass}__label`}>
{label && label}
{!label && doc && (
<Fragment>
{typeof doc?.filename === 'string' ? doc?.filename : `[${t('untitled')}]`}
</Fragment>
)}
</div>
</div>
);
};

View File

@@ -0,0 +1,12 @@
import { SanitizedCollectionConfig } from '../../../../collections/config/types';
export type Props = {
className?: string
collection?: SanitizedCollectionConfig
doc?: Record<string, unknown>
onClick?: () => void
onKeyDown?: () => void
thumbnail?: React.ReactNode
label?: string
alignLabel?: 'left' | 'center'
}

View File

@@ -1,44 +0,0 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Props } from './types';
import Thumbnail from '../Thumbnail';
import './index.scss';
const baseClass = 'upload-card';
const UploadCard: React.FC<Props> = (props) => {
const {
className,
onClick,
doc,
collection,
} = props;
const { t } = useTranslation('general');
const classes = [
baseClass,
className,
typeof onClick === 'function' && `${baseClass}--has-on-click`,
].filter(Boolean).join(' ');
return (
<div
className={classes}
onClick={typeof onClick === 'function' ? onClick : undefined}
>
<Thumbnail
size="expand"
doc={doc}
collection={collection}
/>
<div className={`${baseClass}__filename`}>
{typeof doc?.filename === 'string' ? doc?.filename : `[${t('untitled')}]`}
</div>
</div>
);
};
export default UploadCard;

View File

@@ -1,8 +0,0 @@
import { SanitizedCollectionConfig } from '../../../../collections/config/types';
export type Props = {
className?: string
collection: SanitizedCollectionConfig,
doc: Record<string, unknown>
onClick?: () => void,
}

View File

@@ -13,7 +13,7 @@
width: 16.66%;
}
.upload-card {
.thumbnail-card {
margin: base(.5);
max-width: initial;
}

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { Props } from './types';
import UploadCard from '../UploadCard';
import { ThumbnailCard } from '../ThumbnailCard';
import './index.scss';
@@ -14,9 +14,9 @@ const UploadGallery: React.FC<Props> = (props) => {
<ul className={baseClass}>
{docs.map((doc) => (
<li key={String(doc.id)}>
<UploadCard
<ThumbnailCard
doc={doc}
{...{ collection }}
collection={collection}
onClick={() => onCardClick(doc)}
/>
</li>

View File

@@ -7,6 +7,7 @@ $icon-margin: base(.25);
position: sticky;
top: 0;
display: flex;
width: 100%;
align-items: center;
z-index: 1;
@@ -17,9 +18,15 @@ $icon-margin: base(.25);
.search {
position: absolute;
top: 50%;
transform: translate3d(0, -50%, 0);
right: base(.25);
width: $icon-width;
margin: 0 $icon-margin;
.stroke {
stroke: var(--theme-elevation-300);
}
}
@include mid-break {

View File

@@ -1,39 +0,0 @@
@import '../../../../../../scss/styles';
.block-selection {
display: inline-flex;
flex-direction: column;
flex-wrap: wrap;
width: 33%;
padding: base(.75) base(.5);
cursor: pointer;
align-items: center;
background: none;
border-radius: 0;
box-shadow: 0;
border: 0;
color: currentColor;
&:hover {
background-color: var(--theme-elevation-100);
}
&__image {
svg,
img {
max-width: 100%;
}
}
&__label {
margin-top: base(.25);
font-weight: 600;
text-align: center;
white-space: initial;
}
@include mid-break {
width: unset;
}
}

View File

@@ -1,52 +0,0 @@
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { getTranslation } from '../../../../../../../utilities/getTranslation';
import DefaultBlockImage from '../../../../../graphics/DefaultBlockImage';
import { Props } from './types';
import './index.scss';
const baseClass = 'block-selection';
const BlockSelection: React.FC<Props> = (props) => {
const {
onClick,
addRow,
addRowIndex,
block,
} = props;
const { i18n } = useTranslation();
const {
labels, slug, imageURL, imageAltText,
} = block;
const handleBlockSelection = useCallback(() => {
addRow(addRowIndex, slug);
if (typeof onClick === 'function') {
onClick();
}
}, [onClick, addRow, addRowIndex, slug]);
return (
<button
className={baseClass}
tabIndex={0}
type="button"
onClick={handleBlockSelection}
>
<div className={`${baseClass}__image`}>
{imageURL ? (
<img
src={imageURL}
alt={imageAltText}
/>
) : <DefaultBlockImage />}
</div>
<div className={`${baseClass}__label`}>{getTranslation(labels.singular, i18n)}</div>
</button>
);
};
export default BlockSelection;

View File

@@ -1,8 +0,0 @@
import { Block } from '../../../../../../../fields/config/types';
export type Props = {
addRow: (i: number, block: string) => void
addRowIndex: number
block: Block
onClick?: () => void
}

View File

@@ -1,10 +1,62 @@
@import '../../../../../scss/styles.scss';
.blocks-drawer {
margin-top: base(2.5);
width: 100%;
&__wrapper {
margin-top: base(2.5);
width: 100%;
}
@include mid-break {
&__blocks-wrapper {
padding: base(0.5);
margin-top: base(1.5);
}
&__blocks {
position: relative;
margin: -#{base(1)};
display: flex;
flex-wrap: wrap;
padding: 0;
list-style: none;
}
&__block {
margin: base(0.5);
width: calc(25% - #{base(1)});
}
&__default-image {
width: 100%;
overflow: hidden;
padding-top: base(0.75);
}
@include mid-break {
&__wrapper {
margin-top: base(1.5);
}
&__blocks-wrapper {
padding: base(0.25);
}
&__blocks {
margin: -#{base(0.5)};
}
&__block {
margin: base(0.25);
width: calc(33.33% - #{base(0.5)});
}
}
@include small-break {
&__blocks-wrapper {
margin-top: base(0.75);
}
&__block {
width: calc(50% - #{base(0.5)});
}
}
}

View File

@@ -3,10 +3,11 @@ import { useModal } from '@faceless-ui/modal';
import { useTranslation } from 'react-i18next';
import BlockSearch from './BlockSearch';
import { Props } from './types';
import BlockSelection from './BlockSelection';
import { Drawer } from '../../../../elements/Drawer';
import { Gutter } from '../../../../elements/Gutter';
import { getTranslation } from '../../../../../../utilities/getTranslation';
import { ThumbnailCard } from '../../../../elements/ThumbnailCard';
import DefaultBlockImage from '../../../../graphics/DefaultBlockImage';
import './index.scss';
@@ -37,23 +38,48 @@ export const BlocksDrawer: React.FC<Props> = (props) => {
return (
<Drawer slug={drawerSlug}>
<Gutter className={baseClass}>
<Gutter className={`${baseClass}__wrapper`}>
<h2>
{t('addLabel', { label: getTranslation(labels.singular, i18n) })}
</h2>
<BlockSearch setSearchTerm={setSearchTerm} />
<div className={baseClass}>
{filteredBlocks?.map((block, index) => (
<BlockSelection
key={index}
block={block}
onClick={() => {
closeModal(drawerSlug);
}}
addRow={addRow}
addRowIndex={addRowIndex}
/>
))}
<div className={`${baseClass}__blocks-wrapper`}>
<ul className={`${baseClass}__blocks`}>
{filteredBlocks?.map((block, index) => {
const {
labels: blockLabels,
slug,
imageURL,
imageAltText,
} = block;
return (
<li
key={index}
className={`${baseClass}__block`}
>
<ThumbnailCard
onClick={() => {
addRow(addRowIndex, slug);
closeModal(drawerSlug);
}}
thumbnail={imageURL ? (
<img
src={imageURL}
alt={imageAltText}
/>
) : (
<div className={`${baseClass}__default-image`}>
<DefaultBlockImage />
</div>
)}
label={getTranslation(blockLabels.singular, i18n)}
alignLabel="center"
/>
</li>
);
})}
</ul>
</div>
</Gutter>
</Drawer>

View File

@@ -81,10 +81,10 @@ $focus-box-shadow: 0 0 0 $style-stroke-width-m var(--theme-success-500);
}
@mixin shadow {
box-shadow: 0 12px 45px rgba(0, 0, 0, .03);
box-shadow: 0 4px 12px rgba(0, 0, 0, .07);
&:hover {
box-shadow: 0 12px 45px rgba(0, 0, 0, .07);
box-shadow: 0 4px 12px rgba(0, 0, 0, .1);
}
}

View File

@@ -207,7 +207,7 @@ describe('fields', () => {
await expect(blocksDrawer).toBeVisible();
// select the first block in the drawer
const firstBlockSelector = await blocksDrawer.locator('.blocks-drawer .block-selection').first();
const firstBlockSelector = await blocksDrawer.locator('.blocks-drawer__blocks .blocks-drawer__block').first();
await expect(firstBlockSelector).toContainText('Text');
await firstBlockSelector.click();
@@ -231,7 +231,7 @@ describe('fields', () => {
await expect(blocksDrawer).toBeVisible();
// select the first block in the drawer
const firstBlockSelector = blocksDrawer.locator('.blocks-drawer .block-selection').first();
const firstBlockSelector = blocksDrawer.locator('.blocks-drawer__blocks .blocks-drawer__block').first();
await expect(firstBlockSelector).toContainText('Text');
await firstBlockSelector.click();
@@ -253,7 +253,7 @@ describe('fields', () => {
await expect(blocksDrawer).toBeVisible();
// select the first block in the drawer
const firstBlockSelector = blocksDrawer.locator('.blocks-drawer .block-selection').first();
const firstBlockSelector = blocksDrawer.locator('.blocks-drawer__blocks .blocks-drawer__block').first();
await expect(firstBlockSelector).toContainText('Text en');
await firstBlockSelector.click();
@@ -426,7 +426,7 @@ describe('fields', () => {
// Close the drawer
await editLinkModal.locator('button[type="submit"]').click();
await expect(editLinkModal).not.toBeVisible();
await expect(editLinkModal).toBeHidden();
});
test('should populate relationship link', async () => {
@@ -449,7 +449,7 @@ describe('fields', () => {
// Close the drawer
await editLinkModal.locator('button[type="submit"]').click();
await expect(editLinkModal).not.toBeVisible();
await expect(editLinkModal).toBeHidden();
});
test('should populate new links', async () => {

View File

@@ -49,7 +49,7 @@ describe('uploads', () => {
test('should show upload filename in upload collection list', async () => {
await page.goto(mediaURL.list);
const media = page.locator('.upload-card__filename');
const media = page.locator('.thumbnail-card__label');
await wait(110);
await expect(media).toHaveText('image.png');