Add preview button with eye icon in block chooser

pull/12716/head
Sage Abdullah 2025-01-14 15:04:43 +00:00
rodzic d25a67a4b8
commit 77bcbf702d
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: EB1A33CC51CC0217
7 zmienionych plików z 189 dodań i 125 usunięć

Wyświetl plik

@ -77,6 +77,31 @@ $spacing-sm: theme('spacing.5');
}
}
.w-combobox__option-row {
display: grid;
grid-template-columns: 1fr theme('spacing.6');
}
.w-combobox__option-preview {
background: none;
padding: 0;
width: 100%;
.icon {
color: theme('colors.icon-secondary');
width: theme('spacing.3');
height: theme('spacing.3');
}
}
.w-combobox__option-row--col1 {
grid-column: 1 / span 1;
}
.w-combobox__option-row--col2 {
grid-column: 2 / span 1;
}
.w-combobox__option {
display: grid;
grid-template-columns: theme('spacing.8') 1fr;
@ -100,14 +125,6 @@ $spacing-sm: theme('spacing.5');
}
}
.w-combobox__option--col1 {
grid-column: 1 / span 1;
}
.w-combobox__option--col2 {
grid-column: 2 / span 1;
}
.w-combobox__option-icon {
color: theme('colors.icon-secondary');
height: theme('spacing.4');

Wyświetl plik

@ -113,8 +113,8 @@ describe('ComboBox', () => {
it('combines two categories into one, with two columns', () => {
expect(wrapper.find('.w-combobox__optgroup-label')).toHaveLength(1);
expect(wrapper.find('.w-combobox__option--col1')).toHaveLength(3);
expect(wrapper.find('.w-combobox__option--col2')).toHaveLength(2);
expect(wrapper.find('.w-combobox__option-row--col1')).toHaveLength(3);
expect(wrapper.find('.w-combobox__option-row--col2')).toHaveLength(2);
});
});
});

Wyświetl plik

@ -10,6 +10,7 @@ import findMatches from './findMatches';
export const comboBoxTriggerLabel = gettext('Insert a block');
export const comboBoxLabel = gettext('Search options…');
export const comboBoxNoResults = gettext('No results');
const comboBoxPreviewLabel = gettext('Preview');
export interface ComboBoxCategory<ItemType> {
type: string;
@ -23,6 +24,7 @@ export interface ComboBoxItem {
description?: string | null;
icon?: string | JSX.Element | null;
blockDefId?: string;
isPreviewable?: boolean;
category?: string;
render?: (props: { option: ComboBoxItem }) => JSX.Element | string;
}
@ -183,7 +185,7 @@ export default function ComboBox<ComboBoxOption extends ComboBoxItem>({
setLastHighlightedIndex(highlightedIndex);
}
const selectedBlock =
const previewedBlock =
inputItems[highlightedIndex] || inputItems[lastHighlightedIndex];
return (
@ -257,21 +259,36 @@ export default function ComboBox<ComboBoxOption extends ComboBoxItem>({
return (
<div
key={item.type}
{...getItemProps({ item, index: itemIndex })}
className={`w-combobox__option w-combobox__option--col${itemColumn}`}
className={`w-combobox__option-row w-combobox__option-row--col${itemColumn}`}
>
<div className="w-combobox__option-icon">
{icon}
{/* Support for rich text options using text as an icon (for example "B" for bold). */}
{itemLabel && !hasIcon ? (
<span>{itemLabel}</span>
) : null}
</div>
<div className="w-combobox__option-text">
{item.render
? item.render({ option: item })
: description}
<div
{...getItemProps({ item, index: itemIndex })}
className="w-combobox__option"
>
<div className="w-combobox__option-icon">
{icon}
{/* Support for rich text options using text as an icon (for example "B" for bold). */}
{itemLabel && !hasIcon ? (
<span>{itemLabel}</span>
) : null}
</div>
<div className="w-combobox__option-text">
{item.render
? item.render({ option: item })
: description}
</div>
</div>
{item.isPreviewable ? (
<button
className="w-combobox__option-preview"
aria-label={comboBoxPreviewLabel}
type="button"
onClick={() => setHighlightedIndex(itemIndex)}
>
<Icon name="view" />
</button>
) : null}
</div>
);
})}
@ -280,7 +297,12 @@ export default function ComboBox<ComboBoxOption extends ComboBoxItem>({
})}
</div>
</div>
{selectedBlock ? <ComboBoxPreview item={selectedBlock} /> : null}
{previewedBlock?.isPreviewable ? (
<ComboBoxPreview
item={previewedBlock}
previewLabel={comboBoxPreviewLabel}
/>
) : null}
</div>
);
}

