Skip to content

Commit

Permalink
Created control to trigger on key press
Browse files Browse the repository at this point in the history
  • Loading branch information
Victor Sanchez Olaya committed Apr 15, 2020
0 parents commit 1fbc36b
Show file tree
Hide file tree
Showing 15 changed files with 7,097 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules

# generated directory
**/generated

# output directory
/out

# msbuild output directories
/bin
/obj
45 changes: 45 additions & 0 deletions OnKeyPress.pcfproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PowerAppsTargetsPath>$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\PowerApps</PowerAppsTargetsPath>
</PropertyGroup>

<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<Import Project="$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Pcf.props" Condition="Exists('$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Pcf.props')"/>

<PropertyGroup>
<Name>OnKeyPress</Name>
<ProjectGuid>c03f341e-b9ec-4f9e-874e-bcd0afa1b4d8</ProjectGuid>
<OutputPath>$(MSBuildThisFileDirectory)out\controls</OutputPath>
</PropertyGroup>

<PropertyGroup>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<!--Remove TargetFramework when this is available in 16.1-->
<TargetFramework>net462</TargetFramework>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.PowerApps.MSBuild.Pcf" Version="1.*"/>
</ItemGroup>

<ItemGroup>
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\.gitignore"/>
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\bin\**"/>
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\obj\**"/>
<ExcludeDirectories Include="$(OutputPath)\**"/>
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\*.pcfproj"/>
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\*.pcfproj.user"/>
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\*.sln"/>
<ExcludeDirectories Include="$(MSBuildThisFileDirectory)\node_modules\**"/>
</ItemGroup>

<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)\**" Exclude="@(ExcludeDirectories)"/>
</ItemGroup>

<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" />
<Import Project="$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Pcf.targets" Condition="Exists('$(PowerAppsTargetsPath)\Microsoft.PowerApps.VisualStudio.Pcf.targets')"/>

</Project>
28 changes: 28 additions & 0 deletions OnKeyPress/ControlManifest.Input.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8" ?>
<manifest>
<control namespace="VictorSanchez" constructor="OnKeyPress" version="0.0.1" display-name-key="On Key Press" description-key="Trigger on change of the field when on key press" control-type="standard">
<!-- property node identifies a specific, configurable piece of data that the control expects from CDS -->
<property name="field" display-name-key="Field" description-key="Field to be attach the event" of-type-group="all" usage="bound" required="true" />
<type-group name="all">
<type>SingleLine.Text</type>
<type>SingleLine.Email</type>
<type>SingleLine.URL</type>
<type>SingleLine.Phone</type>
<type>SingleLine.Ticker</type>
<type>Currency</type>
<type>DateAndTime.DateAndTime</type>
<type>DateAndTime.DateOnly</type>
<type>Decimal</type>
<type>Enum</type>
<type>FP</type>
<type>Multiple</type>
<type>OptionSet</type>
<type>TwoOptions</type>
<type>Whole.None</type>
</type-group>
<resources>
<code path="index.ts" order="1"/>
</resources>

</control>
</manifest>
6 changes: 6 additions & 0 deletions OnKeyPress/IOnKeyPressProps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface IOnKeyPress {
userInputChanged: (newValue: string) => void,
inputValue?: string,
isControlDisabled: boolean,
isControlVisible: boolean,
}
47 changes: 47 additions & 0 deletions OnKeyPress/OnKeyPress.styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ITextFieldStyleProps, ITextFieldStyles } from 'office-ui-fabric-react/lib/TextField';
export const textFieldStyles = (props: ITextFieldStyleProps): Partial<ITextFieldStyles> => ({
fieldGroup: {
border: props.focused ? "1px solid black !important" : "1px solid transparent !important",
backgroundColor: 'transparent !important',
height: '33px',
selectors: {
":after": {
border: "none"
},
":hover": {
border: props.disabled ? "1px solid rgb(226, 226, 226) !important" : "1px solid black !important",
backgroundColor: props.disabled ? "rgb(226, 226, 226) !important" : 'transparent'
}
}
},
field: {
height: '33px',
fontWeight: props.focused ? 400 : 600,
fontFamily: "SegoeUI,'Segoe UI'",
color: props.disabled ? 'rgb(51, 51, 51) !important' : 'black !important',
backgroundColor: 'transparent !important',
selectors: {
":hover": {
fontWeight: props.disabled ? 600 : 400,
},
'::placeholder': {
fontFamily: "SegoeUI,'Segoe UI'",
fontSize: '14px',
fontWeight: 600,
color: 'black !important'
},
'hover::placeholder': {
fontWeight: 400
}
}
},
root: {
flex: 1,
backgroundColor: 'transparent !important',
height: '35px',
},
wrapper: {
backgroundColor: 'transparent !important',
height: '35px',
}
});
47 changes: 47 additions & 0 deletions OnKeyPress/OnKeyPress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as React from "react";

