feat(ui): adds admin.components.listControlsMenu option (#10981)
### What? Adds new option `admin.components.listControlsMenu` to allow custom components to be injected after the existing list controls in the collection list view. ### Why? Needed to facilitate import/export plugin. #### Preview & Testing Use `pnpm dev admin` to see example component and see test added to `test/admin/e2e/list-view`. <img width="1443" alt="Screenshot 2025-02-04 at 4 59 33 PM" src="https://github.com/user-attachments/assets/dffe3a4b-5370-4004-86e6-23dabccdac52" /> --------- Co-authored-by: Dan Ribbens <DanRibbens@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
7a73265bd6
commit
7277f17f14
@@ -158,6 +158,7 @@ The following options are available:
|
||||
| **`beforeListTable`** | An array of components to inject _before_ the built-in List View's table |
|
||||
| **`afterList`** | An array of components to inject _after_ the built-in List View |
|
||||
| **`afterListTable`** | An array of components to inject _after_ the built-in List View's table |
|
||||
| **`listControlsMenu`** | An array of components to render as buttons within a menu next to the List Controls (after the Columns and Filters options) |
|
||||
| **`Description`** | A component to render below the Collection label in the List View. An alternative to the `admin.description` property. |
|
||||
| **`edit.SaveButton`** | Replace the default Save Button with a Custom Component. [Drafts](../versions/drafts) must be disabled. |
|
||||
| **`edit.SaveDraftButton`** | Replace the default Save Draft Button with a Custom Component. [Drafts](../versions/drafts) must be enabled and autosave must be disabled. |
|
||||
|
||||
@@ -33,6 +33,15 @@ export const renderListViewSlots = ({
|
||||
})
|
||||
}
|
||||
|
||||
if (collectionConfig.admin.components?.listControlsMenu) {
|
||||
result.ListControlsMenu = RenderServerComponent({
|
||||
clientProps,
|
||||
Component: collectionConfig.admin.components.listControlsMenu,
|
||||
importMap: payload.importMap,
|
||||
serverProps,
|
||||
})
|
||||
}
|
||||
|
||||
if (collectionConfig.admin.components?.afterListTable) {
|
||||
result.AfterListTable = RenderServerComponent({
|
||||
clientProps,
|
||||
|
||||
@@ -30,6 +30,7 @@ export function iterateCollections({
|
||||
})
|
||||
|
||||
addToImportMap(collection.admin?.components?.afterList)
|
||||
addToImportMap(collection.admin?.components?.listControlsMenu)
|
||||
addToImportMap(collection.admin?.components?.afterListTable)
|
||||
addToImportMap(collection.admin?.components?.beforeList)
|
||||
addToImportMap(collection.admin?.components?.beforeListTable)
|
||||
|
||||
@@ -310,6 +310,7 @@ export type CollectionAdminOptions = {
|
||||
*/
|
||||
Upload?: CustomUpload
|
||||
}
|
||||
listControlsMenu?: CustomComponent[]
|
||||
views?: {
|
||||
/**
|
||||
* Set to a React component to replace the entire Edit View, including all nested routes.
|
||||
|
||||
@@ -209,6 +209,7 @@ export const clientTranslationKeys = createClientTranslationKeys([
|
||||
'general:loading',
|
||||
'general:locale',
|
||||
'general:menu',
|
||||
'general:listControlMenu',
|
||||
'general:moveDown',
|
||||
'general:moveUp',
|
||||
'general:next',
|
||||
|
||||
@@ -261,6 +261,7 @@ export const arTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'المغادرة على أي حال',
|
||||
leaveWithoutSaving: 'المغادرة بدون حفظ',
|
||||
light: 'فاتح',
|
||||
listControlMenu: 'قائمة التحكم',
|
||||
livePreview: 'معاينة مباشرة',
|
||||
loading: 'يتمّ التّحميل',
|
||||
locale: 'اللّغة',
|
||||
|
||||
@@ -265,6 +265,7 @@ export const azTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Heç olmasa çıx',
|
||||
leaveWithoutSaving: 'Saxlamadan çıx',
|
||||
light: 'Açıq',
|
||||
listControlMenu: 'Siyahı nəzarət menyusu',
|
||||
livePreview: 'Öncədən baxış',
|
||||
loading: 'Yüklənir',
|
||||
locale: 'Lokal',
|
||||
|
||||
@@ -264,6 +264,7 @@ export const bgTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Напусни въпреки това',
|
||||
leaveWithoutSaving: 'Напусни без да запазиш',
|
||||
light: 'Светла',
|
||||
listControlMenu: 'Меню за контрол на списъка',
|
||||
livePreview: 'Предварителен преглед',
|
||||
loading: 'Зарежда се',
|
||||
locale: 'Локализация',
|
||||
|
||||
@@ -265,6 +265,7 @@ export const caTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Deixa-ho de totes maneres',
|
||||
leaveWithoutSaving: 'Deixa sense desar',
|
||||
light: 'Clar',
|
||||
listControlMenu: 'Menú de control de llista',
|
||||
livePreview: 'Previsualització en viu',
|
||||
loading: 'Carregant',
|
||||
locale: 'Idioma',
|
||||
|
||||
@@ -263,6 +263,7 @@ export const csTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Přesto odejít',
|
||||
leaveWithoutSaving: 'Odejít bez uložení',
|
||||
light: 'Světlé',
|
||||
listControlMenu: 'Nabídka ovládání seznamu',
|
||||
livePreview: 'Náhled',
|
||||
loading: 'Načítání',
|
||||
locale: 'Místní verze',
|
||||
|
||||
@@ -263,6 +263,7 @@ export const daTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Forlad alligevel',
|
||||
leaveWithoutSaving: 'Forlad uden at gemme',
|
||||
light: 'Lys',
|
||||
listControlMenu: 'Kontrolmenu for liste',
|
||||
livePreview: 'Live-forhåndsvisning',
|
||||
loading: 'Loader',
|
||||
locale: 'Lokalitet',
|
||||
|
||||
@@ -269,6 +269,7 @@ export const deTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Trotzdem verlassen',
|
||||
leaveWithoutSaving: 'Ohne speichern verlassen',
|
||||
light: 'Hell',
|
||||
listControlMenu: 'Kontrollmenü auflisten',
|
||||
livePreview: 'Vorschau',
|
||||
loading: 'Lädt',
|
||||
locale: 'Sprache',
|
||||
|
||||
@@ -265,6 +265,7 @@ export const enTranslations = {
|
||||
leaveAnyway: 'Leave anyway',
|
||||
leaveWithoutSaving: 'Leave without saving',
|
||||
light: 'Light',
|
||||
listControlMenu: 'List control menu',
|
||||
livePreview: 'Live Preview',
|
||||
loading: 'Loading',
|
||||
locale: 'Locale',
|
||||
|
||||
@@ -269,6 +269,7 @@ export const esTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Salir de todos modos',
|
||||
leaveWithoutSaving: 'Salir sin guardar',
|
||||
light: 'Claro',
|
||||
listControlMenu: 'Menú de control de lista',
|
||||
livePreview: 'Previsualizar',
|
||||
loading: 'Cargando',
|
||||
locale: 'Regional',
|
||||
|
||||
@@ -262,6 +262,7 @@ export const etTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Lahku ikkagi',
|
||||
leaveWithoutSaving: 'Lahku ilma salvestamata',
|
||||
light: 'Hele',
|
||||
listControlMenu: 'Loendikontrolli menüü',
|
||||
livePreview: 'Reaalajas eelvaade',
|
||||
loading: 'Laadimine',
|
||||
locale: 'Keel',
|
||||
|
||||
@@ -263,6 +263,7 @@ export const faTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'به هر حال ترک کن',
|
||||
leaveWithoutSaving: 'ترک کردن بدون ذخیره',
|
||||
light: 'روشن',
|
||||
listControlMenu: 'منوی کنترل لیست',
|
||||
livePreview: 'پیشنمایش',
|
||||
loading: 'در حال بارگذاری',
|
||||
locale: 'زبان',
|
||||
|
||||
@@ -272,6 +272,7 @@ export const frTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Quitter quand même',
|
||||
leaveWithoutSaving: 'Quitter sans sauvegarder',
|
||||
light: 'Clair',
|
||||
listControlMenu: 'Menu de contrôle de liste',
|
||||
livePreview: 'Aperçu',
|
||||
loading: 'Chargement en cours',
|
||||
locale: 'Paramètres régionaux',
|
||||
|
||||
@@ -259,6 +259,7 @@ export const heTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'צא בכל זאת',
|
||||
leaveWithoutSaving: 'צא מבלי לשמור',
|
||||
light: 'בהיר',
|
||||
listControlMenu: 'תפריט בקרת רשימה',
|
||||
livePreview: 'תצוגה מקדימה חיה',
|
||||
loading: 'טוען',
|
||||
locale: 'שפה',
|
||||
|
||||
@@ -265,6 +265,7 @@ export const hrTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Svejedno napusti',
|
||||
leaveWithoutSaving: 'Napusti bez spremanja',
|
||||
light: 'Svijetlo',
|
||||
listControlMenu: 'Izbornik za kontrolu popisa',
|
||||
livePreview: 'Pregled',
|
||||
loading: 'Učitavanje',
|
||||
locale: 'Jezik',
|
||||
|
||||
@@ -267,6 +267,7 @@ export const huTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Távozás mindenképp',
|
||||
leaveWithoutSaving: 'Távozás mentés nélkül',
|
||||
light: 'Világos',
|
||||
listControlMenu: 'Lista vezérlő menü',
|
||||
livePreview: 'Előnézet',
|
||||
loading: 'Betöltés',
|
||||
locale: 'Nyelv',
|
||||
|
||||
@@ -268,6 +268,7 @@ export const itTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Esci comunque',
|
||||
leaveWithoutSaving: 'Esci senza salvare',
|
||||
light: 'Chiaro',
|
||||
listControlMenu: 'Menu di controllo elenco',
|
||||
livePreview: 'Anteprima dal vivo',
|
||||
loading: 'Caricamento',
|
||||
locale: 'Locale',
|
||||
|
||||
@@ -265,6 +265,7 @@ export const jaTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'すぐに画面を離れる',
|
||||
leaveWithoutSaving: '内容が保存されていません',
|
||||
light: 'ライトモード',
|
||||
listControlMenu: 'リスト制御メニュー',
|
||||
livePreview: 'プレビュー',
|
||||
loading: 'ローディング中',
|
||||
locale: 'ロケール',
|
||||
|
||||
@@ -263,6 +263,7 @@ export const koTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: '그래도 나가시겠습니까?',
|
||||
leaveWithoutSaving: '저장하지 않고 나가기',
|
||||
light: '라이트',
|
||||
listControlMenu: '목록 제어 메뉴',
|
||||
livePreview: '실시간 미리보기',
|
||||
loading: '불러오는 중',
|
||||
locale: 'locale',
|
||||
|
||||
@@ -267,6 +267,7 @@ export const myTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'ဘာဖြစ်ဖြစ် ထွက်မည်။',
|
||||
leaveWithoutSaving: 'မသိမ်းဘဲ ထွက်မည်။',
|
||||
light: 'အလင်း',
|
||||
listControlMenu: 'စာရင်းထိန်းချုပ် မီနူး',
|
||||
livePreview: 'အစမ်းကြည့်ရန်',
|
||||
loading: 'ဖွင့်နေသည်',
|
||||
locale: 'ဒေသ',
|
||||
|
||||
@@ -265,6 +265,7 @@ export const nbTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Forlat likevel',
|
||||
leaveWithoutSaving: 'Forlat uten å lagre',
|
||||
light: 'Lys',
|
||||
listControlMenu: 'Kontrollmeny for liste',
|
||||
livePreview: 'Forhåndsvisning',
|
||||
loading: 'Laster',
|
||||
locale: 'Lokalitet',
|
||||
|
||||
@@ -268,6 +268,7 @@ export const nlTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Toch weggaan',
|
||||
leaveWithoutSaving: 'Verlaten zonder op te slaan',
|
||||
light: 'Licht',
|
||||
listControlMenu: 'Controlelijstmenu',
|
||||
livePreview: 'Voorbeeld',
|
||||
loading: 'Laden',
|
||||
locale: 'Taal',
|
||||
|
||||
@@ -265,6 +265,7 @@ export const plTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Wyjdź mimo to',
|
||||
leaveWithoutSaving: 'Wyjdź bez zapisywania',
|
||||
light: 'Jasny',
|
||||
listControlMenu: 'Menu kontroli listy',
|
||||
livePreview: 'Podgląd',
|
||||
loading: 'Ładowanie',
|
||||
locale: 'Ustawienia regionalne',
|
||||
|
||||
@@ -265,6 +265,7 @@ export const ptTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Sair mesmo assim',
|
||||
leaveWithoutSaving: 'Sair sem salvar',
|
||||
light: 'Claro',
|
||||
listControlMenu: 'Menu de controle de lista',
|
||||
livePreview: 'Pré-visualização',
|
||||
loading: 'Carregando',
|
||||
locale: 'Local',
|
||||
|
||||
@@ -269,6 +269,7 @@ export const roTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Pleacă oricum',
|
||||
leaveWithoutSaving: 'Plecare fără a salva',
|
||||
light: 'Light',
|
||||
listControlMenu: 'Meniu de control al listei',
|
||||
livePreview: 'Previzualizare',
|
||||
loading: 'Încărcare',
|
||||
locale: 'Localitate',
|
||||
|
||||
@@ -265,6 +265,7 @@ export const rsTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Свеједно напусти',
|
||||
leaveWithoutSaving: 'Напусти без чувања',
|
||||
light: 'Светло',
|
||||
listControlMenu: 'Meni za kontrolu liste',
|
||||
livePreview: 'Преглед',
|
||||
loading: 'Учитавање',
|
||||
locale: 'Језик',
|
||||
|
||||
@@ -265,6 +265,7 @@ export const rsLatinTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Svejedno napusti',
|
||||
leaveWithoutSaving: 'Napusti bez čuvanja',
|
||||
light: 'Svetlo',
|
||||
listControlMenu: 'Kontrolni meni liste',
|
||||
livePreview: 'Pregled',
|
||||
loading: 'Učitavanje',
|
||||
locale: 'Jezik',
|
||||
|
||||
@@ -267,6 +267,7 @@ export const ruTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Все равно уйти',
|
||||
leaveWithoutSaving: 'Выход без сохранения',
|
||||
light: 'Светлая',
|
||||
listControlMenu: 'Меню управления списком',
|
||||
livePreview: 'Предпросмотр',
|
||||
loading: 'Загрузка',
|
||||
locale: 'Локаль',
|
||||
|
||||
@@ -266,6 +266,7 @@ export const skTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Presto odísť',
|
||||
leaveWithoutSaving: 'Odísť bez uloženia',
|
||||
light: 'Svetlý',
|
||||
listControlMenu: 'Menu ovládania zoznamu',
|
||||
livePreview: 'Náhľad',
|
||||
loading: 'Načítavanie',
|
||||
locale: 'Jazyk',
|
||||
|
||||
@@ -264,6 +264,7 @@ export const slTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Vseeno zapusti',
|
||||
leaveWithoutSaving: 'Zapusti brez shranjevanja',
|
||||
light: 'Svetlo',
|
||||
listControlMenu: 'Meni za nadzor seznama',
|
||||
livePreview: 'Predogled',
|
||||
loading: 'Nalaganje',
|
||||
locale: 'Jezik',
|
||||
|
||||
@@ -265,6 +265,7 @@ export const svTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Lämna ändå',
|
||||
leaveWithoutSaving: 'Lämna utan att spara',
|
||||
light: 'Ljus',
|
||||
listControlMenu: 'Kontrollmeny för lista',
|
||||
livePreview: 'Förhandsvisa',
|
||||
loading: 'Läser in',
|
||||
locale: 'Lokal',
|
||||
|
||||
@@ -261,6 +261,7 @@ export const thTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'ออกจากหน้านี้',
|
||||
leaveWithoutSaving: 'ออกโดยไม่บันทึก',
|
||||
light: 'สว่าง',
|
||||
listControlMenu: 'เมนูควบคุมรายการ',
|
||||
livePreview: 'แสดงตัวอย่าง',
|
||||
loading: 'กำลังโหลด',
|
||||
locale: 'ตำแหน่งที่ตั้ง',
|
||||
|
||||
@@ -268,6 +268,7 @@ export const trTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Yine de ayrıl',
|
||||
leaveWithoutSaving: 'Kaydetmeden ayrıl',
|
||||
light: 'Aydınlık',
|
||||
listControlMenu: 'Liste kontrol menüsü',
|
||||
livePreview: 'Önizleme',
|
||||
loading: 'Yükleniyor',
|
||||
locale: 'Yerel ayar',
|
||||
|
||||
@@ -264,6 +264,7 @@ export const ukTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Все одно вийти',
|
||||
leaveWithoutSaving: 'Вийти без збереження',
|
||||
light: 'Світла',
|
||||
listControlMenu: 'Меню контролю списку',
|
||||
livePreview: 'Попередній перегляд',
|
||||
loading: 'Завантаження',
|
||||
locale: 'Локаль',
|
||||
|
||||
@@ -264,6 +264,7 @@ export const viTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: 'Tiếp tục thoát',
|
||||
leaveWithoutSaving: 'Thay đổi chưa được lưu',
|
||||
light: 'Nền sáng',
|
||||
listControlMenu: 'Menu điều khiển danh sách',
|
||||
livePreview: 'Xem trước',
|
||||
loading: 'Đang tải',
|
||||
locale: 'Ngôn ngữ',
|
||||
|
||||
@@ -255,6 +255,7 @@ export const zhTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: '无论如何都要离开',
|
||||
leaveWithoutSaving: '离开而不保存',
|
||||
light: '亮色',
|
||||
listControlMenu: '列表控制菜单',
|
||||
livePreview: '预览',
|
||||
loading: '加载中...',
|
||||
locale: '语言环境',
|
||||
|
||||
@@ -255,6 +255,7 @@ export const zhTwTranslations: DefaultTranslationsObject = {
|
||||
leaveAnyway: '無論如何都要離開',
|
||||
leaveWithoutSaving: '不儲存直接離開',
|
||||
light: '亮色',
|
||||
listControlMenu: '列表控制菜單',
|
||||
livePreview: '預覽',
|
||||
loading: '載入中...',
|
||||
locale: '語言環境',
|
||||
|
||||
@@ -5,8 +5,10 @@ import { useWindowInfo } from '@faceless-ui/window-info'
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import React, { Fragment, useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { Popup, PopupList } from '../../elements/Popup/index.js'
|
||||
import { useUseTitleField } from '../../hooks/useUseAsTitle.js'
|
||||
import { ChevronIcon } from '../../icons/Chevron/index.js'
|
||||
import { Dots } from '../../icons/Dots/index.js'
|
||||
import { SearchIcon } from '../../icons/Search/index.js'
|
||||
import { useListQuery } from '../../providers/ListQuery/index.js'
|
||||
import { useTranslation } from '../../providers/Translation/index.js'
|
||||
@@ -19,8 +21,8 @@ import { PublishMany } from '../PublishMany/index.js'
|
||||
import { SearchFilter } from '../SearchFilter/index.js'
|
||||
import { UnpublishMany } from '../UnpublishMany/index.js'
|
||||
import { WhereBuilder } from '../WhereBuilder/index.js'
|
||||
import validateWhereQuery from '../WhereBuilder/validateWhereQuery.js'
|
||||
import './index.scss'
|
||||
import validateWhereQuery from '../WhereBuilder/validateWhereQuery.js'
|
||||
import { getTextFieldsToBeSearched } from './getTextFieldsToBeSearched.js'
|
||||
|
||||
const baseClass = 'list-controls'
|
||||
@@ -36,6 +38,7 @@ export type ListControlsProps = {
|
||||
readonly handleSearchChange?: (search: string) => void
|
||||
readonly handleSortChange?: (sort: string) => void
|
||||
readonly handleWhereChange?: (where: Where) => void
|
||||
readonly listControlsMenu?: React.ReactNode | React.ReactNode[]
|
||||
readonly renderedFilters?: Map<string, React.ReactNode>
|
||||
}
|
||||
|
||||
@@ -53,6 +56,7 @@ export const ListControls: React.FC<ListControlsProps> = (props) => {
|
||||
disableBulkEdit,
|
||||
enableColumns = true,
|
||||
enableSort = false,
|
||||
listControlsMenu,
|
||||
renderedFilters,
|
||||
} = props
|
||||
|
||||
@@ -193,6 +197,21 @@ export const ListControls: React.FC<ListControlsProps> = (props) => {
|
||||
{t('general:sort')}
|
||||
</Pill>
|
||||
)}
|
||||
{listControlsMenu && Array.isArray(listControlsMenu) && (
|
||||
<Popup
|
||||
button={<Dots ariaLabel={t('general:listControlMenu')} />}
|
||||
className={`${baseClass}__popup`}
|
||||
horizontalAlign="right"
|
||||
size="large"
|
||||
verticalAlign="bottom"
|
||||
>
|
||||
<PopupList.ButtonGroup>
|
||||
{listControlsMenu.map((control, index) => (
|
||||
<PopupList.Button key={index}>{control}</PopupList.Button>
|
||||
))}
|
||||
</PopupList.ButtonGroup>
|
||||
</Popup>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
27
packages/ui/src/icons/Dots/index.scss
Normal file
27
packages/ui/src/icons/Dots/index.scss
Normal file
@@ -0,0 +1,27 @@
|
||||
@import '../../scss/styles';
|
||||
|
||||
@layer payload-default {
|
||||
.dots {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
background-color: var(--theme-elevation-150);
|
||||
border-radius: $style-radius-m;
|
||||
height: calc(var(--base) * 1.2);
|
||||
width: calc(var(--base) * 1.2);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--theme-elevation-100);
|
||||
}
|
||||
|
||||
> div {
|
||||
width: 2.5px;
|
||||
height: 2.5px;
|
||||
border-radius: 100%;
|
||||
background-color: currentColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
packages/ui/src/icons/Dots/index.tsx
Normal file
17
packages/ui/src/icons/Dots/index.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
export const Dots: React.FC<{ ariaLabel?: string; className?: string }> = ({
|
||||
ariaLabel,
|
||||
className,
|
||||
}) => (
|
||||
<div
|
||||
aria-label={ariaLabel}
|
||||
className={[className && className, 'dots'].filter(Boolean).join(' ')}
|
||||
>
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
</div>
|
||||
)
|
||||
@@ -48,6 +48,7 @@ export type ListViewSlots = {
|
||||
BeforeList?: React.ReactNode
|
||||
BeforeListTable?: React.ReactNode
|
||||
Description?: React.ReactNode
|
||||
ListControlsMenu?: React.ReactNode | React.ReactNode[]
|
||||
Table: React.ReactNode
|
||||
}
|
||||
|
||||
@@ -79,6 +80,7 @@ export const DefaultListView: React.FC<ListViewClientProps> = (props) => {
|
||||
disableBulkEdit,
|
||||
enableRowSelections,
|
||||
hasCreatePermission,
|
||||
ListControlsMenu,
|
||||
listPreferences,
|
||||
newDocumentURL,
|
||||
preferenceKey,
|
||||
@@ -218,6 +220,7 @@ export const DefaultListView: React.FC<ListViewClientProps> = (props) => {
|
||||
collectionSlug={collectionSlug}
|
||||
disableBulkDelete={disableBulkDelete}
|
||||
disableBulkEdit={disableBulkEdit}
|
||||
listControlsMenu={ListControlsMenu}
|
||||
renderedFilters={renderedFilters}
|
||||
/>
|
||||
{BeforeListTable}
|
||||
|
||||
@@ -33,6 +33,26 @@ export const Posts: CollectionConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
listControlsMenu: [
|
||||
{
|
||||
path: '/components/Banner/index.js#Banner',
|
||||
clientProps: {
|
||||
message: 'ListControlsMenu',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/components/Banner/index.js#Banner',
|
||||
clientProps: {
|
||||
message: 'Many of them',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/components/Banner/index.js#Banner',
|
||||
clientProps: {
|
||||
message: 'Ok last one',
|
||||
},
|
||||
},
|
||||
],
|
||||
afterList: [
|
||||
{
|
||||
path: '/components/Banner/index.js#Banner',
|
||||
|
||||
@@ -198,6 +198,19 @@ describe('List View', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('should render custom listControlsMenu component', async () => {
|
||||
await page.goto(postsUrl.list)
|
||||
const kebabMenu = page.locator('.list-controls__popup')
|
||||
await expect(kebabMenu).toBeVisible()
|
||||
await kebabMenu.click()
|
||||
|
||||
await expect(
|
||||
page.locator('.popup-button-list__button').locator('div', {
|
||||
hasText: 'ListControlsMenu',
|
||||
}),
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('should render custom afterListTable component', async () => {
|
||||
await page.goto(postsUrl.list)
|
||||
await expect(
|
||||
|
||||
Reference in New Issue
Block a user