diff --git a/samples/react-accordion/.editorconfig b/samples/react-accordion/.editorconfig new file mode 100644 index 000000000..8ffcdc4ec --- /dev/null +++ b/samples/react-accordion/.editorconfig @@ -0,0 +1,25 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] + +# change these settings to your own preference +indent_style = space +indent_size = 2 + +# we recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[{package,bower}.json] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/samples/react-accordion/.gitignore b/samples/react-accordion/.gitignore new file mode 100644 index 000000000..b19bbe123 --- /dev/null +++ b/samples/react-accordion/.gitignore @@ -0,0 +1,32 @@ +# Logs +logs +*.log +npm-debug.log* + +# Dependency directories +node_modules + +# Build generated files +dist +lib +solution +temp +*.sppkg + +# Coverage directory used by tools like istanbul +coverage + +# OSX +.DS_Store + +# Visual Studio files +.ntvs_analysis.dat +.vs +bin +obj + +# Resx Generated Code +*.resx.ts + +# Styles Generated Code +*.scss.ts diff --git a/samples/react-accordion/.yo-rc.json b/samples/react-accordion/.yo-rc.json new file mode 100644 index 000000000..d4eb46a46 --- /dev/null +++ b/samples/react-accordion/.yo-rc.json @@ -0,0 +1,11 @@ +{ + "@microsoft/generator-sharepoint": { + "isCreatingSolution": true, + "environment": "spo", + "version": "1.5.1", + "libraryName": "react-accordion", + "libraryId": "6d6cf05b-cfe5-4d12-af19-19ec3aedcaf9", + "packageManager": "npm", + "componentType": "webpart" + } +} \ No newline at end of file diff --git a/samples/react-accordion/README.md b/samples/react-accordion/README.md new file mode 100644 index 000000000..f43949517 --- /dev/null +++ b/samples/react-accordion/README.md @@ -0,0 +1,72 @@ +## Using React Accordion plugin with SPFx + +## Summary + +This is a sample web Part that illustrates the use of React Accessible Accordion plugin for building SharePoint Framework client-side web parts to show SharePoint list data in Accordion format. + +![Sample Web Part built using SPFx with React Framework showing list data in accordion format](https://i.stack.imgur.com/QsZ6o.png) + +## Used SharePoint Framework Version +![drop](https://img.shields.io/badge/drop-1.5.1-green.svg) + +## Applies to + +* [SharePoint Framework Developer Preview](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview) +* [Office 365 developer tenant](http://dev.office.com/sharepoint/docs/spfx/set-up-your-developer-tenant) + +## Solution + +Solution|Author(s) +--------|--------- +react-accordion | Gautam Sheth (SharePoint Consultant, RapidCircle) + +## Version history + +Version|Date|Comments +-------|----|-------- +1.0|August 17, 2018|Initial release + +## Disclaimer +**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.** + +--- + +## Minimal Path to Awesome + +- clone this repo +- in the command line run: + - `npm i` + - `gulp serve --nobrowser` +- in your SharePoint Site create a custom list named FAQ +- in the FAQ list, create a column Description(internal name) of type Enhanced rich text +- add some list items with Title and Description values + +- navigate to the hosted version of SharePoint workbench, eg. **https://contoso.sharepoint.com/sites/test/_layouts/15/workbench.aspx** +- add the Web Part to canvas and in its configuration specify: +- name of the list where list items are stored, eg. **FAQ** + + +## Features + +This project contains sample client-side web part built on the SharePoint Framework illustrating how to show list data in Accordion format using React framework. + +This sample illustrates the following concepts on top of the SharePoint Framework: + +- general + - performing SharePoint GET operation in React using inbuilt SP Http Client + - Using Fabric UI button component for pagination + - optimizing REST responses for performance using nometadata option of JSON light + - using PnP Webpart title control of @pnp/spfx-controls-react library + - showing SharePoint list data in Accordion format using React Accessible Accordion plugin + - searching in the fetched data by making use of Search Box from Office Fabric UI + + + + +### Build options + +gulp clean - TODO +gulp test - TODO +gulp serve - TODO +gulp bundle - TODO +gulp package-solution - TODO diff --git a/samples/react-accordion/assets/previewAccordion.PNG b/samples/react-accordion/assets/previewAccordion.PNG new file mode 100644 index 000000000..c37f9040f Binary files /dev/null and b/samples/react-accordion/assets/previewAccordion.PNG differ diff --git a/samples/react-accordion/config/config.json b/samples/react-accordion/config/config.json new file mode 100644 index 000000000..55814e677 --- /dev/null +++ b/samples/react-accordion/config/config.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json", + "version": "2.0", + "bundles": { + "react-accordion-web-part": { + "components": [ + { + "entrypoint": "./lib/webparts/reactAccordion/ReactAccordionWebPart.js", + "manifest": "./src/webparts/reactAccordion/ReactAccordionWebPart.manifest.json" + } + ] + } + }, + "externals": {}, + "localizedResources": { + "ReactAccordionWebPartStrings": "lib/webparts/reactAccordion/loc/{locale}.js", + "ControlStrings": "node_modules/@pnp/spfx-controls-react/lib/loc/{locale}.js" + } +} \ No newline at end of file diff --git a/samples/react-accordion/config/copy-assets.json b/samples/react-accordion/config/copy-assets.json new file mode 100644 index 000000000..3771fd04a --- /dev/null +++ b/samples/react-accordion/config/copy-assets.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json", + "deployCdnPath": "temp/deploy" +} diff --git a/samples/react-accordion/config/deploy-azure-storage.json b/samples/react-accordion/config/deploy-azure-storage.json new file mode 100644 index 000000000..0ceafe571 --- /dev/null +++ b/samples/react-accordion/config/deploy-azure-storage.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json", + "workingDir": "./temp/deploy/", + "account": "", + "container": "react-accordion", + "accessKey": "" +} \ No newline at end of file diff --git a/samples/react-accordion/config/package-solution.json b/samples/react-accordion/config/package-solution.json new file mode 100644 index 000000000..fd5d55eab --- /dev/null +++ b/samples/react-accordion/config/package-solution.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json", + "solution": { + "name": "react-accordion-client-side-solution", + "id": "6d6cf05b-cfe5-4d12-af19-19ec3aedcaf9", + "version": "1.0.0.0", + "includeClientSideAssets": true + }, + "paths": { + "zippedPackage": "solution/react-accordion.sppkg" + } +} diff --git a/samples/react-accordion/config/serve.json b/samples/react-accordion/config/serve.json new file mode 100644 index 000000000..090cfe9e6 --- /dev/null +++ b/samples/react-accordion/config/serve.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json", + "port": 4321, + "https": true, + "initialPage": "https://localhost:5432/workbench", + "api": { + "port": 5432, + "entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/" + } +} diff --git a/samples/react-accordion/config/tslint.json b/samples/react-accordion/config/tslint.json new file mode 100644 index 000000000..b17194770 --- /dev/null +++ b/samples/react-accordion/config/tslint.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/core-build/tslint.schema.json", + // Display errors as warnings + "displayAsWarning": true, + // The TSLint task may have been configured with several custom lint rules + // before this config file is read (for example lint rules from the tslint-microsoft-contrib + // project). If true, this flag will deactivate any of these rules. + "removeExistingRules": true, + // When true, the TSLint task is configured with some default TSLint "rules.": + "useDefaultConfigAsBase": false, + // Since removeExistingRules=true and useDefaultConfigAsBase=false, there will be no lint rules + // which are active, other than the list of rules below. + "lintConfig": { + // Opt-in to Lint rules which help to eliminate bugs in JavaScript + "rules": { + "class-name": false, + "export-name": false, + "forin": false, + "label-position": false, + "member-access": true, + "no-arg": false, + "no-console": false, + "no-construct": false, + "no-duplicate-case": true, + "no-duplicate-variable": true, + "no-eval": false, + "no-function-expression": true, + "no-internal-module": true, + "no-shadowed-variable": true, + "no-switch-case-fall-through": true, + "no-unnecessary-semicolons": true, + "no-unused-expression": true, + "no-use-before-declare": true, + "no-with-statement": true, + "semicolon": true, + "trailing-comma": false, + "typedef": false, + "typedef-whitespace": false, + "use-named-parameter": true, + "valid-typeof": true, + "variable-name": false, + "whitespace": false + } + } +} \ No newline at end of file diff --git a/samples/react-accordion/config/write-manifests.json b/samples/react-accordion/config/write-manifests.json new file mode 100644 index 000000000..bad352605 --- /dev/null +++ b/samples/react-accordion/config/write-manifests.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json", + "cdnBasePath": "" +} \ No newline at end of file diff --git a/samples/react-accordion/gulpfile.js b/samples/react-accordion/gulpfile.js new file mode 100644 index 000000000..7958fd5d8 --- /dev/null +++ b/samples/react-accordion/gulpfile.js @@ -0,0 +1,7 @@ +'use strict'; + +const gulp = require('gulp'); +const build = require('@microsoft/sp-build-web'); +build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`); + +build.initialize(gulp); diff --git a/samples/react-accordion/package.json b/samples/react-accordion/package.json new file mode 100644 index 000000000..40a0971a4 --- /dev/null +++ b/samples/react-accordion/package.json @@ -0,0 +1,36 @@ +{ + "name": "react-accordion", + "version": "0.0.1", + "private": true, + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "build": "gulp bundle", + "clean": "gulp clean", + "test": "gulp test" + }, + "dependencies": { + "@microsoft/sp-core-library": "1.5.1", + "@microsoft/sp-lodash-subset": "1.5.1", + "@microsoft/sp-office-ui-fabric-core": "1.5.1", + "@microsoft/sp-webpart-base": "1.5.1", + "@pnp/spfx-controls-react": "1.7.0", + "@types/es6-promise": "0.0.33", + "@types/react": "15.6.6", + "@types/react-dom": "15.5.6", + "@types/webpack-env": "1.13.1", + "react": "15.6.2", + "react-accessible-accordion": "1.0.2", + "react-dom": "15.6.2" + }, + "devDependencies": { + "@microsoft/sp-build-web": "1.5.1", + "@microsoft/sp-module-interfaces": "1.5.1", + "@microsoft/sp-webpart-workbench": "1.5.1", + "gulp": "~3.9.1", + "@types/chai": "3.4.34", + "@types/mocha": "2.2.38", + "ajv": "~5.2.2" + } +} diff --git a/samples/react-accordion/src/webparts/reactAccordion/ReactAccordionWebPart.manifest.json b/samples/react-accordion/src/webparts/reactAccordion/ReactAccordionWebPart.manifest.json new file mode 100644 index 000000000..1de3bd6f8 --- /dev/null +++ b/samples/react-accordion/src/webparts/reactAccordion/ReactAccordionWebPart.manifest.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json", + "id": "97a28c00-64ee-4ec7-b373-723e39069a96", + "alias": "ReactAccordionWebPart", + "componentType": "WebPart", + // The "*" signifies that the version should be taken from the package.json + "version": "*", + "manifestVersion": 2, + // If true, the component can only be installed on sites where Custom Script is allowed. + // Components that allow authors to embed arbitrary script code should set this to true. + // https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f + "requiresCustomScript": false, + "preconfiguredEntries": [ + { + "groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other + "group": { + "default": "Other" + }, + "title": { + "default": "React Accordion App" + }, + "description": { + "default": "SPFx webpart which shows SharePoint list data in Accordion format" + }, + "officeFabricIconFontName": "Questionnaire", + "properties": { + "description": "SPFx webpart which shows SharePoint list data in Accordion format", + "listName": "FAQ", + "maxItemsPerPage": 5 + } + } + ] +} \ No newline at end of file diff --git a/samples/react-accordion/src/webparts/reactAccordion/ReactAccordionWebPart.ts b/samples/react-accordion/src/webparts/reactAccordion/ReactAccordionWebPart.ts new file mode 100644 index 000000000..464e528c1 --- /dev/null +++ b/samples/react-accordion/src/webparts/reactAccordion/ReactAccordionWebPart.ts @@ -0,0 +1,83 @@ +import * as React from 'react'; +import * as ReactDom from 'react-dom'; +import { Version, DisplayMode } from '@microsoft/sp-core-library'; +import { + BaseClientSideWebPart, + IPropertyPaneConfiguration, + PropertyPaneTextField, + PropertyPaneSlider +} from '@microsoft/sp-webpart-base'; + +import * as strings from 'ReactAccordionWebPartStrings'; +import ReactAccordion from './components/ReactAccordion'; +import { IReactAccordionProps } from './components/IReactAccordionProps'; + +export interface IReactAccordionWebPartProps { + listName: string; + choice: string; + title: string; + displayMode: DisplayMode; + maxItemsPerPage: number; + updateProperty: (value: string) => void; +} + +export default class ReactAccordionWebPart extends BaseClientSideWebPart { + + public render(): void { + const element: React.ReactElement = React.createElement( + ReactAccordion, + { + listName: this.properties.listName, + spHttpClient: this.context.spHttpClient, + siteUrl: this.context.pageContext.web.absoluteUrl, + title: this.properties.title, + displayMode: this.displayMode, + maxItemsPerPage: this.properties.maxItemsPerPage, + updateProperty: (value: string) => { + this.properties.title = value; + } + } + ); + + ReactDom.render(element, this.domElement); + } + + protected onDispose(): void { + ReactDom.unmountComponentAtNode(this.domElement); + } + + protected get dataVersion(): Version { + return Version.parse('1.0'); + } + + protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { + return { + pages: [ + { + header: { + description: strings.PropertyPaneDescription + }, + groups: [ + { + groupName: strings.BasicGroupName, + groupFields: [ + PropertyPaneTextField('listName', { + label: strings.ListNameLabel + }), + PropertyPaneSlider('maxItemsPerPage', { + label: strings.MaxItemsPerPageLabel, + ariaLabel: strings.MaxItemsPerPageLabel, + min: 3, + max: 20, + value: 5, + showValue: true, + step: 1 + }), + ] + } + ] + } + ] + }; + } +} diff --git a/samples/react-accordion/src/webparts/reactAccordion/components/IReactAccordionProps.ts b/samples/react-accordion/src/webparts/reactAccordion/components/IReactAccordionProps.ts new file mode 100644 index 000000000..aa64ca72b --- /dev/null +++ b/samples/react-accordion/src/webparts/reactAccordion/components/IReactAccordionProps.ts @@ -0,0 +1,12 @@ +import { SPHttpClient } from '@microsoft/sp-http'; +import { DisplayMode } from '@microsoft/sp-core-library'; + +export interface IReactAccordionProps { + listName: string; + spHttpClient: SPHttpClient; + siteUrl: string; + title: string, + displayMode: DisplayMode, + maxItemsPerPage: number, + updateProperty: (value: string) => void; +} diff --git a/samples/react-accordion/src/webparts/reactAccordion/components/IReactAccordionState.ts b/samples/react-accordion/src/webparts/reactAccordion/components/IReactAccordionState.ts new file mode 100644 index 000000000..9ad6c65af --- /dev/null +++ b/samples/react-accordion/src/webparts/reactAccordion/components/IReactAccordionState.ts @@ -0,0 +1,9 @@ +import IAccordionListItem from '../models/IAccordionListItem'; + +export interface IReactAccordionState { + status: string; + items: IAccordionListItem[]; + listItems: IAccordionListItem[]; + isLoading: boolean; + loaderMessage: string; +} \ No newline at end of file diff --git a/samples/react-accordion/src/webparts/reactAccordion/components/ReactAccordion.module.scss b/samples/react-accordion/src/webparts/reactAccordion/components/ReactAccordion.module.scss new file mode 100644 index 000000000..2526d496f --- /dev/null +++ b/samples/react-accordion/src/webparts/reactAccordion/components/ReactAccordion.module.scss @@ -0,0 +1,73 @@ +@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss'; + +.reactAccordion { + .container { + max-width: 100%; + margin: 0px auto; + } + + .row { + @include ms-Grid-row; + @include ms-fontColor-white; + background-color: $ms-color-themeDark; + padding: 20px; + } + + .column { + @include ms-Grid-col; + @include ms-lg10; + @include ms-xl8; + @include ms-xlPush2; + @include ms-lgPush1; + } + + .title { + @include ms-font-xl; + @include ms-fontColor-white; + } + + .subTitle { + @include ms-font-l; + @include ms-fontColor-white; + } + + .description { + @include ms-font-l; + @include ms-fontColor-white; + } + + .button { + // Our button + text-decoration: none; + height: 32px; + + // Primary Button + min-width: 80px; + background-color: $ms-color-themePrimary; + border-color: $ms-color-themePrimary; + color: $ms-color-white; + + // Basic Button + outline: transparent; + position: relative; + font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif; + -webkit-font-smoothing: antialiased; + font-size: $ms-font-size-m; + font-weight: $ms-font-weight-regular; + border-width: 0; + text-align: center; + cursor: pointer; + display: inline-block; + padding: 0 16px; + + .label { + font-weight: $ms-font-weight-semibold; + font-size: $ms-font-size-m; + height: 32px; + line-height: 32px; + margin: 0 4px; + vertical-align: top; + display: inline-block; + } + } +} \ No newline at end of file diff --git a/samples/react-accordion/src/webparts/reactAccordion/components/ReactAccordion.tsx b/samples/react-accordion/src/webparts/reactAccordion/components/ReactAccordion.tsx new file mode 100644 index 000000000..95671e6bb --- /dev/null +++ b/samples/react-accordion/src/webparts/reactAccordion/components/ReactAccordion.tsx @@ -0,0 +1,180 @@ +import * as React from 'react'; +import styles from './ReactAccordion.module.scss'; +import { IReactAccordionProps } from './IReactAccordionProps'; +import { SPHttpClient, SPHttpClientResponse, ISPHttpClientOptions } from '@microsoft/sp-http'; +import { PrimaryButton } from 'office-ui-fabric-react/lib/Button'; +import { SearchBox } from 'office-ui-fabric-react/lib/SearchBox'; +import { + Spinner, + SpinnerSize +} from 'office-ui-fabric-react/lib/Spinner'; +import { + Accordion, + AccordionItem, + AccordionItemTitle, + AccordionItemBody, +} from 'react-accessible-accordion'; +import 'react-accessible-accordion/dist/react-accessible-accordion.css'; +import { IReactAccordionState } from "./IReactAccordionState"; +import IAccordionListItem from "../models/IAccordionListItem"; +import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle"; +import './accordion.css'; + +export default class ReactAccordion extends React.Component { + + constructor(props: IReactAccordionProps, state: IReactAccordionState) { + super(props); + this.state = { + status: this.listNotConfigured(this.props) ? 'Please configure list in Web Part properties' : 'Ready', + items: [], + listItems: [], + isLoading: false, + loaderMessage: '' + }; + + if (!this.listNotConfigured(this.props)) { + this.readItems(); + } + + this.searchTextChange = this.searchTextChange.bind(this); + + } + + private listNotConfigured(props: IReactAccordionProps): boolean { + return props.listName === undefined || + props.listName === null || + props.listName.length === 0; + } + + private searchTextChange(event) { + + if (event === undefined || + event === null || + event === "") { + let listItemsCollection = [...this.state.listItems]; + this.setState({ items: listItemsCollection.splice(0, this.props.maxItemsPerPage) }); + } + else { + var updatedList = [...this.state.listItems]; + updatedList = updatedList.filter((item) => { + return item.Title.toLowerCase().search( + event.toLowerCase()) !== -1 || item.Description.toLowerCase().search( + event.toLowerCase()) !== -1; + }); + this.setState({ items: updatedList }); + } + } + + private readItems(): void { + let restAPI = this.props.siteUrl + `/_api/web/Lists/GetByTitle('${this.props.listName}')/items?$select=Title,Description`; + + this.props.spHttpClient.get(restAPI, SPHttpClient.configurations.v1, { + headers: { + 'Accept': 'application/json;odata=nometadata', + 'odata-version': '' + } + }) + .then((response: SPHttpClientResponse): Promise<{ value: IAccordionListItem[] }> => { + return response.json(); + }) + .then((response: { value: IAccordionListItem[] }): void => { + + let listItemsCollection = [...response.value]; + + this.setState({ + status: "", + items: listItemsCollection.splice(0, this.props.maxItemsPerPage), + listItems: response.value, + isLoading: false, + loaderMessage: "" + }); + }, (error: any): void => { + this.setState({ + status: 'Loading all items failed with error: ' + error, + items: [], + isLoading: false, + loaderMessage: "" + }); + }); + + } + + public render(): React.ReactElement { + let displayLoader; + let faqTitle; + let { listItems } = this.state; + let pageCountDivisor: number = this.props.maxItemsPerPage; + let pageCount: number; + let pageButtons = []; + + let _pagedButtonClick = (pageNumber: number, listData: any) => { + let startIndex: number = (pageNumber - 1) * pageCountDivisor; + let listItemsCollection = [...listData]; + this.setState({ items: listItemsCollection.splice(startIndex, pageCountDivisor) }); + }; + + const items: JSX.Element[] = this.state.items.map((item: IAccordionListItem, i: number): JSX.Element => { + return ( + + + {item.Title} + + + + + + + + ); + }); + + if (this.state.isLoading) { + displayLoader = ( + + + + ); + } + else { + displayLoader = (null); + } + + if (this.state.listItems.length > 0) { + pageCount = Math.ceil(this.state.listItems.length / pageCountDivisor); + } + for (let i = 0; i < pageCount; i++) { + pageButtons.push( { _pagedButtonClick(i + 1, listItems); }}> {i + 1} ); + } + return ( + + + {faqTitle} + {displayLoader} + + + + + + + + + {this.state.status} + + {items} + + + + + + {pageButtons} + + + + + ); + } +} diff --git a/samples/react-accordion/src/webparts/reactAccordion/components/accordion.css b/samples/react-accordion/src/webparts/reactAccordion/components/accordion.css new file mode 100644 index 000000000..5e8a0c4af --- /dev/null +++ b/samples/react-accordion/src/webparts/reactAccordion/components/accordion.css @@ -0,0 +1,109 @@ +.accordion__title > *:last-child, +.accordion__body > *:last-child { + margin-bottom: 0; +} + + .accordion__arrow { + display: inline-block; + position: relative; + width: 24px; + height: 12px; + right: 10px; + margin-top: -28px; + color: white !important; + + float: right; + } + + + .accordion__arrow::after, + .accordion__arrow::before { + display: block; + position: absolute; + width: 10px; + height: 2px; + background-color: currentColor; + content: ''; + } + + .accordion__arrow::before { + left: 4px; + transform: rotate(45deg); + } + + [aria-expanded="true"] .accordion__arrow::before { + transform: rotate(-45deg); + } + + .accordion__arrow::after { + right: 4px; + transform: rotate(-45deg); + } + + [aria-expanded="true"] .accordion__arrow::after { + transform: rotate(45deg); + } + + .accordion__arrow::before, .accordion__arrow::after { + transition: transform .25s ease, -webkit-transform .25s ease; + } + +.accordion__item { +background-color: "[theme: themePrimary, default: #0078d7]"; +margin-bottom: 10px; +} +.accordion { + padding-top: 10px; + +} +.accordion__item .accordion__title { +padding: 5px 20px;; +background-color: "[theme: themePrimary, default: #0078d7]"; +} +.accordion__item .accordion__title h3 { + font-weight: normal; + width: 88%; +} +.accordion__item .accordion__body { +padding: 15px 20px; +background-color: "[theme: themeLighterAlt, default: #0078d7]"; +color: "[theme: bodyText, default: #333333]"; +} + +.accordion__item .accordion__body a { + color: "[theme: themePrimary, default: #0078d7]" !important; +} +.accordion__item .accordion__body p { +color: "[theme: bodyText, default: #333333]"; +} + + +/* -------------------------------------------------- */ +/* ---------------- Animation part ------------------ */ +/* -------------------------------------------------- */ + +@keyframes move-down { +0% { transform: translateY(0); } +10% { transform: translateY(0); } +20% { transform: translateY(5px); } +30% { transform: translateY(0); } +100% { transform: translateY(0); } +} + +@keyframes move-up { +0% { transform: translateY(0); } +10% { transform: translateY(0); } +20% { transform: translateY(-5px); } +30% { transform: translateY(0); } +100% { transform: translateY(0); } +} + +.accordion__title--animated:hover .accordion__arrow { +animation-name: move-down; +animation-duration: 1.5s; +} + +.accordion__title--animated[aria-expanded="true"]:hover .accordion__arrow { +animation-name: move-up; +animation-duration: 1.5s; +} diff --git a/samples/react-accordion/src/webparts/reactAccordion/loc/en-us.js b/samples/react-accordion/src/webparts/reactAccordion/loc/en-us.js new file mode 100644 index 000000000..f6c53fd3d --- /dev/null +++ b/samples/react-accordion/src/webparts/reactAccordion/loc/en-us.js @@ -0,0 +1,8 @@ +define([], function () { + return { + "PropertyPaneDescription": "Description", + "BasicGroupName": "Group Name", + "ListNameLabel": "List Name", + "MaxItemsPerPageLabel": "Max number of items per page" + } +}); \ No newline at end of file diff --git a/samples/react-accordion/src/webparts/reactAccordion/loc/mystrings.d.ts b/samples/react-accordion/src/webparts/reactAccordion/loc/mystrings.d.ts new file mode 100644 index 000000000..fdaea494e --- /dev/null +++ b/samples/react-accordion/src/webparts/reactAccordion/loc/mystrings.d.ts @@ -0,0 +1,11 @@ +declare interface IReactAccordionWebPartStrings { + PropertyPaneDescription: string; + BasicGroupName: string; + ListNameLabel: string; + MaxItemsPerPageLabel: string +} + +declare module 'ReactAccordionWebPartStrings' { + const strings: IReactAccordionWebPartStrings; + export = strings; +} diff --git a/samples/react-accordion/src/webparts/reactAccordion/models/IAccordionListItem.ts b/samples/react-accordion/src/webparts/reactAccordion/models/IAccordionListItem.ts new file mode 100644 index 000000000..20153f2a3 --- /dev/null +++ b/samples/react-accordion/src/webparts/reactAccordion/models/IAccordionListItem.ts @@ -0,0 +1,6 @@ +interface IAccordionListItem { + Id: number; + Title: string; + Description: string; +} +export default IAccordionListItem; \ No newline at end of file diff --git a/samples/react-accordion/tsconfig.json b/samples/react-accordion/tsconfig.json new file mode 100644 index 000000000..0c292a892 --- /dev/null +++ b/samples/react-accordion/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "es5", + "forceConsistentCasingInFileNames": true, + "module": "esnext", + "moduleResolution": "node", + "jsx": "react", + "declaration": true, + "sourceMap": true, + "experimentalDecorators": true, + "skipLibCheck": true, + "typeRoots": [ + "./node_modules/@types", + "./node_modules/@microsoft" + ], + "types": [ + "es6-promise", + "webpack-env" + ], + "lib": [ + "es5", + "dom", + "es2015.collection" + ] + } +} diff --git a/samples/react-accordion/tslint.json b/samples/react-accordion/tslint.json new file mode 100644 index 000000000..37c5004a3 --- /dev/null +++ b/samples/react-accordion/tslint.json @@ -0,0 +1,3 @@ +{ + "rulesDirectory": "./config" +} \ No newline at end of file