-
Notifications
You must be signed in to change notification settings - Fork 358
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
471 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
app/javascript/components/AeInlineMethod/FilterNamespace.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import React, { useState, useEffect } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { | ||
TextInput, Select, SelectItem, Button, | ||
} from 'carbon-components-react'; | ||
import { Close16 } from '@carbon/icons-react'; | ||
import { noSelect } from './helper'; | ||
|
||
const FilterNamespace = ({ domains, filterOnChange }) => { | ||
const [searchText, setSearchText] = useState(''); | ||
|
||
useEffect(() => { | ||
filterOnChange({ text: searchText || noSelect }); | ||
}, [searchText]); | ||
|
||
/** Function to render the search text. */ | ||
const renderSearchText = () => ( | ||
<div className="search-wrapper"> | ||
<TextInput | ||
id="search-method" | ||
labelText={__('Search')} | ||
placeholder={__('Search with Name or Relative path')} | ||
value={searchText} | ||
onChange={(event) => setSearchText(event.target.value)} | ||
/> | ||
{ | ||
searchText && ( | ||
<Button | ||
id="clear-search-text" | ||
iconDescription={__('Clear text')} | ||
title={__('Clear text')} | ||
hasIconOnly | ||
kind="primary" | ||
onClick={() => setSearchText('')} | ||
renderIcon={Close16} | ||
size="field" | ||
/> | ||
) | ||
} | ||
</div> | ||
); | ||
|
||
/** Function to render the domain items in a drop-down list. */ | ||
const renderDomainList = () => ( | ||
<Select | ||
id="domain_id" | ||
labelText="Select a domain" | ||
defaultValue="option" | ||
onChange={(event) => filterOnChange({ selectedDomain: event.target.value })} | ||
> | ||
<SelectItem value={noSelect} text="None" /> | ||
{ | ||
domains.map((domain) => <SelectItem key={domain.id} value={domain.id} text={domain.name} />) | ||
} | ||
</Select> | ||
); | ||
|
||
return ( | ||
<div className="inline-filters"> | ||
{renderSearchText()} | ||
{domains && renderDomainList()} | ||
</div> | ||
); | ||
}; | ||
|
||
export default FilterNamespace; | ||
|
||
FilterNamespace.propTypes = { | ||
domains: PropTypes.arrayOf(PropTypes.any), | ||
filterOnChange: PropTypes.func.isRequired, | ||
}; | ||
|
||
FilterNamespace.defaultProps = { | ||
domains: undefined, | ||
}; |
97 changes: 97 additions & 0 deletions
97
app/javascript/components/AeInlineMethod/NamespaceSelector.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import React, { useState, useEffect } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Loading } from 'carbon-components-react'; | ||
import FilterNamespace from './FilterNamespace'; | ||
import MiqDataTable from '../miq-data-table'; | ||
import NotificationMessage from '../notification-message'; | ||
import { CellAction } from '../miq-data-table/helper'; | ||
import { | ||
methodSelectorHeaders, formatMethods, searchUrl, namespaceUrls, | ||
} from './helper'; | ||
import './style.scss'; | ||
|
||
/** Component to search and select AeMethods. */ | ||
const NamespaceSelector = ({ onSelectMethod, selectedIds }) => { | ||
const [data, setData] = useState({ | ||
domains: [], | ||
methods: [], | ||
loading: true, | ||
searchText: undefined, | ||
selectedDomain: undefined, | ||
}); | ||
|
||
/** Loads the 'domains' and 'methods' from its respective URL's during the component's onLoad event. */ | ||
useEffect(() => { | ||
Promise.all([ | ||
http.get(namespaceUrls.aeDomainsUrl), | ||
http.get(namespaceUrls.aeMethodsUrl)]) | ||
.then(([{ domains }, { methods }]) => { | ||
setData({ | ||
...data, | ||
domains, | ||
loading: false, | ||
methods: formatMethods(methods), | ||
}); | ||
}); | ||
}, []); | ||
|
||
/** Function to handle search text and drop-down item onchange events. */ | ||
const handleFilterOnChange = (filterData) => { | ||
const text = filterData.text ? filterData.text : data.searchText; | ||
const selectedDomain = filterData.selectedDomain ? filterData.selectedDomain : data.selectedDomain; | ||
const url = searchUrl(selectedDomain, text); | ||
http.get(url) | ||
.then(({ methods }) => { | ||
setData({ | ||
...data, | ||
selectedDomain, | ||
searchText: text, | ||
methods: formatMethods(methods), | ||
}); | ||
}); | ||
}; | ||
|
||
/** Function to handle the click events for the list. */ | ||
const onCellClickHandler = (selectedRow, cellType, checked) => { | ||
const selectedItems = cellType === CellAction.selectAll | ||
? data.methods.map((item) => item.id) | ||
: [selectedRow]; | ||
onSelectMethod({ selectedItems, cellType, checked }); | ||
}; | ||
|
||
/** Function to render the contents of the list. */ | ||
const renderContents = () => (data.methods && data.methods.length > 0 | ||
? ( | ||
<MiqDataTable | ||
headers={methodSelectorHeaders} | ||
stickyHeader | ||
rows={data.methods} | ||
mode="miq-inline-method-list" | ||
rowCheckBox | ||
sortable={false} | ||
gridChecks={selectedIds} | ||
onCellClick={(selectedRow, cellType, event) => onCellClickHandler(selectedRow, cellType, event.target.checked)} | ||
/> | ||
) | ||
: <NotificationMessage type="error" message={__('No methods available.')} />); | ||
|
||
return ( | ||
<div className="inline-method-selector"> | ||
<FilterNamespace domains={data.domains} filterOnChange={(filterData) => handleFilterOnChange(filterData)} /> | ||
<div className="inline-contents-wrapper"> | ||
{ | ||
data.loading | ||
? <Loading active small withOverlay={false} className="loading" /> | ||
: renderContents() | ||
} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default NamespaceSelector; | ||
|
||
NamespaceSelector.propTypes = { | ||
onSelectMethod: PropTypes.func.isRequired, | ||
selectedIds: PropTypes.arrayOf(PropTypes.any).isRequired, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
export const namespaceUrls = { | ||
aeMethodsUrl: '/miq_ae_class/ae_methods', | ||
aeDomainsUrl: '/miq_ae_class/ae_domains', | ||
}; | ||
|
||
export const noSelect = 'NONE'; | ||
|
||
/** Headers needed for the data-table list. */ | ||
export const methodSelectorHeaders = [ | ||
{ | ||
key: 'name', | ||
header: 'Name', | ||
}, | ||
{ | ||
key: 'path', | ||
header: 'Relative path', | ||
}, | ||
]; | ||
|
||
export const methodListHeaders = [ | ||
...methodSelectorHeaders, | ||
{ key: 'delete', header: __('Delete') }, | ||
]; | ||
|
||
/** Function to format the method data needed for the data-table list. */ | ||
export const formatMethods = (methods) => (methods.map((item) => ({ | ||
id: item.id.toString(), | ||
name: { text: item.name, icon: 'icon node-icon fa-ruby' }, | ||
path: item.relative_path, | ||
}))); | ||
|
||
const deleteMethodButton = () => ({ | ||
is_button: true, | ||
title: __('Delete'), | ||
text: __('Delete'), | ||
alt: __('Delete'), | ||
kind: 'ghost', | ||
callback: 'removeMethod', | ||
}); | ||
|
||
export const formatListMethods = (methods) => (methods.map((item, index) => ({ | ||
id: item.id.toString(), | ||
name: { text: item.name, icon: 'icon node-icon fa-ruby' }, | ||
path: item.relative_path, | ||
delete: deleteMethodButton(item, index), | ||
}))); | ||
|
||
/** Function to return a conditional URL based on the selected filters. */ | ||
export const searchUrl = (selectedDomain, text) => { | ||
const queryParams = []; | ||
if (selectedDomain && selectedDomain !== noSelect) { | ||
queryParams.push(`domain_id=${selectedDomain}`); | ||
} | ||
if (text && text !== noSelect) { | ||
queryParams.push(`search=${text}`); | ||
} | ||
const queryString = queryParams.length > 0 ? `?${queryParams.join('&')}` : ''; | ||
return `${namespaceUrls.aeMethodsUrl}${queryString}`; | ||
}; |
Oops, something went wrong.