Skip to content

Commit

Permalink
Added Mru,Favorites,Search + fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
MscrmTools committed Dec 19, 2022
1 parent 6334d91 commit cbf89aa
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 106 deletions.
10 changes: 8 additions & 2 deletions LookupToPicklist/LookupToPicklist/ControlManifest.Input.xml
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest>
<control namespace="MscrmTools" constructor="LookupToPicklist" version="0.0.3" display-name-key="Control_Display_Name_Key" description-key="Control_Description_Key" control-type="standard" preview-image="img/LookupToPicklist_preview.png">
<control namespace="MscrmTools" constructor="LookupToPicklist" version="0.0.4" display-name-key="Control_Display_Name_Key" description-key="Control_Description_Key" control-type="standard" preview-image="img/LookupToPicklist_preview.png">
<property name="lookup" display-name-key="lookup_Display_Key" description-key="lookup_Desc_Key" of-type="Lookup.Simple" usage="bound" required="true" />
<property name="dependantLookup" display-name-key="dependantLookup_Display_Key" description-key="dependantLookup_Desc_Key" of-type="Lookup.Simple" usage="bound" required="false" />
<property name="attributemask" display-name-key="attributemask_Display_Key" description-key="attributemask_Desc_Key" of-type="SingleLine.Text" usage="input" required="false" />
<property name="favorites" display-name-key="favorites_Display_Key" description-key="favorites_Desc_Key" of-type="SingleLine.Text" usage="input" required="false" />
<property name="sortByName" display-name-key="sortByName_Display_Key" description-key="sortByName_Desc_Key" of-type="Enum" default-value="0" usage="input" required="true">
<value name="Yes" display-name-key="yes_Display_Key" description-key="yes_Desc_Key">1</value>
<value name="No" display-name-key="no_Display_Key" description-key="no_Desc_Key">0</value>
</property>
<property name="addNew" display-name-key="addNew_Display_Key" description-key="addNew_Desc_Key" of-type="Enum" default-value="0" usage="input" required="true">
<property name="mruSize" display-name-key="mruSize_Display_Key" description-key="mruSize_Desc_Key" of-type="Whole.None" usage="input" default-value="3" required="false" />
<property name="addNew" display-name-key="addNew_Display_Key" description-key="addNew_Desc_Key" of-type="Enum" default-value="0" usage="input" required="true">
<value name="Yes" display-name-key="yes_Display_Key" description-key="yes_Desc_Key">1</value>
<value name="No" display-name-key="no_Display_Key" description-key="no_Desc_Key">0</value>
</property>
<property name="addSearch" display-name-key="addSearch_Display_Key" description-key="addSearch_Desc_Key" of-type="Enum" default-value="0" usage="input" required="true">
<value name="Yes" display-name-key="yes_Display_Key" description-key="yes_Desc_Key">1</value>
<value name="No" display-name-key="no_Display_Key" description-key="no_Desc_Key">0</value>
</property>
Expand Down
84 changes: 0 additions & 84 deletions LookupToPicklist/LookupToPicklist/RecordSelector.tsx

This file was deleted.

106 changes: 106 additions & 0 deletions LookupToPicklist/LookupToPicklist/SearchableDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import * as React from 'react';
import { Dropdown, DropdownMenuItemType, IDropdownOption, IDropdownProps, IDropdownStyleProps, IDropdownStyles } from '@fluentui/react/lib/Dropdown';
import { Icon } from '@fluentui/react/lib/Icon';
import { SearchBox } from '@fluentui/react/lib/SearchBox';

export const SearchableDropdown: React.FunctionComponent<IDropdownProps> = props => {
const [searchText, setSearchText] = React.useState<string>('');

return (
<Dropdown
{...props}
styles={DropdownStyle}
onDismiss={() => setSearchText('')}
selectedKey = {props.selectedKey}
options={[
...props.options.map(option => !option.disabled && option.text.toLowerCase().indexOf(searchText.toLowerCase()) > -1 || ["divider","new","FilterHeader","divider_filterHeader","mru","mru_divider1","mru_divider2","mru_divider3","records_header","favorite","mru_divider4","mru_divider5"].includes(option.key?.toString())?
option : { ...option, hidden: true }
),
]}
onRenderOption={(option): JSX.Element=>{
if(option?.data && option.data.isMenu && option.data.icon){
return (
<div>
<Icon style={iconStyles} iconName={option.data.icon} aria-hidden="true" title={option.data.icon} />
<span style={italicStyle}>{option?.text}</span>
</div>
);
}
else if(option?.data && option.data.isMru){
return (
<div>
<Icon style={iconStyles} iconName="Clock" aria-hidden="true" title={option.data.icon} />
<span>{option?.text}</span>
</div>
);
}
else if(option?.data && option.data.isFavorite){
return (
<div>
<Icon style={iconStyles} iconName="FavoriteStar" aria-hidden="true" title={option.data.icon} />
<span>{option?.text}</span>
</div>
);
}
else{

if(option?.itemType === DropdownMenuItemType.Header && option.key === "FilterHeader") {
return(
<SearchBox onChange={(ev, newValue) => setSearchText(newValue ?? "")} underlined={true} placeholder={option.data.label} />
);
}
else{
return (
<div><span>{option?.text}</span>
</div>
);
}
}
}}
/>);
}
const iconStyles = { marginRight: '8px' };
const italicStyle = { fontStyle: 'italic', align:'right' };

