Skip to content

ViewPicker - New control added for Issue 1439 #1505

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ViewPicker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './controls/viewPicker/index';
12 changes: 12 additions & 0 deletions src/common/SPEntities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,15 @@ export interface IUploadImageResult {
ServerRelativeUrl: string;
UniqueId: string;
}

export interface ISPView {
Id: string;
Title: string;
}

/**
* Defines a collection of SharePoint list views
*/
export interface ISPViews {
value: ISPView[];
}
13 changes: 13 additions & 0 deletions src/common/utilities/GeneralHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as strings from 'ControlStrings';

export const IMG_SUPPORTED_EXTENSIONS = ".gif,.jpg,.jpeg,.bmp,.dib,.tif,.tiff,.ico,.png,.jxr,.svg";

import * as _ from '@microsoft/sp-lodash-subset';
/**
* Helper with general methods to simplify some routines
*/
Expand Down Expand Up @@ -405,3 +406,15 @@ export function dateToNumber(date: string | number | Date): number {

return dateObj.getTime();
}

export const setPropertyValue = (properties: any, targetProperty: string, value: any): void => { // eslint-disable-line @typescript-eslint/no-explicit-any
if (!properties) {
return;
}
if (targetProperty.indexOf('.') === -1) { // simple prop
properties[targetProperty] = value;
}
else {
_.set(properties, targetProperty, value);
}
};
100 changes: 100 additions & 0 deletions src/controls/viewPicker/IViewPicker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { BaseComponentContext } from '@microsoft/sp-component-base';
import { ISPView } from "../../../src/common/SPEntities";
import { IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';


/**
* Enum for specifying how the views should be sorted
*/
export enum PropertyFieldViewPickerOrderBy {
Id = 1,
Title
}

export interface IViewPickerProps {
/**
* Context of the current web part
*/
context: BaseComponentContext;

/**
* If provided, additional class name to provide on the dropdown element.
*/
className?: string;

/**
* Custom Field will start to validate after users stop typing for `deferredValidationTime` milliseconds.
* Default value is 200.
*/
deferredValidationTime?: number;

/**
* Whether the property pane field is enabled or not.
*/
disabled?: boolean;

/**
* Filter views from Odata query
*/
filter?: string;

/**
* Property field label displayed on top
*/
label: string;

/**
* The List Id of the list where you want to get the views
*/
listId?: string;

/**
* Input placeholder text. Displayed until option is selected.
*/
placeholder?: string;

/**
* Specify the property on which you want to order the retrieve set of views.
*/
orderBy?: PropertyFieldViewPickerOrderBy;

/**
* Initial selected view of the control
*/
selectedView?: string;

/**
* Whether or not to show a blank option. Default false.
*/
showBlankOption?: boolean;

/**
* Defines view titles which should be excluded from the view picker control
*/
viewsToExclude?: string[];

/**
* Absolute Web Url of target site (user requires permissions)
*/
webAbsoluteUrl?: string;



/**
* Defines a onPropertyChange function to raise when the selected value changed.
* Normally this function must be always defined with the 'this.onPropertyChange'
* method of the web part object.
*/
onPropertyChange? : (newValue: string | string[]) => void;
/**
* Callback that is called before the dropdown is populated
*/
onViewsRetrieved?: (views: ISPView[]) => PromiseLike<ISPView[]> | ISPView[];

}

export interface IViewPickerState {
results: IDropdownOption[];
selectedKey?: string | string[];
errorMessage?: string | string[];
}
177 changes: 177 additions & 0 deletions src/controls/viewPicker/ViewPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import * as React from 'react';
import cloneDeep from 'lodash/cloneDeep';
import { Dropdown, IDropdownOption, IDropdownProps } from 'office-ui-fabric-react/lib/Dropdown';
import { Async } from 'office-ui-fabric-react/lib/Utilities';
import { Label } from 'office-ui-fabric-react/lib/Label';
import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner';
import * as telemetry from '../../common/telemetry';
import { ISPService } from '../../services/ISPService';
import { SPViewPickerService } from '../../services/SPViewPickerService';
import { IViewPickerProps, IViewPickerState } from './IViewPicker';
import { ISPView, ISPViews } from "../../common/SPEntities";


// Empty view value
const EMPTY_VIEW_KEY = 'NO_VIEW_SELECTED';

export class ViewPicker extends React.Component<IViewPickerProps, IViewPickerState> {
private options: IDropdownOption[] = [];
private selectedKey: string| string[] = null;
private latestValidateValue: string;
private async: Async;
private delayedValidate: (value: string) => void;


constructor(props: IViewPickerProps){
super(props);

telemetry.track('ViewPicker');
this.state = {
results: this.options
}

this.async = new Async(this);
this.validate = this.validate.bind(this);
this.onChanged = this.onChanged.bind(this);
this.notifyAfterValidate = this.notifyAfterValidate.bind(this);
this.delayedValidate = this.async.debounce(this.validate, this.props.deferredValidationTime);
}


public componentDidMount(): void {
// Start retrieving the list views
this.loadViews();
}

public componentDidUpdate(prevProps: IViewPickerProps, _prevState: IViewPickerState): void {
if (
this.props.listId !== prevProps.listId ||
this.props.webAbsoluteUrl !== prevProps.webAbsoluteUrl ||
this.props.orderBy !== prevProps.orderBy
) {
this.loadViews();
}
}

/**
* Called when the component will unmount
*/
public componentWillUnmount(): void {
if (typeof this.async !== 'undefined') {
this.async.dispose();
}
}

private loadViews(): void {

const viewService: SPViewPickerService = new SPViewPickerService(this.props, this.props.context);
const viewsToExclude: string[] = this.props.viewsToExclude || [];
this.options = [];
viewService.getViews().then((response: ISPViews) => {
// Start mapping the views that are selected
response.value.forEach((view: ISPView) => {
if (this.props.selectedView === view.Id) {
this.selectedKey = view.Id;
}

// Make sure that the current view is NOT in the 'viewsToExclude' array
if (viewsToExclude.indexOf(view.Title) === -1 && viewsToExclude.indexOf(view.Id) === -1) {
this.options.push({
key: view.Id,
text: view.Title
});
}
});

// Option to unselect the view
this.options.unshift({
key: EMPTY_VIEW_KEY,
text: EMPTY_VIEW_KEY
});

// Update the current component state
this.setState({
results: this.options,
selectedKey: this.selectedKey
});
}).catch(() => { /* no-op; */ });
}

private onChanged(event: React.FormEvent<HTMLDivElement>,option: IDropdownOption, _index?: number): void {
const newValue: string = option.key as string;
this.delayedValidate(newValue);
}

/**
* Validates the new custom field value
*/
private validate(value: string): void {
this.notifyAfterValidate(this.props.selectedView, value);
if (this.latestValidateValue === value) {
return;
}
}


/**
* Notifies the parent Web Part of a property value change
*/
private notifyAfterValidate(oldValue: string, newValue: string): void {
// Check if the user wanted to unselect the view
const propValue = newValue === EMPTY_VIEW_KEY ? '' : newValue;

// Deselect all options
this.options = this.state.results.map(option => {
if (option.selected) {
option.selected = false;
}
return option;
});
// Set the current selected key
this.selectedKey = newValue;
console.log('Selected View key :'+this.selectedKey);
// Update the state
this.setState({
selectedKey: this.selectedKey,
results: this.options
});
}

/**
* Renders the SPViewPicker controls with Office UI Fabric
*/
public render(): JSX.Element {
const { results, selectedKey } = this.state;
const {className, disabled, label, placeholder, showBlankOption} = this.props;

const options : IDropdownOption[] = results.map(v => ({
key : v.key,
text: v.text
}));

if (showBlankOption) {
// Provide empty option
options.unshift({
key: EMPTY_VIEW_KEY,
text: '',
});
}

const dropdownProps: IDropdownProps = {
className,
options,
disabled: disabled,
label,
placeholder,
onChange: this.onChanged,
};

// Renders content
return (
<>
<Dropdown {...dropdownProps} />
</>
);
}

}
2 changes: 2 additions & 0 deletions src/controls/viewPicker/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './IViewPicker';
export * from './ViewPicker';
5 changes: 5 additions & 0 deletions src/services/ISPViewPickerService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ISPViews } from "../../src/common/SPEntities";

export interface ISPViewPickerService {
getViews(): Promise<ISPViews>;
}
Loading