This is an automated email from the ASF dual-hosted git repository.

kgabryje pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new d49fd01ff3 feat(CRUD): add new empty state (#19310)
d49fd01ff3 is described below

commit d49fd01ff3e3ee153e5e50352ec2151f028a5456
Author: Stephen Liu <[email protected]>
AuthorDate: Mon Apr 11 18:04:45 2022 +0800

    feat(CRUD): add new empty state (#19310)
    
    * feat(CRUD): add new empty state
    
    * fix ci
    
    * add svg license
---
 .../src/assets/images/filter-results.svg           | 34 +++++++++++++++++
 superset-frontend/src/components/Button/index.tsx  |  1 +
 .../src/components/EmptyState/index.tsx            | 19 ++++++++--
 .../src/components/ListView/Filters/Base.ts        |  4 ++
 .../src/components/ListView/Filters/DateRange.tsx  | 27 ++++++++++----
 .../src/components/ListView/Filters/Search.tsx     | 23 ++++++++----
 .../src/components/ListView/Filters/Select.tsx     | 36 ++++++++++++------
 .../src/components/ListView/Filters/index.tsx      | 36 ++++++++++++++----
 .../src/components/ListView/ListView.tsx           | 43 +++++++++++++++-------
 superset-frontend/src/components/ListView/utils.ts |  1 +
 .../src/views/CRUD/alert/AlertList.tsx             | 17 ++++-----
 .../src/views/CRUD/annotation/AnnotationList.tsx   | 22 ++++-------
 .../CRUD/annotationlayers/AnnotationLayersList.tsx | 20 +++-------
 13 files changed, 196 insertions(+), 87 deletions(-)

diff --git a/superset-frontend/src/assets/images/filter-results.svg 
b/superset-frontend/src/assets/images/filter-results.svg
new file mode 100644
index 0000000000..770a54b34f
--- /dev/null
+++ b/superset-frontend/src/assets/images/filter-results.svg
@@ -0,0 +1,34 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<svg width="120" height="150" viewBox="0 0 120 150" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+<path d="M100.133 19.8391L100.134 19.8402L119.5 
40.6963V149.5H0.5V0.5H82.2811L100.133 19.8391Z" fill="#F7F7F7" 
stroke="#D9D9D9"/>
+<path d="M82.5 0V42H120" stroke="#D9D9D9"/>
+<mask id="path-3-inside-1_738_30486" fill="white">
+<rect x="24" y="65" width="71.7778" height="9.44444" rx="0.5"/>
+</mask>
+<rect x="24" y="65" width="71.7778" height="9.44444" rx="0.5" fill="white" 
stroke="#D9D9D9" stroke-width="2" mask="url(#path-3-inside-1_738_30486)"/>
+<mask id="path-4-inside-2_738_30486" fill="white">
+<rect x="39.1113" y="85.7778" width="41.5556" height="9.44444" rx="0.5"/>
+</mask>
+<rect x="39.1113" y="85.7778" width="41.5556" height="9.44444" rx="0.5" 
fill="white" stroke="#D9D9D9" stroke-width="2" 
mask="url(#path-4-inside-2_738_30486)"/>
+<mask id="path-5-inside-3_738_30486" fill="white">
+<rect x="50.4443" y="106.556" width="18.8889" height="9.44444" rx="0.5"/>
+</mask>
+<rect x="50.4443" y="106.556" width="18.8889" height="9.44444" rx="0.5" 
fill="white" stroke="#D9D9D9" stroke-width="2" 
mask="url(#path-5-inside-3_738_30486)"/>
+</svg>
diff --git a/superset-frontend/src/components/Button/index.tsx 
b/superset-frontend/src/components/Button/index.tsx
index 30d4e3d9ac..b8e428d6ca 100644
--- a/superset-frontend/src/components/Button/index.tsx
+++ b/superset-frontend/src/components/Button/index.tsx
@@ -56,6 +56,7 @@ export interface ButtonProps {
     | 'rightTop'
     | 'rightBottom';
   onClick?: OnClickHandler;
+  onMouseDown?: OnClickHandler;
   disabled?: boolean;
   buttonStyle?: ButtonStyle;
   buttonSize?: 'default' | 'small' | 'xsmall';
diff --git a/superset-frontend/src/components/EmptyState/index.tsx 
b/superset-frontend/src/components/EmptyState/index.tsx
index 02c1d7c4a2..7ba54567e4 100644
--- a/superset-frontend/src/components/EmptyState/index.tsx
+++ b/superset-frontend/src/components/EmptyState/index.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import React, { ReactNode } from 'react';
+import React, { ReactNode, SyntheticEvent } from 'react';
 import { styled, css, SupersetTheme } from '@superset-ui/core';
 import { Empty } from 'src/components';
 import Button from 'src/components/Button';
@@ -140,6 +140,11 @@ const ImageContainer = ({ image, size }: 
ImageContainerProps) => (
   />
 );
 
+const handleMouseDown = (e: SyntheticEvent) => {
+  e.preventDefault();
+  e.stopPropagation();
+};
+
 export const EmptyStateBig = ({
   title,
   image,
@@ -159,7 +164,11 @@ export const EmptyStateBig = ({
       <BigTitle>{title}</BigTitle>
       {description && <BigDescription>{description}</BigDescription>}
       {buttonAction && buttonText && (
-        <ActionButton buttonStyle="primary" onClick={buttonAction}>
+        <ActionButton
+          buttonStyle="primary"
+          onClick={buttonAction}
+          onMouseDown={handleMouseDown}
+        >
           {buttonText}
         </ActionButton>
       )}
@@ -186,7 +195,11 @@ export const EmptyStateMedium = ({
       <Title>{title}</Title>
       {description && <Description>{description}</Description>}
       {buttonText && buttonAction && (
-        <ActionButton buttonStyle="primary" onClick={buttonAction}>
+        <ActionButton
+          buttonStyle="primary"
+          onClick={buttonAction}
+          onMouseDown={handleMouseDown}
+        >
           {buttonText}
         </ActionButton>
       )}
diff --git a/superset-frontend/src/components/ListView/Filters/Base.ts 
b/superset-frontend/src/components/ListView/Filters/Base.ts
index 03d805a751..6baca649ff 100644
--- a/superset-frontend/src/components/ListView/Filters/Base.ts
+++ b/superset-frontend/src/components/ListView/Filters/Base.ts
@@ -31,3 +31,7 @@ export const FilterContainer = styled.div`
   align-items: center;
   width: ${SELECT_WIDTH}px;
 `;
+
+export type FilterHandler = {
+  clearFilter: () => void;
+};
diff --git a/superset-frontend/src/components/ListView/Filters/DateRange.tsx 
b/superset-frontend/src/components/ListView/Filters/DateRange.tsx
index c391d6ff67..4dfaf11f79 100644
--- a/superset-frontend/src/components/ListView/Filters/DateRange.tsx
+++ b/superset-frontend/src/components/ListView/Filters/DateRange.tsx
@@ -16,12 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React, { useState, useMemo } from 'react';
+import React, {
+  useState,
+  useMemo,
+  forwardRef,
+  useImperativeHandle,
+} from 'react';
 import moment, { Moment } from 'moment';
 import { styled } from '@superset-ui/core';
 import { RangePicker } from 'src/components/DatePicker';
 import { FormLabel } from 'src/components/Form';
-import { BaseFilter } from './Base';
+import { BaseFilter, FilterHandler } from './Base';
 
 interface DateRangeFilterProps extends BaseFilter {
   onSubmit: (val: number[]) => void;
@@ -38,17 +43,23 @@ const RangeFilterContainer = styled.div`
   width: 360px;
 `;
 
-export default function DateRangeFilter({
-  Header,
-  initialValue,
-  onSubmit,
-}: DateRangeFilterProps) {
+function DateRangeFilter(
+  { Header, initialValue, onSubmit }: DateRangeFilterProps,
+  ref: React.RefObject<FilterHandler>,
+) {
   const [value, setValue] = useState<ValueState | null>(initialValue ?? null);
   const momentValue = useMemo((): [Moment, Moment] | null => {
     if (!value || (Array.isArray(value) && !value.length)) return null;
     return [moment(value[0]), moment(value[1])];
   }, [value]);
 
+  useImperativeHandle(ref, () => ({
+    clearFilter: () => {
+      setValue(null);
+      onSubmit([]);
+    },
+  }));
+
   return (
     <RangeFilterContainer>
       <FormLabel>{Header}</FormLabel>
@@ -72,3 +83,5 @@ export default function DateRangeFilter({
     </RangeFilterContainer>
   );
 }
+
+export default forwardRef(DateRangeFilter);
diff --git a/superset-frontend/src/components/ListView/Filters/Search.tsx 
b/superset-frontend/src/components/ListView/Filters/Search.tsx
index f327ac4b39..60cfe41bac 100644
--- a/superset-frontend/src/components/ListView/Filters/Search.tsx
+++ b/superset-frontend/src/components/ListView/Filters/Search.tsx
@@ -16,13 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React, { useState } from 'react';
+import React, { forwardRef, useImperativeHandle, useState } from 'react';
 import { t, styled } from '@superset-ui/core';
 import Icons from 'src/components/Icons';
 import { AntdInput } from 'src/components';
 import { SELECT_WIDTH } from 'src/components/ListView/utils';
 import { FormLabel } from 'src/components/Form';
-import { BaseFilter } from './Base';
+import { BaseFilter, FilterHandler } from './Base';
 
 interface SearchHeaderProps extends BaseFilter {
   Header: string;
@@ -42,12 +42,10 @@ const StyledInput = styled(AntdInput)`
   border-radius: ${({ theme }) => theme.gridUnit}px;
 `;
 
-export default function SearchFilter({
-  Header,
-  name,
-  initialValue,
-  onSubmit,
-}: SearchHeaderProps) {
+function SearchFilter(
+  { Header, name, initialValue, onSubmit }: SearchHeaderProps,
+  ref: React.RefObject<FilterHandler>,
+) {
   const [value, setValue] = useState(initialValue || '');
   const handleSubmit = () => {
     if (value) {
@@ -61,6 +59,13 @@ export default function SearchFilter({
     }
   };
 
+  useImperativeHandle(ref, () => ({
+    clearFilter: () => {
+      setValue('');
+      onSubmit('');
+    },
+  }));
+
   return (
     <Container>
       <FormLabel>{Header}</FormLabel>
@@ -78,3 +83,5 @@ export default function SearchFilter({
     </Container>
   );
 }
+
+export default forwardRef(SearchFilter);
diff --git a/superset-frontend/src/components/ListView/Filters/Select.tsx 
b/superset-frontend/src/components/ListView/Filters/Select.tsx
index b2e5e639d4..525061fd27 100644
--- a/superset-frontend/src/components/ListView/Filters/Select.tsx
+++ b/superset-frontend/src/components/ListView/Filters/Select.tsx
@@ -16,12 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React, { useState, useMemo } from 'react';
+import React, {
+  useState,
+  useMemo,
+  forwardRef,
+  useImperativeHandle,
+} from 'react';
 import { t } from '@superset-ui/core';
 import { Select } from 'src/components';
 import { Filter, SelectOption } from 'src/components/ListView/types';
 import { FormLabel } from 'src/components/Form';
-import { FilterContainer, BaseFilter } from './Base';
+import { FilterContainer, BaseFilter, FilterHandler } from './Base';
 
 interface SelectFilterProps extends BaseFilter {
   fetchSelects?: Filter['fetchSelects'];
@@ -31,14 +36,17 @@ interface SelectFilterProps extends BaseFilter {
   selects: Filter['selects'];
 }
 
-function SelectFilter({
-  Header,
-  name,
-  fetchSelects,
-  initialValue,
-  onSelect,
-  selects = [],
-}: SelectFilterProps) {
+function SelectFilter(
+  {
+    Header,
+    name,
+    fetchSelects,
+    initialValue,
+    onSelect,
+    selects = [],
+  }: SelectFilterProps,
+  ref: React.RefObject<FilterHandler>,
+) {
   const [selectedOption, setSelectedOption] = useState(initialValue);
 
   const onChange = (selected: SelectOption) => {
@@ -53,6 +61,12 @@ function SelectFilter({
     setSelectedOption(undefined);
   };
 
+  useImperativeHandle(ref, () => ({
+    clearFilter: () => {
+      onClear();
+    },
+  }));
+
   const fetchAndFormatSelects = useMemo(
     () => async (inputValue: string, page: number, pageSize: number) => {
       if (fetchSelects) {
@@ -88,4 +102,4 @@ function SelectFilter({
     </FilterContainer>
   );
 }
-export default SelectFilter;
+export default forwardRef(SelectFilter);
diff --git a/superset-frontend/src/components/ListView/Filters/index.tsx 
b/superset-frontend/src/components/ListView/Filters/index.tsx
index 5b630ebe9c..348ed3850d 100644
--- a/superset-frontend/src/components/ListView/Filters/index.tsx
+++ b/superset-frontend/src/components/ListView/Filters/index.tsx
@@ -16,7 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React from 'react';
+import React, {
+  createRef,
+  forwardRef,
+  useImperativeHandle,
+  useMemo,
+} from 'react';
 import { withTheme } from '@superset-ui/core';
 
 import {
@@ -28,6 +33,7 @@ import {
 import SearchFilter from './Search';
 import SelectFilter from './Select';
 import DateRangeFilter from './DateRange';
+import { FilterHandler } from './Base';
 
 interface UIFiltersProps {
   filters: Filters;
@@ -35,11 +41,24 @@ interface UIFiltersProps {
   updateFilterValue: (id: number, value: FilterValue['value']) => void;
 }
 
-function UIFilters({
-  filters,
-  internalFilters = [],
-  updateFilterValue,
-}: UIFiltersProps) {
+function UIFilters(
+  { filters, internalFilters = [], updateFilterValue }: UIFiltersProps,
+  ref: React.RefObject<{ clearFilters: () => void }>,
+) {
+  const filterRefs = useMemo(
+    () =>
+      Array.from({ length: filters.length }, () => createRef<FilterHandler>()),
+    [filters.length],
+  );
+
+  useImperativeHandle(ref, () => ({
+    clearFilters: () => {
+      filterRefs.forEach((filter: any) => {
+        filter.current?.clearFilter?.();
+      });
+    },
+  }));
+
   return (
     <>
       {filters.map(
@@ -49,6 +68,7 @@ function UIFilters({
           if (input === 'select') {
             return (
               <SelectFilter
+                ref={filterRefs[index]}
                 Header={Header}
                 fetchSelects={fetchSelects}
                 initialValue={initialValue}
@@ -65,6 +85,7 @@ function UIFilters({
           if (input === 'search' && typeof Header === 'string') {
             return (
               <SearchFilter
+                ref={filterRefs[index]}
                 Header={Header}
                 initialValue={initialValue}
                 key={id}
@@ -76,6 +97,7 @@ function UIFilters({
           if (input === 'datetime_range') {
             return (
               <DateRangeFilter
+                ref={filterRefs[index]}
                 Header={Header}
                 initialValue={initialValue}
                 key={id}
@@ -91,4 +113,4 @@ function UIFilters({
   );
 }
 
-export default withTheme(UIFilters);
+export default withTheme(forwardRef(UIFilters));
diff --git a/superset-frontend/src/components/ListView/ListView.tsx 
b/superset-frontend/src/components/ListView/ListView.tsx
index 4b979cbf56..e91626003f 100644
--- a/superset-frontend/src/components/ListView/ListView.tsx
+++ b/superset-frontend/src/components/ListView/ListView.tsx
@@ -17,10 +17,8 @@
  * under the License.
  */
 import { t, styled } from '@superset-ui/core';
-import React, { useEffect } from 'react';
-import { Empty } from 'src/components';
+import React, { useCallback, useEffect, useRef } from 'react';
 import Alert from 'src/components/Alert';
-import EmptyImage from 'src/assets/images/empty.svg';
 import cx from 'classnames';
 import Button from 'src/components/Button';
 import Icons from 'src/components/Icons';
@@ -38,6 +36,7 @@ import {
   ViewModeType,
 } from './types';
 import { ListViewError, useListViewState } from './utils';
+import { EmptyStateBig, EmptyStateProps } from '../EmptyState';
 
 const ListViewStyles = styled.div`
   text-align: center;
@@ -223,10 +222,7 @@ export interface ListViewProps<T extends object = any> {
   defaultViewMode?: ViewModeType;
   highlightRowId?: number;
   showThumbnails?: boolean;
-  emptyState?: {
-    message?: string;
-    slot?: React.ReactNode;
-  };
+  emptyState?: EmptyStateProps;
 }
 
 function ListView<T extends object = any>({
@@ -248,7 +244,7 @@ function ListView<T extends object = any>({
   cardSortSelectOptions,
   defaultViewMode = 'card',
   highlightRowId,
-  emptyState = {},
+  emptyState,
 }: ListViewProps<T>) {
   const {
     getTableProps,
@@ -263,6 +259,7 @@ function ListView<T extends object = any>({
     toggleAllRowsSelected,
     setViewMode,
     state: { pageIndex, pageSize, internalFilters, viewMode },
+    query,
   } = useListViewState({
     bulkSelectColumnConfig,
     bulkSelectMode: bulkSelectEnabled && Boolean(bulkActions.length),
@@ -291,6 +288,14 @@ function ListView<T extends object = any>({
     });
   }
 
+  const filterControlsRef = useRef<{ clearFilters: () => void }>(null);
+
+  const handleClearFilterControls = useCallback(() => {
+    if (query.filters) {
+      filterControlsRef.current?.clearFilters();
+    }
+  }, [query.filters]);
+
   const cardViewEnabled = Boolean(renderCard);
 
   useEffect(() => {
@@ -308,6 +313,7 @@ function ListView<T extends object = any>({
           <div className="controls">
             {filterable && (
               <FilterControls
+                ref={filterControlsRef}
                 filters={filters}
                 internalFilters={internalFilters}
                 updateFilterValue={applyFilterValue}
@@ -394,12 +400,21 @@ function ListView<T extends object = any>({
           )}
           {!loading && rows.length === 0 && (
             <EmptyWrapper className={viewMode}>
-              <Empty
-                image={<EmptyImage />}
-                description={emptyState.message || t('No Data')}
-              >
-                {emptyState.slot || null}
-              </Empty>
+              {query.filters ? (
+                <EmptyStateBig
+                  title={t('No results match your filter criteria')}
+                  description={t('Try different criteria to display results.')}
+                  image="filter-results.svg"
+                  buttonAction={() => handleClearFilterControls()}
+                  buttonText={t('clear all filters')}
+                />
+              ) : (
+                <EmptyStateBig
+                  {...emptyState}
+                  title={emptyState?.title || t('No Data')}
+                  image={emptyState?.image || 'filter-results.svg'}
+                />
+              )}
             </EmptyWrapper>
           )}
         </div>
diff --git a/superset-frontend/src/components/ListView/utils.ts 
b/superset-frontend/src/components/ListView/utils.ts
index 346bde0982..78873f51f1 100644
--- a/superset-frontend/src/components/ListView/utils.ts
+++ b/superset-frontend/src/components/ListView/utils.ts
@@ -378,6 +378,7 @@ export function useListViewState({
     toggleAllRowsSelected,
     applyFilterValue,
     setViewMode,
+    query,
   };
 }
 
diff --git a/superset-frontend/src/views/CRUD/alert/AlertList.tsx 
b/superset-frontend/src/views/CRUD/alert/AlertList.tsx
index 2d84cb0b97..f0f9d7423b 100644
--- a/superset-frontend/src/views/CRUD/alert/AlertList.tsx
+++ b/superset-frontend/src/views/CRUD/alert/AlertList.tsx
@@ -22,7 +22,6 @@ import { useHistory } from 'react-router-dom';
 import { t, SupersetClient, makeApi, styled } from '@superset-ui/core';
 import moment from 'moment';
 import ActionsBar, { ActionProps } from 'src/components/ListView/ActionsBar';
-import Button from 'src/components/Button';
 import FacePile from 'src/components/FacePile';
 import { Tooltip } from 'src/components/Tooltip';
 import ListView, {
@@ -366,15 +365,15 @@ function AlertList({
     });
   }
 
-  const EmptyStateButton = (
-    <Button buttonStyle="primary" onClick={() => handleAlertEdit(null)}>
-      <i className="fa fa-plus" /> {title}
-    </Button>
-  );
-
   const emptyState = {
-    message: t('No %s yet', titlePlural),
-    slot: canCreate ? EmptyStateButton : null,
+    title: t('No %s yet', titlePlural),
+    image: 'filter-results.svg',
+    buttonAction: () => handleAlertEdit(null),
+    buttonText: canCreate ? (
+      <>
+        <i className="fa fa-plus" /> {title}{' '}
+      </>
+    ) : null,
   };
 
   const filters: Filters = useMemo(
diff --git a/superset-frontend/src/views/CRUD/annotation/AnnotationList.tsx 
b/superset-frontend/src/views/CRUD/annotation/AnnotationList.tsx
index c91099a6d5..a4599b9ff5 100644
--- a/superset-frontend/src/views/CRUD/annotation/AnnotationList.tsx
+++ b/superset-frontend/src/views/CRUD/annotation/AnnotationList.tsx
@@ -24,7 +24,6 @@ import moment from 'moment';
 import rison from 'rison';
 
 import ActionsBar, { ActionProps } from 'src/components/ListView/ActionsBar';
-import Button from 'src/components/Button';
 import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
 import DeleteModal from 'src/components/DeleteModal';
 import ListView, { ListViewProps } from 'src/components/ListView';
@@ -239,22 +238,17 @@ function AnnotationList({
     hasHistory = false;
   }
 
-  const EmptyStateButton = (
-    <Button
-      buttonStyle="primary"
-      onClick={() => {
-        handleAnnotationEdit(null);
-      }}
-    >
+  const emptyState = {
+    title: t('No annotation yet'),
+    image: 'filter-results.svg',
+    buttonAction: () => {
+      handleAnnotationEdit(null);
+    },
+    buttonText: (
       <>
         <i className="fa fa-plus" /> {t('Annotation')}
       </>
-    </Button>
-  );
-
-  const emptyState = {
-    message: t('No annotation yet'),
-    slot: EmptyStateButton,
+    ),
   };
 
   return (
diff --git 
a/superset-frontend/src/views/CRUD/annotationlayers/AnnotationLayersList.tsx 
b/superset-frontend/src/views/CRUD/annotationlayers/AnnotationLayersList.tsx
index b93e31d380..0265682dc7 100644
--- a/superset-frontend/src/views/CRUD/annotationlayers/AnnotationLayersList.tsx
+++ b/superset-frontend/src/views/CRUD/annotationlayers/AnnotationLayersList.tsx
@@ -32,7 +32,6 @@ import ListView, {
   Filters,
   FilterOperator,
 } from 'src/components/ListView';
-import Button from 'src/components/Button';
 import DeleteModal from 'src/components/DeleteModal';
 import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
 import AnnotationLayerModal from './AnnotationLayerModal';
@@ -311,22 +310,15 @@ function AnnotationLayersList({
     [],
   );
 
-  const EmptyStateButton = (
-    <Button
-      buttonStyle="primary"
-      onClick={() => {
-        handleAnnotationLayerEdit(null);
-      }}
-    >
+  const emptyState = {
+    title: t('No annotation layers yet'),
+    image: 'filter-results.svg',
+    buttonAction: () => handleAnnotationLayerEdit(null),
+    buttonText: (
       <>
         <i className="fa fa-plus" /> {t('Annotation layer')}
       </>
-    </Button>
-  );
-
-  const emptyState = {
-    message: t('No annotation layers yet'),
-    slot: EmptyStateButton,
+    ),
   };
 
   const onLayerAdd = (id?: number) => {

Reply via email to