export const DropdownStyle = (props: IDropdownStyleProps): Partial<IDropdownStyles> => ({
...(props.disabled ? {
root: {
width: "100%"
},
title: {
color: "rgb(50, 49, 48)",
borderColor: "transparent",
backgroundColor: "transparent",
fontWeight: 600,
":hover": {
backgroundColor: "rgb(226, 226, 226)"
}
},
caretDown: {
color: "transparent"
}
}: {
root: {
width: "100%"
},
title: {
borderColor: "transparent",
fontWeight: 600,
":hover": {
borderColor: "rgb(96, 94, 92)",
fontWeight: 400
}
},
caretDown: {
color: "transparent",
":hover": {
color: "rgb(96, 94, 92)"
}
},
dropdown: {
":focus:after": {
borderColor: "transparent"
}
}
})
});
100 changes: 80 additions & 20 deletions LookupToPicklist/LookupToPicklist/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { IInputs, IOutputs } from './generated/ManifestTypes'
import * as React from 'react'
import * as ReactDom from 'react-dom'
import { RecordSelector } from './RecordSelector'
import { DropdownMenuItemType, IDropdownOption } from '@fluentui/react/lib/Dropdown'
import { SearchableDropdown } from './SearchableDropdown'

export class LookupToPicklist implements ComponentFramework.StandardControl<IInputs, IOutputs> {

Expand All @@ -18,6 +18,7 @@ export class LookupToPicklist implements ComponentFramework.StandardControl<IInp
private actionOptions: IDropdownOption[];
private currentValue?: ComponentFramework.LookupValue[];
private newlyCreatedId: string|null;
private parentId: string|null;

constructor() {
}
Expand All @@ -29,9 +30,6 @@ export class LookupToPicklist implements ComponentFramework.StandardControl<IInp

this.entityName = context.parameters.lookup.getTargetEntityType()
this.viewId = context.parameters.lookup.getViewId();

console.log("Entité: " + this.entityName + " / Vue : " + this.viewId);

var self = this;

context.utils.getEntityMetadata(this.entityName).then(metadata => {
Expand Down Expand Up @@ -61,7 +59,16 @@ console.log("Entité: " + this.entityName + " / Vue : " + this.viewId);
private retrieveRecords(){
var thisCtrl = this;

thisCtrl._context.webAPI.retrieveRecord('savedquery', this.viewId, "?$select=fetchxml,returnedtypecode").then(view =>{
let filter = "";
if(this.viewId){
filter = "?$top=1&$select=fetchxml,returnedtypecode&$filter=savedqueryid eq " + this.viewId;
}
else{
filter = "?$top=1&$select=fetchxml,returnedtypecode&$filter=returnedtypecode eq '" + this.entityName + "' and querytype eq 64";
}

thisCtrl._context.webAPI.retrieveMultipleRecords('savedquery',filter).then(result =>{
let view =result.entities[0];
var xml = view.fetchxml;
if(thisCtrl._context.parameters.dependantLookup && thisCtrl._context.parameters.dependantLookup.raw && thisCtrl._context.parameters.dependantLookup.raw.length > 0){
var dependentId = thisCtrl._context.parameters.dependantLookup.raw[0].id;
Expand Down Expand Up @@ -113,9 +120,15 @@ console.log("Entité: " + this.entityName + " / Vue : " + this.viewId);

public updateView(context: ComponentFramework.Context<IInputs>): void {
if(context.updatedProperties.includes("dependantLookup")){
this.retrieveRecords();
let newParentId = context.parameters.dependantLookup.raw.length > 0 ? context.parameters.dependantLookup.raw[0].id : null;
if(newParentId !== this.parentId){
this.parentId = newParentId;
this.currentValue = undefined;
this.notifyOutputChanged();
this.retrieveRecords();
}
}
else{
else if(context.updatedProperties.includes("lookup")){
this.renderControl(context);
}
}
Expand Down Expand Up @@ -144,8 +157,52 @@ console.log("Entité: " + this.entityName + " / Vue : " + this.viewId);
return 0;
})
}

let searchOptions = thisCtrl._context.parameters.addSearch.raw === "1" ? [
{ key: 'FilterHeader', text: '-', itemType: DropdownMenuItemType.Header, data:{label: thisCtrl._context.resources.getString("searchPlaceHolder")} },
{ key: 'divider_filterHeader', text: '-', itemType: DropdownMenuItemType.Divider }
] : [];

let options = [{key: '---', text:'---'},...this.availableOptions];
let mruOptions = [];
// @ts-ignore
let mrus = thisCtrl._context.parameters.lookup.getRecentItems();
// @ts-ignore
if(mrus?.length > 0 && context.parameters.lookup.getLookupConfiguration().isMruDisabled === false){
let maxSize = thisCtrl._context.parameters.mruSize?.raw ?? 999;

mruOptions.push({key:"mru", text:thisCtrl._context.resources.getString("recentItems"), itemType: DropdownMenuItemType.Header},{ key: 'mru_divider1', text: '-', itemType: DropdownMenuItemType.Divider });
for(let i=0;i< mrus.length && i < maxSize; i++){
mruOptions.push({key: mrus[i].objectId+"_mru", text:this.availableOptions.find(o => o.key===mrus[i].objectId)?.text ?? mrus[i].title, itemType: DropdownMenuItemType.Normal, data:{isMru:true}});
}

mruOptions.push({ key: 'mru_divider2', text: '-', itemType: DropdownMenuItemType.Divider });
}

let favoritesOptions = [];
if(thisCtrl._context.parameters.favorites.raw !== null && thisCtrl._context.parameters.favorites.raw.length > 0){
favoritesOptions.push({key:"favorite", text:thisCtrl._context.resources.getString("favorites"), itemType: DropdownMenuItemType.Header},{ key: 'mru_divider4', text: '-', itemType: DropdownMenuItemType.Divider });
let favorites = <Array<String>>JSON.parse(thisCtrl._context.parameters.favorites.raw);

for(let i of favorites){
let favOption = this.availableOptions.find(o => o.key===i);
if(favOption){
favoritesOptions.push({key: i +"_fav", text:favOption.text, itemType: DropdownMenuItemType.Normal, data:{isFavorite:true}});
}
}

favoritesOptions.push({ key: 'mru_divider5', text: '-', itemType: DropdownMenuItemType.Divider });
}

if(favoritesOptions.length > 0){
favoritesOptions.push({ key: 'records_header', text: thisCtrl.entityDisplayName, itemType: DropdownMenuItemType.Header });
favoritesOptions.push({ key: 'mru_divider3', text: '-', itemType: DropdownMenuItemType.Divider });
}
else if(mruOptions.length > 0){
mruOptions.push({ key: 'records_header', text: thisCtrl.entityDisplayName, itemType: DropdownMenuItemType.Header });
mruOptions.push({ key: 'mru_divider3', text: '-', itemType: DropdownMenuItemType.Divider });
}

let options = [...searchOptions,...mruOptions,...favoritesOptions,{key: '---', text:'---'},...this.availableOptions];
if(thisCtrl._context.parameters.addNew.raw === "1")
{
// If rights to read and create entity
Expand All @@ -155,34 +212,37 @@ console.log("Entité: " + this.entityName + " / Vue : " + this.viewId);
}
}

const recordSelector = React.createElement(RecordSelector, {
selectedRecordId: recordId,
availableOptions: options,
const recordSelector = React.createElement(SearchableDropdown, {
selectedKey: recordId,
options: options,
isDisabled: context.mode.isControlDisabled,
onChange: (selectedOption?: IDropdownOption) => {
if(selectedOption?.key === "new"){
onChange: (event: React.FormEvent<HTMLDivElement>, option?: IDropdownOption, index?: number) => {
if(option?.key === "new"){
context.navigation.openForm({
entityName : this.entityName,
useQuickCreateForm: true,
windowPosition: 2
}).then((result)=>{
if(result.savedEntityReference.length > 0){
if(result.savedEntityReference?.length > 0){
thisCtrl.newlyCreatedId = result.savedEntityReference[0].id.replace('{','').replace('}','').toLowerCase();
}
thisCtrl.retrieveRecords();
});
}
else if (typeof selectedOption === 'undefined' || selectedOption.key === '---') {
else if (typeof option === 'undefined' || option.key === '---') {
this.currentValue = undefined

this.notifyOutputChanged();
} else {
option.selected = true;
this.currentValue = [{
id: <string>selectedOption.key,
name: selectedOption.text,
id: (<string>option.key).split('_mru')[0].split('_fav')[0],
name: option.text,
entityType: this.entityName
}]
}
}];

this.notifyOutputChanged();
this.notifyOutputChanged();
}
}
})

Expand Down
Loading

0 comments on commit cbf89aa

Please sign in to comment.