Wyświetl plik

@ -53,134 +53,154 @@ exports[`ComboBox rendering matches the snapshot 1`] = `
Blocks
</div>
<div
aria-selected="false"
className="w-combobox__option w-combobox__option--col1"
id="downshift-1-item-0"
className="w-combobox__option-row w-combobox__option-row--col1"
key="blockquote"
onClick={[Function]}
onMouseDown={[Function]}
onMouseMove={[Function]}
role="option"
>
<div
className="w-combobox__option-icon"
aria-selected="false"
className="w-combobox__option"
id="downshift-1-item-0"
onClick={[Function]}
onMouseDown={[Function]}
onMouseMove={[Function]}
role="option"
>
<Icon
name="blockquote"
/>
</div>
<div
className="w-combobox__option-text"
>
Blockquote
<div
className="w-combobox__option-icon"
>
<Icon
name="blockquote"
/>
</div>
<div
className="w-combobox__option-text"
>
Blockquote
</div>
</div>
</div>
<div
aria-selected="false"
className="w-combobox__option w-combobox__option--col1"
id="downshift-1-item-1"
className="w-combobox__option-row w-combobox__option-row--col1"
key="paragraph"
onClick={[Function]}
onMouseDown={[Function]}
onMouseMove={[Function]}
role="option"
>
<div
className="w-combobox__option-icon"
aria-selected="false"
className="w-combobox__option"
id="downshift-1-item-1"
onClick={[Function]}
onMouseDown={[Function]}
onMouseMove={[Function]}
role="option"
>
<span
className="my-icon"
<div
className="w-combobox__option-icon"
>
P
</span>
</div>
<div
className="w-combobox__option-text"
>
Paragraph
<span
className="my-icon"
>
P
</span>
</div>
<div
className="w-combobox__option-text"
>
Paragraph
</div>
</div>
</div>
<div
aria-selected="false"
className="w-combobox__option w-combobox__option--col1"
id="downshift-1-item-2"
className="w-combobox__option-row w-combobox__option-row--col1"
key="heading-one"
onClick={[Function]}
onMouseDown={[Function]}
onMouseMove={[Function]}
role="option"
>
<div
className="w-combobox__option-icon"
aria-selected="false"
className="w-combobox__option"
id="downshift-1-item-2"
onClick={[Function]}
onMouseDown={[Function]}
onMouseMove={[Function]}
role="option"
>
<Icon
name="custom"
viewBox="0 0 1024 1024"
<div
className="w-combobox__option-icon"
>
<path
d="M 83.625 "
key="M 83.625 "
/>
<path
d="L 232.535156 "
key="L 232.535156 "
/>
</Icon>
</div>
<div
className="w-combobox__option-text"
>
Heading 1
<Icon
name="custom"
viewBox="0 0 1024 1024"
>
<path
d="M 83.625 "
key="M 83.625 "
/>
<path
d="L 232.535156 "
key="L 232.535156 "
/>
</Icon>
</div>
<div
className="w-combobox__option-text"
>
Heading 1
</div>
</div>
</div>
<div
aria-selected="false"
className="w-combobox__option w-combobox__option--col2"
id="downshift-1-item-3"
className="w-combobox__option-row w-combobox__option-row--col2"
key="heading-two"
onClick={[Function]}
onMouseDown={[Function]}
onMouseMove={[Function]}
role="option"
>
<div
className="w-combobox__option-icon"
aria-selected="false"
className="w-combobox__option"
id="downshift-1-item-3"
onClick={[Function]}
onMouseDown={[Function]}
onMouseMove={[Function]}
role="option"
>
<span>
H2
</span>
</div>
<div
className="w-combobox__option-text"
>
<span
className="custom-text"
<div
className="w-combobox__option-icon"
>
H2
</span>
<span>
H2
</span>
</div>
<div
className="w-combobox__option-text"
>
<span
className="custom-text"
>
H2
</span>
</div>
</div>
</div>
<div
aria-selected="false"
className="w-combobox__option w-combobox__option--col2"
id="downshift-1-item-4"
className="w-combobox__option-row w-combobox__option-row--col2"
key="link"
onClick={[Function]}
onMouseDown={[Function]}
onMouseMove={[Function]}
role="option"
>
<div
className="w-combobox__option-icon"
aria-selected="false"
className="w-combobox__option"
id="downshift-1-item-4"
onClick={[Function]}
onMouseDown={[Function]}
onMouseMove={[Function]}
role="option"
>
<span>
🔗
</span>
</div>
<div
className="w-combobox__option-text"
>
Link
<div
className="w-combobox__option-icon"
>
<span>
🔗
</span>
</div>
<div
className="w-combobox__option-text"
>
Link
</div>
</div>
</div>
</div>

Wyświetl plik

@ -10,10 +10,12 @@ interface ComboBoxItem {
export interface ComboBoxPreviewProps {
item: ComboBoxItem;
previewLabel: string;
}
export default function ComboBoxPreview({
item: { label, description, blockDefId },
previewLabel,
}: ComboBoxPreviewProps) {
const previewURL = blockDefId
? new URL(WAGTAIL_CONFIG.ADMIN_URLS.BLOCK_PREVIEW, window.location.href)
@ -23,13 +25,15 @@ export default function ComboBoxPreview({
<div className="w-combobox-preview">
<iframe
className="w-combobox-preview__iframe"
title="Preview"
title={previewLabel}
src={previewURL?.toString()}
/>
<div className="w-combobox-preview__label">{label}</div>
{description ? (
<p className="w-combobox-preview__description">{description}</p>
) : null}
<div className="w-combobox-preview__details">
<div className="w-combobox-preview__label">{label}</div>
{description ? (
<p className="w-combobox-preview__description">{description}</p>
) : null}
</div>
</div>
);
}

Wyświetl plik

@ -124,6 +124,7 @@ class StreamBlockMenu extends BaseInsertionControl {
description: blockDef.meta.description,
icon: blockDef.meta.icon,
blockDefId: blockDef.meta.blockDefId,
isPreviewable: blockDef.meta.isPreviewable,
}));
return {

Wyświetl plik

@ -751,7 +751,7 @@ exports[`telepath: wagtail.blocks.StreamBlock it renders menus on opening 1`] =
<button type="button" title="Insert a block" class="c-sf-add-button" aria-expanded="true">
<svg class="icon icon-plus" aria-hidden="true"><use href="#icon-plus"></use></svg>
</button>
<div data-tippy-root="" id="tippy-5" style="z-index: 9999; visibility: visible; transition: none; position: absolute; left: 0px; top: 0px; margin: 0px;"><div class="tippy-box" data-state="hidden" tabindex="-1" data-theme="dropdown" data-animation="fade" style="max-width: none; transition-duration: 0ms;" role="tooltip"><div class="tippy-content" data-state="hidden" style="transition-duration: 0ms;"><div><div class="w-combobox-container"><div class="w-combobox"><label id="downshift-0-label" for="downshift-0-input" class="w-sr-only">Search options…</label><div class="w-combobox__field"><input aria-activedescendant="" aria-autocomplete="list" aria-controls="downshift-0-menu" aria-expanded="false" aria-labelledby="downshift-0-label" autocomplete="off" id="downshift-0-input" role="combobox" type="text" placeholder="Search options…" value=""></div><div id="downshift-0-menu" role="listbox" aria-labelledby="downshift-0-label" class="w-combobox__menu"><div class="w-combobox__optgroup"><div role="option" aria-selected="false" id="downshift-0-item-0" class="w-combobox__option w-combobox__option--col1"><div class="w-combobox__option-icon"><svg class="icon icon-placeholder" aria-hidden="true"><use href="#icon-placeholder"></use></svg></div><div class="w-combobox__option-text">Test Block A</div></div><div role="option" aria-selected="false" id="downshift-0-item-1" class="w-combobox__option w-combobox__option--col2"><div class="w-combobox__option-icon"><svg class="icon icon-pilcrow" aria-hidden="true"><use href="#icon-pilcrow"></use></svg></div><div class="w-combobox__option-text">Test Block B</div></div></div></div></div></div></div></div></div></div></div><div data-streamfield-child="" data-contentpath="2">
<div data-tippy-root="" id="tippy-5" style="z-index: 9999; visibility: visible; transition: none; position: absolute; left: 0px; top: 0px; margin: 0px;"><div class="tippy-box" data-state="hidden" tabindex="-1" data-theme="dropdown" data-animation="fade" style="max-width: none; transition-duration: 0ms;" role="tooltip"><div class="tippy-content" data-state="hidden" style="transition-duration: 0ms;"><div><div class="w-combobox-container"><div class="w-combobox"><label id="downshift-0-label" for="downshift-0-input" class="w-sr-only">Search options…</label><div class="w-combobox__field"><input aria-activedescendant="" aria-autocomplete="list" aria-controls="downshift-0-menu" aria-expanded="false" aria-labelledby="downshift-0-label" autocomplete="off" id="downshift-0-input" role="combobox" type="text" placeholder="Search options…" value=""></div><div id="downshift-0-menu" role="listbox" aria-labelledby="downshift-0-label" class="w-combobox__menu"><div class="w-combobox__optgroup"><div class="w-combobox__option-row w-combobox__option-row--col1"><div role="option" aria-selected="false" id="downshift-0-item-0" class="w-combobox__option"><div class="w-combobox__option-icon"><svg class="icon icon-placeholder" aria-hidden="true"><use href="#icon-placeholder"></use></svg></div><div class="w-combobox__option-text">Test Block A</div></div></div><div class="w-combobox__option-row w-combobox__option-row--col2"><div role="option" aria-selected="false" id="downshift-0-item-1" class="w-combobox__option"><div class="w-combobox__option-icon"><svg class="icon icon-pilcrow" aria-hidden="true"><use href="#icon-pilcrow"></use></svg></div><div class="w-combobox__option-text">Test Block B</div></div></div></div></div></div></div></div></div></div></div></div><div data-streamfield-child="" data-contentpath="2">
<input type="hidden" name="the-prefix-1-deleted" value="">
<input type="hidden" name="the-prefix-1-order" value="1">
<input type="hidden" name="the-prefix-1-type" value="test_block_b">
@ -974,7 +974,7 @@ exports[`telepath: wagtail.blocks.StreamBlock setError renders error messages 1`
</div>"
`;
exports[`telepath: wagtail.blocks.StreamBlock with labels that need escaping it renders correctly 1`] = `"<div class="w-combobox__optgroup"><div role="option" aria-selected="false" id="downshift-2-item-0" class="w-combobox__option w-combobox__option--col1"><div class="w-combobox__option-icon"><svg class="icon icon-placeholder" aria-hidden="true"><use href="#icon-placeholder"></use></svg></div><div class="w-combobox__option-text">Test Block &lt;A&gt;</div></div><div role="option" aria-selected="false" id="downshift-2-item-1" class="w-combobox__option w-combobox__option--col2"><div class="w-combobox__option-icon"><svg class="icon icon-pilcrow" aria-hidden="true"><use href="#icon-pilcrow"></use></svg></div><div class="w-combobox__option-text">Test Block &lt;B&gt;</div></div></div>"`;
exports[`telepath: wagtail.blocks.StreamBlock with labels that need escaping it renders correctly 1`] = `"<div class="w-combobox__optgroup"><div class="w-combobox__option-row w-combobox__option-row--col1"><div role="option" aria-selected="false" id="downshift-2-item-0" class="w-combobox__option"><div class="w-combobox__option-icon"><svg class="icon icon-placeholder" aria-hidden="true"><use href="#icon-placeholder"></use></svg></div><div class="w-combobox__option-text">Test Block &lt;A&gt;</div></div></div><div class="w-combobox__option-row w-combobox__option-row--col2"><div role="option" aria-selected="false" id="downshift-2-item-1" class="w-combobox__option"><div class="w-combobox__option-icon"><svg class="icon icon-pilcrow" aria-hidden="true"><use href="#icon-pilcrow"></use></svg></div><div class="w-combobox__option-text">Test Block &lt;B&gt;</div></div></div></div>"`;
exports[`telepath: wagtail.blocks.StreamBlock with unique block type it can add block 1`] = `
"<div class="c-sf-help">