feat(dropdown accessibility): Wrap dropdown triggers with buttons for accessibility (#32189)
This commit is contained in:
parent
a78968c68e
commit
60bbd72028
|
|
@ -873,7 +873,9 @@ const SqlEditor: FC<Props> = ({
|
||||||
dropdownRender={() => renderDropdown()}
|
dropdownRender={() => renderDropdown()}
|
||||||
trigger={['click']}
|
trigger={['click']}
|
||||||
>
|
>
|
||||||
<Icons.MoreHoriz iconColor={theme.colors.grayscale.base} />
|
<Button buttonSize="xsmall" type="link" showMarginRight={false}>
|
||||||
|
<Icons.MoreHoriz iconColor={theme.colors.grayscale.base} />
|
||||||
|
</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import {
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
import Icons from 'src/components/Icons';
|
import Icons from 'src/components/Icons';
|
||||||
import type { SqlLabRootState } from 'src/SqlLab/types';
|
import type { SqlLabRootState } from 'src/SqlLab/types';
|
||||||
import { Skeleton, AntdBreadcrumb as Breadcrumb } from 'src/components';
|
import { Skeleton, AntdBreadcrumb as Breadcrumb, Button } from 'src/components';
|
||||||
import { Dropdown } from 'src/components/Dropdown';
|
import { Dropdown } from 'src/components/Dropdown';
|
||||||
import FilterableTable from 'src/components/FilterableTable';
|
import FilterableTable from 'src/components/FilterableTable';
|
||||||
import Tabs from 'src/components/Tabs';
|
import Tabs from 'src/components/Tabs';
|
||||||
|
|
@ -324,11 +324,13 @@ const TablePreview: FC<Props> = ({ dbId, catalog, schema, tableName }) => {
|
||||||
)}
|
)}
|
||||||
trigger={['click']}
|
trigger={['click']}
|
||||||
>
|
>
|
||||||
<Icons.DownSquareOutlined
|
<Button buttonSize="xsmall" type="link">
|
||||||
iconSize="m"
|
<Icons.DownSquareOutlined
|
||||||
style={{ marginTop: 2, marginLeft: 4 }}
|
iconSize="m"
|
||||||
aria-label={t('Table actions')}
|
style={{ marginTop: 2, marginLeft: 4 }}
|
||||||
/>
|
aria-label={t('Table actions')}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</Title>
|
</Title>
|
||||||
{isMetadataRefreshing ? (
|
{isMetadataRefreshing ? (
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import { Menu } from 'src/components/Menu';
|
||||||
import FaveStar from 'src/components/FaveStar';
|
import FaveStar from 'src/components/FaveStar';
|
||||||
import FacePile from 'src/components/FacePile';
|
import FacePile from 'src/components/FacePile';
|
||||||
import { handleChartDelete, CardStyles } from 'src/views/CRUD/utils';
|
import { handleChartDelete, CardStyles } from 'src/views/CRUD/utils';
|
||||||
|
import Button from 'src/components/Button';
|
||||||
|
|
||||||
interface ChartCardProps {
|
interface ChartCardProps {
|
||||||
chart: Chart;
|
chart: Chart;
|
||||||
|
|
@ -172,8 +173,10 @@ export default function ChartCard({
|
||||||
isStarred={favoriteStatus}
|
isStarred={favoriteStatus}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Dropdown dropdownRender={() => menu}>
|
<Dropdown dropdownRender={() => menu} trigger={['click', 'hover']}>
|
||||||
<Icons.MoreVert iconColor={theme.colors.grayscale.base} />
|
<Button buttonSize="xsmall" type="link">
|
||||||
|
<Icons.MoreVert iconColor={theme.colors.grayscale.base} />
|
||||||
|
</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</ListViewCard.Actions>
|
</ListViewCard.Actions>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import { PublishedLabel } from 'src/components/Label';
|
||||||
import FacePile from 'src/components/FacePile';
|
import FacePile from 'src/components/FacePile';
|
||||||
import FaveStar from 'src/components/FaveStar';
|
import FaveStar from 'src/components/FaveStar';
|
||||||
import { Dashboard } from 'src/views/CRUD/types';
|
import { Dashboard } from 'src/views/CRUD/types';
|
||||||
|
import { Button } from 'src/components';
|
||||||
|
|
||||||
interface DashboardCardProps {
|
interface DashboardCardProps {
|
||||||
isChart?: boolean;
|
isChart?: boolean;
|
||||||
|
|
@ -179,8 +180,10 @@ function DashboardCard({
|
||||||
isStarred={favoriteStatus}
|
isStarred={favoriteStatus}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Dropdown dropdownRender={() => menu}>
|
<Dropdown dropdownRender={() => menu} trigger={['hover', 'click']}>
|
||||||
<Icons.MoreVert iconColor={theme.colors.grayscale.base} />
|
<Button buttonSize="xsmall" type="link">
|
||||||
|
<Icons.MoreVert iconColor={theme.colors.grayscale.base} />
|
||||||
|
</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</ListViewCard.Actions>
|
</ListViewCard.Actions>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ describe('SavedQueries', () => {
|
||||||
it('renders a submenu with clickable tables and buttons', async () => {
|
it('renders a submenu with clickable tables and buttons', async () => {
|
||||||
expect(wrapper.find(SubMenu)).toExist();
|
expect(wrapper.find(SubMenu)).toExist();
|
||||||
expect(wrapper.find('[role="tab"]')).toHaveLength(1);
|
expect(wrapper.find('[role="tab"]')).toHaveLength(1);
|
||||||
expect(wrapper.find('button')).toHaveLength(2);
|
expect(wrapper.find('button')).toHaveLength(5);
|
||||||
clickTab(0);
|
clickTab(0);
|
||||||
await waitForComponentToPaint(wrapper);
|
await waitForComponentToPaint(wrapper);
|
||||||
expect(fetchMock.calls(/saved_query\/\?q/)).toHaveLength(2);
|
expect(fetchMock.calls(/saved_query\/\?q/)).toHaveLength(2);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { styled, SupersetClient, t, useTheme } from '@superset-ui/core';
|
import { styled, SupersetClient, t, useTheme } from '@superset-ui/core';
|
||||||
import SyntaxHighlighter from 'react-syntax-highlighter/dist/cjs/light';
|
import SyntaxHighlighter from 'react-syntax-highlighter/dist/cjs/light';
|
||||||
|
|
@ -39,6 +39,7 @@ import {
|
||||||
PAGE_SIZE,
|
PAGE_SIZE,
|
||||||
shortenSQL,
|
shortenSQL,
|
||||||
} from 'src/views/CRUD/utils';
|
} from 'src/views/CRUD/utils';
|
||||||
|
import { Button } from 'src/components';
|
||||||
import SubMenu from './SubMenu';
|
import SubMenu from './SubMenu';
|
||||||
import EmptyState from './EmptyState';
|
import EmptyState from './EmptyState';
|
||||||
import { WelcomeTable } from './types';
|
import { WelcomeTable } from './types';
|
||||||
|
|
@ -191,33 +192,36 @@ const SavedQueries = ({
|
||||||
filters: getFilterValues(tab, WelcomeTable.SavedQueries, user),
|
filters: getFilterValues(tab, WelcomeTable.SavedQueries, user),
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderMenu = (query: Query) => (
|
const renderMenu = useCallback(
|
||||||
<Menu>
|
(query: Query) => (
|
||||||
{canEdit && (
|
<Menu>
|
||||||
<Menu.Item>
|
{canEdit && (
|
||||||
<Link to={`/sqllab?savedQueryId=${query.id}`}>{t('Edit')}</Link>
|
<Menu.Item>
|
||||||
</Menu.Item>
|
<Link to={`/sqllab?savedQueryId=${query.id}`}>{t('Edit')}</Link>
|
||||||
)}
|
</Menu.Item>
|
||||||
<Menu.Item
|
)}
|
||||||
onClick={() => {
|
|
||||||
if (query.id) {
|
|
||||||
copyQueryLink(query.id, addDangerToast, addSuccessToast);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('Share')}
|
|
||||||
</Menu.Item>
|
|
||||||
{canDelete && (
|
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setQueryDeleteModal(true);
|
if (query.id) {
|
||||||
setCurrentlyEdited(query);
|
copyQueryLink(query.id, addDangerToast, addSuccessToast);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('Delete')}
|
{t('Share')}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
)}
|
{canDelete && (
|
||||||
</Menu>
|
<Menu.Item
|
||||||
|
onClick={() => {
|
||||||
|
setQueryDeleteModal(true);
|
||||||
|
setCurrentlyEdited(query);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Delete')}
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
</Menu>
|
||||||
|
),
|
||||||
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (loading) return <LoadingCards cover={showThumbnails} />;
|
if (loading) return <LoadingCards cover={showThumbnails} />;
|
||||||
|
|
@ -315,10 +319,15 @@ const SavedQueries = ({
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Dropdown dropdownRender={() => renderMenu(q)}>
|
<Dropdown
|
||||||
<Icons.MoreVert
|
dropdownRender={() => renderMenu(q)}
|
||||||
iconColor={theme.colors.grayscale.base}
|
trigger={['click', 'hover']}
|
||||||
/>
|
>
|
||||||
|
<Button buttonSize="xsmall" type="link">
|
||||||
|
<Icons.MoreVert
|
||||||
|
iconColor={theme.colors.grayscale.base}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</ListViewCard.Actions>
|
</ListViewCard.Actions>
|
||||||
</QueryData>
|
</QueryData>
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import ListViewCard from 'src/components/ListViewCard';
|
||||||
import Icons from 'src/components/Icons';
|
import Icons from 'src/components/Icons';
|
||||||
import { Tag } from 'src/views/CRUD/types';
|
import { Tag } from 'src/views/CRUD/types';
|
||||||
import { deleteTags } from 'src/features/tags/tags';
|
import { deleteTags } from 'src/features/tags/tags';
|
||||||
|
import { Button } from 'src/components';
|
||||||
|
|
||||||
interface TagCardProps {
|
interface TagCardProps {
|
||||||
tag: Tag;
|
tag: Tag;
|
||||||
|
|
@ -108,8 +109,10 @@ function TagCard({
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Dropdown dropdownRender={() => menu}>
|
<Dropdown dropdownRender={() => menu} trigger={['click', 'hover']}>
|
||||||
<Icons.MoreVert iconColor={theme.colors.grayscale.base} />
|
<Button buttonSize="xsmall" type="link">
|
||||||
|
<Icons.MoreVert iconColor={theme.colors.grayscale.base} />
|
||||||
|
</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</ListViewCard.Actions>
|
</ListViewCard.Actions>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue