From c26e0113bd6036c17b266644d7df89d92c171d6c Mon Sep 17 00:00:00 2001 From: Kemal S Date: Fri, 24 May 2019 15:28:34 +0200 Subject: [PATCH] added ui styling & added item creation component --- .../src/stores/AppStore.ts | 8 +- .../src/stores/ConfigStore.ts | 10 +- .../MobxTutorialWebPart.manifest.json | 3 +- .../mobxTutorial/MobxTutorialWebPart.ts | 14 ++- .../components/DetailedFakeItemViewer.tsx | 22 ++++- .../components/FakeItemContainer.tsx | 31 ++++-- .../components/FakeItemCreator.tsx | 95 +++++++++++++++++++ .../mobxTutorial/components/ListCreator.tsx | 11 ++- .../components/MobxTutorial.module.scss | 60 ++++-------- .../mobxTutorial/components/MobxTutorial.tsx | 33 ++++--- .../components/ProgressIndicator.tsx | 2 +- .../src/webparts/mobxTutorial/loc/en-us.js | 3 +- .../webparts/mobxTutorial/loc/mystrings.d.ts | 1 + 13 files changed, 214 insertions(+), 79 deletions(-) create mode 100644 samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/FakeItemCreator.tsx diff --git a/samples/react-mobx-multiple-stores/src/stores/AppStore.ts b/samples/react-mobx-multiple-stores/src/stores/AppStore.ts index 2d9166857..15eb662d1 100644 --- a/samples/react-mobx-multiple-stores/src/stores/AppStore.ts +++ b/samples/react-mobx-multiple-stores/src/stores/AppStore.ts @@ -36,7 +36,10 @@ export class AppStore { @computed public get appStatus(): string { - return `The current status is: ${this.status}`; + let result: string = `The current status is '${this.status}'. `; + result += this.status === ApplicationStatus.CreateItems || this.items.length > 0 ? `List '${this.listTitle}' successfully created. ` : ""; + result += this.status === ApplicationStatus.Completed ? `In total there were ${this.items.length} items added. ` : ""; + return result + `${this.rootStore.configStore.allowImportantItems ? "" : "Adding important items is currently not allowed."}`; } @computed @@ -45,7 +48,7 @@ export class AppStore { } @computed - public get isLoading(): boolean { + public get isInitializing(): boolean { return this.isLoadingConfiguration || this.isLoadingOtherStuff; } @@ -54,6 +57,7 @@ export class AppStore { return new Promise((resolve, reject) => { // Mock creating list setTimeout(() => { + // Make sure we change our state in an action. runInAction(() => { this.status = ApplicationStatus.CreateItems; this.listTitle = listTitle; diff --git a/samples/react-mobx-multiple-stores/src/stores/ConfigStore.ts b/samples/react-mobx-multiple-stores/src/stores/ConfigStore.ts index df66ed3aa..a6186ff42 100644 --- a/samples/react-mobx-multiple-stores/src/stores/ConfigStore.ts +++ b/samples/react-mobx-multiple-stores/src/stores/ConfigStore.ts @@ -10,7 +10,7 @@ export class ConfigStore { constructor(private rootStore: RootStore) { this.setInitialState(); - // Mock REST call for fetching configuration data, 5 seconds + // Mock REST call for fetching configuration data setTimeout(() => { this.loadConfigration(); }, 1000); @@ -19,14 +19,13 @@ export class ConfigStore { @action public setInitialState(): void { this.isLoading = true; + this.allowImportantItems = true; this.applicationTitle = null; - this.allowImportantItems = false; } @action private loadConfigration() { this.isLoading = false; - this.allowImportantItems = true; this.rootStore.appStore.isLoadingConfiguration = false; } @@ -34,4 +33,9 @@ export class ConfigStore { public setApplicationTitle(title: string): void { this.applicationTitle = title; } + + @action + public setAllowImportantItems(allow: boolean): void { + this.allowImportantItems = allow; + } } \ No newline at end of file diff --git a/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/MobxTutorialWebPart.manifest.json b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/MobxTutorialWebPart.manifest.json index 4effb5568..27bb51e1f 100644 --- a/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/MobxTutorialWebPart.manifest.json +++ b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/MobxTutorialWebPart.manifest.json @@ -21,7 +21,8 @@ "description": { "default": "An example of a webpart using mobx for managing the application state" }, "officeFabricIconFontName": "Page", "properties": { - "ApplicationTitle": "Mobx Tutorial Title (change in webpart properties) 😎" + "ApplicationTitle": "Mobx Tutorial Title (change in webpart properties) 😎", + "AllowImportantItems": true } }] } diff --git a/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/MobxTutorialWebPart.ts b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/MobxTutorialWebPart.ts index 057eeab42..ad327cf0e 100644 --- a/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/MobxTutorialWebPart.ts +++ b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/MobxTutorialWebPart.ts @@ -1,6 +1,6 @@ import { Version } from '@microsoft/sp-core-library'; import { IPropertyPaneConfiguration, PropertyPaneTextField } from '@microsoft/sp-property-pane'; -import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base'; +import { BaseClientSideWebPart, PropertyPaneCheckbox } from '@microsoft/sp-webpart-base'; import { configure } from "mobx"; import * as strings from 'MobxTutorialWebPartStrings'; import * as React from 'react'; @@ -8,10 +8,12 @@ import * as ReactDom from 'react-dom'; import { RootStore } from '../../stores/RootStore'; import MobxTutorialProvider from './components/MobxTutorialProvider'; +// State modification should always happen through actions configure({ enforceActions: "always" }); export interface IMobxTutorialWebPartProps { ApplicationTitle: string; + AllowImportantItems: boolean; } export default class MobxTutorialWebPart extends BaseClientSideWebPart { @@ -20,6 +22,7 @@ export default class MobxTutorialWebPart extends BaseClientSideWebPart((resolve, reject) => { const { configStore } = this.dependencies.rootStore; + configStore.setAllowImportantItems(this.properties.AllowImportantItems); configStore.setApplicationTitle(this.properties.ApplicationTitle); resolve(); }); @@ -37,10 +40,14 @@ export default class MobxTutorialWebPart extends BaseClientSideWebPart { + private _columns: IColumn[]; public state = { items: [] }; + constructor(props: DetailedFakeItemViewerProps) { + super(props); + + this._columns = [ + { key: 'Title', name: 'Title', fieldName: 'title', minWidth: 100, maxWidth: 200, isResizable: true }, + { key: 'Important', name: 'Important', fieldName: 'important', minWidth: 100, maxWidth: 150, isResizable: true, onRender: (item: IFakeItem) => { return item.important ? "Yes" : "No"; } } + ]; + } + public render(): React.ReactElement { const { items } = this.props; return (
-
    {items.map(x =>
  • {x.title} {x.important ? '!IMPORTANT' : null}
  • )}
+ (items)} + columns={this._columns} + setKey="set" + layoutMode={DetailsListLayoutMode.fixedColumns} + selectionPreservedOnEmptyClick={true} + />
); } diff --git a/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/FakeItemContainer.tsx b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/FakeItemContainer.tsx index ed9adc952..55919f108 100644 --- a/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/FakeItemContainer.tsx +++ b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/FakeItemContainer.tsx @@ -4,6 +4,8 @@ import * as React from 'react'; import { AppStore } from "../../../stores/AppStore"; import { Stores } from '../../../stores/RootStore'; import { DetailedFakeItemViewer } from "./DetailedFakeItemViewer"; +import { FakeItemCreator } from "./FakeItemCreator"; +import styles from "./MobxTutorial.module.scss"; export type FakeItemContainerStoreProps = { appStore: AppStore; @@ -18,14 +20,27 @@ export class FakeItemContainer extends React.Component { const { appStore } = this.props; return ( -
- { appStore.addListItem({ title: "dsf", important: true }); }} - allowDisabledFocus={true} - /> - Count items: {appStore.items.length} | Count important items: {appStore.importantItems.length} - +
+ +
+ { appStore.confirmItems(); }} + className={styles.inputElement} + /> +
+ +
+
+ +
+ +
+

Count items: {appStore.items.length} | Count important items: {appStore.importantItems.length}

+ +
+
+
); } diff --git a/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/FakeItemCreator.tsx b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/FakeItemCreator.tsx new file mode 100644 index 000000000..16d79bd53 --- /dev/null +++ b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/FakeItemCreator.tsx @@ -0,0 +1,95 @@ +import { inject, observer } from "mobx-react"; +import { DefaultButton } from 'office-ui-fabric-react/lib/Button'; +import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox'; +import { TextField } from "office-ui-fabric-react/lib/TextField"; +import * as React from 'react'; +import { AppStore } from "../../../stores/AppStore"; +import { Stores } from '../../../stores/RootStore'; +import styles from "./MobxTutorial.module.scss"; +import { ConfigStore } from "../../../stores/ConfigStore"; + +export type FakeItemCreatorStoreProps = { + appStore: AppStore; + configStore: ConfigStore; +}; + +export type FakeItemCreatorOwnProps = { +}; + +export type FakeItemCreatorState = { + itemTitle: string; + isImportant: boolean; + requiredTitle: string; +}; + + +export type FakeItemCreatorProps = Partial & FakeItemCreatorOwnProps; + +const initialState: FakeItemCreatorState = { + itemTitle: "", + isImportant: false, + requiredTitle: undefined +}; + +@inject(Stores.AppStore, Stores.ConfigurationStore) +@observer +export class FakeItemCreator extends React.Component { + public state = initialState; + + public render(): React.ReactElement { + const { configStore } = this.props; + return ( +
+ + + + + + Add + +
+ ); + } + + private _onAddFakeItem = (): void => { + if (this.state.itemTitle === "" && this.state.itemTitle.length === 0) { + this.setState({ ...this.state, requiredTitle: "Required" }); + return; + } + + const { appStore, configStore } = this.props; + appStore.addListItem({ title: this.state.itemTitle, important: configStore.allowImportantItems && this.state.isImportant }); + this.setState(initialState); + } + + private _onChangeItemTitle = (ev: React.FormEvent, newValue?: string): void => { + if (newValue === "" && newValue.length === 0) { + this.setState({ ...this.state, itemTitle: newValue, requiredTitle: "Required" }); + } + else { + this.setState({ ...this.state, itemTitle: newValue, requiredTitle: undefined }); + } + } + + private _onIsImportantCheckboxChange = (ev: React.FormEvent, isChecked: boolean): void => { + this.setState({ ...this.state, isImportant: isChecked }); + } +} diff --git a/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/ListCreator.tsx b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/ListCreator.tsx index 1964f5b35..1a38ffadb 100644 --- a/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/ListCreator.tsx +++ b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/ListCreator.tsx @@ -5,6 +5,7 @@ import { TextField } from 'office-ui-fabric-react/lib/TextField'; import * as React from 'react'; import { AppStore } from '../../../stores/AppStore'; import { Stores } from "../../../stores/RootStore"; +import styles from './MobxTutorial.module.scss'; export type ListCreatorStoreProps = { appStore: AppStore; @@ -30,17 +31,20 @@ export class ListCreator extends React.Component); return ( -
+
this.createList()} disabled={this.state.loading} + className={styles.inputElement} > {this.state.loading ? spinner : "Create List"} @@ -51,7 +55,7 @@ export class ListCreator extends React.Component 0) { this.setState({ ...this.state, loading: true, errorMessage: undefined }); - await this.props.appStore.createList("MOCK TITLE"); + await this.props.appStore.createList(this.state.listTitle); this.setState({ ...this.state, loading: false }); } else { @@ -66,7 +70,6 @@ export class ListCreator extends React.Component { public render(): React.ReactElement { const { appStore, configStore } = this.props; - if (appStore.isLoading) - return (); + if (appStore.isInitializing) + return (); return (
-
{configStore.applicationTitle}
-
-
1) Create List
- -
+ {appStore.status === ApplicationStatus.CreateList ? +
+
1) Create List
+ +
+ : + null + } -
-
2) Create Items
- -
+ {appStore.status === ApplicationStatus.CreateItems ? +
+
2) Create Items
+ +
+ : + null + }
); diff --git a/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/ProgressIndicator.tsx b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/ProgressIndicator.tsx index 3de7bd552..3607f1fb7 100644 --- a/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/ProgressIndicator.tsx +++ b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/components/ProgressIndicator.tsx @@ -15,6 +15,6 @@ export type ProgressIndicatorProps = Partial & Prog export class ProgressIndicator extends React.Component { public render(): React.ReactElement { const { appStore } = this.props; - return (<>{appStore.appStatus}); + return (

{appStore.appStatus}

); } } diff --git a/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/loc/en-us.js b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/loc/en-us.js index f73fe7461..4979fc08c 100644 --- a/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/loc/en-us.js +++ b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/loc/en-us.js @@ -2,6 +2,7 @@ define([], function() { return { "PropertyPaneDescription": "Application configuration", "BasicGroupName": "Group Name", - "AppTitleFieldLabel": "Application Title Field" + "AppTitleFieldLabel": "Application Title Field", + "AllowImportantItemsLabel": "Allow important items" } }); \ No newline at end of file diff --git a/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/loc/mystrings.d.ts b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/loc/mystrings.d.ts index d21d26e9a..a9176a6f4 100644 --- a/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/loc/mystrings.d.ts +++ b/samples/react-mobx-multiple-stores/src/webparts/mobxTutorial/loc/mystrings.d.ts @@ -2,6 +2,7 @@ declare interface IMobxTutorialWebPartStrings { PropertyPaneDescription: string; BasicGroupName: string; AppTitleFieldLabel: string; + AllowImportantItemsLabel: string; } declare module 'MobxTutorialWebPartStrings' {