Skip to content

Commit

Permalink
Display tooltip for disabled menu toggle page action dropdown
Browse files Browse the repository at this point in the history
  • Loading branch information
marshmalien committed Jul 12, 2024
1 parent f19c4c2 commit 0362654
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 58 deletions.
7 changes: 2 additions & 5 deletions cypress/e2e/hub/execution-environments.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,9 @@ describe('Execution Environments', () => {
cy.get('tbody').find('tr').should('have.length', 1);
cy.get('tbody').within(() => {
cy.getByDataCy('container-repository-name-column-cell').should('contain', eeName);
cy.get('[data-cy="actions-dropdown"]')
.click()
.then(() => {
cy.get(`[data-cy="delete-execution-environment"]`).click();
});
cy.get('[data-cy="actions-dropdown"]').click();
});
cy.get(`[data-cy="delete-execution-environment"]`).click();
cy.get('[data-ouia-component-id="Permanently delete execution environments"]').within(
() => {
cy.get('[data-ouia-component-id="confirm"]').click();
Expand Down
125 changes: 76 additions & 49 deletions framework/PageActions/PageActionDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Button,
ButtonVariant,
Divider,
Dropdown,
Expand All @@ -10,7 +11,7 @@ import {
MenuToggleElement,
Tooltip,
} from '@patternfly/react-core';
import { CircleIcon, EllipsisVIcon } from '@patternfly/react-icons';
import { CircleIcon, EllipsisVIcon, CaretDownIcon } from '@patternfly/react-icons';
import { ComponentClass, FunctionComponent, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import styled from 'styled-components';
Expand Down Expand Up @@ -127,56 +128,82 @@ export function PageActionDropdown<T extends object>(props: PageActionDropdownPr
label
);

const disabledMenuToggle = (
<Button
data-cy={id}
icon={
CustomIcon ? (
<Icon>
<CustomIcon />
</Icon>
) : undefined
}
style={{
backgroundColor: '#f0f0f0',
}}
isAriaDisabled
>
{dropdownMenuLabel}
<span style={{ paddingLeft: '16px' }}>
<CaretDownIcon aria-hidden />
</span>
</Button>
);

return (
<Tooltip content={tooltipContent} trigger={tooltipContent ? undefined : 'manual'}>
<Dropdown
isOpen={dropdownOpen}
onSelect={() => setDropdownOpen(false)}
onOpenChange={(isOpen) => setDropdownOpen(isOpen)}
popperProps={{
appendTo: () => document.body,
preventOverflow: true,
enableFlip: true,
position: position,
}}
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
<MenuToggle
ref={toggleRef}
data-cy={id}
id={isKebab ? 'toggle-kebab' : 'toggle-dropdown'}
className={isKebab ? 'toggle-kebab' : 'toggle-dropdown'}
isDisabled={!!isDisabled}
aria-label={isKebab ? 'kebab dropdown toggle' : 'dropdown toggle'}
variant={isSecondary ? 'secondary' : isPrimary ? 'primary' : 'plain'}
onClick={() => setDropdownOpen(!dropdownOpen)}
isExpanded={dropdownOpen}
style={isPrimary && !label ? { color: 'var(--pf-v5-global--Color--light-100)' } : {}}
icon={
CustomIcon ? (
<Icon>
<CustomIcon />
</Icon>
) : undefined
}
>
{dropdownMenuLabel ?? <EllipsisVIcon />}
</MenuToggle>
)}
>
<DropdownList>
{actions.map((action, index) => (
<PageDropdownActionItem
key={'label' in action ? action.label : `action-${index}`}
action={action}
selectedItems={selectedItems ?? []}
selectedItem={selectedItem}
hasIcons={hasIcons}
hasSwitches={hasSwitches}
index={index}
/>
))}
</DropdownList>
</Dropdown>
{isDisabled ? (
disabledMenuToggle
) : (
<Dropdown
isOpen={dropdownOpen}
onSelect={() => setDropdownOpen(false)}
onOpenChange={(isOpen) => setDropdownOpen(isOpen)}
popperProps={{
appendTo: () => document.body,
preventOverflow: true,
enableFlip: true,
position: position,
}}
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
<MenuToggle
ref={toggleRef}
data-cy={id}
id={isKebab ? 'toggle-kebab' : 'toggle-dropdown'}
className={isKebab ? 'toggle-kebab' : 'toggle-dropdown'}
isDisabled={!!isDisabled}
aria-label={isKebab ? 'kebab dropdown toggle' : 'dropdown toggle'}
variant={isSecondary ? 'secondary' : isPrimary ? 'primary' : 'plain'}
onClick={() => setDropdownOpen(!dropdownOpen)}
isExpanded={dropdownOpen}
style={isPrimary && !label ? { color: 'var(--pf-v5-global--Color--light-100)' } : {}}
icon={
CustomIcon ? (
<Icon>
<CustomIcon />
</Icon>
) : undefined
}
>
{dropdownMenuLabel ?? <EllipsisVIcon />}
</MenuToggle>
)}
>
<DropdownList>
{actions.map((action, index) => (
<PageDropdownActionItem
key={'label' in action ? action.label : `action-${index}`}
action={action}
selectedItems={selectedItems ?? []}
selectedItem={selectedItem}
hasIcons={hasIcons}
hasSwitches={hasSwitches}
index={index}
/>
))}
</DropdownList>
</Dropdown>
)}
</Tooltip>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('Instance Groups List', () => {
});
it('Create group button is disabled if the user does not have permission to create instance groups', () => {
cy.mount(<InstanceGroups />);
cy.get('button[data-cy="create-group"]').should('have.attr', 'disabled');
cy.get('button[data-cy="create-group"]').should('have.attr', 'aria-disabled', 'true');
});
it('Delete instance group row action is disabled if the user does not have permission to edit instance groups', () => {
cy.mount(<InstanceGroups />);
Expand Down
2 changes: 1 addition & 1 deletion frontend/awx/resources/inventories/Inventories.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe('Inventories', () => {
}));
cy.mount(<Inventories />);
cy.contains('button', /^Create inventory$/).as('createButton');
cy.get('@createButton').should('have.attr', 'disabled');
cy.get('@createButton').should('have.attr', 'aria-disabled', 'true');
cy.get('@createButton').click({ force: true });
cy.hasTooltip(
/^You do not have permission to create an inventory. Please contact your organization administrator if there is an issue with your access.$/
Expand Down
2 changes: 1 addition & 1 deletion frontend/awx/resources/templates/TemplatesList.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ describe('TemplatesList', () => {

it('Create Template button is disabled if the user does not have permission to create templates', () => {
cy.mount(<TemplatesList />);
cy.contains('button', /^Create template$/).should('be.disabled');
cy.contains('button', /^Create template$/).should('have.attr', 'aria-disabled', 'true');
});

it('Should render template create form for users with proper permissions', () => {
Expand Down
48 changes: 47 additions & 1 deletion frontend/eda/access/roles/EdaRoles.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// import { ToolbarFilterType } from '../../../../framework';
// import * as useOptions from '../../../common/crud/useOptions';
import { EdaItemsResponse } from '../../common/EdaItemsResponse';
import { EdaRole } from '../../interfaces/EdaRole';
import { EdaRoles } from './EdaRoles';

/*
Expand Down Expand Up @@ -55,8 +57,22 @@ describe('EdaRoles.cy.ts', () => {
});

it('Disables edit and delete row action for built-in roles', () => {
cy.fixture('edaRoleDefinitions').then((edaRoles: EdaItemsResponse<EdaRole>) => {
const role = edaRoles.results.find((role) => role.name === 'Activation Admin');
cy.intercept(
{ method: 'GET', url: '/api/eda/v1/role_definitions/*' },
{
body: {
count: 1,
next: null,
previous: null,
page: 1,
results: [role],
},
}
);
});
cy.mountEda(<EdaRoles />);
cy.filterTableByTextFilter('name', 'Activation Admin');
cy.contains('td', 'Activation Admin')
.parent()
.within(() => {
Expand All @@ -67,6 +83,21 @@ describe('EdaRoles.cy.ts', () => {
});

it('Enables edit and delete row action for editable roles when user is superuser', () => {
cy.fixture('edaRoleDefinitions').then((edaRoles: EdaItemsResponse<EdaRole>) => {
const role = edaRoles.results.find((role) => role.name === 'View projects');
cy.intercept(
{ method: 'GET', url: '/api/eda/v1/role_definitions/*' },
{
body: {
count: 1,
next: null,
previous: null,
page: 1,
results: [role],
},
}
);
});
cy.mountEda(<EdaRoles />);
cy.contains('td', 'View projects')
.parent()
Expand All @@ -78,6 +109,21 @@ describe('EdaRoles.cy.ts', () => {
});

it('Disables edit and delete row action for editable roles when user is normal user', () => {
cy.fixture('edaRoleDefinitions').then((edaRoles: EdaItemsResponse<EdaRole>) => {
const role = edaRoles.results.find((role) => role.name === 'View projects');
cy.intercept(
{ method: 'GET', url: '/api/eda/v1/role_definitions/*' },
{
body: {
count: 1,
next: null,
previous: null,
page: 1,
results: [role],
},
}
);
});
cy.mountEda(<EdaRoles />, undefined, 'edaNormalUser.json');
cy.contains('td', 'View projects')
.parent()
Expand Down

0 comments on commit 0362654

Please sign in to comment.