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
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) { readRoles(userId, params) {
return this.http.get(`${this.baseUrl}${userId}/roles/`, { return this.http.get(`${this.baseUrl}${userId}/roles/`, {
params, params,

View File

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

View File

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

View File

@@ -68,7 +68,7 @@ function HostList() {
actions: results[1].data.actions, actions: results[1].data.actions,
relatedSearchableKeys: ( relatedSearchableKeys: (
results[1]?.data?.related_search_fields || [] 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), searchableKeys: getSearchableKeys(results[1].data.actions?.GET),
}; };
}, [location]), }, [location]),

View File

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

View File

@@ -59,7 +59,7 @@ function InventoryHostList() {
actions: hostOptions.data.actions, actions: hostOptions.data.actions,
relatedSearchableKeys: ( relatedSearchableKeys: (
hostOptions?.data?.related_search_fields || [] 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), searchableKeys: getSearchableKeys(hostOptions.data.actions?.GET),
}; };
}, [id, search]), }, [id, search]),

View File

@@ -91,6 +91,7 @@ describe('<InventoryHostList />', () => {
GET: {}, GET: {},
POST: {}, 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 () => { test('should fetch hosts from api and render them in the list', async () => {
expect(InventoriesAPI.readHosts).toHaveBeenCalled(); expect(InventoriesAPI.readHosts).toHaveBeenCalled();
expect(wrapper.find('InventoryHostItem').length).toBe(3); 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 () => { test('should render Run Commands button', async () => {

View File

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

View File

@@ -27,7 +27,10 @@ const QS_CONFIG = getQSConfig('system_job_templates', {
const buildSearchKeys = (options) => { const buildSearchKeys = (options) => {
const actions = options?.data?.actions?.GET || {}; const actions = options?.data?.actions?.GET || {};
const searchableKeys = getSearchableKeys(actions); 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 }; return { searchableKeys, relatedSearchableKeys };
}; };

View File

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

View File

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