diff --git a/common/changes/@uifabric/experiments/pickerHeader_2018-01-17-19-36.json b/common/changes/@uifabric/experiments/pickerHeader_2018-01-17-19-36.json new file mode 100644 index 0000000000000..43ab94179b734 --- /dev/null +++ b/common/changes/@uifabric/experiments/pickerHeader_2018-01-17-19-36.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@uifabric/experiments", + "comment": "add optional title element to extendedPicker, css changes to have selected items flow on the same row as input", + "type": "minor" + } + ], + "packageName": "@uifabric/experiments", + "email": "amyngu@microsoft.com" +} \ No newline at end of file diff --git a/packages/experiments/src/components/ExtendedPicker/BaseExtendedPicker.tsx b/packages/experiments/src/components/ExtendedPicker/BaseExtendedPicker.tsx index 50e8a2bed5337..b02d495cb1d07 100644 --- a/packages/experiments/src/components/ExtendedPicker/BaseExtendedPicker.tsx +++ b/packages/experiments/src/components/ExtendedPicker/BaseExtendedPicker.tsx @@ -15,6 +15,7 @@ import * as stylesImport from './BaseExtendedPicker.scss'; import { IBaseExtendedPickerProps, IBaseExtendedPicker } from './BaseExtendedPicker.types'; import { IBaseFloatingPickerProps, BaseFloatingPicker } from '../../FloatingPicker'; import { BaseSelectedItemsList, IBaseSelectedItemsListProps } from '../../SelectedItemsList'; +import { Selection, SelectionMode, SelectionZone } from 'office-ui-fabric-react/lib/Selection'; // tslint:disable-next-line:no-any const styles: any = stylesImport; @@ -38,6 +39,7 @@ export class BaseExtendedPicker> extend protected root: HTMLElement; protected input: BaseAutoFill; protected focusZone: FocusZone; + protected selection: Selection; protected floatingPickerProps: IBaseFloatingPickerProps; protected selectedItemsListProps: IBaseSelectedItemsListProps; @@ -46,6 +48,9 @@ export class BaseExtendedPicker> extend let items: T[] = basePickerProps.selectedItems || basePickerProps.defaultSelectedItems || []; + this.selection = new Selection({ onSelectionChanged: () => this.onSelectionChange() }); + this.selection.setItems(items); + this.state = { items: items ? items : [], suggestedDisplayValue: '', @@ -91,32 +96,40 @@ export class BaseExtendedPicker> extend onKeyDown={ this.onBackspace } onCopy={ this.onCopy } > -
- { this.renderSelectedItemsList() } - { this.canAddItems() && () } -
+ + +
+ { this.props.headerComponent } + { this.renderSelectedItemsList() } + { this.canAddItems() && () } +
+
{ this.renderSuggestions() } ); } + protected onSelectionChange(): void { + this.forceUpdate(); + } + protected canAddItems(): boolean { const { items } = this.state; const { itemLimit } = this.props; @@ -217,6 +230,7 @@ export class BaseExtendedPicker> extend protected _onSuggestionSelected(item: T): void { this.selectedItemsList.addItems([item]); this.input.clear(); + this.floatingPicker.hidePicker(); } diff --git a/packages/experiments/src/components/ExtendedPicker/BaseExtendedPicker.types.ts b/packages/experiments/src/components/ExtendedPicker/BaseExtendedPicker.types.ts index de96937dd06b6..49b9a584262b8 100644 --- a/packages/experiments/src/components/ExtendedPicker/BaseExtendedPicker.types.ts +++ b/packages/experiments/src/components/ExtendedPicker/BaseExtendedPicker.types.ts @@ -18,6 +18,11 @@ export interface IBaseExtendedPicker { export interface IBaseExtendedPickerProps { componentRef?: (component?: IBaseExtendedPicker) => void; + /** + * Header/title element for the picker + */ + headerComponent?: JSX.Element; + /** * Initial items that have already been selected and should appear in the people picker. */ diff --git a/packages/experiments/src/components/ExtendedPicker/examples/ExtendedPeoplePicker.Basic.Example.tsx b/packages/experiments/src/components/ExtendedPicker/examples/ExtendedPeoplePicker.Basic.Example.tsx index 9ecc290c64d07..57cf88fb3707f 100644 --- a/packages/experiments/src/components/ExtendedPicker/examples/ExtendedPeoplePicker.Basic.Example.tsx +++ b/packages/experiments/src/components/ExtendedPicker/examples/ExtendedPeoplePicker.Basic.Example.tsx @@ -16,6 +16,7 @@ import './ExtendedPeoplePicker.Basic.Example.scss'; import { FloatingPeoplePicker, IBaseFloatingPickerProps } from '../../FloatingPicker'; import { IBaseSelectedItemsListProps, IExtendedPersonaProps, ISelectedPeopleProps, SelectedPeopleList } from '../../SelectedItemsList'; +import { Selection } from 'office-ui-fabric-react/lib/Selection'; export interface IPeoplePickerExampleState { peopleList: IPersonaProps[]; @@ -38,6 +39,7 @@ export class ExtendedPeoplePickerTypesExample extends BaseComponent<{}, IPeopleP private _picker: ExtendedPeoplePicker; private floatingPickerProps: IBaseFloatingPickerProps; private selectedItemsListProps: ISelectedPeopleProps; + private selection: Selection; constructor(props: {}) { super(props); @@ -49,6 +51,8 @@ export class ExtendedPeoplePickerTypesExample extends BaseComponent<{}, IPeopleP peopleList.push(target); }); + this.selection = new Selection({ onSelectionChanged: () => this._onSelectionChange() }); + this.state = { peopleList: peopleList, mostRecentlyUsed: mru, @@ -70,6 +74,7 @@ export class ExtendedPeoplePickerTypesExample extends BaseComponent<{}, IPeopleP onExpandGroup: this._onExpandItem, removeMenuItemText: 'Remove', copyMenuItemText: 'Copy name', + selection: this.selection }; } @@ -100,10 +105,15 @@ export class ExtendedPeoplePickerTypesExample extends BaseComponent<{}, IPeopleP 'aria-label': 'People Picker' } } componentRef={ this._setComponentRef } + headerComponent={ this._renderHeader() } /> ); } + private _renderHeader(): JSX.Element { + return
TO:
; + } + private _onRenderFloatingPicker(props: IBaseFloatingPickerProps): JSX.Element { return (); } @@ -112,6 +122,10 @@ export class ExtendedPeoplePickerTypesExample extends BaseComponent<{}, IPeopleP return (); } + private _onSelectionChange(): void { + this.forceUpdate(); + } + @autobind private _setComponentRef(component: ExtendedPeoplePicker): void { this._picker = component; diff --git a/packages/experiments/src/components/SelectedItemsList/BaseSelectedItemsList.tsx b/packages/experiments/src/components/SelectedItemsList/BaseSelectedItemsList.tsx index fe5e8c56e06ce..97c45f74aebe2 100644 --- a/packages/experiments/src/components/SelectedItemsList/BaseSelectedItemsList.tsx +++ b/packages/experiments/src/components/SelectedItemsList/BaseSelectedItemsList.tsx @@ -3,10 +3,8 @@ import { BaseComponent, KeyCodes, autobind, - css } from '../../Utilities'; -import { FocusZone, FocusZoneDirection } from 'office-ui-fabric-react/lib/FocusZone'; -import { Selection, SelectionZone, SelectionMode } from 'office-ui-fabric-react/lib/Selection'; + import { IBaseSelectedItemsList, IBaseSelectedItemsListProps, ISelectedItemProps } from './BaseSelectedItemsList.types'; export interface IBaseSelectedItemsListState { @@ -24,18 +22,12 @@ export interface IBaseSelectedItemsListState { export class BaseSelectedItemsList> extends BaseComponent implements IBaseSelectedItemsList { - protected selection: Selection; - protected root: HTMLElement; - protected focusZone: FocusZone; constructor(basePickerProps: P) { super(basePickerProps); let items: T[] = basePickerProps.selectedItems || basePickerProps.defaultSelectedItems || []; - - this.selection = new Selection({ onSelectionChanged: () => this.onSelectionChange() }); - this.selection.setItems(items); this.state = { items: items, }; @@ -56,11 +48,11 @@ export class BaseSelectedItemsList> if (processedItemPromiseLikes && processedItemPromiseLikes.then) { processedItemPromiseLikes.then((resolvedProcessedItems: T[]) => { let newItems: T[] = this.state.items.concat(resolvedProcessedItems); - this.updateSelectedItems(newItems); + this.updateItems(newItems); }); } else { let newItems: T[] = this.state.items.concat(processedItemObjects); - this.updateSelectedItems(newItems); + this.updateItems(newItems); } this.setState({ suggestedDisplayValue: '' }); } @@ -71,30 +63,30 @@ export class BaseSelectedItemsList> // tslint:disable-next-line:no-any if (index > -1) { let newItems = items.slice(0, index).concat(items.slice(index + 1)); - this.updateSelectedItems(newItems); + this.updateItems(newItems); } } @autobind public onCopy(ev: React.ClipboardEvent): void { - if (this.props.onCopyItems && this.selection.getSelectedCount() > 0) { - let selectedItems: T[] = this.selection.getSelection() as T[]; + if (this.props.onCopyItems && this.props.selection.getSelectedCount() > 0) { + let selectedItems: T[] = this.props.selection.getSelection() as T[]; this.copyItems(selectedItems); } } public unselectAll(): void { - this.selection.setAllSelected(false); + this.props.selection.setAllSelected(false); } public componentWillUpdate(newProps: P, newState: IBaseSelectedItemsListState): void { if (newState.items && newState.items !== this.state.items) { - this.selection.setItems(newState.items); + this.props.selection.setItems(newState.items); } } public componentDidMount(): void { - this.selection.setItems(this.state.items); + this.props.selection.setItems(this.state.items); } public componentWillReceiveProps(newProps: P): void { @@ -105,31 +97,8 @@ export class BaseSelectedItemsList> } } - public render(): JSX.Element { - let { className } = this.props; - - return ( -
- - -
- { this.renderItems() } -
-
-
-
- ); + public render(): JSX.Element[] { + return this.renderItems(); } @autobind @@ -143,7 +112,7 @@ export class BaseSelectedItemsList> item, index, key: item.key ? item.key : index, - selected: this.selection.isIndexSelected(index), + selected: this.props.selection.isIndexSelected(index), onRemoveItem: () => this.removeItem(item), onItemChange: this.onItemChange, removeButtonAriaLabel: removeButtonAriaLabel, @@ -151,10 +120,6 @@ export class BaseSelectedItemsList> })); } - protected onSelectionChange(): void { - this.forceUpdate(); - } - protected onChange(items?: T[]): void { if (this.props.onChange) { (this.props.onChange as (items?: T[]) => void)(items); @@ -182,7 +147,7 @@ export class BaseSelectedItemsList> let newItems: T[] = items; newItems[index] = changedItem; - this.updateSelectedItems(newItems); + this.updateItems(newItems); } } @@ -193,7 +158,7 @@ export class BaseSelectedItemsList> if (index >= 0) { let newItems: T[] = items.slice(0, index).concat(items.slice(index + 1)); - this.updateSelectedItems(newItems); + this.updateItems(newItems); } } @@ -206,15 +171,15 @@ export class BaseSelectedItemsList> let firstItemToRemove = itemsToRemove[0]; let index: number = items.indexOf(firstItemToRemove); - this.updateSelectedItems(newItems, index); + this.updateItems(newItems, index); } // This is protected because we may expect the backspace key to work differently in a different kind of picker. // This lets the subclass override it and provide it's own onBackspace. For an example see the BasePickerListBelow protected onBackspace(ev: React.KeyboardEvent): void { if (this.state.items.length) { - if (this.selection.getSelectedCount() > 0) { - this.removeItems(this.selection.getSelection()); + if (this.props.selection.getSelectedCount() > 0) { + this.removeItems(this.props.selection.getSelection()); } else { this.removeItem(this.state.items[this.state.items.length - 1]); } @@ -225,7 +190,7 @@ export class BaseSelectedItemsList> * Controls what happens whenever there is an action that impacts the selected items. * If selectedItems is provided as a property then this will act as a controlled component and it will not update it's own state. */ - protected updateSelectedItems(items: T[], focusIndex?: number): void { + protected updateItems(items: T[], focusIndex?: number): void { if (this.props.selectedItems) { // If the component is a controlled component then the controlling component will need this.onChange(items); diff --git a/packages/experiments/src/components/SelectedItemsList/BaseSelectedItemsList.types.ts b/packages/experiments/src/components/SelectedItemsList/BaseSelectedItemsList.types.ts index 2f5262bf02dd2..930e018eaeefd 100644 --- a/packages/experiments/src/components/SelectedItemsList/BaseSelectedItemsList.types.ts +++ b/packages/experiments/src/components/SelectedItemsList/BaseSelectedItemsList.types.ts @@ -1,5 +1,6 @@ import * as React from 'react'; import { IPickerItemProps, ISuggestionModel, ValidationState } from 'office-ui-fabric-react/lib/Pickers'; +import { Selection } from 'office-ui-fabric-react/lib/Selection'; export interface IBaseSelectedItemsList { /** Gets the current value of the input. */ @@ -17,6 +18,8 @@ export interface ISelectedItemProps extends IPickerItemProps { // tslint:disable-next-line:no-any export interface IBaseSelectedItemsListProps extends React.Props { componentRef?: (component?: IBaseSelectedItemsList) => void; + + selection: Selection; /** * A callback for when items are copied */ diff --git a/packages/experiments/src/components/SelectedItemsList/SelectedPeopleList/SelectedPeopleList.tsx b/packages/experiments/src/components/SelectedItemsList/SelectedPeopleList/SelectedPeopleList.tsx index 3fd25ed60c77a..07055f1731e3f 100644 --- a/packages/experiments/src/components/SelectedItemsList/SelectedPeopleList/SelectedPeopleList.tsx +++ b/packages/experiments/src/components/SelectedItemsList/SelectedPeopleList/SelectedPeopleList.tsx @@ -44,7 +44,7 @@ export class SelectedPeopleList extends BasePeopleSelectedItemsList { let filteredExpandedItems = expandedItems.filter((item: any) => items.indexOf(item) === -1); if (index > -1) { let newItems = items.slice(0, index).concat(filteredExpandedItems).concat(items.slice(index + 1)); - this.updateSelectedItems(newItems); + this.updateItems(newItems); } } @@ -59,7 +59,7 @@ export class SelectedPeopleList extends BasePeopleSelectedItemsList { item, index, key: item.key ? item.key : index, - selected: this.selection.isIndexSelected(index), + selected: this.props.selection.isIndexSelected(index), onRemoveItem: () => this.removeItem(item), onItemChange: this.onItemChange, removeButtonAriaLabel: removeButtonAriaLabel, diff --git a/packages/experiments/src/components/SelectedItemsList/examples/SelectedPeopleList.Basic.Example.tsx b/packages/experiments/src/components/SelectedItemsList/examples/SelectedPeopleList.Basic.Example.tsx index b77f6a7a7d775..1402db0f8d37b 100644 --- a/packages/experiments/src/components/SelectedItemsList/examples/SelectedPeopleList.Basic.Example.tsx +++ b/packages/experiments/src/components/SelectedItemsList/examples/SelectedPeopleList.Basic.Example.tsx @@ -8,12 +8,13 @@ import { import { PrimaryButton } from 'office-ui-fabric-react/lib/Button'; import { people, groupOne, groupTwo } from '../../ExtendedPicker'; import 'office-ui-fabric-react/lib/components/Pickers/PeoplePicker/examples/PeoplePicker.Types.Example.scss'; -import { IBaseSelectedItemsListProps } from '../BaseSelectedItemsList.types'; import { IExtendedPersonaProps, SelectedPeopleList } from '../SelectedPeopleList/SelectedPeopleList'; +import { Selection } from 'office-ui-fabric-react/lib/Selection'; -export class PeopleSelectedItemsListExample extends BaseComponent, {}> { +export class PeopleSelectedItemsListExample extends BaseComponent<{}, {}> { private _selectionList: SelectedPeopleList; private index: number; + private selection: Selection = new Selection({ onSelectionChanged: () => this._onSelectionChange() }); public render(): JSX.Element { return ( @@ -39,6 +40,7 @@ export class PeopleSelectedItemsListExample extends BaseComponent ); } @@ -65,6 +67,10 @@ export class PeopleSelectedItemsListExample extends BaseComponent {