Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/authz-module/authz-home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const AuthzHome = () => {
defaultActiveKey={hash ? 'permissionsRoles' : 'team'}
className="page-band bg-light-100"
>
<Tab eventKey="team" title={intl.formatMessage(messages['authz.tabs.team'])} className="page-band py-5">
<Tab eventKey="team" title={intl.formatMessage(messages['authz.tabs.team'])} className="page-band py-3">
<TeamMembersTable presetScope={presetScope} />
</Tab>
<Tab id="libraries-permissions-roles-tab" eventKey="permissionsRoles" title={intl.formatMessage(messages['authz.tabs.permissionsRoles'])} className="page-band py-5">
Expand Down
4 changes: 1 addition & 3 deletions src/authz-module/components/AuthZLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ interface AuthZLayoutProps extends AuthZTitleProps {
const AuthZLayout = ({ children, ...titleProps }: AuthZLayoutProps) => (
<div className="authz-module">
<AuthZTitle {...titleProps} />
<div className="bg-light-200">
{children}
</div>
{children}
</div>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,33 @@ describe('MultipleChoiceFilter', () => {
expect(mockOnSearchChange).toHaveBeenCalled();
expect(mockOnSearchChange).toHaveBeenLastCalledWith('test search');
});

describe('size prop', () => {
it('applies the small size to the toggle by default', () => {
renderWrapper(<MultipleChoiceFilter {...defaultProps} />);
expect(screen.getByRole('button', { name: /test filter/i })).toHaveClass('btn-sm');
});

it('applies the large size to the toggle when size is "lg"', () => {
renderWrapper(<MultipleChoiceFilter {...defaultProps} size="lg" />);
expect(screen.getByRole('button', { name: /test filter/i })).toHaveClass('btn-lg');
});

it('applies the small size to the search input by default', async () => {
const user = userEvent.setup();
renderWrapper(<MultipleChoiceFilter {...defaultProps} isSearchable onSearchChange={jest.fn()} />);
await user.click(screen.getByRole('button', { name: /test filter/i }));
expect(screen.getByRole('textbox')).toHaveClass('form-control-sm');
});

it('applies no size modifier to the search input when size is "md"', async () => {
// "md" is mapped to undefined because Paragon's Form.Control has no "md" size.
const user = userEvent.setup();
renderWrapper(<MultipleChoiceFilter {...defaultProps} size="md" isSearchable onSearchChange={jest.fn()} />);
await user.click(screen.getByRole('button', { name: /test filter/i }));
const searchInput = screen.getByRole('textbox');
expect(searchInput).not.toHaveClass('form-control-sm');
expect(searchInput).not.toHaveClass('form-control-lg');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { FilterList, Info, Search } from '@openedx/paragon/icons';
import { useState } from 'react';
import messages from '@src/authz-module/components/messages';
import { FilterChoice, MultipleChoiceFilterProps } from './types';
import { validateSizeFormControl } from '@src/authz-module/utils';

const MultipleChoiceFilter = ({
filterButtonText,
Expand All @@ -17,6 +18,7 @@ const MultipleChoiceFilter = ({
onSearchChange,
iconSrc,
disabled = false,
size = 'sm',
}: MultipleChoiceFilterProps) => {
const [searchValue, setSearchValue] = useState<string | undefined>(undefined);
const { formatMessage } = useIntl();
Expand Down Expand Up @@ -55,7 +57,7 @@ const MultipleChoiceFilter = ({

return (
<Dropdown className="no-caret-dropdown filters">
<Dropdown.Toggle variant={checkedBoxes.length > 0 ? 'primary' : 'outline-primary'}>
<Dropdown.Toggle variant={checkedBoxes.length > 0 ? 'primary' : 'outline-primary'} size={size}>
<Stack direction="horizontal" gap={2}>
{iconSrc && <Icon color="primary" src={iconSrc} />}
{filterButtonText}
Expand All @@ -76,6 +78,7 @@ const MultipleChoiceFilter = ({
onSearchChange?.(e.target.value);
}}
value={searchValue}
size={validateSizeFormControl(size)}
/>
)}
<Form.CheckboxSet
Expand Down
20 changes: 20 additions & 0 deletions src/authz-module/components/TableControlBar/SearchFilter.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,24 @@ describe('SearchFilter', () => {
renderWrapper(<SearchFilter {...defaultProps} filterValue={undefined as any} />);
expect(screen.getByRole('textbox')).toHaveValue('');
});

describe('size prop', () => {
it('applies the small size by default', () => {
renderWrapper(<SearchFilter {...defaultProps} />);
expect(screen.getByRole('textbox')).toHaveClass('form-control-sm');
});

it('applies the large size when size is "lg"', () => {
renderWrapper(<SearchFilter {...defaultProps} size="lg" />);
expect(screen.getByRole('textbox')).toHaveClass('form-control-lg');
});

it('applies no size modifier when size is "md"', () => {
// "md" is mapped to undefined because Paragon's Form.Group has no "md" size.
renderWrapper(<SearchFilter {...defaultProps} size="md" />);
const input = screen.getByRole('textbox');
expect(input).not.toHaveClass('form-control-sm');
expect(input).not.toHaveClass('form-control-lg');
});
});
});
6 changes: 4 additions & 2 deletions src/authz-module/components/TableControlBar/SearchFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@ import {
Icon,
} from '@openedx/paragon';
import { Search } from '@openedx/paragon/icons';
import { validateSizeFormControl } from '@src/authz-module/utils';

interface SearchFilterProps {
filterValue: string;
setFilter: (value: string) => void;
placeholder: string;
size?: 'sm' | 'md' | 'lg';
}

const SearchFilter = ({
filterValue, setFilter, placeholder,
filterValue, setFilter, placeholder, size = 'sm'
}: SearchFilterProps) => (
<Form.Group className="m-0">
<Form.Group className="m-0" size={validateSizeFormControl(size)}>
<Form.Control
className="mw-xs mr-0"
trailingElement={<Icon src={Search} />}
Expand Down
1 change: 1 addition & 0 deletions src/authz-module/components/TableControlBar/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export interface MultipleChoiceFilterProps {
onSearchChange?: (value: string) => void;
iconSrc?: React.ComponentType | undefined;
disabled?: boolean;
size?: 'sm' | 'md' | 'lg' | 'inline';
}
4 changes: 0 additions & 4 deletions src/authz-module/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
.authz-module {
--height-action-divider: 30px;

.pgn__data-table-wrapper {
padding: 1rem;
}

.filters .dropdown-toggle::after {
display: none !important;
}
Expand Down
4 changes: 4 additions & 0 deletions src/authz-module/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,7 @@ export const getScopeManageActionPermission = (scope: string) => {
scope,
};
};

// "md" is mapped to undefined because Paragon's Form.Control has no "md" size.
export const validateSizeFormControl = (size) =>

@dcoa dcoa Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I approved because it matches the Figma, but I don't understand what is the value of this utility?

If the idea is display all the search in small size, why just not apply sm directly to the control component?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is to keep the component flexible/reusable so in case a different size is needed we have an easy way to implement it.

size === 'md' ? undefined : size;
Loading