SPFx React Accordion sample (#599)
* React Accordion Webpart added * Updated to imgur image url
This commit is contained in:
parent
53d14f2898
commit
4a47af2d14
|
@ -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
|
|
@ -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
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-accordion" />
|
||||||
|
|
||||||
|
### Build options
|
||||||
|
|
||||||
|
gulp clean - TODO
|
||||||
|
gulp test - TODO
|
||||||
|
gulp serve - TODO
|
||||||
|
gulp bundle - TODO
|
||||||
|
gulp package-solution - TODO
|
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||||
|
"deployCdnPath": "temp/deploy"
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||||
|
"workingDir": "./temp/deploy/",
|
||||||
|
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||||
|
"container": "react-accordion",
|
||||||
|
"accessKey": "<!-- ACCESS KEY -->"
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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/"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||||
|
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||||
|
}
|
|
@ -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);
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -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<IReactAccordionWebPartProps> {
|
||||||
|
|
||||||
|
public render(): void {
|
||||||
|
const element: React.ReactElement<IReactAccordionProps> = 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
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
import IAccordionListItem from '../models/IAccordionListItem';
|
||||||
|
|
||||||
|
export interface IReactAccordionState {
|
||||||
|
status: string;
|
||||||
|
items: IAccordionListItem[];
|
||||||
|
listItems: IAccordionListItem[];
|
||||||
|
isLoading: boolean;
|
||||||
|
loaderMessage: string;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<IReactAccordionProps, IReactAccordionState> {
|
||||||
|
|
||||||
|
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<IReactAccordionProps> {
|
||||||
|
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 (
|
||||||
|
<AccordionItem>
|
||||||
|
<AccordionItemTitle className="accordion__title">
|
||||||
|
<h3 className="u-position-relative ms-fontColor-white">{item.Title}</h3>
|
||||||
|
<div className="accordion__arrow ms-fontColor-white" role="presentation" />
|
||||||
|
</AccordionItemTitle>
|
||||||
|
<AccordionItemBody className="accordion__body">
|
||||||
|
<div className="" dangerouslySetInnerHTML={{ __html: item.Description }}>
|
||||||
|
</div>
|
||||||
|
</AccordionItemBody>
|
||||||
|
</AccordionItem>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.state.isLoading) {
|
||||||
|
displayLoader = (<div className={`ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}`}>
|
||||||
|
<div className='ms-Grid-col ms-u-lg12'>
|
||||||
|
<Spinner size={SpinnerSize.large} label={this.state.loaderMessage} />
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
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(<PrimaryButton onClick={() => { _pagedButtonClick(i + 1, listItems); }}> {i + 1} </PrimaryButton>);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={styles.reactAccordion}>
|
||||||
|
<div className={styles.container}>
|
||||||
|
{faqTitle}
|
||||||
|
{displayLoader}
|
||||||
|
<WebPartTitle displayMode={this.props.displayMode}
|
||||||
|
title={this.props.title}
|
||||||
|
updateProperty={this.props.updateProperty} />
|
||||||
|
<div className='ms-Grid-row'>
|
||||||
|
<div className='ms-Grid-col ms-u-lg12'>
|
||||||
|
<SearchBox
|
||||||
|
onChange={this.searchTextChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={`ms-Grid-row`}>
|
||||||
|
<div className='ms-Grid-col ms-u-lg12'>
|
||||||
|
{this.state.status}
|
||||||
|
<Accordion accordion={false}>
|
||||||
|
{items}
|
||||||
|
</Accordion>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='ms-Grid-row'>
|
||||||
|
<div className='ms-Grid-col ms-u-lg12'>
|
||||||
|
{pageButtons}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
define([], function () {
|
||||||
|
return {
|
||||||
|
"PropertyPaneDescription": "Description",
|
||||||
|
"BasicGroupName": "Group Name",
|
||||||
|
"ListNameLabel": "List Name",
|
||||||
|
"MaxItemsPerPageLabel": "Max number of items per page"
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,11 @@
|
||||||
|
declare interface IReactAccordionWebPartStrings {
|
||||||
|
PropertyPaneDescription: string;
|
||||||
|
BasicGroupName: string;
|
||||||
|
ListNameLabel: string;
|
||||||
|
MaxItemsPerPageLabel: string
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'ReactAccordionWebPartStrings' {
|
||||||
|
const strings: IReactAccordionWebPartStrings;
|
||||||
|
export = strings;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
interface IAccordionListItem {
|
||||||
|
Id: number;
|
||||||
|
Title: string;
|
||||||
|
Description: string;
|
||||||
|
}
|
||||||
|
export default IAccordionListItem;
|
|
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"rulesDirectory": "./config"
|
||||||
|
}
|
Loading…
Reference in New Issue