import { Stack, TextField, initializeIcons } from 'office-ui-fabric-react/lib';
import { IOnKeyPress } from './IOnKeyPressProps'
import { textFieldStyles } from './OnKeyPress.styles'
export class OnKeyPressCode extends React.Component<IOnKeyPress> {
private _textFieldValue: string;

constructor(props: IOnKeyPress) {
super(props);
initializeIcons();
this._textFieldValue = this.props.inputValue || "";
}
public componentWillReceiveProps(newProps: IOnKeyPress): void {
this.setState(newProps);
}

public render(): JSX.Element {
let textField = this.props.isControlVisible ? <TextField
placeholder="---"
onChange={this.userInputOnChange}
autoComplete="off"
styles={textFieldStyles}
value={this._textFieldValue}
disabled={this.props.isControlDisabled}
/> : "";
return (
<div>
<Stack style={{ flexDirection: 'row' }}>
{textField}
</Stack>
</div>
);
}

/**
* Everytime is triggered the onKeyUp it will trigger this functionality
*/
private userInputOnChange = async (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>): Promise<any> => {
// Get the target
const target = event.target as HTMLTextAreaElement;
//Set the value of our textfield to the input
this._textFieldValue = target.value;
this.setState((prevState: IOnKeyPress): IOnKeyPress => prevState);
this.props.userInputChanged(this._textFieldValue);
};
}
88 changes: 88 additions & 0 deletions OnKeyPress/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { IInputs, IOutputs } from "./generated/ManifestTypes";
import * as React from "react";
import * as ReactDOM from "react-dom";
import { IOnKeyPress } from './IOnKeyPressProps'
import { OnKeyPressCode } from './OnKeyPress'
export class OnKeyPress implements ComponentFramework.StandardControl<IInputs, IOutputs> {
// Reference to ComponentFramework Context object
private _context: ComponentFramework.Context<IInputs>;
// reference to the notifyOutputChanged method
private _notifyOutputChanged: () => void;
// reference to the container div
private _container: HTMLDivElement;
private _value: string;

private props: IOnKeyPress = {
isControlDisabled: false,
isControlVisible: false,
userInputChanged: this.userInputChanged.bind(this),
inputValue: "",
}
/**
* Empty constructor.
*/
constructor() { }

/**
* Used to initialize the control instance. Controls can kick off remote server calls and other initialization actions here.
* Data-set values are not initialized here, use updateView.
* @param context The entire property bag available to control via Context Object; It contains values as set up by the customizer mapped to property names defined in the manifest, as well as utility functions.
* @param notifyOutputChanged A callback method to alert the framework that the control has new outputs ready to be retrieved asynchronously.
* @param state A piece of data that persists in one session for a single user. Can be set at any point in a controls life cycle by calling 'setControlState' in the Mode interface.
* @param container If a control is marked control-type='standard', it will receive an empty div element within which it can render its content.
*/
public async init(
context: ComponentFramework.Context<IInputs>,
notifyOutputChanged: () => void,
state: ComponentFramework.Dictionary,
container: HTMLDivElement) {

this._context = context;
this._container = container;
this._notifyOutputChanged = notifyOutputChanged;
this.props.inputValue = this._context.parameters.field.raw || "";
}

/**
* Called when any value in the property bag has changed. This includes field values, data-sets, global values such as container height and width, offline status, control metadata values such as label, visible, etc.
* @param context The entire property bag available to control via Context Object; It contains values as set up by the customizer mapped to names defined in the manifest, as well as utility functions
*/
public updateView(context: ComponentFramework.Context<IInputs>): void {
// Add code to update control view
this.props.isControlDisabled = context.mode.isControlDisabled;
this.props.isControlVisible = context.mode.isVisible;
var visible = context.mode.isVisible
ReactDOM.render(
React.createElement(
OnKeyPressCode,
this.props
)
,
this._container
);
}

/**
* It is called by the framework prior to a control receiving new data.
* @returns an object based on nomenclature defined in manifest, expecting object[s] for property marked as “bound” or “output”
*/
public getOutputs(): IOutputs {
return {
field: this._value
};
}

/**
* Called when the control is to be removed from the DOM tree. Controls should use this call for cleanup.
* i.e. cancelling any pending remote calls, removing listeners, etc.
*/
public destroy(): void {
// Add code to cleanup control if necessary
ReactDOM.unmountComponentAtNode(this._container);
}

private userInputChanged(newValue: string) {
this._value = newValue;
this._notifyOutputChanged();
}
}
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# On Key Press control

## Description

This control will fill the gap the [addOnKeyPress](https://docs.microsoft.com/en-us/previous-versions/dynamicscrm-2016/developers-guide/gg334266(v=crm.8)#addonkeypress) has left.
It has been created to trigger the onChange of the field in each key press.


## Download

## How to configure

1. Add the control to the field that needs the onChange
2. The control will trigger the event onChange of the field in the onKeyPress

## Example of configuration

Form configuration
![](examples/configuration.png)


On Change configuration
![](examples/onchange.png)


Example working
![](examples/example.gif)

Binary file added examples/configuration.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/example.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/onchange.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 1fbc36b

Please sign in to comment.