Merge pull request #10959 from AlexSCorey/10642-MisalignedTableHeaders

Fixes misalignment on template list and advanced search Key dropdown bug
This commit is contained in:
Sarah Akus 2021-08-31 13:08:46 -04:00 committed by GitHub
commit 4d1fa4d262
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 65 additions and 11 deletions

View File

@ -29,6 +29,12 @@ class Users extends Base {
});
}
readOrganizationOptions(userId, params) {
return this.http.options(`${this.baseUrl}${userId}/organizations/`, {
params,
});
}
readRoles(userId, params) {
return this.http.get(`${this.baseUrl}${userId}/roles/`, {
params,

View File

@ -68,7 +68,6 @@ export function HeaderCell({
columnIndex,
idPrefix,
className,
alignRight,
children,
}) {
const sort = sortKey
@ -83,7 +82,7 @@ export function HeaderCell({
id={sortKey ? `${idPrefix}-${sortKey}` : null}
className={className}
sort={sort}
css={alignRight ? 'text-align: right' : null}
css={children === 'Actions' ? 'text-align: right' : null}
>
{children}
</Th>

View File

@ -144,7 +144,7 @@ function CredentialList() {
<HeaderRow qsConfig={QS_CONFIG}>
<HeaderCell sortKey="name">{t`Name`}</HeaderCell>
<HeaderCell>{t`Type`}</HeaderCell>
<HeaderCell alignRight>{t`Actions`}</HeaderCell>
<HeaderCell>{t`Actions`}</HeaderCell>
</HeaderRow>
}
renderRow={(item, index) => (

View File

@ -68,7 +68,7 @@ function HostList() {
actions: results[1].data.actions,
relatedSearchableKeys: (
results[1]?.data?.related_search_fields || []
).map((val) => val.slice(0, -8)),
).map((val) => (val.endsWith('search') ? val.slice(0, -8) : val)),
searchableKeys: getSearchableKeys(results[1].data.actions?.GET),
};
}, [location]),

View File

@ -95,6 +95,7 @@ describe('<HostList />', () => {
GET: {},
POST: {},
},
related_search_fields: ['first_key__search', 'ansible_facts'],
},
});
});
@ -121,6 +122,10 @@ describe('<HostList />', () => {
});
await waitForLoaded(wrapper);
expect(
wrapper.find('PaginatedTable').props().toolbarRelatedSearchableKeys
).toStrictEqual(['first_key', 'ansible_facts']);
expect(HostsAPI.read).toHaveBeenCalled();
expect(wrapper.find('HostListItem')).toHaveLength(3);
});

View File

@ -59,7 +59,7 @@ function InventoryHostList() {
actions: hostOptions.data.actions,
relatedSearchableKeys: (
hostOptions?.data?.related_search_fields || []
).map((val) => val.slice(0, -8)),
).map((val) => (val.endsWith('search') ? val.slice(0, -8) : val)),
searchableKeys: getSearchableKeys(hostOptions.data.actions?.GET),
};
}, [id, search]),

View File

@ -91,6 +91,7 @@ describe('<InventoryHostList />', () => {
GET: {},
POST: {},
},
related_search_fields: ['first_key__search', 'ansible_facts'],
},
});
@ -123,6 +124,9 @@ describe('<InventoryHostList />', () => {
test('should fetch hosts from api and render them in the list', async () => {
expect(InventoriesAPI.readHosts).toHaveBeenCalled();
expect(wrapper.find('InventoryHostItem').length).toBe(3);
expect(
wrapper.find('PaginatedTable').props().toolbarRelatedSearchableKeys
).toStrictEqual(['first_key', 'ansible_facts']);
});
test('should render Run Commands button', async () => {

View File

@ -36,7 +36,14 @@ function InventorySourceList() {
const {
isLoading,
error: fetchError,
result: { result, sourceCount, sourceChoices, sourceChoicesOptions },
result: {
result,
sourceCount,
sourceChoices,
sourceChoicesOptions,
searchableKeys,
relatedSearchableKeys,
},
request: fetchSources,
} = useRequest(
useCallback(async () => {
@ -50,12 +57,20 @@ function InventorySourceList() {
sourceCount: results[0].data.count,
sourceChoices: results[1].data.actions.GET.source.choices,
sourceChoicesOptions: results[1].data.actions,
searchableKeys: Object.keys(results[1].data.actions?.GET || {}).filter(
(key) => results[1].data.actions?.GET[key].filterable
),
relatedSearchableKeys: (
results[1]?.data?.related_search_fields || []
).map((val) => val.slice(0, -8)),
};
}, [id, search]),
{
result: [],
sourceCount: 0,
sourceChoices: [],
searchableKeys: [],
relatedSearchableKeys: [],
}
);
@ -149,6 +164,8 @@ function InventorySourceList() {
<>
<PaginatedTable
contentError={fetchError}
toolbarSearchableKeys={searchableKeys}
toolbarRelatedSearchableKeys={relatedSearchableKeys}
hasContentLoading={
isLoading ||
isDeleteLoading ||

View File

@ -27,7 +27,10 @@ const QS_CONFIG = getQSConfig('system_job_templates', {
const buildSearchKeys = (options) => {
const actions = options?.data?.actions?.GET || {};
const searchableKeys = getSearchableKeys(actions);
const relatedSearchableKeys = options?.data?.related_search_fields || [];
const relatedSearchableKeys = (
options?.data?.related_search_fields || []
).map((val) => val.slice(0, -8));
return { searchableKeys, relatedSearchableKeys };
};

View File

@ -23,17 +23,29 @@ function UserOrganizationList() {
const { id: userId } = useParams();
const {
result: { organizations, count },
result: { organizations, count, searchableKeys, relatedSearchableKeys },
error: contentError,
isLoading,
request: fetchOrgs,
} = useRequest(
useCallback(async () => {
const params = parseQueryString(QS_CONFIG, location.search);
const {
data: { results, count: orgCount },
} = await UsersAPI.readOrganizations(userId, params);
const [
{
data: { results, count: orgCount },
},
actions,
] = await Promise.all([
UsersAPI.readOrganizations(userId, params),
UsersAPI.readOrganizationOptions(),
]);
return {
searchableKeys: Object.keys(actions.data.actions?.GET || {}).filter(
(key) => actions.data.actions?.GET[key].filterable
),
relatedSearchableKeys: (actions?.data?.related_search_fields || []).map(
(val) => val.slice(0, -8)
),
organizations: results,
count: orgCount,
};
@ -41,6 +53,8 @@ function UserOrganizationList() {
{
organizations: [],
count: 0,
searchableKeys: [],
relatedSearchableKeys: [],
}
);
@ -52,6 +66,8 @@ function UserOrganizationList() {
<PaginatedTable
items={organizations}
contentError={contentError}
toolbarSearchableKeys={searchableKeys}
toolbarRelatedSearchableKeys={relatedSearchableKeys}
hasContentLoading={isLoading}
itemCount={count}
pluralizedItemName={t`Organizations`}

View File

@ -33,6 +33,9 @@ describe('<UserOrganizationlist />', () => {
count: 1,
},
});
UsersAPI.readOrganizationOptions.mockResolvedValue({
data: { actions: { GET: {} } },
});
await act(async () => {
wrapper = mountWithContexts(
<Route
@ -69,5 +72,6 @@ describe('<UserOrganizationlist />', () => {
page_size: 20,
type: 'organization',
});
expect(UsersAPI.readOrganizationOptions).toBeCalled();
});
});